Avenla_KlarnaCheckout - Version 1.0.3

Version Notes

Klarna Checkout module

Download this release

Release Info

Developer Avenla Oy
Extension Avenla_KlarnaCheckout
Version 1.0.3
Comparing to
See all releases


Version 1.0.3

Files changed (102) hide show
  1. app/code/community/Avenla/KlarnaCheckout/Block/Adminhtml/System/Config/Fieldset/Info.php +64 -0
  2. app/code/community/Avenla/KlarnaCheckout/Block/KCO.php +25 -0
  3. app/code/community/Avenla/KlarnaCheckout/Block/KCO/Confirmation.php +128 -0
  4. app/code/community/Avenla/KlarnaCheckout/Block/KCO/Info.php +43 -0
  5. app/code/community/Avenla/KlarnaCheckout/Helper/Api.php +100 -0
  6. app/code/community/Avenla/KlarnaCheckout/Helper/Data.php +149 -0
  7. app/code/community/Avenla/KlarnaCheckout/Model/Api.php +411 -0
  8. app/code/community/Avenla/KlarnaCheckout/Model/Config.php +192 -0
  9. app/code/community/Avenla/KlarnaCheckout/Model/KCO.php +239 -0
  10. app/code/community/Avenla/KlarnaCheckout/Model/Observer.php +155 -0
  11. app/code/community/Avenla/KlarnaCheckout/Model/Order.php +264 -0
  12. app/code/community/Avenla/KlarnaCheckout/Model/Source/Countries.php +46 -0
  13. app/code/community/Avenla/KlarnaCheckout/Model/Source/Orderlocale.php +45 -0
  14. app/code/community/Avenla/KlarnaCheckout/Model/Source/Servermode.php +37 -0
  15. app/code/community/Avenla/KlarnaCheckout/Model/Source/Taxclass.php +30 -0
  16. app/code/community/Avenla/KlarnaCheckout/controllers/KCOController.php +282 -0
  17. app/code/community/Avenla/KlarnaCheckout/etc/config.xml +172 -0
  18. app/code/community/Avenla/KlarnaCheckout/etc/system.xml +161 -0
  19. app/design/adminhtml/default/default/template/KCO/info.phtml +64 -0
  20. app/design/adminhtml/default/default/template/KCO/system/config/fieldset/info.phtml +48 -0
  21. app/design/frontend/base/default/layout/KCO.xml +68 -0
  22. app/design/frontend/base/default/template/KCO/KCO.phtml +76 -0
  23. app/design/frontend/base/default/template/KCO/cart.phtml +206 -0
  24. app/design/frontend/base/default/template/KCO/cart/crosssell.phtml +48 -0
  25. app/design/frontend/base/default/template/KCO/cart/shipping.phtml +139 -0
  26. app/design/frontend/base/default/template/KCO/info.phtml +24 -0
  27. app/design/frontend/base/default/template/KCO/link.phtml +29 -0
  28. app/design/frontend/base/default/template/KCO/onepage/link.phtml +25 -0
  29. app/etc/modules/Avenla_KlarnaCheckout.xml +11 -0
  30. app/locale/fi_FI/Avenla_KlarnaCheckout.csv +44 -0
  31. lib/Klarna/Country.php +760 -0
  32. lib/Klarna/Currency.php +104 -0
  33. lib/Klarna/Encoding.php +250 -0
  34. lib/Klarna/Exceptions.php +735 -0
  35. lib/Klarna/Flags.php +319 -0
  36. lib/Klarna/Klarna.php +4611 -0
  37. lib/Klarna/Language.php +300 -0
  38. lib/Klarna/checkout/checkouthtml.intf.php +97 -0
  39. lib/Klarna/checkout/threatmetrix.class.php +136 -0
  40. lib/Klarna/examples/activate.php +66 -0
  41. lib/Klarna/examples/activateInvoice.php +53 -0
  42. lib/Klarna/examples/activatePart.php +67 -0
  43. lib/Klarna/examples/activateReservation.php +240 -0
  44. lib/Klarna/examples/addTransaction.php +159 -0
  45. lib/Klarna/examples/calc_monthly_cost.php +59 -0
  46. lib/Klarna/examples/cancelReservation.php +55 -0
  47. lib/Klarna/examples/changeReservation.php +60 -0
  48. lib/Klarna/examples/checkOrderStatus.php +64 -0
  49. lib/Klarna/examples/creditInvoice.php +59 -0
  50. lib/Klarna/examples/creditPart.php +80 -0
  51. lib/Klarna/examples/deleteInvoice.php +49 -0
  52. lib/Klarna/examples/emailInvoice.php +56 -0
  53. lib/Klarna/examples/fetchPClasses.php +54 -0
  54. lib/Klarna/examples/getAddresses.php +86 -0
  55. lib/Klarna/examples/getPClasses.php +63 -0
  56. lib/Klarna/examples/invoiceAddress.php +80 -0
  57. lib/Klarna/examples/invoiceAmount.php +56 -0
  58. lib/Klarna/examples/invoicePartAmount.php +70 -0
  59. lib/Klarna/examples/reserveAmount.php +170 -0
  60. lib/Klarna/examples/reserveOCR.php +59 -0
  61. lib/Klarna/examples/returnAmount.php +63 -0
  62. lib/Klarna/examples/sendInvoice.php +58 -0
  63. lib/Klarna/examples/splitReservation.php +66 -0
  64. lib/Klarna/examples/update.php +129 -0
  65. lib/Klarna/examples/updateChargeAmount.php +60 -0
  66. lib/Klarna/examples/updateGoodsQty.php +61 -0
  67. lib/Klarna/examples/updateOrderNo.php +59 -0
  68. lib/Klarna/klarnaaddr.php +609 -0
  69. lib/Klarna/klarnacalc.php +655 -0
  70. lib/Klarna/klarnaconfig.php +176 -0
  71. lib/Klarna/klarnapclass.php +573 -0
  72. lib/Klarna/pclasses/jsonstorage.class.php +150 -0
  73. lib/Klarna/pclasses/mysqlstorage.class.php +335 -0
  74. lib/Klarna/pclasses/sqlstorage.class.php +473 -0
  75. lib/Klarna/pclasses/storage.intf.php +229 -0
  76. lib/Klarna/pclasses/xmlstorage.class.php +280 -0
  77. lib/Klarna/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc +3776 -0
  78. lib/Klarna/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc +955 -0
  79. lib/Klarna/transport/xmlrpc-3.0.0.beta/lib/xmlrpcs.inc +1246 -0
  80. lib/KlarnaCheckout/Checkout.php +50 -0
  81. lib/KlarnaCheckout/Checkout/BasicConnector.php +320 -0
  82. lib/KlarnaCheckout/Checkout/ConnectionErrorException.php +43 -0
  83. lib/KlarnaCheckout/Checkout/Connector.php +58 -0
  84. lib/KlarnaCheckout/Checkout/ConnectorException.php +43 -0
  85. lib/KlarnaCheckout/Checkout/ConnectorInterface.php +64 -0
  86. lib/KlarnaCheckout/Checkout/Digest.php +54 -0
  87. lib/KlarnaCheckout/Checkout/Exception.php +44 -0
  88. lib/KlarnaCheckout/Checkout/HTTP/CURLFactory.php +52 -0
  89. lib/KlarnaCheckout/Checkout/HTTP/CURLHandle.php +105 -0
  90. lib/KlarnaCheckout/Checkout/HTTP/CURLHandleInterface.php +73 -0
  91. lib/KlarnaCheckout/Checkout/HTTP/CURLHeaders.php +95 -0
  92. lib/KlarnaCheckout/Checkout/HTTP/CURLTransport.php +182 -0
  93. lib/KlarnaCheckout/Checkout/HTTP/Request.php +185 -0
  94. lib/KlarnaCheckout/Checkout/HTTP/Response.php +142 -0
  95. lib/KlarnaCheckout/Checkout/HTTP/Transport.php +54 -0
  96. lib/KlarnaCheckout/Checkout/HTTP/TransportInterface.php +83 -0
  97. lib/KlarnaCheckout/Checkout/Order.php +259 -0
  98. lib/KlarnaCheckout/Checkout/ResourceInterface.php +81 -0
  99. lib/KlarnaCheckout/Checkout/UserAgent.php +109 -0
  100. package.xml +21 -0
  101. skin/frontend/base/default/KCO/dropdown.png +0 -0
  102. skin/frontend/base/default/KCO/kco.css +134 -0
app/code/community/Avenla/KlarnaCheckout/Block/Adminhtml/System/Config/Fieldset/Info.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+ class Avenla_KlarnaCheckout_Block_Adminhtml_System_Config_Fieldset_Info extends Mage_Adminhtml_Block_Abstract
22
+ implements Varien_Data_Form_Element_Renderer_Interface
23
+ {
24
+ protected $_template = 'KCO/system/config/fieldset/info.phtml';
25
+ private $helper;
26
+
27
+ /**
28
+ * Render fieldset html
29
+ *
30
+ * @param Varien_Data_Form_Element_Abstract Element
31
+ * @return string
32
+ */
33
+ public function render(Varien_Data_Form_Element_Abstract $element)
34
+ {
35
+ $this->helper = Mage::helper("klarnaCheckout");
36
+ $this->assign('logoSrc', $this->helper->getLogoSrc());
37
+ $this->assign('apiLink', Mage::helper('klarnaCheckout/api')->getApiDocumentationUrl());
38
+
39
+ return $this->toHtml();
40
+ }
41
+
42
+ /**
43
+ * Check store configuration
44
+ *
45
+ * @return array
46
+ */
47
+ public function getAlerts(){
48
+ $alerts = array();
49
+
50
+ if(!$this->helper->getConnectionStatus())
51
+ $alerts[] = "Connection to Klarna failed, please check your eid/shared secret and store settings.";
52
+
53
+ if(Mage::getStoreConfig('tax/calculation/discount_tax') != 1)
54
+ $alerts[] = "Discount is applied before taxes, this may cause different price on Klarna Checkout. Please check store tax configation.";
55
+
56
+ if(Mage::getStoreConfig('tax/calculation/price_includes_tax') != 1)
57
+ $alerts[] = "Catalog prices are set excluding tax, this may result in different prices in Checkout.";
58
+
59
+ if(!Mage::getModel('klarnaCheckout/config')->getLicenseAgreement())
60
+ $alerts[] = "By accepting the license agreement and filling in your contact information you can use Klarna Checkout module for free.";
61
+
62
+ return $alerts;
63
+ }
64
+ }
app/code/community/Avenla/KlarnaCheckout/Block/KCO.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+
22
+ class Avenla_KlarnaCheckout_Block_KCO extends Mage_Core_Block_Template
23
+ {
24
+
25
+ }
app/code/community/Avenla/KlarnaCheckout/Block/KCO/Confirmation.php ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+ class Avenla_KlarnaCheckout_Block_KCO_Confirmation extends Mage_Core_Block_Template
22
+ {
23
+
24
+ protected function _toHtml()
25
+ {
26
+ return $this->getKcoFrame();
27
+ }
28
+
29
+ /**
30
+ * Return Klarna Checkout confirmation page
31
+ *
32
+ * @return string
33
+ */
34
+ private function getKcoFrame()
35
+ {
36
+ $order = Mage::getModel("klarnaCheckout/order")->getOrder(null, $this->getCheckoutID());
37
+ $order->fetch();
38
+ $result = "";
39
+
40
+ if(Mage::getModel('klarnaCheckout/config')->getGoogleAnalyticsNo() !== false && isset($_SESSION['klarna_checkout']))
41
+ $result .= $this->getGoogleCode($order);
42
+
43
+ $result .= $order['gui']['snippet'];
44
+
45
+ $link_to_store = '<div class="buttons-set"><button type="button" class="button"
46
+ title="'. $this->__('Continue Shopping') .'" onclick="window.location=\''. $this->getUrl() .'\'">
47
+ <span><span>'. $this->__('Continue Shopping') .'</span></span></button></div>';
48
+
49
+ $result .= $link_to_store;
50
+
51
+ unset($_SESSION['klarna_checkout']);
52
+
53
+ return $result;
54
+ }
55
+
56
+ /**
57
+ * Get Google Analytics Ecommerce tracking code
58
+ *
59
+ * @param Klarna_Checkout_Order $ko
60
+ * @return string
61
+ */
62
+ private function getGoogleCode($ko)
63
+ {
64
+ if(count($ko['cart']['items']) < 1)
65
+ return;
66
+
67
+ foreach($ko['cart']['items'] as $p){
68
+ $shipping_fee = "";
69
+ if($p['type'] == 'shipping_fee')
70
+ $shipping_fee = $p['total_price_including_tax'];
71
+ }
72
+
73
+ $gc = '<script type="text/javascript">';
74
+ $gc .= "//<![CDATA[\n";
75
+ $gc .= 'var _gaq = _gaq || [];';
76
+ $gc .= '_gaq.push(["_setAccount", "' . Mage::getModel('klarnaCheckout/config')->getGoogleAnalyticsNo() . '"]);';
77
+ $gc .= '_gaq.push(["_trackPageview"]);';
78
+ $gc .= '_gaq.push(["_addTrans",';
79
+ $gc .= '"' . $ko['merchant_reference']['orderid1'] . '",';
80
+ $gc .= '"' . Mage::app()->getStore()->getName() . '",';
81
+ $gc .= '"' . $ko['cart']['total_price_including_tax'] / 100 . '",';
82
+ $gc .= '"' . $ko['cart']['total_tax_amount'] / 100 . '",';
83
+ $gc .= '"' . $shipping_fee / 100 . '",';
84
+ $gc .= '"' . $ko['billing_address']['city'] . '",';
85
+ $gc .= '"",' ;
86
+ $gc .= '"' . $ko['billing_address']['country'] . '"';
87
+ $gc .= ']);' . "\n";
88
+
89
+ foreach ($ko['cart']['items'] as $p){
90
+
91
+ if($p['type'] == 'shipping_fee')
92
+ continue;
93
+
94
+ $cat = "";
95
+ $product = Mage::getModel('catalog/product')->loadByAttribute('sku', $p['reference']);
96
+ if($product){
97
+ $categoryIds = Mage::getModel('catalog/product')
98
+ ->loadByAttribute('sku', $p['reference'])
99
+ ->getCategoryIds();
100
+
101
+ if(!empty($categoryIds))
102
+ $cat = Mage::getModel('catalog/category')->load(end($categoryIds))->getName();
103
+ }
104
+
105
+ $gc .= '_gaq.push(["_addItem",';
106
+ $gc .= '"' . $ko['merchant_reference']['orderid1'] . '",';
107
+ $gc .= '"' . $p['reference'] . '",';
108
+ $gc .= '"' . $p['name'] . '",';
109
+ $gc .= '"' . $cat . '",';
110
+ $gc .= '"' . $p['unit_price'] / 100 . '",';
111
+ $gc .= '"' . $p['quantity'] . '"';
112
+ $gc .= ']);' . "\n";
113
+
114
+ }
115
+
116
+ $gc .= '_gaq.push(["_set", "currencyCode", "EUR"]); ';
117
+ $gc .= '_gaq.push(["_trackTrans"]);';
118
+ $gc .= '(function() { ';
119
+ $gc .= 'var ga = document.createElement("script"); ga.type = "text/javascript"; ga.async = true; ';
120
+ $gc .= 'ga.src = ("https:" == document.location.protocol ? "https://ssl" : "http://www") + ".google-analytics.com/ga.js";';
121
+ $gc .= 'var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ga, s);';
122
+ $gc .= ' })();';
123
+ $gc .= '//]]>' . "\n";
124
+ $gc .= '</script>';
125
+
126
+ return $gc;
127
+ }
128
+ }
app/code/community/Avenla/KlarnaCheckout/Block/KCO/Info.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+ class Avenla_KlarnaCheckout_Block_KCO_Info extends Mage_Payment_Block_Info
22
+ {
23
+ protected function _toHtml()
24
+ {
25
+ $this->setTemplate('KCO/info.phtml');
26
+ $helper = Mage::helper("klarnaCheckout");
27
+ $payment = $this->getMethod();
28
+
29
+ $this->assign('info', $this->getInfo());
30
+ $this->assign('imgSrc', $helper->getLogoSrc());
31
+ $this->assign('guiUrl', $helper->getKlarnaMerchantsUrl());
32
+
33
+ if (count($this->getInfo()->getAdditionalInformation("klarna_order_invoice")) > 0){
34
+ $server = $this->getInfo()->getAdditionalInformation("klarna_server");
35
+ $this->assign('pdfUrl', $server . "/packslips/");
36
+ }
37
+
38
+ if (strlen($this->getInfo()->getAdditionalInformation("klarna_message")) > 0)
39
+ $this->assign('message', $this->getInfo()->getAdditionalInformation("klarna_message"));
40
+
41
+ return parent::_toHtml();
42
+ }
43
+ }
app/code/community/Avenla/KlarnaCheckout/Helper/Api.php ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+ class Avenla_KlarnaCheckout_Helper_Api extends Mage_Core_Helper_Abstract
22
+ {
23
+
24
+ /**
25
+ * Get default country from store config
26
+ *
27
+ * @return string
28
+ */
29
+ public function getCountry()
30
+ {
31
+ return Mage::getStoreConfig('general/country/default');
32
+ }
33
+
34
+ /**
35
+ * Get Klarna reservation number from order
36
+ *
37
+ * @param Mage_Sales_Model_Order $mo
38
+ * @return mixed
39
+ */
40
+ public function getReservationNumber($mo)
41
+ {
42
+ if($rno = $mo->getPayment()->getAdditionalInformation("klarna_order_reservation"))
43
+ return $rno;
44
+
45
+ return false;
46
+ }
47
+
48
+ /**
49
+ * Get Klarna invoice numbers from order
50
+ *
51
+ * @param Mage_Sales_Model_Order $mo
52
+ * @return array
53
+ */
54
+ public function getKlarnaInvoices($mo)
55
+ {
56
+ if($result = $mo->getPayment()->getAdditionalInformation("klarna_order_invoice"))
57
+ return $result;
58
+
59
+ return array();
60
+ }
61
+
62
+ /**
63
+ * Save Klarna invoice numbers to order
64
+ *
65
+ * @param Mage_Sales_Model_Order $mo
66
+ * @param array $klarnainvoices
67
+ * @return Mage_Sales_Model_Order
68
+ */
69
+ public function saveKlarnaInvoices($mo, $klarnainvoices)
70
+ {
71
+ $mo->getPayment()->setAdditionalInformation("klarna_order_invoice", $klarnainvoices);
72
+ return $mo;
73
+ }
74
+
75
+ /**
76
+ * Handle failed activation
77
+ *
78
+ * @param Mage_Sales_Model_Order $mo
79
+ * @param string $rno
80
+ * @param Exception $e
81
+ */
82
+ public function failedActivation($mo, $rno, $e)
83
+ {
84
+ $mo->addStatusHistoryComment(
85
+ $this->__('Failed to activate reservation %s', $rno) ."(" . $e->getMessage() . ")"
86
+ );
87
+ $mo->save();
88
+ Mage::unregister('kco_save');
89
+ Mage::logException($e);
90
+ }
91
+
92
+ /**
93
+ * Get Klarna API documentation URL
94
+ *
95
+ */
96
+ public function getApiDocumentationUrl()
97
+ {
98
+ return Avenla_KlarnaCheckout_Model_Config::KLARNA_DOC_URL;
99
+ }
100
+ }
app/code/community/Avenla/KlarnaCheckout/Helper/Data.php ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+ class Avenla_KlarnaCheckout_Helper_Data extends Mage_Core_Helper_Data
22
+ {
23
+
24
+ /**
25
+ * Get confirmation url
26
+ *
27
+ * @return string
28
+ */
29
+ public function getConfirmationUri()
30
+ {
31
+ return rtrim(Mage::getUrl('klarnaCheckout/KCO/confirmation?klarna_order={checkout.order.uri}'), "/");
32
+ }
33
+
34
+ /**
35
+ * Get push url
36
+ *
37
+ * @return string
38
+ */
39
+ public function getPushUri()
40
+ {
41
+ $storeId = Mage::app()->getStore()->getStoreId();
42
+ return rtrim(Mage::getUrl('klarnaCheckout/KCO/push?storeid='.$storeId.'&klarna_order={checkout.order.uri}'), "/");
43
+ }
44
+
45
+ /**
46
+ * Get url of checkout page
47
+ *
48
+ * @return string
49
+ */
50
+ public function getCheckoutUri()
51
+ {
52
+ return rtrim(Mage::helper('checkout/url')->getCheckoutUrl(), "/");
53
+ }
54
+
55
+ /**
56
+ * Get url of cart page
57
+ *
58
+ * @return string
59
+ */
60
+ public function getCartUri()
61
+ {
62
+ return Mage::getUrl('checkout/cart');
63
+ }
64
+
65
+ /**
66
+ * Get Klarna logo url
67
+ *
68
+ * @param int $width
69
+ * @return string
70
+ */
71
+ public function getLogoSrc($width = 88)
72
+ {
73
+ $eid = Mage::getSingleton('klarnaCheckout/KCO')->getConfig()->getKlarnaEid();
74
+ return "https://cdn.klarna.com/public/images/FI/logos/v1/basic/FI_basic_logo_std_blue-black.png?width=" . $width . "&eid=" . $eid;
75
+ }
76
+
77
+ /**
78
+ * Get link text
79
+ *
80
+ * @return string
81
+ */
82
+ public function getLinkText()
83
+ {
84
+ return Mage::getSingleton('klarnaCheckout/KCO')->getConfig()->getLinkText();
85
+ }
86
+
87
+ /**
88
+ * Get url to Klarna online GUI
89
+ *
90
+ * @return string
91
+ */
92
+ public function getKlarnaMerchantsUrl()
93
+ {
94
+ return Avenla_KlarnaCheckout_Model_Config::ONLINE_GUI_URL;
95
+ }
96
+
97
+ /**
98
+ * Send test query to Klarna to verify given merchant credentials
99
+ *
100
+ * @return bool
101
+ */
102
+ public function getConnectionStatus($country = null)
103
+ {
104
+ try{
105
+ $ko = Mage::getModel("klarnaCheckout/order")->dummyOrder($country);
106
+ if($ko == null)
107
+ return false;
108
+
109
+ $ko->fetch();
110
+ return true;
111
+ }
112
+ catch (Exception $e) {
113
+ return false;
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Check if Klarna order was made in current store
119
+ *
120
+ * @param Klarna_Checkout_Order $ko
121
+ * @return bool
122
+ */
123
+ public function isOrderFromCurrentStore($ko)
124
+ {
125
+ $uri = $ko['merchant']['push_uri'];
126
+ preg_match('/storeid=(.*?)&klarna_order/', $uri, $res);
127
+
128
+ if($res[1] == Mage::app()->getStore()->getStoreId())
129
+ return true;
130
+
131
+ return false;
132
+ }
133
+
134
+ /**
135
+ * Get order shipping tax rate
136
+ * @return float $taxRate
137
+ */
138
+ public function getShippingVatRate()
139
+ {
140
+ $taxRate = 0;
141
+ $taxHelper = Mage::helper('tax/data');
142
+ $taxClass = $taxHelper->getShippingTaxClass(Mage::app()->getStore());
143
+ $taxClasses = Mage::helper("core")->jsonDecode(Mage::helper("tax")->getAllRatesByProductClass());
144
+ if(isset($taxClasses["value_".$taxClass]))
145
+ $taxRate = $taxClasses["value_".$taxClass];
146
+
147
+ return $taxRate;
148
+ }
149
+ }
app/code/community/Avenla/KlarnaCheckout/Model/Api.php ADDED
@@ -0,0 +1,411 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+
22
+ require_once(Mage::getBaseDir('lib') . '/Klarna/Klarna.php');
23
+ require_once(Mage::getBaseDir('lib') . '/Klarna/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc');
24
+ require_once(Mage::getBaseDir('lib') . '/Klarna/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc');
25
+
26
+ class Avenla_KlarnaCheckout_Model_Api extends Mage_Core_Model_Abstract
27
+ {
28
+ protected $klarna;
29
+ private $helper;
30
+
31
+ public function __construct()
32
+ {
33
+ $this->helper = Mage::helper('klarnaCheckout/api');
34
+ $this->klarna = new Klarna();
35
+ $this->klarna->setCountry($this->helper->getCountry());
36
+ $config = Mage::getSingleton('klarnaCheckout/KCO')->getConfig();
37
+
38
+ try{
39
+ $this->klarna->config(
40
+ $config->getKlarnaEid(),
41
+ $config->getKlarnaSharedSecret(),
42
+ $this->klarna->getCountry(),
43
+ $this->klarna->getLanguage(),
44
+ $this->klarna->getCurrency(),
45
+ $config->isLive() ? Klarna::LIVE : Klarna::BETA
46
+ );
47
+ }
48
+ catch (Exception $e) {
49
+ Mage::logException($e);
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Activate Klarna reservation
55
+ *
56
+ * @param Magento_Sales_Order
57
+ * @param string $invoiceId|null
58
+ * @return bool
59
+ */
60
+ public function activateReservation($mo, $invoiceId = null)
61
+ {
62
+ Mage::register('kco_save', true);
63
+
64
+ if($invoiceId != null){
65
+ $result = $this->activateFromInvoice($mo, Mage::getModel('sales/order_invoice')->load($invoiceId));
66
+ }
67
+ else if(false !== $qtys = $this->checkIfPartial($mo)){
68
+ $result = $this->activatePartialReservation($mo, $qtys);
69
+ }
70
+ else{
71
+ $result = $this->activateFullReservation($mo);
72
+ }
73
+ Mage::unregister('kco_save');
74
+
75
+ return $result;
76
+ }
77
+
78
+ /**
79
+ * Check if activation is partial or full
80
+ *
81
+ * @param Magento_Sales_Order $mo
82
+ * @return mixed
83
+ */
84
+ private function checkIfPartial($mo)
85
+ {
86
+ $qtys = array();
87
+ $partial = false;
88
+
89
+ foreach ($mo->getAllItems() as $item)
90
+ {
91
+ if($item->getQtyShipped() > $item->getQtyInvoiced()){
92
+ $qtys[$item->getId()] = $item->getQtyShipped() - $item->getQtyInvoiced();
93
+ }
94
+
95
+ if($item->getQtyShipped() != $item->getQtyOrdered())
96
+ $partial = true;
97
+ }
98
+
99
+ if($partial)
100
+ return $qtys;
101
+
102
+ return false;
103
+ }
104
+
105
+ /**
106
+ * Do partial activation
107
+ *
108
+ * @param Magento_Sales_Order $mo
109
+ * @param array $qtys
110
+ * @return bool
111
+ */
112
+ public function activatePartialReservation($mo, $qtys)
113
+ {
114
+ if(!Mage::getSingleton('klarnaCheckout/KCO')->getConfig()->activatePartial())
115
+ return false;
116
+
117
+ foreach($qtys as $key => $qty){
118
+ $sku = Mage::getModel('sales/order_item')->load($key)->getSku();
119
+ $this->klarna->addArtNo($qty, $sku);
120
+ }
121
+
122
+ try{
123
+ $rno = $this->helper->getReservationNumber($mo);
124
+ $result = $this->klarna->activate($rno);
125
+
126
+ $mo = $this->createMageInvoice($mo, $result, $qtys);
127
+ $mo = $this->checkExpiration($mo);
128
+ $mo->save();
129
+
130
+ return true;
131
+ }
132
+ catch(Exception $e) {
133
+ $this->helper->failedActivation($mo, $rno, $e);
134
+ return false;
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Do full activation
140
+ *
141
+ * @param Magento_Sales_Order $mo
142
+ * @return bool
143
+ */
144
+ public function activateFullReservation($mo)
145
+ {
146
+ if($rno = $this->helper->getReservationNumber($mo)){
147
+ try{
148
+ $result = $this->klarna->activate($rno);
149
+ $mo = $this->createMageInvoice($mo, $result);
150
+ $mo = $this->checkExpiration($mo);
151
+ $mo->save();
152
+
153
+ return true;
154
+ }
155
+ catch(Exception $e) {
156
+ $this->helper->failedActivation($mo, $rno, $e);
157
+ return false;
158
+ }
159
+ }
160
+ return false;
161
+ }
162
+
163
+ /**
164
+ * Create invoice for Magento order
165
+ *
166
+ * @param Magento_Sales_Order $mo
167
+ * @param array $result
168
+ * @param array $qtys
169
+ * @return Magento_Sales_Order
170
+ */
171
+ private function createMageInvoice($mo, $result, $qtys = null)
172
+ {
173
+ $invoice = Mage::getModel('sales/service_order', $mo)->prepareInvoice($qtys);
174
+
175
+ if (!$invoice->getTotalQty())
176
+ Mage::throwException(Mage::helper('core')->__('Cannot create an invoice without products'));
177
+
178
+ if(Mage::registry('kco_transaction') != null)
179
+ Mage::unregister('kco_transaction');
180
+
181
+ Mage::register('kco_transaction', $result[1]);
182
+ $amount = $invoice->getGrandTotal();
183
+ $invoice->setRequestedCaptureCase(Mage_Sales_Model_Order_Invoice::CAPTURE_ONLINE);
184
+ $invoice->register();
185
+
186
+ $mo->getPayment()->setTransactionId($result[1]);
187
+
188
+ Mage::getModel('core/resource_transaction')
189
+ ->addObject($invoice)
190
+ ->addObject($invoice->getOrder())
191
+ ->save();
192
+
193
+ $invoice->save();
194
+ $klarnainvoices = $this->helper->getKlarnaInvoices($mo);
195
+ $klarnainvoices[$invoice->getId()] = array(
196
+ 'invoice' => $result[1],
197
+ 'risk' => $result[0]
198
+ );
199
+
200
+ $mo->addStatusHistoryComment($this->helper->__('Captured amount of %s .Created Klarna invoice %s', $amount, $result[1]));
201
+ $mo = $this->helper->saveKlarnaInvoices($mo, $klarnainvoices);
202
+
203
+ return $mo;
204
+ }
205
+
206
+ /**
207
+ * Check reservation expiration
208
+ *
209
+ * @param Magento_Sales_Order $mo
210
+ * @return Magento_Sales_Order
211
+ */
212
+ private function checkExpiration($mo)
213
+ {
214
+ $expr = $mo->getPayment()->getAdditionalInformation("klarna_order_reservation_expiration");
215
+ $expiration = new Zend_Date($expr);
216
+
217
+ if($expiration < new Zend_Date()){
218
+ $formattedExpiration = Mage::helper('core')->formatDate(
219
+ $expr,'medium', false);
220
+
221
+ $mo->getPayment()->setAdditionalInformation(
222
+ 'klarna_message',
223
+ 'Reservation was activated after expiration (expired '.$formattedExpiration.')'
224
+
225
+ );
226
+ }
227
+ return $mo;
228
+ }
229
+
230
+ /**
231
+ * Activate reservation from Magento invoice
232
+ *
233
+ * @param Magento_Sales_Order $mo
234
+ * @param Magento_Sales_Order_Invoice $invoice
235
+ * @return bool
236
+ */
237
+ public function activateFromInvoice($mo, $invoice)
238
+ {
239
+ if($rno = $this->helper->getReservationNumber($mo)){
240
+
241
+ if (abs($mo->getTotalDue() - $invoice->getGrandTotal()) > .0001){
242
+ foreach($invoice->getAllItems() as $item)
243
+ {
244
+ $this->klarna->addArtNo($item->getQty(), $item->getSku());
245
+ }
246
+ }
247
+
248
+ try{
249
+ $result = $this->klarna->activate($rno);
250
+
251
+ if(Mage::registry('kco_invoicekey') != null)
252
+ Mage::unregister('kco_invoicekey');
253
+
254
+ Mage::register('kco_invoicekey', $result[1]);
255
+
256
+ $klarnainvoices = $this->helper->getKlarnaInvoices($mo);
257
+ $klarnainvoices[$result[1]] = array(
258
+ 'invoice' => $result[1],
259
+ 'risk' => $result[0]
260
+ );
261
+
262
+ if(Mage::registry('kco_transaction') != null)
263
+ Mage::unregister('kco_transaction');
264
+
265
+ Mage::register('kco_transaction', $result[1]);
266
+
267
+ $mo = $this->helper->saveKlarnaInvoices($mo, $klarnainvoices);
268
+ $mo = $this->checkExpiration($mo);
269
+
270
+ $mo->save();
271
+
272
+ return true;
273
+ }
274
+ catch(Exception $e) {
275
+ $this->helper->failedActivation($mo, $rno, $e);
276
+ return false;
277
+ }
278
+ }
279
+ }
280
+
281
+
282
+ /**
283
+ * Credit Klarna invoice
284
+ *
285
+ * @param string $invoiceNo
286
+ * @return bool
287
+ */
288
+ public function creditInvoice($invoiceNo)
289
+ {
290
+ try {
291
+ $result = $this->klarna->creditInvoice($invoiceNo);
292
+ return $this->emailInvoice($result);
293
+ }
294
+ catch(Exception $e) {
295
+ Mage::logException($e);
296
+ return false;
297
+ }
298
+ }
299
+
300
+ /**
301
+ * Credit Klarna invoice partially
302
+ *
303
+ * @param string $invoiceNo
304
+ * @param array $products
305
+ * @param float $adjustment |null
306
+ * @param float $adjustmentTaxRate | null
307
+ * @return bool
308
+ */
309
+ public function creditPart($invoiceNo, $products, $adjustment = null, $adjustmentTaxRate = null)
310
+ {
311
+ if($adjustment){
312
+ $this->klarna->addArticle(
313
+ 1,
314
+ 'Adjustment',
315
+ $this->helper->__('Adjustment fee'),
316
+ $adjustment,
317
+ $adjustmentTaxRate,
318
+ 0,
319
+ KlarnaFlags::INC_VAT | KlarnaFlags::IS_HANDLING
320
+ );
321
+ }
322
+
323
+ foreach($products as $key => $p){
324
+ $this->klarna->addArtNo($p, $key);
325
+ }
326
+
327
+ try {
328
+ $result = $this->klarna->creditPart($invoiceNo);
329
+ return $this->emailInvoice($result);
330
+ }
331
+ catch(Exception $e) {
332
+ Mage::logException($e);
333
+ return false;
334
+ }
335
+ }
336
+
337
+ /**
338
+ * Return amount from Klarna invoice
339
+ *
340
+ * @param string $invoiceNo
341
+ * @param float $amount
342
+ * @param float $vat|0
343
+ * @return bool
344
+ */
345
+ public function returnAmount($invoiceNo, $amount, $vat = 0)
346
+ {
347
+ try {
348
+ $result = $this->klarna->returnAmount(
349
+ $invoiceNo,
350
+ $amount,
351
+ $vat,
352
+ KlarnaFlags::INC_VAT
353
+ );
354
+ return $this->emailInvoice($result);
355
+ }
356
+ catch(Exception $e) {
357
+ Mage::logException($e);
358
+ return false;
359
+ }
360
+ }
361
+
362
+ /**
363
+ * Cancel Klarna reservation
364
+ *
365
+ * @param string $rno
366
+ * @param Mage_Sales_Model_Order $mo|null
367
+ * @return bool
368
+ */
369
+ public function cancelReservation($rno, $mo = null)
370
+ {
371
+ try {
372
+ $result = $this->klarna->cancelReservation($rno);
373
+
374
+ if($mo){
375
+ $mo->addStatusHistoryComment(
376
+ $this->helper->__('Klarna reservation <b>%s</b> was canceled.', $rno)
377
+ );
378
+ }
379
+ return true;
380
+ }
381
+ catch(Exception $e) {
382
+ if($mo){
383
+ $mo->addStatusHistoryComment(
384
+ $this->helper->__('Failed to cancel Klarna reservation <b>%s</b>.(%s - %s)',
385
+ $rno,
386
+ $e->getMessage(),
387
+ $e->getCode())
388
+ );
389
+ }
390
+ Mage::logException($e);
391
+ return false;
392
+ }
393
+ }
394
+
395
+ /**
396
+ * Send invoice e-mail
397
+ *
398
+ * @param string $invoiceNo
399
+ * @return bool
400
+ */
401
+ public function emailInvoice($invoiceNo)
402
+ {
403
+ try {
404
+ $result = $this->klarna->emailInvoice($invoiceNo);
405
+ return true;
406
+ } catch(Exception $e) {
407
+ Mage::logException($e);
408
+ return false;
409
+ }
410
+ }
411
+ }
app/code/community/Avenla/KlarnaCheckout/Model/Config.php ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+ class Avenla_KlarnaCheckout_Model_Config extends Varien_Object
22
+ {
23
+ const KCO_LIVE_URL = 'https://checkout.klarna.com';
24
+ const KCO_DEMO_URL = 'https://checkout.testdrive.klarna.com';
25
+ const KCO_LIVE_S_URL = 'https://online.klarna.com';
26
+ const KCO_DEMO_S_URL = 'https://testdrive.klarna.com';
27
+ const KLARNA_DOC_URL = 'http://developers.klarna.com/';
28
+ const ONLINE_GUI_URL = 'https://merchants.klarna.com';
29
+ const LICENSE_URL = 'http://productdownloads.avenla.com/magento-modules/klarna-checkout/license';
30
+
31
+ /**
32
+ * Return config var
33
+ *
34
+ * @param string $key
35
+ * @param string $default value for non-existing key
36
+ * @return mixed
37
+ */
38
+ public function getConfigData($key, $default=false)
39
+ {
40
+ $store = Mage::app()->getStore();
41
+
42
+ if(Mage::app()->getStore()->getId() == 0)
43
+ $store = Mage::app()->getRequest()->getParam('store', 0);
44
+
45
+ if (!$this->hasData($key)) {
46
+ $value = Mage::getStoreConfig('payment/klarnaCheckout_payment/'.$key, $store);
47
+ if (is_null($value) || false===$value) {
48
+ $value = $default;
49
+ }
50
+ $this->setData($key, $value);
51
+ }
52
+ return $this->getData($key);
53
+ }
54
+
55
+ /**
56
+ * Get Klarna merchant eid
57
+ *
58
+ * @return string
59
+ */
60
+ public function getKlarnaEid()
61
+ {
62
+ return $this->getConfigData('merchantid');
63
+ }
64
+
65
+ /**
66
+ * Get Klarna merchant shared secret
67
+ *
68
+ * @return string
69
+ */
70
+ public function getKlarnaSharedSecret()
71
+ {
72
+ return Mage::helper('core')->decrypt($this->getConfigData('sharedsecret'));
73
+ }
74
+
75
+ /**
76
+ * Get terms url
77
+ *
78
+ * @return string
79
+ */
80
+ public function getTermsUri()
81
+ {
82
+ return Mage::getUrl($this->getConfigData('terms_url'));
83
+ }
84
+
85
+ /**
86
+ * Get Klarna Checkout mode (LIVE OR BETA)
87
+ *
88
+ * @return bool
89
+ */
90
+ public function isLive()
91
+ {
92
+ if($this->getConfigData('server') == "LIVE")
93
+ return true;
94
+
95
+ return false;
96
+ }
97
+
98
+ /**
99
+ * Get selected locale for Klarna Checkout
100
+ *
101
+ * @return string locale
102
+ */
103
+ public function getLocale()
104
+ {
105
+ return $this->getConfigData('locale');
106
+ }
107
+
108
+ /**
109
+ * Get module status
110
+ *
111
+ * @return bool
112
+ */
113
+ public function isActive()
114
+ {
115
+ return $this->getConfigData('active');
116
+ }
117
+
118
+ /**
119
+ * Get partial shipment activation mode
120
+ *
121
+ * @return bool
122
+ */
123
+ public function activatePartial()
124
+ {
125
+ return $this->getConfigData('activate_partial');
126
+ }
127
+
128
+ /**
129
+ * Get Google Analytics number or false if not found
130
+ *
131
+ * @return mixed
132
+ */
133
+ public function getGoogleAnalyticsNo()
134
+ {
135
+ $ga = $this->getConfigData('google_analytics');
136
+ if(strlen($ga) < 1)
137
+ return false;
138
+
139
+ return $this->getConfigData('google_analytics');
140
+ }
141
+
142
+ /**
143
+ * Get method title
144
+ *
145
+ * @return string
146
+ */
147
+ public function getTitle()
148
+ {
149
+ if(strlen($this->getConfigData('title')) > 0)
150
+ return $this->getConfigData('title');
151
+
152
+ return "Klarna Checkout";
153
+ }
154
+
155
+ /**
156
+ * Get link text
157
+ *
158
+ * @return string
159
+ */
160
+ public function getLinkText()
161
+ {
162
+ if(strlen($this->getConfigData('linktext')) > 0)
163
+ return $this->getConfigData('linktext');
164
+
165
+ return "Go to Klarna Checkout";
166
+ }
167
+ /**
168
+ * Get tax rate for credit memo adjustment
169
+ *
170
+ * @return float
171
+ */
172
+ public function getReturnTaxRate()
173
+ {
174
+ $taxClass = $this->getConfigData('return_tax');
175
+
176
+ $taxClasses = Mage::helper("core")->jsonDecode(Mage::helper("tax")->getAllRatesByProductClass());
177
+ if(isset($taxClasses["value_".$taxClass]))
178
+ return $taxClasses["value_".$taxClass];
179
+
180
+ return 0;
181
+ }
182
+
183
+ /**
184
+ * Get license agreement status
185
+ *
186
+ * @return bool
187
+ */
188
+ public function getLicenseAgreement()
189
+ {
190
+ return $this->getConfigData('license');
191
+ }
192
+ }
app/code/community/Avenla/KlarnaCheckout/Model/KCO.php ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+
22
+ class Avenla_KlarnaCheckout_Model_KCO extends Mage_Payment_Model_Method_Abstract
23
+ {
24
+ protected $_code = 'klarnaCheckout_payment';
25
+ protected $_formBlockType = 'klarnaCheckout/KCO_form';
26
+ protected $_infoBlockType = 'klarnaCheckout/KCO_info';
27
+
28
+ protected $_isGateway = true;
29
+ protected $_canAuthorize = true;
30
+ protected $_canCapture = true;
31
+ protected $_canCapturePartial = true;
32
+ protected $_canRefund = true;
33
+ protected $_canRefundInvoicePartial = true;
34
+ protected $_canVoid = false;
35
+ protected $_canUseInternal = false;
36
+ protected $_canUseCheckout = false;
37
+ protected $_canUseForMultishipping = false;
38
+ protected $_order = null;
39
+
40
+ /**
41
+ * Get Config model
42
+ *
43
+ * @return object Avenla_KlarnaCheckout_Model_Config
44
+ */
45
+ public function getConfig()
46
+ {
47
+ return Mage::getSingleton('klarnaCheckout/config');
48
+ }
49
+
50
+ /**
51
+ * Check if Klarna Checkout is available
52
+ *
53
+ * @param Mage_Sales_Model_Quote|null $quote
54
+ * @return bool
55
+ */
56
+ public function isAvailable($quote = null, $country = null)
57
+ {
58
+ $connectionStatus = Mage::helper('klarnaCheckout')->getConnectionStatus($country);
59
+
60
+ if($quote == null)
61
+ $quote = Mage::getSingleton('checkout/session')->getQuote();
62
+
63
+ // If KCO is needed to be disabled with some shipping methods
64
+ // $preventMethods = array('itellaSmartpost_itellaSmartpost');
65
+ // if (in_array($quote->getShippingAddress()->getShippingMethod() , $preventMethods))
66
+ // return false;
67
+
68
+ return parent::isAvailable($quote) &&
69
+ $connectionStatus &&
70
+ (Mage::getSingleton('customer/session')->isLoggedIn() || Mage::helper('checkout')->isAllowedGuestCheckout($quote)) &&
71
+ count($quote->getAllVisibleItems()) >= 1 && $this->getConfig()->getLicenseAgreement();
72
+ }
73
+
74
+ /**
75
+ * Capture payment
76
+ *
77
+ * @param Varien_Object $payment
78
+ * @param float $amount
79
+ * @return Avenla_KlarnaCheckout_Model_KCO
80
+ */
81
+ public function capture(Varien_Object $payment, $amount)
82
+ {
83
+ $order = $payment->getOrder();
84
+ $currentId = Mage::app()->getStore()->getStoreId();
85
+ Mage::app()->setCurrentStore($order->getStore()->getStoreId());
86
+
87
+ if(Mage::registry('kco_transaction') == null){
88
+ foreach ($order->getInvoiceCollection() as $invoice) {
89
+ if($invoice->getId() == null){
90
+ $inv = $invoice;
91
+ }
92
+ }
93
+ if(isset($inv))
94
+ Mage::getModel('klarnaCheckout/api')->activateFromInvoice($order, $inv);
95
+ }
96
+
97
+ if($id = Mage::registry('kco_transaction')){
98
+ $payment->setTransactionId($id);
99
+ $payment->setIsTransactionClosed(1);
100
+ Mage::unregister('kco_transaction');
101
+ }
102
+
103
+ Mage::app()->setCurrentStore($currentId);
104
+ return $this;
105
+ }
106
+
107
+ /**
108
+ * Register KCO save before redund
109
+ *
110
+ * @param $invoice
111
+ * @param Varien_Object $payment
112
+ * @return Avenla_KlarnaCheckout_Model_KCO
113
+ */
114
+ public function processBeforeRefund($invoice, $payment)
115
+ {
116
+ Mage::register('kco_save', true);
117
+ return $this;
118
+ }
119
+
120
+ /**
121
+ * Unregister KCO save after redund
122
+ *
123
+ * @param $creditmemo
124
+ * @param Varien_Object $payment
125
+ * @return Avenla_KlarnaCheckout_Model_KCO
126
+ */
127
+ public function processCreditmemo($creditmemo, $payment)
128
+ {
129
+ Mage::unregister('kco_save');
130
+ return $this;
131
+ }
132
+
133
+ /**
134
+ * Refund specified amount from invoice
135
+ *
136
+ * @param Varien_Object $payment
137
+ * @param float $amount
138
+ * @return Avenla_KlarnaCheckout_Model_KCO
139
+ */
140
+ public function refund(Varien_Object $payment, $amount)
141
+ {
142
+ $order = $payment->getOrder();
143
+
144
+ $currentId = Mage::app()->getStore()->getStoreId();
145
+ Mage::app()->setCurrentStore($order->getStore()->getStoreId());
146
+ $api = Mage::getModel('klarnaCheckout/api');
147
+ $rno = Mage::helper('klarnaCheckout/api')->getReservationNumber($order);
148
+
149
+ if($rno === false)
150
+ return $this;
151
+
152
+ $creditmemo = $payment->getCreditmemo();
153
+ $invoice = $creditmemo->getInvoice();
154
+ $klarna_invoice = $invoice->getTransactionId();
155
+
156
+ $products = array();
157
+ $result = array();
158
+ $total_refund = false;
159
+
160
+ if (abs($invoice->getGrandTotal() - $creditmemo->getGrandTotal()) < .0001)
161
+ $total_refund = true;
162
+
163
+ foreach ($creditmemo->getAllItems() as $item)
164
+ {
165
+ $invoiceItem = Mage::getResourceModel('sales/order_invoice_item_collection')
166
+ ->addAttributeToSelect('*')
167
+ ->setInvoiceFilter($invoice->getId())
168
+ ->addFieldToFilter('order_item_id', $item->getOrderItemId())
169
+ ->getFirstItem();
170
+
171
+ $diff = $item->getQty() - $invoiceItem->getQty();
172
+
173
+ if($diff > 0)
174
+ $total_refund = false;
175
+
176
+ if($item->getQty() > 0)
177
+ $products[$item->getSku()] = $item->getQty();
178
+ }
179
+
180
+ if($total_refund){
181
+ $result[] = $api->creditInvoice($klarna_invoice)
182
+ ? "Refunded Klarna invoice " . $klarna_invoice
183
+ : "Failed to refund Klarna invoice " . $klarna_invoice;
184
+ }
185
+ else{
186
+ $fee = null;
187
+ if($creditmemo->getAdjustment() < 0)
188
+ $fee = abs($creditmemo->getAdjustment());
189
+
190
+ if(!empty($products) || $creditmemo->getShippingAmount() > 0){
191
+ if (abs($invoice->getShippingAmount() - $creditmemo->getShippingAmount()) < .0001)
192
+ $products['shipping_fee'] = 1;
193
+
194
+ if($fee != null){
195
+ $response = $api->creditPart($klarna_invoice, $products, $fee, $this->getConfig()->getReturnTaxRate());
196
+ }
197
+ else{
198
+ $response = $api->creditPart($klarna_invoice, $products);
199
+ }
200
+
201
+ if($response){
202
+ $t = "Credited products: ";
203
+ foreach($products as $key => $p){
204
+ $t .= $key."(".$p.") ";
205
+ }
206
+ $result[] = $t;
207
+ }
208
+ else{
209
+ $result[] = "Failed to do partial refund";
210
+ }
211
+
212
+ if($creditmemo->getShippingAmount() > 0 && !array_key_exists('shipping_fee', $products)){
213
+ $result[] = $api->returnAmount($klarna_invoice, $creditmemo->getShippingAmount(), Mage::helper('klarnaCheckout')->getShippingVatRate())
214
+ ? "Refunded amount of " . $creditmemo->getShippingAmount() . " from shipment on Klarna invoice " . $klarna_invoice
215
+ : "Failed to refund amount of " . $creditmemo->getShippingAmount() . " from shipment on Klarna invoice " . $klarna_invoice;
216
+
217
+ }
218
+ }
219
+
220
+ if($creditmemo->getAdjustment() > 0){
221
+ $result[] = $api->returnAmount($klarna_invoice, $creditmemo->getAdjustment(), $this->getConfig()->getReturnTaxRate())
222
+ ? "Refunded amount of " . $creditmemo->getAdjustment() . " on Klarna invoice " . $klarna_invoice
223
+ : "Failed to refund amount of " . $creditmemo->getAdjustment() . " on Klarna invoice " . $klarna_invoice;
224
+ }
225
+ }
226
+
227
+ if(!empty($result)) {
228
+ foreach($result as $msg)
229
+ {
230
+ $order->addStatusHistoryComment($msg);
231
+ }
232
+ $order->save();
233
+ }
234
+
235
+ Mage::app()->setCurrentStore($currentId);
236
+
237
+ return $this;
238
+ }
239
+ }
app/code/community/Avenla/KlarnaCheckout/Model/Observer.php ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+
22
+ class Avenla_KlarnaCheckout_Model_Observer
23
+ {
24
+ private $api;
25
+ private $helper;
26
+ private $apiHelper;
27
+
28
+ public function __construct()
29
+ {
30
+ $this->helper = Mage::helper('klarnaCheckout');
31
+ $this->apiHelper = Mage::helper('klarnaCheckout/api');
32
+ }
33
+
34
+ /**
35
+ * Process order after status change
36
+ *
37
+ * @param Varien_Event_Observer $observer
38
+ */
39
+ public function orderStatusChanged($observer)
40
+ {
41
+ if(Mage::registry('kco_save'))
42
+ return $this;
43
+
44
+ $order = $observer->getEvent()->getOrder();
45
+ $rno = $this->apiHelper->getReservationNumber($order);
46
+ Mage::app()->setCurrentStore($order->getStore()->getStoreId());
47
+ $this->api = Mage::getModel('klarnaCheckout/api');
48
+
49
+ switch ($order->getState()) {
50
+ case Mage_Sales_Model_Order::STATE_COMPLETE:
51
+ if($rno !== false && $order->canInvoice())
52
+ $this->api->activateReservation($order);
53
+
54
+ break;
55
+
56
+ case Mage_Sales_Model_Order::STATE_CANCELED:
57
+ if($rno !== false)
58
+ $this->api->cancelReservation($rno, $order);
59
+
60
+ break;
61
+
62
+ default:
63
+ if($rno !== false){
64
+ $mixed = false;
65
+ foreach($order->getAllItems() as $item){
66
+ if($item->getQtyShipped() > $item->getQtyInvoiced())
67
+ $mixed = true;
68
+ }
69
+
70
+ if($mixed)
71
+ $this->api->activateReservation($order);
72
+ }
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Process invoice after save
78
+ *
79
+ * @param Varien_Event_Observer $observer
80
+ */
81
+ public function invoiceSaved($observer)
82
+ {
83
+ if(Mage::registry('kco_save'))
84
+ return $this;
85
+
86
+ if($kco_invoicekey = Mage::registry('kco_invoicekey')){
87
+ $invoice = $observer->getEvent()->getInvoice();
88
+ $order = $invoice->getOrder();
89
+ $rno = $this->apiHelper->getReservationNumber($order);
90
+
91
+ if($rno !== false){
92
+ if(false !== $klarnainvoices = $this->apiHelper->getKlarnaInvoices($order)){
93
+ if (!array_key_exists($invoice->getId(), $klarnainvoices)){
94
+ $klarnainvoices[$invoice->getId()] = $klarnainvoices[$kco_invoicekey];
95
+ unset($klarnainvoices[$kco_invoicekey]);
96
+
97
+ $order = $this->apiHelper->saveKlarnaInvoices($order, $klarnainvoices);
98
+ Mage::register('kco_save', true);
99
+ $order->save();
100
+ Mage::unregister('kco_save');
101
+ }
102
+ }
103
+ }
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Add Klarna link in default Checkout
109
+ *
110
+ * @param Varien_Event_Observer $observer
111
+ */
112
+ public function insertKlarnaLink($observer)
113
+ {
114
+ $block = $observer->getBlock();
115
+ $isLogged = Mage::helper('customer')->isLoggedIn();
116
+
117
+ if (
118
+ $block->getType() == 'checkout/onepage_login' ||
119
+ ($isLogged && $block->getType() == 'checkout/onepage_billing') ||
120
+ ($block->getType() == 'checkout/onepage_payment_methods' && $block->getBlockAlias() != 'methods') &&
121
+ Mage::getSingleton('klarnaCheckout/KCO')->isAvailable()
122
+ )
123
+ {
124
+ $child = clone $block;
125
+ $child->setType('klarnaCheckout/KCO_Link');
126
+ $block->setChild('original', $child);
127
+ $block->setTemplate('KCO/link.phtml');
128
+
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Add activate reservation button to admin order view
134
+ *
135
+ * @param Varien_Event_Observer $observer
136
+ */
137
+ public function addActivate($observer)
138
+ {
139
+ $block = $observer->getEvent()->getBlock();
140
+
141
+ if(get_class($block) =='Mage_Adminhtml_Block_Sales_Order_View'
142
+ && $block->getRequest()->getControllerName() == 'sales_order')
143
+ {
144
+ $order = $block->getOrder();
145
+
146
+ if($order->getPayment()->getAdditionalInformation("klarna_order_reference")){
147
+ $block->addButton('activate_klarna_reservation', array(
148
+ 'label' => Mage::helper('klarnaCheckout')->__('Activate Klarna reservation'),
149
+ 'onclick' => 'setLocation(\'' . $block->getUrl('klarnaCheckout/KCO/activateReservation', array('order_id' => $order->getId())) . '\')',
150
+ 'class' => 'save'
151
+ ));
152
+ }
153
+ }
154
+ }
155
+ }
app/code/community/Avenla/KlarnaCheckout/Model/Order.php ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+
22
+ require_once(Mage::getBaseDir('lib') . '/KlarnaCheckout/Checkout.php');
23
+
24
+ class Avenla_KlarnaCheckout_Model_Order extends Klarna_Checkout_Order
25
+ {
26
+ private $helper;
27
+ private $quote;
28
+ private $config;
29
+ private $cart = array();
30
+ private $dummy = false;
31
+ public $connector;
32
+ public $order;
33
+ private $mobile;
34
+
35
+ public function __construct()
36
+ {
37
+ $this->helper = Mage::helper("klarnaCheckout");
38
+ $this->config = Mage::getSingleton('klarnaCheckout/KCO')->getConfig();
39
+
40
+ $url = $this->config->isLive()
41
+ ? Avenla_KlarnaCheckout_Model_Config::KCO_LIVE_URL
42
+ : Avenla_KlarnaCheckout_Model_Config::KCO_DEMO_URL;
43
+
44
+ parent::$baseUri = $url . '/checkout/orders';
45
+ parent::$contentType = "application/vnd.klarna.checkout.aggregated-order-v2+json";
46
+ Mage::log("SS:". $this->config->getKlarnaSharedSecret());
47
+
48
+ $this->connector = Klarna_Checkout_Connector::create($this->config->getKlarnaSharedSecret());
49
+ }
50
+
51
+ /**
52
+ * Get Klarna Checkout order
53
+ *
54
+ * @param Mage_Sales_Model_Quote $quote
55
+ * @param string $checkoutId
56
+ * @return Klarna_Checkout_Order
57
+ */
58
+ public function getOrder($quote = null, $checkoutId = null, $mobile = false)
59
+ {
60
+
61
+ $this->order = new Klarna_Checkout_Order($this->connector, $checkoutId);
62
+ $this->mobile = $mobile;
63
+ if(!$quote)
64
+ return $this->order;
65
+
66
+ $this->quote = $quote;
67
+ $this->addProductsToCart();
68
+ $this->processDiscount();
69
+ $this->getShippingCosts();
70
+
71
+ $checkoutId ? $this->updateOrder() : $this->createOrder();
72
+
73
+ return $this->order;
74
+ }
75
+
76
+ /**
77
+ * Create new Klarna Checkout order
78
+ *
79
+ */
80
+ private function createOrder($country = null)
81
+ {
82
+ try{
83
+ $create['purchase_country'] = $country != null
84
+ ? $country
85
+ : Mage::getStoreConfig('general/country/default', Mage::app()->getStore());
86
+
87
+ $create['purchase_currency'] = $this->dummy ? 'EUR' : $this->quote->getBaseCurrencyCode();
88
+ $create['locale'] = $this->config->getLocale();
89
+ $create['merchant']['id'] = $this->config->getKlarnaEid();
90
+ $create['merchant']['terms_uri'] = $this->config->getTermsUri();
91
+ $create['merchant']['checkout_uri'] = $this->helper->getCheckoutUri();
92
+ $create['merchant']['confirmation_uri'] = $this->helper->getConfirmationUri();
93
+ $create['merchant']['push_uri'] = $this->helper->getPushUri();
94
+ $create['merchant_reference']['orderid1'] = $this->quote ? $this->quote->getId() : '12345';
95
+ $create['gui']['options'] = array('disable_autofocus');
96
+ $create['gui']['layout'] = $this->mobile ? 'mobile' : 'desktop';
97
+
98
+ $info = $this->getCustomerInfo();
99
+ if(!empty($info))
100
+ $create['shipping_address'] = $info;
101
+
102
+ foreach ($this->cart as $item){
103
+ $create['cart']['items'][] = $item;
104
+ }
105
+
106
+ $this->order->create($create);
107
+ if(!$this->dummy)
108
+ $this->order->fetch();
109
+ }
110
+ catch (Exception $e) {
111
+ Mage::logException($e);
112
+ $this->order = null;
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Update existing Klarna Checkout order
118
+ *
119
+ */
120
+ public function updateOrder()
121
+ {
122
+ try {
123
+ $this->order->fetch();
124
+
125
+ if(!$this->helper->isOrderFromCurrentStore($this->order)){
126
+ $this->createOrder();
127
+ return;
128
+ }
129
+
130
+ $update['cart']['items'] = array();
131
+ $update['merchant_reference']['orderid1'] = $this->quote->getId();
132
+
133
+ $info = $this->getCustomerInfo();
134
+ if(!empty($info))
135
+ $update['shipping_address'] = $info;
136
+
137
+ foreach ($this->cart as $item){
138
+ $update['cart']['items'][] = $item;
139
+ }
140
+
141
+ $this->order->update($update);
142
+ }
143
+ catch (Exception $e) {
144
+ Mage::logException($e);
145
+ $this->order = null;
146
+ unset($_SESSION['klarna_checkout']);
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Get customer info for Klarna Checkout
152
+ *
153
+ * @return array
154
+ */
155
+ private function getCustomerInfo()
156
+ {
157
+ $info = array();
158
+ if($this->quote){
159
+ $sa = $this->quote->getShippingAddress();
160
+ $sa->getPostcode() != null ? $info['postal_code'] = $sa->getPostcode() : '';
161
+ $sa->getEmail() != null ? $info['email'] = $sa->getEmail() : '';
162
+ }
163
+ return $info;
164
+ }
165
+
166
+ /**
167
+ * Process items from quote to Klarna Checkout order cart
168
+ *
169
+ */
170
+ private function addProductsToCart()
171
+ {
172
+ $mCart = $this->quote->getAllVisibleItems();
173
+ if(count($mCart) > 0){
174
+ foreach ($mCart as $i)
175
+ {
176
+ $this->cart[] = array(
177
+ 'type' => 'physical',
178
+ 'reference' => $i->getSku(),
179
+ 'name' => $i->getName(),
180
+ 'uri' => $i->getUrlPath(),
181
+ 'quantity' => (int)$i->getQty(),
182
+ 'unit_price' => round($i->getPriceInclTax(), 2) * 100,
183
+ 'discount_rate' => round($i->getDiscountPercent(), 2) * 100,
184
+ 'tax_rate' => round($i->getTaxPercent(), 2) * 100
185
+ );
186
+ }
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Process discount from quote to Klarna Checkout order
192
+ *
193
+ */
194
+ private function processDiscount()
195
+ {
196
+ $totals = $this->quote->getTotals();
197
+
198
+ // TODO : Calculate discount tax rate ! Cannot be 0 always.
199
+ // Check discount tax configuration too.
200
+
201
+ if(isset($totals['discount'])){
202
+ $discount = $totals['discount'];
203
+ $this->cart[] = array(
204
+ 'type' => 'discount',
205
+ 'reference' => $discount->getcode(),
206
+ 'name' => $discount->getTitle(),
207
+ 'quantity' => 1,
208
+ 'unit_price' => round($discount->getValue(), 2) * 100,
209
+ 'tax_rate' => 0
210
+ );
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Process shipping costs from quote to Klarna Checkout order
216
+ *
217
+ */
218
+ private function getShippingCosts()
219
+ {
220
+ if($this->quote->getShippingAddress()->getShippingMethod() != null){
221
+
222
+ $taxRate = 0;
223
+ $taxHelper = Mage::helper('tax/data');
224
+ $taxClass = $taxHelper->getShippingTaxClass(Mage::app()->getStore());
225
+ $taxClasses = Mage::helper("core")->jsonDecode(Mage::helper("tax")->getAllRatesByProductClass());
226
+ if(isset($taxClasses["value_".$taxClass]))
227
+ $taxRate = $taxClasses["value_".$taxClass];
228
+
229
+ $shippingCosts = array(
230
+ 'type' => 'shipping_fee',
231
+ 'reference' => 'shipping_fee',
232
+ 'name' => $this->quote->getShippingAddress()->getShippingDescription(),
233
+ 'quantity' => 1,
234
+ 'unit_price' => round($this->quote->getShippingAddress()->getShippingInclTax(), 2) * 100,
235
+ 'tax_rate' => (int)($taxRate * 100)
236
+ );
237
+
238
+ $this->cart[] = $shippingCosts;
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Create dummy order with test product
244
+ *
245
+ * @return Klarna_Checkout_Order
246
+ */
247
+ public function dummyOrder($country = null)
248
+ {
249
+ $this->dummy = true;
250
+ $this->order = new Klarna_Checkout_Order($this->connector, null);
251
+
252
+ $this->cart = array(
253
+ array(
254
+ 'reference' => '123456789',
255
+ 'name' => 'Test product',
256
+ 'quantity' => 1,
257
+ 'unit_price' => 4490,
258
+ 'tax_rate' => 2400
259
+ ));
260
+ $this->createOrder($country);
261
+
262
+ return $this->order;
263
+ }
264
+ }
app/code/community/Avenla/KlarnaCheckout/Model/Source/Countries.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+
22
+ class Avenla_KlarnaCheckout_Model_Source_Countries
23
+ {
24
+ public function toOptionArray()
25
+ {
26
+ return array(
27
+ array(
28
+ 'label' => Mage::helper('core')->__('Norway'),
29
+ 'value' => 'NO'
30
+ ),
31
+ array(
32
+ 'label' => Mage::helper('core')->__('Sweden'),
33
+ 'value' => 'SE'
34
+ ),
35
+ array(
36
+ 'label' => Mage::helper('core')->__('Finland'),
37
+ 'value' => 'FI'
38
+ ),
39
+ array(
40
+ 'label' => Mage::helper('core')->__('Germany'),
41
+ 'value' => 'DE'
42
+ )
43
+ );
44
+ }
45
+
46
+ }
app/code/community/Avenla/KlarnaCheckout/Model/Source/Orderlocale.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+
22
+ class Avenla_KlarnaCheckout_Model_Source_Orderlocale
23
+ {
24
+ public function toOptionArray()
25
+ {
26
+ return array(
27
+ array(
28
+ 'label' => Mage::app()->getLocale()->getCountryTranslation('FI'),
29
+ 'value' => 'fi-fi'
30
+ ),
31
+ array(
32
+ 'label' => Mage::app()->getLocale()->getCountryTranslation('SE'),
33
+ 'value' => 'sv-se'
34
+ ),
35
+ array(
36
+ 'label' => Mage::app()->getLocale()->getCountryTranslation('NO'),
37
+ 'value' => 'nb-no'
38
+ ),
39
+ array(
40
+ 'label' => Mage::app()->getLocale()->getCountryTranslation('DE'),
41
+ 'value' => 'de-de'
42
+ )
43
+ );
44
+ }
45
+ }
app/code/community/Avenla/KlarnaCheckout/Model/Source/Servermode.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+
22
+ class Avenla_KlarnaCheckout_Model_Source_Servermode
23
+ {
24
+ public function toOptionArray()
25
+ {
26
+ return array(
27
+ array(
28
+ 'label' => 'Live',
29
+ 'value' => 'LIVE'
30
+ ),
31
+ array(
32
+ 'label' => 'Testdrive',
33
+ 'value' => 'DEMO',
34
+ )
35
+ );
36
+ }
37
+ }
app/code/community/Avenla/KlarnaCheckout/Model/Source/Taxclass.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+
22
+ class Avenla_KlarnaCheckout_Model_Source_Taxclass
23
+ {
24
+ public function toOptionArray()
25
+ {
26
+ $options = Mage::getModel('tax/class_source_product')->toOptionArray();
27
+ return $options;
28
+ }
29
+
30
+ }
app/code/community/Avenla/KlarnaCheckout/controllers/KCOController.php ADDED
@@ -0,0 +1,282 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+ class Avenla_KlarnaCheckout_KCOController extends Mage_Core_Controller_Front_Action
22
+ {
23
+ /**
24
+ * Load Klarna Checkout iframe
25
+ *
26
+ */
27
+ public function loadKcoFrameAction()
28
+ {
29
+ $mobile = false;
30
+
31
+ if($this->getRequest()->getParam('mobile') == true){
32
+ $mobile = true;
33
+ }
34
+
35
+ $result = array();
36
+
37
+ $quote = Mage::getSingleton('checkout/session')->getQuote();
38
+ $kco = Mage::getSingleton('klarnaCheckout/KCO');
39
+
40
+ if (!$quote->validateMinimumAmount()){
41
+ $minimumAmount = Mage::app()->getLocale()->currency(Mage::app()->getStore()->getCurrentCurrencyCode())
42
+ ->toCurrency(Mage::getStoreConfig('sales/minimum_order/amount'));
43
+
44
+ $warning = Mage::getStoreConfig('sales/minimum_order/description')
45
+ ? Mage::getStoreConfig('sales/minimum_order/description')
46
+ : Mage::helper('checkout')->__('Minimum order amount is %s', $minimumAmount);
47
+
48
+ $result['msg'] = $warning;
49
+ }
50
+
51
+ if(!$kco->isAvailable($quote, $quote->getShippingAddress()->getCountry())){
52
+ $result['msg'] = $this->__("Klarna Checkout is not available");
53
+ }
54
+ else{
55
+ $ko = null;
56
+ $kcoOrder = Mage::getModel("klarnaCheckout/order");
57
+
58
+ if (array_key_exists('klarna_checkout', $_SESSION))
59
+ $ko = $kcoOrder->getOrder($quote, $_SESSION['klarna_checkout'], $mobile);
60
+
61
+ if ($ko == null)
62
+ $ko = $kcoOrder->getOrder($quote, null, $mobile);
63
+
64
+ if($ko != null){
65
+ $_SESSION['klarna_checkout'] = $sessionId = $ko->getLocation();
66
+
67
+ if($quote->getShippingAddress()->getPostcode() == null)
68
+ $result['msg'] = $this->__("Please fill in your post code");
69
+
70
+ if($quote->getShippingAddress()->getCountry() == null)
71
+ $result['msg'] = $this->__("Please select country");
72
+
73
+ if (!$quote->isVirtual() && $quote->getShippingAddress()->getShippingMethod() == null)
74
+ $result['msg'] = $this->__("Please select shipping method to use Klarna Checkout");
75
+
76
+ $result['klarnaframe'] = $ko['gui']['snippet'];
77
+ }
78
+ }
79
+
80
+ $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
81
+ }
82
+
83
+ /**
84
+ * Confirmation action for Klarna Checkout
85
+ *
86
+ */
87
+ public function confirmationAction()
88
+ {
89
+ $redirect = false;
90
+ @$checkoutId = $_GET['klarna_order'];
91
+ $ko = Mage::getModel("klarnaCheckout/order")->getOrder(null, $checkoutId);
92
+
93
+ try{
94
+ $ko->fetch();
95
+ if ($ko['status'] == "checkout_complete" || $ko['status'] == "created"){
96
+ $this->emptyCart();
97
+ $this->loadLayout();
98
+ $this->getLayout()->getBlock('klarnaCheckout.confirmation')->setCheckoutID($checkoutId);
99
+ $this->renderLayout();
100
+ }
101
+ else{
102
+ $redirect = true;
103
+ }
104
+ }
105
+ catch(Exception $e) {
106
+ Mage::logException($e);
107
+ $redirect = true;
108
+ }
109
+
110
+ if($redirect){
111
+ header('Location: ' . Mage::helper('checkout/url')->getCartUrl());
112
+ exit();
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Convert Klarna address to Magento address
118
+ *
119
+ *
120
+ * @param array $address
121
+ * @param string $region
122
+ * @param string $region_code
123
+ */
124
+ private function convertAddress($address, $region = '', $region_code = '')
125
+ {
126
+ $country_id = strtoupper($address['country']);
127
+
128
+ if($region_code == '')
129
+ $region_code = 1;
130
+
131
+ $magentoAddress = array(
132
+ 'firstname' => $address['given_name'],
133
+ 'lastname' => $address['family_name'],
134
+ 'email' => $address['email'],
135
+ 'street' => $address['street_address'],
136
+ 'city' => $address['city'],
137
+ 'region_id' => $region_code,
138
+ 'region' => $region,
139
+ 'postcode' => $address['postal_code'],
140
+ 'country_id' => strtoupper($address['country']),
141
+ 'telephone' => $address['phone']
142
+ );
143
+
144
+ return $magentoAddress;
145
+ }
146
+
147
+ /**
148
+ * Activate Klarna reservation (manually from order view)
149
+ *
150
+ */
151
+ public function activateReservationAction()
152
+ {
153
+ try {
154
+ if($orderId = $this->getRequest()->getParam('order_id')){
155
+ $order = Mage::getModel('sales/order')->load($orderId);
156
+
157
+ if(Mage::helper('klarnaCheckout/api')->getReservationNumber($order) !== false){
158
+ Mage::register('kco_save', true);
159
+ $currentId = Mage::app()->getStore()->getStoreId();
160
+ Mage::app()->setCurrentStore($order->getStore()->getStoreId());
161
+ Mage::getModel("klarnaCheckout/api")->activateFullReservation($order);
162
+ Mage::app()->setCurrentStore($currentId);
163
+ Mage::unregister('kco_save');
164
+ }
165
+ else{
166
+ $this->_getSession()->addError($this->__('No Klarna reservation number found in order'));
167
+ $this->_redirectReferer();
168
+ return;
169
+ }
170
+ }
171
+ }
172
+ catch(Exception $e) {
173
+ $this->_getSession()->addError($this->__($e->getMessage()));
174
+ $this->_redirectReferer();
175
+ }
176
+ $this->_redirectReferer();
177
+ }
178
+
179
+ /**
180
+ * Push action for Klarna Checkout
181
+ *
182
+ */
183
+ public function pushAction()
184
+ {
185
+ @$checkoutId = $_GET['klarna_order'];
186
+ Mage::app()->setCurrentStore($_GET['storeid']);
187
+ $ko = Mage::getModel("klarnaCheckout/order")->getOrder(null, $checkoutId);
188
+ $ko->fetch();
189
+ $quoteID = $ko['merchant_reference']['orderid1'];
190
+
191
+ if ($ko['status'] == "checkout_complete" && $quoteID){
192
+ $quote = Mage::getModel("sales/quote")->load($quoteID);
193
+
194
+ if(count($quote->getAllItems()) < 1){
195
+ Mage::log("No valid quote found for Klarna order, reservation canceled.");
196
+ Mage::getModel('klarnaCheckout/api')->cancelReservation($ko['reservation']);
197
+ return;
198
+ }
199
+
200
+ $mo = $this->quoteToOrder($quote, $ko);
201
+
202
+ $url = Mage::getSingleton('klarnaCheckout/KCO')->getConfig()->isLive()
203
+ ? Avenla_KlarnaCheckout_Model_Config::KCO_LIVE_S_URL
204
+ : Avenla_KlarnaCheckout_Model_Config::KCO_DEMO_S_URL;
205
+
206
+ $mo->getPayment()->setAdditionalInformation("klarna_server", $url);
207
+ $mo->getPayment()->setAdditionalInformation("klarna_order_id", $ko['id']);
208
+ $mo->getPayment()->setAdditionalInformation("klarna_order_reference", $ko['reference']);
209
+ $mo->getPayment()->setAdditionalInformation("klarna_order_reservation", $ko['reservation']);
210
+ $mo->getPayment()->setAdditionalInformation("klarna_order_reservation_expiration", $ko['expires_at']);
211
+ $mo->getPayment()->save();
212
+
213
+ $update['merchant_reference']['orderid1'] = $mo->getIncrementId();
214
+ $update['status'] = 'created';
215
+ $ko->update($update);
216
+
217
+ if($ko['status'] != "created"){
218
+ $this->cancelOrder($mo, $this->__('Order canceled: Failed to create order in Klarna.'));
219
+ }
220
+ else{
221
+ $mo->getSendConfirmation(null);
222
+ $mo->sendNewOrderEmail();
223
+ }
224
+ }
225
+ else{
226
+ if($ko['status'] != "checkout_complete")
227
+ Mage::log("Klarna reservation " . $ko['reservation'] ." got to pushAction with status: " . $ko['status']);
228
+
229
+ if(!$quoteID){
230
+ Mage::getModel('klarnaCheckout/api')->cancelReservation($ko['reservation']);
231
+ Mage::log("Couldn't find quote id for Klarna reservation " . $ko['reservation']);
232
+ }
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Convert Magento quote to order
238
+ *
239
+ * @param Mage_Sales_Model_Quote
240
+ * @param Klarna_Checkout_Order
241
+ * @return Mage_Sales_Model_Order
242
+ */
243
+ private function quoteToOrder($quote, $ko)
244
+ {
245
+ // The address given to Klarna is set to both
246
+ // billing and shipping
247
+
248
+ $quote->setCustomerEmail($ko['billing_address']['email'])->save();
249
+ $quote->getBillingAddress()->addData($this->convertAddress($ko['billing_address']));
250
+ $quote->getShippingAddress()->addData($this->convertAddress($ko['billing_address']));
251
+ $quote->getPayment()->setMethod(Mage::getModel("klarnaCheckout/KCO")->getCode());
252
+ $quote->collectTotals()->save();
253
+ $service = Mage::getModel('sales/service_quote', $quote);
254
+ $service->submitAll();
255
+
256
+ return $service->getOrder();
257
+ }
258
+
259
+ /**
260
+ * Cancel Magento order
261
+ *
262
+ * @param Mage_Sales_Model_Order
263
+ * @param string msg
264
+ */
265
+ private function cancelOrder($mo, $msg)
266
+ {
267
+ $mo->cancel();
268
+ $mo->setStatus($msg);
269
+ $mo->save();
270
+ }
271
+
272
+ /**
273
+ * Clear the checkout session after successful checkout
274
+ *
275
+ */
276
+ private function emptyCart()
277
+ {
278
+ $quote = Mage::getSingleton('checkout/session')->getQuote();
279
+ $quote->setIsActive(false)->save();
280
+ Mage::getSingleton('checkout/session')->clear();
281
+ }
282
+ }
app/code/community/Avenla/KlarnaCheckout/etc/config.xml ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+
4
+ This file is released under a custom license by Avenla Oy.
5
+ All rights reserved
6
+
7
+ License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
8
+ For questions and support - klarna-support@avenla.com
9
+
10
+ @category Avenla
11
+ @package Avenla_KlarnaCheckout
12
+ @copyright Copyright (c) Avenla Oy
13
+ @link http://www.avenla.fi
14
+
15
+
16
+
17
+ Avenla KlarnaCheckout
18
+
19
+ @category Avenla
20
+ @package Avenla_KlarnaCheckout
21
+
22
+ -->
23
+ <config>
24
+ <modules>
25
+ <Avenla_KlarnaCheckout>
26
+ <version>1.0.3</version>
27
+ </Avenla_KlarnaCheckout>
28
+ </modules>
29
+ <global>
30
+ <blocks>
31
+ <klarnaCheckout>
32
+ <class>Avenla_KlarnaCheckout_Block</class>
33
+ </klarnaCheckout>
34
+ </blocks>
35
+ <models>
36
+ <klarnaCheckout>
37
+ <class>Avenla_KlarnaCheckout_Model</class>
38
+ </klarnaCheckout>
39
+ </models>
40
+ <helpers>
41
+ <klarnaCheckout>
42
+ <class>Avenla_KlarnaCheckout_Helper</class>
43
+ </klarnaCheckout>
44
+ </helpers>
45
+ <payment>
46
+ <groups>
47
+ <klarnaCheckout>KlarnaCheckout</klarnaCheckout>
48
+ </groups>
49
+ </payment>
50
+ <events>
51
+ <core_block_abstract_to_html_before>
52
+ <observers>
53
+ <Avenla_KlarnaCheckout_core_block_abstract_to_html_before>
54
+ <class>klarnaCheckout/observer</class>
55
+ <method>addActivate</method>
56
+ </Avenla_KlarnaCheckout_core_block_abstract_to_html_before>
57
+ </observers>
58
+ </core_block_abstract_to_html_before>
59
+ <sales_order_save_commit_after>
60
+ <observers>
61
+ <avenla_klarnacheckout_sales_order_save_commit_after>
62
+ <type>singleton</type>
63
+ <class>klarnaCheckout/observer</class>
64
+ <method>orderStatusChanged</method>
65
+ </avenla_klarnacheckout_sales_order_save_commit_after>
66
+ </observers>
67
+ </sales_order_save_commit_after>
68
+ <sales_order_invoice_save_after>
69
+ <observers>
70
+ <avenla_klarnacheckout_sales_order_invoice_save_after>
71
+ <type>singleton</type>
72
+ <class>klarnaCheckout/observer</class>
73
+ <method>invoiceSaved</method>
74
+ </avenla_klarnacheckout_sales_order_invoice_save_after>
75
+ </observers>
76
+ </sales_order_invoice_save_after>
77
+ </events>
78
+ </global>
79
+
80
+ <default>
81
+ <payment>
82
+ <klarnaCheckout_payment>
83
+ <model>klarnaCheckout/KCO</model>
84
+ <group>klarnaCheckout</group>
85
+ <payment_action>authorize</payment_action>
86
+ <active>1</active>
87
+ <title>Klarna Checkout</title>
88
+ <allowspecific>1</allowspecific>
89
+ </klarnaCheckout_payment>
90
+ </payment>
91
+ </default>
92
+
93
+ <frontend>
94
+ <translate>
95
+ <modules>
96
+ <translations>
97
+ <files>
98
+ <default>Avenla_KlarnaCheckout.csv</default>
99
+ </files>
100
+ </translations>
101
+ </modules>
102
+ </translate>
103
+ <layout>
104
+ <updates>
105
+ <klarnaCheckout>
106
+ <file>KCO.xml</file>
107
+ </klarnaCheckout>
108
+ </updates>
109
+ </layout>
110
+ <routers>
111
+ <checkout>
112
+ <args>
113
+ <modules>
114
+ <Avenla_KlarnaCheckout before="Mage_Checkout">Avenla_KlarnaCheckout</Avenla_KlarnaCheckout>
115
+ </modules>
116
+ </args>
117
+ </checkout>
118
+ <klarnaCheckout>
119
+ <use>standard</use>
120
+ <args>
121
+ <module>Avenla_KlarnaCheckout</module>
122
+ <frontName>klarnaCheckout</frontName>
123
+ </args>
124
+ </klarnaCheckout>
125
+ </routers>
126
+ <events>
127
+ <core_block_abstract_to_html_before>
128
+ <observers>
129
+ <avenla_klarnacheckout_observer>
130
+ <type>model</type>
131
+ <class>klarnaCheckout/observer</class>
132
+ <method>insertKlarnaLink</method>
133
+ </avenla_klarnacheckout_observer>
134
+ </observers>
135
+ </core_block_abstract_to_html_before>
136
+ </events>
137
+ </frontend>
138
+
139
+ <adminhtml>
140
+ <translate>
141
+ <modules>
142
+ <translations>
143
+ <files>
144
+ <default>Avenla_KlarnaCheckout.csv</default>
145
+ </files>
146
+ </translations>
147
+ </modules>
148
+ </translate>
149
+ <acl>
150
+ <resources>
151
+ <all>
152
+ <title>Allow Everything</title>
153
+ </all>
154
+ <admin>
155
+ <children>
156
+ <system>
157
+ <children>
158
+ <config>
159
+ <children>
160
+ <Avenla>
161
+ <title>Avenla - All</title>
162
+ </Avenla>
163
+ </children>
164
+ </config>
165
+ </children>
166
+ </system>
167
+ </children>
168
+ </admin>
169
+ </resources>
170
+ </acl>
171
+ </adminhtml>
172
+ </config>
app/code/community/Avenla/KlarnaCheckout/etc/system.xml ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ This file is released under a custom license by Avenla Oy.
4
+ All rights reserved
5
+
6
+ License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ For questions and support - klarna-support@avenla.com
8
+
9
+ @category Avenla
10
+ @package Avenla_KlarnaCheckout
11
+ @copyright Copyright (c) Avenla Oy
12
+ @link http://www.avenla.fi
13
+
14
+
15
+ Avenla KlarnaCheckout
16
+
17
+ @category Avenla
18
+ @package Avenla_KlarnaCheckout
19
+
20
+ -->
21
+ <config>
22
+ <sections>
23
+ <payment>
24
+ <groups>
25
+ <klarnaCheckout_payment translate="label" module="klarnaCheckout">
26
+ <label>Klarna Checkout</label>
27
+ <sort_order>700</sort_order>
28
+ <show_in_default>1</show_in_default>
29
+ <show_in_website>1</show_in_website>
30
+ <show_in_store>1</show_in_store>
31
+ <fields>
32
+ <klarna_notice translate="label" module="klarnaCheckout">
33
+ <frontend_model>klarnaCheckout/adminhtml_system_config_fieldset_info</frontend_model>
34
+ <sort_order>2</sort_order>
35
+ <show_in_default>1</show_in_default>
36
+ <show_in_website>1</show_in_website>
37
+ <show_in_store>1</show_in_store>
38
+ </klarna_notice>
39
+ <license translate="label">
40
+ <label>I accept the terms in the license agreement</label>
41
+ <frontend_type>select</frontend_type>
42
+ <source_model>adminhtml/system_config_source_yesno</source_model>
43
+ <sort_order>2</sort_order>
44
+ <show_in_default>1</show_in_default>
45
+ <show_in_website>0</show_in_website>
46
+ <show_in_store>0</show_in_store>
47
+ </license>
48
+ <active translate="label">
49
+ <label>Enabled</label>
50
+ <frontend_type>select</frontend_type>
51
+ <source_model>adminhtml/system_config_source_yesno</source_model>
52
+ <sort_order>3</sort_order>
53
+ <show_in_default>1</show_in_default>
54
+ <show_in_website>1</show_in_website>
55
+ <show_in_store>1</show_in_store>
56
+ </active>
57
+ <server translate="label">
58
+ <label>Server</label>
59
+ <frontend_type>select</frontend_type>
60
+ <source_model>klarnaCheckout/source_servermode</source_model>
61
+ <sort_order>4</sort_order>
62
+ <show_in_default>1</show_in_default>
63
+ <show_in_website>1</show_in_website>
64
+ <show_in_store>1</show_in_store>
65
+ </server>
66
+ <merchantid translate="label">
67
+ <label>Merchant ID</label>
68
+ <frontend_type>text</frontend_type>
69
+ <sort_order>5</sort_order>
70
+ <show_in_default>1</show_in_default>
71
+ <show_in_website>1</show_in_website>
72
+ <show_in_store>1</show_in_store>
73
+ </merchantid>
74
+ <sharedsecret translate="label">
75
+ <label>Shared secret</label>
76
+ <frontend_type>obscure</frontend_type>
77
+ <backend_model>adminhtml/system_config_backend_encrypted</backend_model>
78
+ <sort_order>6</sort_order>
79
+ <show_in_default>1</show_in_default>
80
+ <show_in_website>1</show_in_website>
81
+ <show_in_store>1</show_in_store>
82
+ </sharedsecret>
83
+ <specificcountry>
84
+ <label>Activated Countries</label>
85
+ <frontend_type>multiselect</frontend_type>
86
+ <source_model>klarnaCheckout/source_countries</source_model>
87
+ <sort_order>7</sort_order>
88
+ <show_in_default>1</show_in_default>
89
+ <show_in_website>1</show_in_website>
90
+ <show_in_store>1</show_in_store>
91
+ </specificcountry>
92
+ <locale translate="label">
93
+ <label>Locale</label>
94
+ <comment><![CDATA[Checkout won't be displayed if selected locale is not allowed by Klarna]]></comment>
95
+ <frontend_type>select</frontend_type>
96
+ <source_model>klarnaCheckout/source_orderlocale</source_model>
97
+ <sort_order>7</sort_order>
98
+ <show_in_default>1</show_in_default>
99
+ <show_in_website>1</show_in_website>
100
+ <show_in_store>1</show_in_store>
101
+ </locale>
102
+ <activate_partial translate="label">
103
+ <label>Activate partial shipments</label>
104
+ <comment><![CDATA[When selected, partial shipments will be automatically activated in Klarna]]></comment>
105
+ <frontend_type>select</frontend_type>
106
+ <source_model>adminhtml/system_config_source_yesno</source_model>
107
+ <sort_order>8</sort_order>
108
+ <show_in_default>1</show_in_default>
109
+ <show_in_website>1</show_in_website>
110
+ <show_in_store>1</show_in_store>
111
+ </activate_partial>
112
+ <return_tax translate="label">
113
+ <label>Tax class for all amount returns / adjustment fee</label>
114
+ <frontend_type>select</frontend_type>
115
+ <source_model>klarnaCheckout/source_taxclass</source_model>
116
+ <sort_order>9</sort_order>
117
+ <show_in_default>1</show_in_default>
118
+ <show_in_website>1</show_in_website>
119
+ <show_in_store>1</show_in_store>
120
+ </return_tax>
121
+ <title translate="label">
122
+ <label>Title</label>
123
+ <frontend_type>text</frontend_type>
124
+ <sort_order>10</sort_order>
125
+ <show_in_default>1</show_in_default>
126
+ <show_in_website>1</show_in_website>
127
+ <show_in_store>1</show_in_store>
128
+ </title>
129
+ <linktext translate="label">
130
+ <label>Link text</label>
131
+ <comment><![CDATA[Text shown in Klarna link at Magento Checkout]]></comment>
132
+ <frontend_type>text</frontend_type>
133
+ <sort_order>11</sort_order>
134
+ <show_in_default>1</show_in_default>
135
+ <show_in_website>1</show_in_website>
136
+ <show_in_store>1</show_in_store>
137
+ </linktext>
138
+ <terms_url translate="label">
139
+ <label>Terms URL</label>
140
+ <comment><![CDATA[Relative to Website Base URL]]></comment>
141
+ <frontend_type>text</frontend_type>
142
+ <sort_order>12</sort_order>
143
+ <show_in_default>1</show_in_default>
144
+ <show_in_website>1</show_in_website>
145
+ <show_in_store>1</show_in_store>
146
+ </terms_url>
147
+ <google_analytics translate="label">
148
+ <label>Google Analytics account no</label>
149
+ <comment><![CDATA[Enter account number to use Google Ecommerce tracking]]></comment>
150
+ <frontend_type>text</frontend_type>
151
+ <sort_order>13</sort_order>
152
+ <show_in_default>1</show_in_default>
153
+ <show_in_website>1</show_in_website>
154
+ <show_in_store>1</show_in_store>
155
+ </google_analytics>
156
+ </fields>
157
+ </klarnaCheckout_payment>
158
+ </groups>
159
+ </payment>
160
+ </sections>
161
+ </config>
app/design/adminhtml/default/default/template/KCO/info.phtml ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+ ?>
22
+ <?php if ($this->getRequest()->getActionName() == "view"): ?>
23
+ <img src="<?php echo $imgSrc; ?>" alt="Klarna Checkout" />
24
+ <a href="<?php echo $guiUrl; ?>" target="_blank" style="float:right"><?php echo $this->__('Klarna online GUI'); ?></a>
25
+
26
+ <?php if (count($info->getAdditionalInformation("klarna_order_invoice")) > 0): ?>
27
+ <?php foreach($info->getAdditionalInformation("klarna_order_invoice") as $inv): ?>
28
+ <p>
29
+ <b><?php echo $this->__('Klarna Invoice number: %s', $inv["invoice"]); ?></b>
30
+
31
+ <?php if(isset($pdfUrl)): ?>
32
+ <a href="<?php echo $pdfUrl . $inv['invoice'] . ".pdf" ?>" target="_blank"><?php echo $this->__('Packing slip'); ?></a>
33
+ <?php endif; ?>
34
+ </p>
35
+ <?php endforeach; ?>
36
+ <?php endif; ?>
37
+
38
+ <?php if($this->getInfo()->getOrder()->getState() != Mage_Sales_Model_Order::STATE_COMPLETE): ?>
39
+ <p><b>
40
+ <?php echo $this->__('Klarna reservation number: %s', $info->getAdditionalInformation("klarna_order_reservation"));?>
41
+
42
+ <?php if (strlen($info->getAdditionalInformation("klarna_order_reservation_expiration")) > 0): ?>
43
+ <p>
44
+ <?php echo $this->__('Klarna reservation expires: %s',
45
+ Mage::helper('core')->formatDate($info->getAdditionalInformation("klarna_order_reservation_expiration"), 'medium', false)); ?>
46
+ </p>
47
+ <?php
48
+ $expiration = new Zend_Date($info->getAdditionalInformation("klarna_order_reservation_expiration"));
49
+ $now = new Zend_Date();
50
+
51
+ if($expiration < $now):
52
+ ?>
53
+ <p class="klarna_alert"><?php echo $this->__('Klarna reservation has expired.'); ?></p>
54
+ <?php endif; ?>
55
+ <?php endif; ?>
56
+ </b></p>
57
+ <?php endif; ?>
58
+
59
+ <?php if(isset($message)): ?>
60
+ <p><b><?php echo $message; ?></b></p>
61
+ <?php endif; ?>
62
+ <?php else: ?>
63
+ <p>Klarna Checkout</p>
64
+ <?php endif; ?>
app/design/adminhtml/default/default/template/KCO/system/config/fieldset/info.phtml ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+ ?>
22
+
23
+ <div class="comment">
24
+ <div style="padding:10px;background-color:#fff;border:1px solid #ddd;margin-bottom:7px;">
25
+ <img src="<?php echo $logoSrc; ?>" alt="Klarna Checkout" />
26
+ <a href="<?php echo $apiLink; ?>" target="_blank" style="float:right;"><?php echo $this->__('Klarna API Documentation'); ?></a>
27
+ <div class="infoContent">
28
+ <?php
29
+ $alerts = $this->getAlerts();
30
+ if(!empty($alerts)):
31
+ ?>
32
+ <ul>
33
+ <?php foreach($alerts as $alert): ?>
34
+ <li class="klarna_alert" style="color:red;}">
35
+ <?php echo '<b>' . $this->__('Warning!') .'</b> '. $this->__($alert); ?></li>
36
+ <?php endforeach; ?>
37
+ </ul>
38
+ <?php endif; ?>
39
+
40
+ <b>
41
+ <a href="<?php echo Avenla_KlarnaCheckout_Model_Config::LICENSE_URL ?>" target="_blank">
42
+ <?php echo $this->__("License agreement"); ?>
43
+ </a>
44
+ </b>
45
+
46
+ </div>
47
+ </div>
48
+ </div>
app/design/frontend/base/default/layout/KCO.xml ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+
4
+ This file is released under a custom license by Avenla Oy.
5
+ All rights reserved
6
+
7
+ License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
8
+ For questions and support - klarna-support@avenla.com
9
+
10
+ @category Avenla
11
+ @package Avenla_KlarnaCheckout
12
+ @copyright Copyright (c) Avenla Oy
13
+ @link http://www.avenla.fi
14
+
15
+
16
+ Avenla KlarnaCheckout
17
+
18
+ @category Avenla
19
+ @package Avenla_KlarnaCheckout
20
+ -->
21
+
22
+ <layout version="0.1.0">
23
+
24
+ <default>
25
+ <reference name="head">
26
+ <action method="addCss" ifconfig="payment/klarnaCheckout_payment/active"><stylesheet>KCO/kco.css</stylesheet></action>
27
+ </reference>
28
+ </default>
29
+
30
+ <klarnacheckout_kco_confirmation>
31
+ <reference name="root">
32
+ <action method="setTemplate">
33
+ <template>page/1column.phtml</template>
34
+ </action>
35
+ </reference>
36
+ <reference name="content">
37
+ <block type="klarnaCheckout/KCO_confirmation" name="klarnaCheckout.confirmation" as="confirmation" />
38
+ </reference>
39
+ </klarnacheckout_kco_confirmation>
40
+
41
+ <checkout_cart_index>
42
+ <reference name="checkout.cart.top_methods">
43
+ <action method="unsetChild" ifconfig="payment/klarnaCheckout_payment/active"><name>checkout.cart.methods.onepage.top</name></action>
44
+ <action method="unsetChild" ifconfig="payment/klarnaCheckout_payment/active">"<name>checkout.cart.methods.onepage</name></action>
45
+ </reference>
46
+
47
+ <reference name="checkout.cart">
48
+ <action method="setCartTemplate" ifconfig="payment/klarnaCheckout_payment/active"><value>KCO/cart.phtml</value></action>
49
+ <action method="chooseTemplate"/>
50
+ <block type="klarnaCheckout/KCO" name="checkout.cart.klarnacheckout" as="klarnacheckout" template="KCO/KCO.phtml" />
51
+ </reference>
52
+
53
+ <reference name="checkout.cart.shipping">
54
+ <action method="setTemplate" ifconfig="payment/klarnaCheckout_payment/active"><template>KCO/cart/shipping.phtml</template></action>
55
+ </reference>
56
+
57
+ <reference name="checkout.cart.crosssell">
58
+ <action method="setTemplate" ifconfig="payment/klarnaCheckout_payment/active"><template>KCO/cart/crosssell.phtml</template></action>
59
+ </reference>
60
+
61
+ <reference name="checkout.cart.methods.onepage">
62
+ <action method="setTemplate" ifconfig="payment/klarnaCheckout_payment/active"><template>KCO/onepage/link.phtml</template></action>
63
+ </reference>
64
+ </checkout_cart_index>
65
+
66
+ </layout>
67
+
68
+
app/design/frontend/base/default/template/KCO/KCO.phtml ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+ ?>
22
+
23
+ <div id="klarnaMsg" style="display:none"><h2></h2></div>
24
+
25
+ <div id="klarnaWrapper" name="klarnaWrapper">
26
+ <div id="klarnaOverlay"></div>
27
+ <div id="klarnaFrame"></div>
28
+ </div>
29
+
30
+ <script>
31
+ function loadFrame()
32
+ {
33
+ var kcoloadurl = '<?php echo $this->getUrl("klarnaCheckout/KCO/loadKcoFrame/") ?>';
34
+
35
+ if($$('div.payments')[0]){
36
+ if($$('div.payments')[0].getWidth() < 750)
37
+ kcoloadurl = kcoloadurl + 'mobile/1';
38
+ }
39
+
40
+ new Ajax.Request(kcoloadurl, {
41
+ method:'POST',
42
+ onSuccess: function(k) {
43
+ var response = eval("(" + k.responseText + ")");
44
+ if(response.msg){
45
+ $('klarnaMsg').update('<h2>'+ response.msg +'</h2>').show();
46
+ }
47
+ else{
48
+ $('klarnaOverlay').hide();
49
+ }
50
+
51
+ if(response.klarnaframe)
52
+ $('klarnaFrame').update(response.klarnaframe);
53
+ }
54
+ });
55
+ }
56
+
57
+ document.observe('dom:loaded', function(){
58
+ loadFrame();
59
+ });
60
+
61
+ _send = XMLHttpRequest.prototype.send;
62
+ XMLHttpRequest.prototype.send = function() {
63
+ var callback = this.onreadystatechange;
64
+ this.onreadystatechange = function() {
65
+ if (this.readyState == 4) {
66
+ var response = eval("(" + this.response + ")");
67
+ if(response !== undefined){
68
+ if (response.klarnaframe === undefined && response.msg === undefined)
69
+ loadFrame();
70
+ }
71
+ }
72
+ callback.apply(this, arguments);
73
+ }
74
+ _send.apply(this, arguments);
75
+ }
76
+ </script>
app/design/frontend/base/default/template/KCO/cart.phtml ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+ ?>
22
+ <div class="cart">
23
+ <div class="page-title title-buttons">
24
+ <h1><?php echo $this->__('Shopping Cart') ?></h1>
25
+ <?php if(!$this->hasError()): ?>
26
+ <ul class="checkout-types">
27
+ <?php foreach ($this->getMethods('top_methods') as $method): ?>
28
+ <?php if ($methodHtml = $this->getMethodHtml($method)): ?>
29
+ <li><?php echo $methodHtml; ?></li>
30
+ <?php endif; ?>
31
+ <?php endforeach; ?>
32
+ </ul>
33
+ <?php endif; ?>
34
+ </div>
35
+ <?php echo $this->getMessagesBlock()->getGroupedHtml() ?>
36
+ <?php echo $this->getChildHtml('form_before') ?>
37
+ <form action="<?php echo $this->getUrl('checkout/cart/updatePost') ?>" method="post">
38
+ <?php echo $this->getBlockHtml('formkey'); ?>
39
+ <fieldset>
40
+ <table id="shopping-cart-table" class="data-table cart-table">
41
+ <col width="1" />
42
+ <col />
43
+ <col width="1" />
44
+ <?php if ($this->helper('wishlist')->isAllowInCart()) : ?>
45
+ <col width="1" />
46
+ <?php endif ?>
47
+ <?php if ($this->helper('tax')->displayCartPriceExclTax() || $this->helper('tax')->displayCartBothPrices()): ?>
48
+ <col width="1" />
49
+ <?php endif; ?>
50
+ <?php if ($this->helper('tax')->displayCartPriceInclTax() || $this->helper('tax')->displayCartBothPrices()): ?>
51
+ <col width="1" />
52
+ <?php endif; ?>
53
+ <col width="1" />
54
+ <?php if ($this->helper('tax')->displayCartPriceExclTax() || $this->helper('tax')->displayCartBothPrices()): ?>
55
+ <col width="1" />
56
+ <?php endif; ?>
57
+ <?php if ($this->helper('tax')->displayCartPriceInclTax() || $this->helper('tax')->displayCartBothPrices()): ?>
58
+ <col width="1" />
59
+ <?php endif; ?>
60
+ <col width="1" />
61
+
62
+ <?php $mergedCells = ($this->helper('tax')->displayCartBothPrices() ? 2 : 1); ?>
63
+ <thead>
64
+ <tr>
65
+ <th rowspan="<?php echo $mergedCells; ?>">&nbsp;</th>
66
+ <th rowspan="<?php echo $mergedCells; ?>"><span class="nobr"><?php echo $this->__('Product Name') ?></span></th>
67
+ <th rowspan="<?php echo $mergedCells; ?>"></th>
68
+ <?php if ($this->helper('wishlist')->isAllowInCart()) : ?>
69
+ <th rowspan="<?php echo $mergedCells; ?>" class="a-center"><span class="nobr"><?php echo $this->__('Move to Wishlist') ?></span></th>
70
+ <?php endif ?>
71
+ <th class="a-center" colspan="<?php echo $mergedCells; ?>"><span class="nobr"><?php echo $this->__('Unit Price') ?></span></th>
72
+ <th rowspan="<?php echo $mergedCells; ?>" class="a-center"><?php echo $this->__('Qty') ?></th>
73
+ <th class="a-center" colspan="<?php echo $mergedCells; ?>"><?php echo $this->__('Subtotal') ?></th>
74
+ <th rowspan="<?php echo $mergedCells; ?>" class="a-center">&nbsp;</th>
75
+ </tr>
76
+ <?php if ($this->helper('tax')->displayCartBothPrices()): ?>
77
+ <tr>
78
+ <th class="a-right"><?php echo $this->helper('tax')->getIncExcTaxLabel(false) ?></th>
79
+ <th><?php echo $this->helper('tax')->getIncExcTaxLabel(true) ?></th>
80
+ <th class="a-right"><?php echo $this->helper('tax')->getIncExcTaxLabel(false) ?></th>
81
+ <th><?php echo $this->helper('tax')->getIncExcTaxLabel(true) ?></th>
82
+ </tr>
83
+ <?php endif; ?>
84
+ </thead>
85
+ <tfoot>
86
+ <tr>
87
+ <td colspan="50" class="a-right">
88
+ <?php if($this->getContinueShoppingUrl()): ?>
89
+ <button type="button" title="<?php echo $this->__('Continue Shopping') ?>" class="button btn-continue" onclick="setLocation('<?php echo $this->getContinueShoppingUrl() ?>')"><span><span><?php echo $this->__('Continue Shopping') ?></span></span></button>
90
+ <?php endif; ?>
91
+ <button type="submit" name="update_cart_action" value="update_qty" title="<?php echo $this->__('Update Shopping Cart'); ?>" class="button btn-update"><span><span><?php echo $this->__('Update Shopping Cart'); ?></span></span></button>
92
+ <button type="submit" name="update_cart_action" value="empty_cart" title="<?php echo $this->__('Clear Shopping Cart'); ?>" class="button btn-empty" id="empty_cart_button"><span><span><?php echo $this->__('Clear Shopping Cart'); ?></span></span></button>
93
+ <!--[if lt IE 8]>
94
+ <input type="hidden" id="update_cart_action_container" />
95
+ <script type="text/javascript">
96
+ //<![CDATA[
97
+ Event.observe(window, 'load', function()
98
+ {
99
+ // Internet Explorer (lt 8) does not support value attribute in button elements
100
+ $emptyCartButton = $('empty_cart_button');
101
+ $cartActionContainer = $('update_cart_action_container');
102
+ if ($emptyCartButton && $cartActionContainer) {
103
+ Event.observe($emptyCartButton, 'click', function()
104
+ {
105
+ $emptyCartButton.setAttribute('name', 'update_cart_action_temp');
106
+ $cartActionContainer.setAttribute('name', 'update_cart_action');
107
+ $cartActionContainer.setValue('empty_cart');
108
+ });
109
+ }
110
+
111
+ });
112
+ //]]>
113
+ </script>
114
+ <![endif]-->
115
+ </td>
116
+ </tr>
117
+ </tfoot>
118
+ <tbody>
119
+ <?php foreach($this->getItems() as $_item): ?>
120
+ <?php echo $this->getItemHtml($_item) ?>
121
+ <?php endforeach ?>
122
+ </tbody>
123
+ </table>
124
+ <script type="text/javascript">decorateTable('shopping-cart-table')</script>
125
+ </fieldset>
126
+ </form>
127
+
128
+
129
+ <div class="cart-collaterals">
130
+ <div class="col1-set">
131
+ <div class="col-1">
132
+ <?php echo $this->getChildHtml('crosssell') ?>
133
+ </div>
134
+ </div>
135
+ <div class="col1-set">
136
+ <div class="col-1">
137
+ <?php echo $this->getChildHtml('checkout.cart.extra') ?>
138
+ </div>
139
+ </div>
140
+
141
+ <?php if (!$this->getIsVirtual()): echo $this->getChildHtml('shipping'); endif; ?>
142
+
143
+ <div class="col2-set totals_row">
144
+ <div class="col-1">
145
+ <?php echo $this->getChildHtml('coupon') ?>
146
+ </div>
147
+ <div class="col-2">
148
+ <div class="totals">
149
+ <?php echo $this->getChildHtml('totals'); ?>
150
+ </div>
151
+ </div>
152
+ </div>
153
+ <div class="col1-set">
154
+ <div class="col-1">
155
+ <div class="payments">
156
+ <?php if(!$this->hasError()): ?>
157
+ <div class="paymentMenuContainer">
158
+ <ul>
159
+ <li class="active"><a href="#"><?php echo Mage::getSingleton('klarnaCheckout/config')->getTitle(); ?></a></li>
160
+ <?php foreach ($this->getMethods('methods') as $method): ?>
161
+ <?php if ($methodHtml = $this->getMethodHtml($method)): ?>
162
+ <?php if(strlen(preg_replace( "/\r|\n/", "", $methodHtml)) > 0): ?>
163
+ <li><?php echo $methodHtml; ?></li>
164
+ <?php endif; ?>
165
+ <?php endif; ?>
166
+ <?php endforeach; ?>
167
+ </ul>
168
+ </div>
169
+ <div class="payment_container">
170
+ <?php echo $this->getChildHtml('klarnacheckout'); ?>
171
+ </div>
172
+ <script>
173
+ document.observe('dom:loaded', function(){
174
+ var width = 0;
175
+
176
+ $$('div.payments ul li').each(function(e){
177
+ width += e.getWidth();
178
+ width += 5;
179
+ });
180
+
181
+ if(width > $$('div.col-main')[0].getWidth()){
182
+ $$('div.payments')[0].removeClassName('payments').addClassName('responsive-payments');
183
+ $$('div.responsive-payments ul')[0].insert({before: '<span class="paymentnavi-toggle"></span>'});
184
+ }
185
+ if($$('.paymentnavi-toggle')[0] !== undefined){
186
+ $$('.paymentnavi-toggle')[0].observe('click', function(event) {
187
+ $$('div.responsive-payments ul li').each(function(e, index){
188
+ if(index > 0){
189
+ if(e.visible()){
190
+ e.style.display = 'none';
191
+ }
192
+ else{
193
+ e.style.display = 'block';
194
+ }
195
+ }
196
+ });
197
+ });
198
+ }
199
+ });
200
+ </script>
201
+ <?php endif; ?>
202
+ </div>
203
+ </div>
204
+ </div>
205
+ </div>
206
+ </div>
app/design/frontend/base/default/template/KCO/cart/crosssell.phtml ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+ ?>
22
+
23
+ <?php if($this->getItemCount()): ?>
24
+ <div class="crosssell">
25
+ <h2><?php echo $this->__('Based on your selection, you may be interested in the following items:') ?></h2>
26
+ <ul id="crosssell-products-list">
27
+ <?php foreach ($this->getItems() as $_item): ?>
28
+ <li class="item">
29
+ <a class="product-image" href="<?php echo $_item->getProductUrl() ?>" title="<?php echo $this->escapeHtml($_item->getName()) ?>"><img src="<?php echo $this->helper('catalog/image')->init($_item, 'thumbnail')->resize(75); ?>" width="75" height="75" alt="<?php echo $this->escapeHtml($_item->getName()) ?>" /></a>
30
+ <div class="product-details">
31
+ <h3 class="product-name"><a href="<?php echo $_item->getProductUrl() ?>"><?php echo $this->escapeHtml($_item->getName()) ?></a></h3>
32
+ <?php echo $this->getPriceHtml($_item, true) ?>
33
+ <button type="button" title="<?php echo $this->__('Add to Cart') ?>" class="button btn-cart" onclick="setLocation('<?php echo $this->getAddToCartUrl($_item) ?>')"><span><span><?php echo $this->__('Add to Cart') ?></span></span></button>
34
+ <ul class="add-to-links">
35
+ <?php if ($this->helper('wishlist')->isAllow()) : ?>
36
+ <li><a href="<?php echo $this->getAddToWishlistUrl($_item) ?>" class="link-wishlist"><?php echo $this->__('Add to Wishlist') ?></a></li>
37
+ <?php endif; ?>
38
+ <?php if($_compareUrl=$this->getAddToCompareUrl($_item)): ?>
39
+ <li><span class="separator">|</span> <a href="<?php echo $_compareUrl ?>" class="link-compare"><?php echo $this->__('Add to Compare') ?></a></li>
40
+ <?php endif; ?>
41
+ </ul>
42
+ </div>
43
+ </li>
44
+ <?php endforeach; ?>
45
+ </ul>
46
+ <script type="text/javascript">decorateList('crosssell-products-list', 'none-recursive')</script>
47
+ </div>
48
+ <?php endif; ?>
app/design/frontend/base/default/template/KCO/cart/shipping.phtml ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+ ?>
22
+ <?php /** @var $this Mage_Checkout_Block_Cart_Shipping */ ?>
23
+ <div class="shipping col2-set">
24
+
25
+ <h2><?php echo $this->__('Estimate Shipping and Tax') ?></h2>
26
+ <div class="shipping-form">
27
+ <div class="col-1">
28
+ <form action="<?php echo $this->getUrl('checkout/cart/estimatePost') ?>" method="post" id="shipping-zip-form">
29
+ <p><?php echo $this->__('Enter your destination to get a shipping estimate.') ?></p>
30
+ <ul class="form-list">
31
+ <li>
32
+ <label for="country" class="required"><em>*</em><?php echo $this->__('Country') ?></label>
33
+ <div class="input-box">
34
+ <?php echo Mage::getBlockSingleton('directory/data')->getCountryHtmlSelect($this->getEstimateCountryId()) ?>
35
+ </div>
36
+ </li>
37
+ <?php //if($this->getStateActive()): ?>
38
+ <li>
39
+ <label for="region_id"<?php if ($this->isStateProvinceRequired()) echo ' class="required"' ?>><?php if ($this->isStateProvinceRequired()) echo '<em>*</em>' ?><?php echo $this->__('State/Province') ?></label>
40
+ <div class="input-box">
41
+ <select id="region_id" name="region_id" title="<?php echo $this->__('State/Province') ?>" style="display:none;"<?php echo ($this->isStateProvinceRequired() ? ' class="validate-select"' : '') ?>>
42
+ <option value=""><?php echo $this->__('Please select region, state or province') ?></option>
43
+ </select>
44
+ <script type="text/javascript">
45
+ //<![CDATA[
46
+ $('region_id').setAttribute('defaultValue', "<?php echo $this->getEstimateRegionId() ?>");
47
+ //]]>
48
+ </script>
49
+ <input type="text" id="region" name="region" value="<?php echo $this->escapeHtml($this->getEstimateRegion()) ?>" title="<?php echo $this->__('State/Province') ?>" class="input-text" style="display:none;" />
50
+ </div>
51
+ </li>
52
+ <?php //endif; ?>
53
+ <?php if($this->getCityActive()): ?>
54
+ <li>
55
+ <label for="city"<?php if ($this->isCityRequired()) echo ' class="required"' ?>><?php if ($this->isCityRequired()) echo '<em>*</em>' ?><?php echo $this->__('City') ?></label>
56
+ <div class="input-box">
57
+ <input class="input-text<?php if ($this->isCityRequired()):?> required-entry<?php endif;?>" id="city" type="text" name="estimate_city" value="<?php echo $this->escapeHtml($this->getEstimateCity()) ?>" />
58
+ </div>
59
+ </li>
60
+ <?php endif; ?>
61
+ <li>
62
+ <label for="postcode"<?php if ($this->isZipCodeRequired()) echo ' class="required"' ?>><?php if ($this->isZipCodeRequired()) echo '<em>*</em>' ?><?php echo $this->__('Zip/Postal Code') ?></label>
63
+ <div class="input-box">
64
+ <input class="input-text validate-postcode<?php if ($this->isZipCodeRequired()):?> required-entry<?php endif;?>" type="text" id="postcode" name="estimate_postcode" value="<?php echo $this->escapeHtml($this->getEstimatePostcode()) ?>" />
65
+ </div>
66
+ </li>
67
+ </ul>
68
+ <div class="buttons-set">
69
+ <button type="button" title="<?php echo $this->__('Get a Quote') ?>" onclick="coShippingMethodForm.submit()" class="button"><span><span><?php echo $this->__('Get a Quote') ?></span></span></button>
70
+ </div>
71
+ </form>
72
+ </div>
73
+ <script type="text/javascript">
74
+ //<![CDATA[
75
+ new RegionUpdater('country', 'region', 'region_id', <?php echo $this->helper('directory')->getRegionJson() ?>);
76
+ //]]>
77
+ </script>
78
+
79
+ <?php if (($_shippingRateGroups = $this->getEstimateRates())): ?>
80
+ <div class="col-2">
81
+ <form id="co-shipping-method-form" action="<?php echo $this->getUrl('checkout/cart/estimateUpdatePost') ?>">
82
+ <dl class="sp-methods">
83
+ <?php foreach ($_shippingRateGroups as $code => $_rates): ?>
84
+ <dt><?php echo $this->escapeHtml($this->getCarrierName($code)) ?></dt>
85
+ <dd>
86
+ <ul>
87
+ <?php foreach ($_rates as $_rate): ?>
88
+ <li<?php if ($_rate->getErrorMessage()) echo ' class="error-msg"';?>>
89
+ <?php if ($_rate->getErrorMessage()): ?>
90
+ <?php echo $this->escapeHtml($_rate->getErrorMessage()) ?>
91
+ <?php else: ?>
92
+ <input name="estimate_method" type="radio" value="<?php echo $this->escapeHtml($_rate->getCode()) ?>" id="s_method_<?php echo $_rate->getCode() ?>"<?php if($_rate->getCode()===$this->getAddressShippingMethod()) echo ' checked="checked"' ?> class="radio" />
93
+ <label for="s_method_<?php echo $_rate->getCode() ?>"><?php echo $this->escapeHtml($_rate->getMethodTitle()) ?>
94
+ <?php $_excl = $this->getShippingPrice($_rate->getPrice(), $this->helper('tax')->displayShippingPriceIncludingTax()); ?>
95
+ <?php $_incl = $this->getShippingPrice($_rate->getPrice(), true); ?>
96
+ <?php echo $_excl; ?>
97
+ <?php if ($this->helper('tax')->displayShippingBothPrices() && $_incl != $_excl): ?>
98
+ (<?php echo $this->__('Incl. Tax'); ?> <?php echo $_incl; ?>)
99
+ <?php endif; ?>
100
+ </label>
101
+ <?php endif ?>
102
+ </li>
103
+ <?php endforeach; ?>
104
+ </ul>
105
+ </dd>
106
+ <?php endforeach; ?>
107
+ </dl>
108
+ <div class="buttons-set">
109
+ <button type="submit" title="<?php echo $this->__('Update Total') ?>" class="button" name="do" value="<?php echo $this->__('Update Total') ?>"><span><span><?php echo $this->__('Update Total') ?></span></span></button>
110
+ </div>
111
+ </form>
112
+ </div>
113
+ <?php endif; ?>
114
+ <script type="text/javascript">
115
+ //<![CDATA[
116
+ var coShippingMethodForm = new VarienForm('shipping-zip-form');
117
+ var countriesWithOptionalZip = <?php echo $this->helper('directory')->getCountriesWithOptionalZip(true) ?>;
118
+
119
+ coShippingMethodForm.submit = function () {
120
+ var country = $F('country');
121
+ var optionalZip = false;
122
+
123
+ for (i=0; i < countriesWithOptionalZip.length; i++) {
124
+ if (countriesWithOptionalZip[i] == country) {
125
+ optionalZip = true;
126
+ }
127
+ }
128
+ if (optionalZip) {
129
+ $('postcode').removeClassName('required-entry');
130
+ }
131
+ else {
132
+ $('postcode').addClassName('required-entry');
133
+ }
134
+ return VarienForm.prototype.submit.bind(coShippingMethodForm)();
135
+ }
136
+ //]]>
137
+ </script>
138
+ </div>
139
+ </div>
app/design/frontend/base/default/template/KCO/info.phtml ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+ ?>
22
+
23
+ <img src="<?php echo $imgSrc; ?>" alt="Klarna Checkout" />
24
+ <p><?php echo $this->__('Klarna Checkout'); ?></p>
app/design/frontend/base/default/template/KCO/link.phtml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+ ?>
22
+ <?php $helper = Mage::helper("klarnaCheckout"); ?>
23
+
24
+ <a href="<?php echo $helper->getCartUri();?>" class="klarnaLink">
25
+ <img src="<?php echo $helper->getLogoSrc(); ?>" alt="Klarna Checkout" />
26
+ <p class="klarna_link_text"><?php echo $this->__($helper->getLinkText());?></p>
27
+ </a>
28
+
29
+ <?php echo $this->getChildHtml('original'); ?>
app/design/frontend/base/default/template/KCO/onepage/link.phtml ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is released under a custom license by Avenla Oy.
4
+ * All rights reserved
5
+ *
6
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
7
+ * For questions and support - klarna-support@avenla.com
8
+ *
9
+ * @category Avenla
10
+ * @package Avenla_KlarnaCheckout
11
+ * @copyright Copyright (c) Avenla Oy
12
+ * @link http://www.avenla.fi
13
+ */
14
+
15
+ /**
16
+ * Avenla KlarnaCheckout
17
+ *
18
+ * @category Avenla
19
+ * @package Avenla_KlarnaCheckout
20
+ */
21
+ ?>
22
+
23
+ <?php if ($this->isPossibleOnepageCheckout()):?>
24
+ <a href="<?php echo $this->getCheckoutUrl() ?>" <?php if ($this->isDisabled()): ?> class="disabled"<?php endif; ?>><?php echo $this->__('Proceed to Checkout') ?></a>
25
+ <?php endif?>
app/etc/modules/Avenla_KlarnaCheckout.xml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <config>
2
+ <modules>
3
+ <Avenla_KlarnaCheckout>
4
+ <active>true</active>
5
+ <codePool>community</codePool>
6
+ <depends>
7
+ <Mage_Payment />
8
+ </depends>
9
+ </Avenla_KlarnaCheckout>
10
+ </modules>
11
+ </config>
app/locale/fi_FI/Avenla_KlarnaCheckout.csv ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "Klarna online GUI", "Klarna hallinta"
2
+ "Klarna Invoice number: %s", "Klarna laskunumero: %s"
3
+ "Packing slip", "Lasku"
4
+ "Klarna reservation number: %s", "Klarna varausnumero: %s"
5
+ "Klarna reservation expires: %s", "Klarna varaus erääntyy: %s"
6
+ "Klarna reservation has expired.", "Klarna varaus on erääntynyt."
7
+ "Klarna API Documentation", "Klarna API dokumentaatio"
8
+ "Warning!", "Huom!"
9
+ "Go to Klarna Checkout", "Siirry Klarna Checkouttiin"
10
+ "Connection to Klarna failed, please check your eid/shared secret and store settings.", "Yhteys Klarnaan epäonnistui, tarkista tunnukset sekä kaupan asetukset."
11
+ "Discount is applied before taxes, this may cause different price on Klarna Checkout. Please check store tax configation.", "Alennus lisätään ennen veroja: tämä voi aiheuttaa eri hinnan Klarna Checkoutissa. Tarkista kaupan veroasetukset."
12
+ "Catalog prices are set excluding tax, this may result in different prices in Checkout.", "Katalogin hinnat on annettu verottomina, tämä voi aiheuttaa eri hintoja Klarna Checkoutissa."
13
+ "Klarna Checkout is not available", "Klarna Checkout ei ole käytettävissä"
14
+ "Please select shipping method to use Klarna Checkout", "Valitse toimitustapa käyttääksesi Klarna Checkouttia"
15
+ "No Klarna reservation number found in order", "Tilauksesta ei löytynyt Klarna varausnumeroa"
16
+ "Order canceled: Failed to create order in Klarna.", "Tilaus peruutettu: tilausta ei onnistuttu luomaan Klarnaan."
17
+ "Enabled", "Käytössä"
18
+ "Server", "Palvelin"
19
+ "Merchant ID", "Kauppias ID"
20
+ "Shared secret", "Salausavain"
21
+ "Activated Countries", "Aktivoidut maat"
22
+ "Locale", "Kieli"
23
+ "Klarna Checkout won't be displayed if selected locale is not allowed by Klarna", "Klarna Checkouttia ei näytetä mikäli valittu kieli ei ole Klarnan tukema."
24
+ "Activate partial shipments", "Aktivoi osittaiset lähetykset"
25
+ "When selected, partial shipments will be automatically activated in Klarna", "Mikäli päällä, osittaiset toimitukset aktivoidaan automaattisesti Klarnassa"
26
+ "Title", "Otsikko"
27
+ "Terms URL", "Ehtojen URL"
28
+ "Relative to Website Base URL", "Relatiivinen sivuston osoitteeseen"
29
+ "Google Analytics account no", "Google Analytics tilin nro"
30
+ "Enter account number to use Google Ecommerce tracking", "Syötä tilin numero käyttääksesi Google Ecommerce seurantaa"
31
+ "Failed to activate reservation %s", "Varauksen %s aktivointi epäonnistui"
32
+ "Activate Klarna reservation", "Aktivoi Klarna varaus"
33
+ "Captured amount of %s .Created Klarna invoice %s", "Maksettu summa %s . Luotiin Klarna lasku: %s"
34
+ "Klarna reservation <b>%s</b> was canceled.", "Klarna varaus <b>%s</b> peruutettiin."
35
+ "Failed to cancel Klarna reservation <b>%s</b>.(%s - %s)", "Varauksen <b>%s</b> peruuttaminen epäonnistui.(%s - %s)"
36
+ "Adjustment fee", "Tasaus"
37
+ "Tax class for all amount returns / adjustment fee", "Veroluokka summa hyvityksille / kuluille"
38
+ "Link text", "Linkin teksti"
39
+ "Other payment methods", "Muut maksutavat"
40
+ "Please fill in your post code", "Ole hyvä ja täytä postinumerosi"
41
+ "Please select country", "Ole hyvä ja valitse maa"
42
+ "I accept the terms in the license agreement", "Hyväksyn sopimusehdot"
43
+ "License agreement", "Sopimusehdot"
44
+ "By accepting the license agreement and filling in your contact information you can use Klarna Checkout module for free.", "Hyväksymällä sopimusehdot ja täyttämällä yhteystietosi voit käyttää Klarna Checkout moduulia ilmaiseksi."
lib/Klarna/Country.php ADDED
@@ -0,0 +1,760 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * KlarnaCountry
5
+ *
6
+ * PHP Version 5.3
7
+ *
8
+ * @category Payment
9
+ * @package KlarnaAPI
10
+ * @author MS Dev <ms.modules@klarna.com>
11
+ * @copyright 2012 Klarna AB (http://klarna.com)
12
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
13
+ * @link http://integration.klarna.com/
14
+ */
15
+
16
+ /**
17
+ * Country Constants class
18
+ *
19
+ * @category Payment
20
+ * @package KlarnaAPI
21
+ * @author MS Dev <ms.modules@klarna.com>
22
+ * @copyright 2012 Klarna AB (http://klarna.com)
23
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
24
+ * @link http://integration.klarna.com/
25
+ */
26
+ class KlarnaCountry
27
+ {
28
+ /**
29
+ * Country constant for Austria (AT).<br>
30
+ * ISO3166_AT
31
+ *
32
+ * @var int
33
+ */
34
+ const AT = 15;
35
+
36
+ /**
37
+ * Country constant for Denmark (DK).<br>
38
+ * ISO3166_DK
39
+ *
40
+ * @var int
41
+ */
42
+ const DK = 59;
43
+
44
+ /**
45
+ * Country constant for Finland (FI).<br>
46
+ * ISO3166_FI
47
+ *
48
+ * @var int
49
+ */
50
+ const FI = 73;
51
+
52
+ /**
53
+ * Country constant for Germany (DE).<br>
54
+ * ISO3166_DE
55
+ *
56
+ * @var int
57
+ */
58
+ const DE = 81;
59
+
60
+ /**
61
+ * Country constant for Netherlands (NL).<br>
62
+ * ISO3166_NL
63
+ *
64
+ * @var int
65
+ */
66
+ const NL = 154;
67
+
68
+ /**
69
+ * Country constant for Norway (NO).<br>
70
+ * ISO3166_NO
71
+ *
72
+ * @var int
73
+ */
74
+ const NO = 164;
75
+
76
+ /**
77
+ * Country constant for Sweden (SE).<br>
78
+ * ISO3166_SE
79
+ *
80
+ * @var int
81
+ */
82
+ const SE = 209;
83
+
84
+ /**
85
+ * Converts a country code, e.g. 'de' or 'deu' to the KlarnaCountry constant.
86
+ *
87
+ * @param string $val country code iso-alpha-2 or iso-alpha-3
88
+ *
89
+ * @return int|null
90
+ */
91
+ public static function fromCode($val)
92
+ {
93
+ $val = strtoupper($val);
94
+ if (strlen($val) === 3) {
95
+ if (self::$_tlcFlip === array()) {
96
+ self::$_tlcFlip = array_flip(self::$_tlcMap);
97
+ }
98
+ if (!array_key_exists($val, self::$_tlcFlip)) {
99
+ return null;
100
+ }
101
+ $val = self::$_tlcFlip[$val];
102
+ }
103
+ if (array_key_exists($val, self::$_countries)) {
104
+ return self::$_countries[$val];
105
+ }
106
+ return null;
107
+ }
108
+
109
+ /**
110
+ * Converts a KlarnaCountry constant to the respective country code.
111
+ *
112
+ * @param int $val KlarnaCountry constant
113
+ * @param bool $alpha3 Whether to return a ISO-3166-1 alpha-3 code
114
+ *
115
+ * @return string|null
116
+ */
117
+ public static function getCode($val, $alpha3 = false)
118
+ {
119
+ if (self::$_countryFlip === array()) {
120
+ self::$_countryFlip = array_flip(self::$_countries);
121
+ }
122
+ if (!array_key_exists($val, self::$_countryFlip)) {
123
+ return null;
124
+ }
125
+ $result = self::$_countryFlip[$val];
126
+ if ($alpha3) {
127
+ return self::$_tlcMap[$result];
128
+ }
129
+ return $result;
130
+ }
131
+
132
+ /**
133
+ * Checks country against currency and returns true if they match.
134
+ *
135
+ * @param int $country {@link KlarnaCountry}
136
+ * @param int $language {@link KlarnaLanguage}
137
+ *
138
+ * @deprecated Do not use.
139
+ *
140
+ * @return bool
141
+ */
142
+ public static function checkLanguage($country, $language)
143
+ {
144
+ switch($country) {
145
+ case KlarnaCountry::AT:
146
+ case KlarnaCountry::DE:
147
+ return ($language === KlarnaLanguage::DE);
148
+ case KlarnaCountry::NL:
149
+ return ($language === KlarnaLanguage::NL);
150
+ case KlarnaCountry::FI:
151
+ return ($language === KlarnaLanguage::FI);
152
+ case KlarnaCountry::DK:
153
+ return ($language === KlarnaLanguage::DA);
154
+ case KlarnaCountry::NO:
155
+ return ($language === KlarnaLanguage::NB);
156
+ case KlarnaCountry::SE:
157
+ return ($language === KlarnaLanguage::SV);
158
+ default:
159
+ //Country not yet supported by Klarna.
160
+ return false;
161
+ }
162
+ }
163
+ /**
164
+ * Checks country against language and returns true if they match.
165
+ *
166
+ * @param int $country {@link KlarnaCountry}
167
+ * @param int $currency {@link KlarnaCurrency}
168
+ *
169
+ * @deprecated Do not use.
170
+ *
171
+ * @return bool
172
+ */
173
+ public static function checkCurrency($country, $currency)
174
+ {
175
+ switch($country) {
176
+ case KlarnaCountry::AT:
177
+ case KlarnaCountry::DE:
178
+ case KlarnaCountry::NL:
179
+ case KlarnaCountry::FI:
180
+ return ($currency === KlarnaCurrency::EUR);
181
+ case KlarnaCountry::DK:
182
+ return ($currency === KlarnaCurrency::DKK);
183
+ case KlarnaCountry::NO:
184
+ return ($currency === KlarnaCurrency::NOK);
185
+ case KlarnaCountry::SE:
186
+ return ($currency === KlarnaCurrency::SEK);
187
+ default:
188
+ //Country not yet supported by Klarna.
189
+ return false;
190
+ }
191
+ }
192
+ /**
193
+ * Get language for supplied country. Defaults to English.
194
+ *
195
+ * @param int $country KlarnaCountry constant
196
+ *
197
+ * @deprecated Do not use.
198
+ *
199
+ * @return int
200
+ */
201
+ public static function getLanguage($country)
202
+ {
203
+ switch($country) {
204
+ case KlarnaCountry::AT:
205
+ case KlarnaCountry::DE:
206
+ return KlarnaLanguage::DE;
207
+ case KlarnaCountry::NL:
208
+ return KlarnaLanguage::NL;
209
+ case KlarnaCountry::FI:
210
+ return KlarnaLanguage::FI;
211
+ case KlarnaCountry::DK:
212
+ return KlarnaLanguage::DA;
213
+ case KlarnaCountry::NO:
214
+ return KlarnaLanguage::NB;
215
+ case KlarnaCountry::SE:
216
+ return KlarnaLanguage::SV;
217
+ default:
218
+ return KlarnaLanguage::EN;
219
+ }
220
+ }
221
+ /**
222
+ * Get currency for supplied country
223
+ *
224
+ * @param int $country KlarnaCountry constant
225
+ *
226
+ * @deprecated Do not use.
227
+ *
228
+ * @return int|false
229
+ */
230
+ public static function getCurrency($country)
231
+ {
232
+ switch($country) {
233
+ case KlarnaCountry::AT:
234
+ case KlarnaCountry::DE:
235
+ case KlarnaCountry::NL:
236
+ case KlarnaCountry::FI:
237
+ return KlarnaCurrency::EUR;
238
+ case KlarnaCountry::DK:
239
+ return KlarnaCurrency::DKK;
240
+ case KlarnaCountry::NO:
241
+ return KlarnaCurrency::NOK;
242
+ case KlarnaCountry::SE:
243
+ return KlarnaCurrency::SEK;
244
+ default:
245
+ return false;
246
+ }
247
+ }
248
+
249
+ private static $_tlcFlip = array();
250
+
251
+ /**
252
+ * Cache for the flipped country array
253
+ *
254
+ * @var array
255
+ */
256
+ private static $_countryFlip = array();
257
+
258
+ /**
259
+ * Array containing all countries and their KRED Code
260
+ *
261
+ * @var array
262
+ */
263
+ private static $_countries = array(
264
+ 'AF' => 1, // AFGHANISTAN
265
+ 'AX' => 2, // ÅLAND ISLANDS
266
+ 'AL' => 3, // ALBANIA
267
+ 'DZ' => 4, // ALGERIA
268
+ 'AS' => 5, // AMERICAN SAMOA
269
+ 'AD' => 6, // ANDORRA
270
+ 'AO' => 7, // ANGOLA
271
+ 'AI' => 8, // ANGUILLA
272
+ 'AQ' => 9, // ANTARCTICA
273
+ 'AG' => 10, // ANTIGUA AND BARBUDA
274
+ 'AR' => 11, // ARGENTINA
275
+ 'AM' => 12, // ARMENIA
276
+ 'AW' => 13, // ARUBA
277
+ 'AU' => 14, // AUSTRALIA
278
+ 'AT' => 15, // AUSTRIA
279
+ 'AZ' => 16, // AZERBAIJAN
280
+ 'BS' => 17, // BAHAMAS
281
+ 'BH' => 18, // BAHRAIN
282
+ 'BD' => 19, // BANGLADESH
283
+ 'BB' => 20, // BARBADOS
284
+ 'BY' => 21, // BELARUS
285
+ 'BE' => 22, // BELGIUM
286
+ 'BZ' => 23, // BELIZE
287
+ 'BJ' => 24, // BENIN
288
+ 'BM' => 25, // BERMUDA
289
+ 'BT' => 26, // BHUTAN
290
+ 'BO' => 27, // BOLIVIA
291
+ 'BA' => 28, // BOSNIA AND HERZEGOVINA
292
+ 'BW' => 29, // BOTSWANA
293
+ 'BV' => 30, // BOUVET ISLAND
294
+ 'BR' => 31, // BRAZIL
295
+ 'IO' => 32, // BRITISH INDIAN OCEAN TERRITORY
296
+ 'BN' => 33, // BRUNEI DARUSSALAM
297
+ 'BG' => 34, // BULGARIA
298
+ 'BF' => 35, // BURKINA FASO
299
+ 'BI' => 36, // BURUNDI
300
+ 'KH' => 37, // CAMBODIA
301
+ 'CM' => 38, // CAMEROON
302
+ 'CA' => 39, // CANADA
303
+ 'CV' => 40, // CAPE VERDE
304
+ 'KY' => 41, // CAYMAN ISLANDS
305
+ 'CF' => 42, // CENTRAL AFRICAN REPUBLIC
306
+ 'TD' => 43, // CHAD
307
+ 'CL' => 44, // CHILE
308
+ 'CN' => 45, // CHINA
309
+ 'CX' => 46, // CHRISTMAS ISLAND
310
+ 'CC' => 47, // COCOS (KEELING) ISLANDS
311
+ 'CO' => 48, // COLOMBIA
312
+ 'KM' => 49, // COMOROS
313
+ 'CG' => 50, // CONGO
314
+ 'CD' => 51, // CONGO, THE DEMOCRATIC REPUBLIC OF THE
315
+ 'CK' => 52, // COOK ISLANDS
316
+ 'CR' => 53, // COSTA RICA
317
+ 'CI' => 54, // COTE D'IVOIRE
318
+ 'HR' => 55, // CROATIA
319
+ 'CU' => 56, // CUBA
320
+ 'CY' => 57, // CYPRUS
321
+ 'CZ' => 58, // CZECH REPUBLIC
322
+ 'DK' => 59, // DENMARK
323
+ 'DJ' => 60, // DJIBOUTI
324
+ 'DM' => 61, // DOMINICA
325
+ 'DO' => 62, // DOMINICAN REPUBLIC
326
+ 'EC' => 63, // ECUADOR
327
+ 'EG' => 64, // EGYPT
328
+ 'SV' => 65, // EL SALVADOR
329
+ 'GQ' => 66, // EQUATORIAL GUINEA
330
+ 'ER' => 67, // ERITREA
331
+ 'EE' => 68, // ESTONIA
332
+ 'ET' => 69, // ETHIOPIA
333
+ 'FK' => 70, // FALKLAND ISLANDS (MALVINAS)
334
+ 'FO' => 71, // FAROE ISLANDS
335
+ 'FJ' => 72, // FIJI
336
+ 'FI' => 73, // FINLAND
337
+ 'FR' => 74, // FRANCE
338
+ 'GF' => 75, // FRENCH GUIANA
339
+ 'PF' => 76, // FRENCH POLYNESIA
340
+ 'TF' => 77, // FRENCH SOUTHERN TERRITORIES
341
+ 'GA' => 78, // GABON
342
+ 'GM' => 79, // GAMBIA
343
+ 'GE' => 80, // GEORGIA
344
+ 'DE' => 81, // GERMANY
345
+ 'GH' => 82, // GHANA
346
+ 'GI' => 83, // GIBRALTAR
347
+ 'GR' => 84, // GREECE
348
+ 'GL' => 85, // GREENLAND
349
+ 'GD' => 86, // GRENADA
350
+ 'GP' => 87, // GUADELOUPE
351
+ 'GU' => 88, // GUAM
352
+ 'GT' => 89, // GUATEMALA
353
+ 'GG' => 90, // GUERNSEY
354
+ 'GN' => 91, // GUINEA
355
+ 'GW' => 92, // GUINEA-BISSAU
356
+ 'GY' => 93, // GUYANA
357
+ 'HT' => 94, // HAITI
358
+ 'HM' => 95, // HEARD ISLAND AND MCDONALD ISLANDS
359
+ 'VA' => 96, // HOLY SEE (VATICAN CITY STATE)
360
+ 'HN' => 97, // HONDURAS
361
+ 'HK' => 98, // HONG KONG
362
+ 'HU' => 99, // HUNGARY
363
+ 'IS' => 100, // ICELAND
364
+ 'IN' => 101, // INDIA
365
+ 'ID' => 102, // INDONESIA
366
+ 'IR' => 103, // IRAN, ISLAMIC REPUBLIC OF
367
+ 'IQ' => 104, // IRAQ
368
+ 'IE' => 105, // IRELAND
369
+ 'IM' => 106, // ISLE OF MAN
370
+ 'IL' => 107, // ISRAEL
371
+ 'IT' => 108, // ITALY
372
+ 'JM' => 109, // JAMAICA
373
+ 'JP' => 110, // JAPAN
374
+ 'JE' => 111, // JERSEY
375
+ 'JO' => 112, // JORDAN
376
+ 'KZ' => 113, // KAZAKHSTAN
377
+ 'KE' => 114, // KENYA
378
+ 'KI' => 115, // KIRIBATI
379
+ 'KP' => 116, // KOREA, DEMOCRATIC PEOPLE'S REPUBLIC OF
380
+ 'KR' => 117, // KOREA, REPUBLIC OF
381
+ 'KW' => 118, // KUWAIT
382
+ 'KG' => 119, // KYRGYZSTAN
383
+ 'LA' => 120, // LAO PEOPLE'S DEMOCRATIC REPUBLIC
384
+ 'LV' => 121, // LATVIA
385
+ 'LB' => 122, // LEBANON
386
+ 'LS' => 123, // LESOTHO
387
+ 'LR' => 124, // LIBERIA
388
+ 'LY' => 125, // LIBYAN ARAB JAMAHIRIYA
389
+ 'LI' => 126, // LIECHTENSTEIN
390
+ 'LT' => 127, // LITHUANIA
391
+ 'LU' => 128, // LUXEMBOURG
392
+ 'MO' => 129, // MACAO
393
+ 'MK' => 130, // MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF
394
+ 'MG' => 131, // MADAGASCAR
395
+ 'MW' => 132, // MALAWI
396
+ 'MY' => 133, // MALAYSIA
397
+ 'MV' => 134, // MALDIVES
398
+ 'ML' => 135, // MALI
399
+ 'MT' => 136, // MALTA
400
+ 'MH' => 137, // MARSHALL ISLANDS
401
+ 'MQ' => 138, // MARTINIQUE
402
+ 'MR' => 139, // MAURITANIA
403
+ 'MU' => 140, // MAURITIUS
404
+ 'YT' => 141, // MAYOTTE
405
+ 'MX' => 142, // MEXICO
406
+ 'FM' => 143, // MICRONESIA FEDERATED STATES OF
407
+ 'MD' => 144, // MOLDOVA, REPUBLIC OF
408
+ 'MC' => 145, // MONACO
409
+ 'MN' => 146, // MONGOLIA
410
+ 'MS' => 147, // MONTSERRAT
411
+ 'MA' => 148, // MOROCCO
412
+ 'MZ' => 149, // MOZAMBIQUE
413
+ 'MM' => 150, // MYANMAR
414
+ 'NA' => 151, // NAMIBIA
415
+ 'NR' => 152, // NAURU
416
+ 'NP' => 153, // NEPAL
417
+ 'NL' => 154, // NETHERLANDS
418
+ 'AN' => 155, // NETHERLANDS ANTILLES
419
+ 'NC' => 156, // NEW CALEDONIA
420
+ 'NZ' => 157, // NEW ZEALAND
421
+ 'NI' => 158, // NICARAGUA
422
+ 'NE' => 159, // NIGER
423
+ 'NG' => 160, // NIGERIA
424
+ 'NU' => 161, // NIUE
425
+ 'NF' => 162, // NORFOLK ISLAND
426
+ 'MP' => 163, // NORTHERN MARIANA ISLANDS
427
+ 'NO' => 164, // NORWAY
428
+ 'OM' => 165, // OMAN
429
+ 'PK' => 166, // PAKISTAN
430
+ 'PW' => 167, // PALAU
431
+ 'PS' => 168, // PALESTINIAN TERRITORY OCCUPIED
432
+ 'PA' => 169, // PANAMA
433
+ 'PG' => 170, // PAPUA NEW GUINEA
434
+ 'PY' => 171, // PARAGUAY
435
+ 'PE' => 172, // PERU
436
+ 'PH' => 173, // PHILIPPINES
437
+ 'PN' => 174, // PITCAIRN
438
+ 'PL' => 175, // POLAND
439
+ 'PT' => 176, // PORTUGAL
440
+ 'PR' => 177, // PUERTO RICO
441
+ 'QA' => 178, // QATAR
442
+ 'RE' => 179, // REUNION
443
+ 'RO' => 180, // ROMANIA
444
+ 'RU' => 181, // RUSSIAN FEDERATION
445
+ 'RW' => 182, // RWANDA
446
+ 'SH' => 183, // SAINT HELENA
447
+ 'KN' => 184, // SAINT KITTS AND NEVIS
448
+ 'LC' => 185, // SAINT LUCIA
449
+ 'PM' => 186, // SAINT PIERRE AND MIQUELON
450
+ 'VC' => 187, // SAINT VINCENT AND THE GRENADINES
451
+ 'WS' => 188, // SAMOA
452
+ 'SM' => 189, // SAN MARINO
453
+ 'ST' => 190, // SAO TOME AND PRINCIPE
454
+ 'SA' => 191, // SAUDI ARABIA
455
+ 'SN' => 192, // SENEGAL
456
+ 'CS' => 193, // SERBIA AND MONTENEGRO
457
+ 'SC' => 194, // SEYCHELLES
458
+ 'SL' => 195, // SIERRA LEONE
459
+ 'SG' => 196, // SINGAPORE
460
+ 'SK' => 197, // SLOVAKIA
461
+ 'SI' => 198, // SLOVENIA
462
+ 'SB' => 199, // SOLOMON ISLANDS
463
+ 'SO' => 200, // SOMALIA
464
+ 'ZA' => 201, // SOUTH AFRICA
465
+ 'GS' => 202, // SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS
466
+ 'ES' => 203, // SPAIN
467
+ 'LK' => 204, // SRI LANKA
468
+ 'SD' => 205, // SUDAN
469
+ 'SR' => 206, // SURINAME
470
+ 'SJ' => 207, // SVALBARD AND JAN MAYEN
471
+ 'SZ' => 208, // SWAZILAND
472
+ 'SE' => 209, // SWEDEN
473
+ 'CH' => 210, // SWITZERLAND
474
+ 'SY' => 211, // SYRIAN ARAB REPUBLIC
475
+ 'TW' => 212, // TAIWAN PROVINCE OF CHINA
476
+ 'TJ' => 213, // TAJIKISTAN
477
+ 'TZ' => 214, // TANZANIA, UNITED REPUBLIC OF
478
+ 'TH' => 215, // THAILAND
479
+ 'TL' => 216, // TIMOR-LESTE
480
+ 'TG' => 217, // TOGO
481
+ 'TK' => 218, // TOKELAU
482
+ 'TO' => 219, // TONGA
483
+ 'TT' => 220, // TRINIDAD AND TOBAGO
484
+ 'TN' => 221, // TUNISIA
485
+ 'TR' => 222, // TURKEY
486
+ 'TM' => 223, // TURKMENISTAN
487
+ 'TC' => 224, // TURKS AND CAICOS ISLANDS
488
+ 'TV' => 225, // TUVALU
489
+ 'UG' => 226, // UGANDA
490
+ 'UA' => 227, // UKRAINE
491
+ 'AE' => 228, // UNITED ARAB EMIRATES
492
+ 'GB' => 229, // UNITED KINGDOM
493
+ 'US' => 230, // UNITED STATES
494
+ 'UM' => 231, // UNITED STATES MINOR OUTLYING ISLANDS
495
+ 'UY' => 232, // URUGUAY
496
+ 'UZ' => 233, // UZBEKISTAN
497
+ 'VU' => 234, // VANUATU
498
+ 'VE' => 235, // VENEZUELA
499
+ 'VN' => 236, // VIET NAM
500
+ 'VG' => 237, // VIRGIN ISLANDS, BRITISH
501
+ 'VI' => 238, // VIRGIN ISLANDS, US
502
+ 'WF' => 239, // WALLIS AND FUTUNA
503
+ 'EH' => 240, // WESTERN SAHARA
504
+ 'YE' => 241, // YEMEN
505
+ 'ZM' => 242, // ZAMBIA
506
+ 'ZW' => 243 // ZIMBABWE
507
+ );
508
+
509
+ private static $_tlcMap = array(
510
+ 'AF' => 'AFG',
511
+ 'AX' => 'ALA',
512
+ 'AL' => 'ALB',
513
+ 'DZ' => 'DZA',
514
+ 'AS' => 'ASM',
515
+ 'AD' => 'AND',
516
+ 'AO' => 'AGO',
517
+ 'AI' => 'AIA',
518
+ 'AQ' => 'ATA',
519
+ 'AG' => 'ATG',
520
+ 'AR' => 'ARG',
521
+ 'AM' => 'ARM',
522
+ 'AW' => 'ABW',
523
+ 'AU' => 'AUS',
524
+ 'AT' => 'AUT',
525
+ 'AZ' => 'AZE',
526
+ 'BS' => 'BHS',
527
+ 'BH' => 'BHR',
528
+ 'BD' => 'BGD',
529
+ 'BB' => 'BRB',
530
+ 'BY' => 'BLR',
531
+ 'BE' => 'BEL',
532
+ 'BZ' => 'BLZ',
533
+ 'BJ' => 'BEN',
534
+ 'BM' => 'BMU',
535
+ 'BT' => 'BTN',
536
+ 'BO' => 'BOL',
537
+ 'BQ' => 'BES',
538
+ 'BA' => 'BIH',
539
+ 'BW' => 'BWA',
540
+ 'BV' => 'BVT',
541
+ 'BR' => 'BRA',
542
+ 'IO' => 'IOT',
543
+ 'BN' => 'BRN',
544
+ 'BG' => 'BGR',
545
+ 'BF' => 'BFA',
546
+ 'BI' => 'BDI',
547
+ 'KH' => 'KHM',
548
+ 'CM' => 'CMR',
549
+ 'CA' => 'CAN',
550
+ 'CV' => 'CPV',
551
+ 'KY' => 'CYM',
552
+ 'CF' => 'CAF',
553
+ 'TD' => 'TCD',
554
+ 'CL' => 'CHL',
555
+ 'CN' => 'CHN',
556
+ 'CX' => 'CXR',
557
+ 'CC' => 'CCK',
558
+ 'CO' => 'COL',
559
+ 'KM' => 'COM',
560
+ 'CG' => 'COG',
561
+ 'CD' => 'COD',
562
+ 'CK' => 'COK',
563
+ 'CR' => 'CRI',
564
+ 'CI' => 'CIV',
565
+ 'HR' => 'HRV',
566
+ 'CU' => 'CUB',
567
+ 'CW' => 'CUW',
568
+ 'CY' => 'CYP',
569
+ 'CZ' => 'CZE',
570
+ 'DK' => 'DNK',
571
+ 'DJ' => 'DJI',
572
+ 'DM' => 'DMA',
573
+ 'DO' => 'DOM',
574
+ 'EC' => 'ECU',
575
+ 'EG' => 'EGY',
576
+ 'SV' => 'SLV',
577
+ 'GQ' => 'GNQ',
578
+ 'ER' => 'ERI',
579
+ 'EE' => 'EST',
580
+ 'ET' => 'ETH',
581
+ 'FK' => 'FLK',
582
+ 'FO' => 'FRO',
583
+ 'FJ' => 'FJI',
584
+ 'FI' => 'FIN',
585
+ 'FR' => 'FRA',
586
+ 'GF' => 'GUF',
587
+ 'PF' => 'PYF',
588
+ 'TF' => 'ATF',
589
+ 'GA' => 'GAB',
590
+ 'GM' => 'GMB',
591
+ 'GE' => 'GEO',
592
+ 'DE' => 'DEU',
593
+ 'GH' => 'GHA',
594
+ 'GI' => 'GIB',
595
+ 'GR' => 'GRC',
596
+ 'GL' => 'GRL',
597
+ 'GD' => 'GRD',
598
+ 'GP' => 'GLP',
599
+ 'GU' => 'GUM',
600
+ 'GT' => 'GTM',
601
+ 'GG' => 'GGY',
602
+ 'GN' => 'GIN',
603
+ 'GW' => 'GNB',
604
+ 'GY' => 'GUY',
605
+ 'HT' => 'HTI',
606
+ 'HM' => 'HMD',
607
+ 'VA' => 'VAT',
608
+ 'HN' => 'HND',
609
+ 'HK' => 'HKG',
610
+ 'HU' => 'HUN',
611
+ 'IS' => 'ISL',
612
+ 'IN' => 'IND',
613
+ 'ID' => 'IDN',
614
+ 'IR' => 'IRN',
615
+ 'IQ' => 'IRQ',
616
+ 'IE' => 'IRL',
617
+ 'IM' => 'IMN',
618
+ 'IL' => 'ISR',
619
+ 'IT' => 'ITA',
620
+ 'JM' => 'JAM',
621
+ 'JP' => 'JPN',
622
+ 'JE' => 'JEY',
623
+ 'JO' => 'JOR',
624
+ 'KZ' => 'KAZ',
625
+ 'KE' => 'KEN',
626
+ 'KI' => 'KIR',
627
+ 'KP' => 'PRK',
628
+ 'KR' => 'KOR',
629
+ 'KW' => 'KWT',
630
+ 'KG' => 'KGZ',
631
+ 'LA' => 'LAO',
632
+ 'LV' => 'LVA',
633
+ 'LB' => 'LBN',
634
+ 'LS' => 'LSO',
635
+ 'LR' => 'LBR',
636
+ 'LY' => 'LBY',
637
+ 'LI' => 'LIE',
638
+ 'LT' => 'LTU',
639
+ 'LU' => 'LUX',
640
+ 'MO' => 'MAC',
641
+ 'MK' => 'MKD',
642
+ 'MG' => 'MDG',
643
+ 'MW' => 'MWI',
644
+ 'MY' => 'MYS',
645
+ 'MV' => 'MDV',
646
+ 'ML' => 'MLI',
647
+ 'MT' => 'MLT',
648
+ 'MH' => 'MHL',
649
+ 'MQ' => 'MTQ',
650
+ 'MR' => 'MRT',
651
+ 'MU' => 'MUS',
652
+ 'YT' => 'MYT',
653
+ 'MX' => 'MEX',
654
+ 'FM' => 'FSM',
655
+ 'MD' => 'MDA',
656
+ 'MC' => 'MCO',
657
+ 'MN' => 'MNG',
658
+ 'ME' => 'MNE',
659
+ 'MS' => 'MSR',
660
+ 'MA' => 'MAR',
661
+ 'MZ' => 'MOZ',
662
+ 'MM' => 'MMR',
663
+ 'NA' => 'NAM',
664
+ 'NR' => 'NRU',
665
+ 'NP' => 'NPL',
666
+ 'NL' => 'NLD',
667
+ 'NC' => 'NCL',
668
+ 'NZ' => 'NZL',
669
+ 'NI' => 'NIC',
670
+ 'NE' => 'NER',
671
+ 'NG' => 'NGA',
672
+ 'NU' => 'NIU',
673
+ 'NF' => 'NFK',
674
+ 'MP' => 'MNP',
675
+ 'NO' => 'NOR',
676
+ 'OM' => 'OMN',
677
+ 'PK' => 'PAK',
678
+ 'PW' => 'PLW',
679
+ 'PS' => 'PSE',
680
+ 'PA' => 'PAN',
681
+ 'PG' => 'PNG',
682
+ 'PY' => 'PRY',
683
+ 'PE' => 'PER',
684
+ 'PH' => 'PHL',
685
+ 'PN' => 'PCN',
686
+ 'PL' => 'POL',
687
+ 'PT' => 'PRT',
688
+ 'PR' => 'PRI',
689
+ 'QA' => 'QAT',
690
+ 'RE' => 'REU',
691
+ 'RO' => 'ROU',
692
+ 'RU' => 'RUS',
693
+ 'RW' => 'RWA',
694
+ 'BL' => 'BLM',
695
+ 'SH' => 'SHN',
696
+ 'KN' => 'KNA',
697
+ 'LC' => 'LCA',
698
+ 'MF' => 'MAF',
699
+ 'PM' => 'SPM',
700
+ 'VC' => 'VCT',
701
+ 'WS' => 'WSM',
702
+ 'SM' => 'SMR',
703
+ 'ST' => 'STP',
704
+ 'SA' => 'SAU',
705
+ 'SN' => 'SEN',
706
+ 'RS' => 'SRB',
707
+ 'SC' => 'SYC',
708
+ 'SL' => 'SLE',
709
+ 'SG' => 'SGP',
710
+ 'SX' => 'SXM',
711
+ 'SK' => 'SVK',
712
+ 'SI' => 'SVN',
713
+ 'SB' => 'SLB',
714
+ 'SO' => 'SOM',
715
+ 'ZA' => 'ZAF',
716
+ 'GS' => 'SGS',
717
+ 'SS' => 'SSD',
718
+ 'ES' => 'ESP',
719
+ 'LK' => 'LKA',
720
+ 'SD' => 'SDN',
721
+ 'SR' => 'SUR',
722
+ 'SJ' => 'SJM',
723
+ 'SZ' => 'SWZ',
724
+ 'SE' => 'SWE',
725
+ 'CH' => 'CHE',
726
+ 'SY' => 'SYR',
727
+ 'TW' => 'TWN',
728
+ 'TJ' => 'TJK',
729
+ 'TZ' => 'TZA',
730
+ 'TH' => 'THA',
731
+ 'TL' => 'TLS',
732
+ 'TG' => 'TGO',
733
+ 'TK' => 'TKL',
734
+ 'TO' => 'TON',
735
+ 'TT' => 'TTO',
736
+ 'TN' => 'TUN',
737
+ 'TR' => 'TUR',
738
+ 'TM' => 'TKM',
739
+ 'TC' => 'TCA',
740
+ 'TV' => 'TUV',
741
+ 'UG' => 'UGA',
742
+ 'UA' => 'UKR',
743
+ 'AE' => 'ARE',
744
+ 'GB' => 'GBR',
745
+ 'US' => 'USA',
746
+ 'UM' => 'UMI',
747
+ 'UY' => 'URY',
748
+ 'UZ' => 'UZB',
749
+ 'VU' => 'VUT',
750
+ 'VE' => 'VEN',
751
+ 'VN' => 'VNM',
752
+ 'VG' => 'VGB',
753
+ 'VI' => 'VIR',
754
+ 'WF' => 'WLF',
755
+ 'EH' => 'ESH',
756
+ 'YE' => 'YEM',
757
+ 'ZM' => 'ZMB',
758
+ 'ZW' => 'ZWE'
759
+ );
760
+ }
lib/Klarna/Currency.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * KlarnaCurrency
5
+ *
6
+ * PHP Version 5.3
7
+ *
8
+ * @category Payment
9
+ * @package KlarnaAPI
10
+ * @author MS Dev <ms.modules@klarna.com>
11
+ * @copyright 2012 Klarna AB (http://klarna.com)
12
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
13
+ * @link http://integration.klarna.com/
14
+ */
15
+
16
+ /**
17
+ * Currency Constants class
18
+ *
19
+ * @category Payment
20
+ * @package KlarnaAPI
21
+ * @author MS Dev <ms.modules@klarna.com>
22
+ * @copyright 2012 Klarna AB (http://klarna.com)
23
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
24
+ * @link http://integration.klarna.com/
25
+ */
26
+ class KlarnaCurrency
27
+ {
28
+
29
+ /**
30
+ * Currency constant for Swedish Crowns (SEK).
31
+ *
32
+ * @var int
33
+ */
34
+ const SEK = 0;
35
+
36
+ /**
37
+ * Currency constant for Norwegian Crowns (NOK).
38
+ *
39
+ * @var int
40
+ */
41
+ const NOK = 1;
42
+
43
+ /**
44
+ * Currency constant for Euro.
45
+ *
46
+ * @var int
47
+ */
48
+ const EUR = 2;
49
+
50
+ /**
51
+ * Currency constant for Danish Crowns (DKK).
52
+ *
53
+ * @var int
54
+ */
55
+ const DKK = 3;
56
+
57
+ /**
58
+ * Converts a currency code, e.g. 'eur' to the KlarnaCurrency constant.
59
+ *
60
+ * @param string $val currency code
61
+ *
62
+ * @return int|null
63
+ */
64
+ public static function fromCode($val)
65
+ {
66
+ switch(strtolower($val)) {
67
+ case 'dkk':
68
+ return self::DKK;
69
+ case 'eur':
70
+ case 'euro':
71
+ return self::EUR;
72
+ case 'nok':
73
+ return self::NOK;
74
+ case 'sek':
75
+ return self::SEK;
76
+ default:
77
+ return null;
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Converts a KlarnaCurrency constant to the respective language code.
83
+ *
84
+ * @param int $val KlarnaCurrency constant
85
+ *
86
+ * @return string|null
87
+ */
88
+ public static function getCode($val)
89
+ {
90
+ switch($val) {
91
+ case self::DKK:
92
+ return 'dkk';
93
+ case self::EUR:
94
+ return 'eur';
95
+ case self::NOK:
96
+ return 'nok';
97
+ case self::SEK:
98
+ return 'sek';
99
+ default:
100
+ return null;
101
+ }
102
+ }
103
+
104
+ }
lib/Klarna/Encoding.php ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * KlarnaEncoding
4
+ *
5
+ * PHP Version 5.3
6
+ *
7
+ * @category Payment
8
+ * @package KlarnaAPI
9
+ * @author MS Dev <ms.modules@klarna.com>
10
+ * @copyright 2012 Klarna AB (http://klarna.com)
11
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
12
+ * @link http://integration.klarna.com/
13
+ */
14
+
15
+ require_once 'Exceptions.php';
16
+
17
+ /**
18
+ * Encoding class
19
+ *
20
+ * @category Payment
21
+ * @package KlarnaAPI
22
+ * @author MS Dev <ms.modules@klarna.com>
23
+ * @copyright 2012 Klarna AB (http://klarna.com)
24
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
25
+ * @link http://integration.klarna.com/
26
+ */
27
+ class KlarnaEncoding
28
+ {
29
+ /**
30
+ * PNO/SSN encoding for Sweden.
31
+ *
32
+ * @var int
33
+ */
34
+ const PNO_SE = 2;
35
+
36
+ /**
37
+ * PNO/SSN encoding for Norway.
38
+ *
39
+ * @var int
40
+ */
41
+ const PNO_NO = 3;
42
+
43
+ /**
44
+ * PNO/SSN encoding for Finland.
45
+ *
46
+ * @var int
47
+ */
48
+ const PNO_FI = 4;
49
+
50
+ /**
51
+ * PNO/SSN encoding for Denmark.
52
+ *
53
+ * @var int
54
+ */
55
+ const PNO_DK = 5;
56
+
57
+ /**
58
+ * PNO/SSN encoding for Germany.
59
+ *
60
+ * @var int
61
+ */
62
+ const PNO_DE = 6;
63
+
64
+ /**
65
+ * PNO/SSN encoding for Netherlands.
66
+ *
67
+ * @var int
68
+ */
69
+ const PNO_NL = 7;
70
+
71
+ /**
72
+ * PNO/SSN encoding for Austria.
73
+ *
74
+ * @var int
75
+ */
76
+ const PNO_AT = 8;
77
+
78
+ /**
79
+ * Encoding constant for customer numbers.
80
+ *
81
+ * @see Klarna::setCustomerNo()
82
+ * @var int
83
+ */
84
+ const CUSTNO = 1000;
85
+
86
+ /**
87
+ * Encoding constant for email address.
88
+ *
89
+ * @var int
90
+ */
91
+ const EMAIL = 1001;
92
+
93
+ /**
94
+ * Encoding constant for cell numbers.
95
+ *
96
+ * @var int
97
+ */
98
+ const CELLNO = 1002;
99
+
100
+ /**
101
+ * Encoding constant for bank bic + account number.
102
+ *
103
+ * @var int
104
+ */
105
+ const BANK_BIC_ACC_NO = 1003;
106
+
107
+ /**
108
+ * Returns the constant for the wanted country.
109
+ *
110
+ * @param string $country country
111
+ *
112
+ * @return int
113
+ */
114
+ public static function get($country)
115
+ {
116
+ switch (strtoupper($country)) {
117
+ case "DE":
118
+ return KlarnaEncoding::PNO_DE;
119
+ case "DK":
120
+ return KlarnaEncoding::PNO_DK;
121
+ case "FI":
122
+ return KlarnaEncoding::PNO_FI;
123
+ case "NL":
124
+ return KlarnaEncoding::PNO_NL;
125
+ case "NO":
126
+ return KlarnaEncoding::PNO_NO;
127
+ case "SE":
128
+ return KlarnaEncoding::PNO_SE;
129
+ case "AT":
130
+ return KlarnaEncoding::PNO_AT;
131
+ default:
132
+ return -1;
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Returns a regexp string for the specified encoding constant.
138
+ *
139
+ * @param int $enc PNO/SSN encoding constant.
140
+ *
141
+ * @return string The regular expression.
142
+ * @throws Klarna_UnknownEncodingException
143
+ */
144
+ public static function getRegexp($enc)
145
+ {
146
+ switch($enc) {
147
+ case self::PNO_SE:
148
+ /*
149
+ * All positions except C contain numbers 0-9.
150
+ *
151
+ * PNO:
152
+ * YYYYMMDDCNNNN, C = -|+ length 13
153
+ * YYYYMMDDNNNN 12
154
+ * YYMMDDCNNNN 11
155
+ * YYMMDDNNNN 10
156
+ *
157
+ * ORGNO:
158
+ * XXXXXXNNNN
159
+ * XXXXXX-NNNN
160
+ * 16XXXXXXNNNN
161
+ * 16XXXXXX-NNNN
162
+ *
163
+ */
164
+ return '/^[0-9]{6,6}(([0-9]{2,2}[-\+]{1,1}[0-9]{4,4})|([-\+]'.
165
+ '{1,1}[0-9]{4,4})|([0-9]{4,6}))$/';
166
+ case self::PNO_NO:
167
+ /*
168
+ * All positions contain numbers 0-9.
169
+ *
170
+ * Pno
171
+ * DDMMYYIIIKK ("fodelsenummer" or "D-nummer") length = 11
172
+ * DDMMYY-IIIKK ("fodelsenummer" or "D-nummer") length = 12
173
+ * DDMMYYYYIIIKK ("fodelsenummer" or "D-nummer") length = 13
174
+ * DDMMYYYY-IIIKK ("fodelsenummer" or "D-nummer") length = 14
175
+ *
176
+ * Orgno
177
+ * Starts with 8 or 9.
178
+ *
179
+ * NNNNNNNNK (orgno) length = 9
180
+ */
181
+ return '/^[0-9]{6,6}((-[0-9]{5,5})|([0-9]{2,2}((-[0-9]'.
182
+ '{5,5})|([0-9]{1,1})|([0-9]{3,3})|([0-9]{5,5))))$/';
183
+ case self::PNO_FI:
184
+ /*
185
+ * Pno
186
+ * DDMMYYCIIIT
187
+ * DDMMYYIIIT
188
+ * C = century, '+' = 1800, '-' = 1900 och 'A' = 2000.
189
+ * I = 0-9
190
+ * T = 0-9, A-F, H, J, K-N, P, R-Y
191
+ *
192
+ * Orgno
193
+ * NNNNNNN-T
194
+ * NNNNNNNT
195
+ * T = 0-9, A-F, H, J, K-N, P, R-Y
196
+ */
197
+ return '/^[0-9]{6,6}(([A\+-]{1,1}[0-9]{3,3}[0-9A-FHJK-NPR-Y]'.
198
+ '{1,1})|([0-9]{3,3}[0-9A-FHJK-NPR-Y]{1,1})|([0-9]{1,1}-{0,1}'.
199
+ '[0-9A-FHJK-NPR-Y]{1,1}))$/i';
200
+ case self::PNO_DK:
201
+ /*
202
+ * Pno
203
+ * DDMMYYNNNG length 10
204
+ * G = gender, odd/even for men/women.
205
+ *
206
+ * Orgno
207
+ * XXXXXXXX length 8
208
+ */
209
+ return '/^[0-9]{8,8}([0-9]{2,2})?$/';
210
+ case self::PNO_NL:
211
+ case self::PNO_DE:
212
+ /**
213
+ * Pno
214
+ * DDMMYYYYG length 9
215
+ * DDMMYYYY 8
216
+ *
217
+ * Orgno
218
+ * XXXXXXX 7 company org nr
219
+ */
220
+ return '/^[0-9]{7,9}$/';
221
+ case self::EMAIL:
222
+ /**
223
+ * Validates an email.
224
+ */
225
+ return '/^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*@[a-zA-Z0-9-]'.
226
+ '+(\.[a-zA-Z0-9-]+)*(\.[a-zA-Z0-9-][a-zA-Z0-9-]+)+$/';
227
+ case self::CELLNO:
228
+ /**
229
+ * Validates a cellno.
230
+ * @TODO Is this encoding only for Sweden?
231
+ */
232
+ return '/^07[\ \-0-9]{8,13}$/';
233
+ default:
234
+ throw new Klarna_UnknownEncodingException($enc);
235
+ }
236
+ }
237
+
238
+ /**
239
+ * Checks if the specified PNO is correct according to specified encoding constant.
240
+ *
241
+ * @param string $pno PNO/SSN string.
242
+ * @param int $enc {@link KlarnaEncoding PNO/SSN encoding} constant.
243
+ *
244
+ * @return bool True if correct.
245
+ */
246
+ public static function checkPNO($pno, $enc = null)
247
+ {
248
+ return strlen($pno) > 0;
249
+ }
250
+ }
lib/Klarna/Exceptions.php ADDED
@@ -0,0 +1,735 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Klarna Exceptions
4
+ *
5
+ * PHP Version 5.3
6
+ *
7
+ * @category Payment
8
+ * @package KlarnaAPI
9
+ * @author MS Dev <ms.modules@klarna.com>
10
+ * @copyright 2012 Klarna AB (http://klarna.com)
11
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
12
+ * @link http://integration.klarna.com/
13
+ */
14
+
15
+ require_once 'Country.php';
16
+
17
+ /**
18
+ * KlarnaException class, only used so it says "KlarnaException" instead of
19
+ * Exception.
20
+ *
21
+ * @category Payment
22
+ * @package KlarnaAPI
23
+ * @author MS Dev <ms.modules@klarna.com>
24
+ * @copyright 2012 Klarna AB (http://klarna.com)
25
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
26
+ * @link http://integration.klarna.com/
27
+ */
28
+ class KlarnaException extends Exception
29
+ {
30
+ /**
31
+ * Returns an error message readable by end customers.
32
+ *
33
+ * @return string
34
+ */
35
+ public function __toString()
36
+ {
37
+ return $this->getMessage() . " (#".$this->code.")";
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Exception for invalid Configuration object
43
+ *
44
+ * @category Payment
45
+ * @package KlarnaAPI
46
+ * @author MS Dev <ms.modules@klarna.com>
47
+ * @copyright 2012 Klarna AB (http://klarna.com)
48
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
49
+ * @link http://integration.klarna.com/
50
+ */
51
+ class Klarna_InvalidConfigurationException extends KlarnaException
52
+ {
53
+ /**
54
+ * Constructor
55
+ */
56
+ public function __construct()
57
+ {
58
+ parent::__construct(
59
+ "Supplied config is not a KlarnaConfig/ArrayAccess object!",
60
+ 50001
61
+ );
62
+ }
63
+ }
64
+ /**
65
+ * Exception for incomplete Configuration object
66
+ *
67
+ * @category Payment
68
+ * @package KlarnaAPI
69
+ * @author MS Dev <ms.modules@klarna.com>
70
+ * @copyright 2012 Klarna AB (http://klarna.com)
71
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
72
+ * @link http://integration.klarna.com/
73
+ */
74
+ class Klarna_IncompleteConfigurationException extends KlarnaException
75
+ {
76
+ /**
77
+ * Constructor
78
+ */
79
+ public function __construct()
80
+ {
81
+ parent::__construct('Klarna instance not fully configured!', 50002);
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Exception for invalid KlarnaAddr object
87
+ *
88
+ * @category Payment
89
+ * @package KlarnaAPI
90
+ * @author MS Dev <ms.modules@klarna.com>
91
+ * @copyright 2012 Klarna AB (http://klarna.com)
92
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
93
+ * @link http://integration.klarna.com/
94
+ */
95
+ class Klarna_InvalidKlarnaAddrException extends KlarnaException
96
+ {
97
+ /**
98
+ * Constructor
99
+ */
100
+ public function __construct()
101
+ {
102
+ parent::__construct(
103
+ "Supplied address is not a KlarnaAddr object!",
104
+ 50011
105
+ );
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Exception for no KlarnaAddr set
111
+ *
112
+ * @category Payment
113
+ * @package KlarnaAPI
114
+ * @author MS Dev <ms.modules@klarna.com>
115
+ * @copyright 2012 Klarna AB (http://klarna.com)
116
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
117
+ * @link http://integration.klarna.com/
118
+ */
119
+ class Klarna_MissingAddressException extends KlarnaException
120
+ {
121
+ /**
122
+ * Constructor
123
+ */
124
+ public function __construct()
125
+ {
126
+ parent::__construct("No address set!", 50035);
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Exception for missing Configuration field
132
+ *
133
+ * @category Payment
134
+ * @package KlarnaAPI
135
+ * @author MS Dev <ms.modules@klarna.com>
136
+ * @copyright 2012 Klarna AB (http://klarna.com)
137
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
138
+ * @link http://integration.klarna.com/
139
+ */
140
+ class Klarna_ConfigFieldMissingException extends KlarnaException
141
+ {
142
+ /**
143
+ * Constructor
144
+ *
145
+ * @param string $field config field
146
+ */
147
+ public function __construct($field)
148
+ {
149
+ parent::__construct("Config field '{$field}' is not valid!", 50003);
150
+
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Exception for Unknown Encoding
156
+ *
157
+ * @category Payment
158
+ * @package KlarnaAPI
159
+ * @author MS Dev <ms.modules@klarna.com>
160
+ * @copyright 2012 Klarna AB (http://klarna.com)
161
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
162
+ * @link http://integration.klarna.com/
163
+ */
164
+ class Klarna_UnknownEncodingException extends KlarnaException
165
+ {
166
+ /**
167
+ * Constructor
168
+ *
169
+ * @param int $encoding encoding
170
+ */
171
+ public function __construct($encoding)
172
+ {
173
+ parent::__construct(
174
+ "Unknown PNO/SSN encoding constant! ({$encoding})", 50091
175
+ );
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Exception for Unknown Address Type
181
+ *
182
+ * @category Payment
183
+ * @package KlarnaAPI
184
+ * @author MS Dev <ms.modules@klarna.com>
185
+ * @copyright 2012 Klarna AB (http://klarna.com)
186
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
187
+ * @link http://integration.klarna.com/
188
+ */
189
+ class Klarna_UnknownAddressTypeException extends KlarnaException
190
+ {
191
+ /**
192
+ * Constructor
193
+ *
194
+ * @param int $type type
195
+ */
196
+ public function __construct($type)
197
+ {
198
+ parent::__construct("Unknown address type: {$type}", 50012);
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Exception for Missing Country
204
+ *
205
+ * @category Payment
206
+ * @package KlarnaAPI
207
+ * @author MS Dev <ms.modules@klarna.com>
208
+ * @copyright 2012 Klarna AB (http://klarna.com)
209
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
210
+ * @link http://integration.klarna.com/
211
+ */
212
+ class Klarna_MissingCountryException extends KlarnaException
213
+ {
214
+ /**
215
+ * Constructor
216
+ */
217
+ public function __construct()
218
+ {
219
+ parent::__construct('You must set country first!', 50046);
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Exception for Unknown Country
225
+ *
226
+ * @category Payment
227
+ * @package KlarnaAPI
228
+ * @author MS Dev <ms.modules@klarna.com>
229
+ * @copyright 2012 Klarna AB (http://klarna.com)
230
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
231
+ * @link http://integration.klarna.com/
232
+ */
233
+ class Klarna_UnknownCountryException extends KlarnaException
234
+ {
235
+ /**
236
+ * Constructor
237
+ *
238
+ * @param mixed $country country
239
+ */
240
+ public function __construct($country)
241
+ {
242
+ parent::__construct("Unknown country! ({$country})", 50006);
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Exception for Unknown Language
248
+ *
249
+ * @category Payment
250
+ * @package KlarnaAPI
251
+ * @author MS Dev <ms.modules@klarna.com>
252
+ * @copyright 2012 Klarna AB (http://klarna.com)
253
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
254
+ * @link http://integration.klarna.com/
255
+ */
256
+ class Klarna_UnknownLanguageException extends KlarnaException
257
+ {
258
+ /**
259
+ * Constructor
260
+ *
261
+ * @param mixed $language language
262
+ */
263
+ public function __construct($language)
264
+ {
265
+ parent::__construct("Unknown language! ({$language})", 50007);
266
+ }
267
+ }
268
+
269
+ /**
270
+ * Exception for Unknown Currency
271
+ *
272
+ * @category Payment
273
+ * @package KlarnaAPI
274
+ * @author MS Dev <ms.modules@klarna.com>
275
+ * @copyright 2012 Klarna AB (http://klarna.com)
276
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
277
+ * @link http://integration.klarna.com/
278
+ */
279
+ class Klarna_UnknownCurrencyException extends KlarnaException
280
+ {
281
+ /**
282
+ * Constructor
283
+ *
284
+ * @param mixed $currency currency
285
+ */
286
+ public function __construct($currency)
287
+ {
288
+ parent::__construct("Unknown currency! ({$currency})", 50008);
289
+ }
290
+ }
291
+
292
+ /**
293
+ * Exception for Missing Arguments
294
+ *
295
+ * @category Payment
296
+ * @package KlarnaAPI
297
+ * @author MS Dev <ms.modules@klarna.com>
298
+ * @copyright 2012 Klarna AB (http://klarna.com)
299
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
300
+ * @link http://integration.klarna.com/
301
+ */
302
+ class Klarna_ArgumentNotSetException extends KlarnaException
303
+ {
304
+ /**
305
+ * Constructor
306
+ *
307
+ * @param string $argument argument
308
+ */
309
+ public function __construct($argument)
310
+ {
311
+ parent::__construct("Argument '{$argument}' not set!", 50005);
312
+ }
313
+ }
314
+
315
+ /**
316
+ * Exception for Country and Currency mismatch
317
+ *
318
+ * @category Payment
319
+ * @package KlarnaAPI
320
+ * @author MS Dev <ms.modules@klarna.com>
321
+ * @copyright 2012 Klarna AB (http://klarna.com)
322
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
323
+ * @link http://integration.klarna.com/
324
+ */
325
+ class Klarna_CountryCurrencyMismatchException extends KlarnaException
326
+ {
327
+ /**
328
+ * Constructor
329
+ *
330
+ * @param mixed $country country
331
+ * @param mixed $currency currency
332
+ */
333
+ public function __construct($country, $currency)
334
+ {
335
+ $countryCode = KlarnaCountry::getCode($country);
336
+ parent::__construct(
337
+ "Mismatching country/currency for '{$countryCode}'! ".
338
+ "[country: $country currency: $currency]",
339
+ 50011
340
+ );
341
+ }
342
+ }
343
+
344
+ /**
345
+ * Exception for Country and Currency mismatch
346
+ *
347
+ * @category Payment
348
+ * @package KlarnaAPI
349
+ * @author MS Dev <ms.modules@klarna.com>
350
+ * @copyright 2012 Klarna AB (http://klarna.com)
351
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
352
+ * @link http://integration.klarna.com/
353
+ */
354
+ class Klarna_CountryLanguageMismatchException extends KlarnaException
355
+ {
356
+ /**
357
+ * Constructor
358
+ *
359
+ * @param mixed $country country
360
+ * @param mixed $language language
361
+ */
362
+ public function __construct($country, $language)
363
+ {
364
+ $countryCode = KlarnaCountry::getCode($country);
365
+ parent::__construct(
366
+ "Mismatching country/language for '{$countryCode}'! ".
367
+ "[country: $country language: $language]",
368
+ 50024
369
+ );
370
+ }
371
+ }
372
+
373
+ /**
374
+ * Exception for Shipping country being different from set country
375
+ *
376
+ * @category Payment
377
+ * @package KlarnaAPI
378
+ * @author MS Dev <ms.modules@klarna.com>
379
+ * @copyright 2012 Klarna AB (http://klarna.com)
380
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
381
+ * @link http://integration.klarna.com/
382
+ */
383
+ class Klarna_ShippingCountryException extends KlarnaException
384
+ {
385
+ /**
386
+ * Constructor
387
+ */
388
+ public function __construct()
389
+ {
390
+ parent::__construct(
391
+ 'Shipping address country must match the country set!', 50041
392
+ );
393
+ }
394
+ }
395
+
396
+ /**
397
+ * Exception for Missing Goodslist
398
+ *
399
+ * @category Payment
400
+ * @package KlarnaAPI
401
+ * @author MS Dev <ms.modules@klarna.com>
402
+ * @copyright 2012 Klarna AB (http://klarna.com)
403
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
404
+ * @link http://integration.klarna.com/
405
+ */
406
+ class Klarna_MissingGoodslistException extends KlarnaException
407
+ {
408
+ /**
409
+ * Constructor
410
+ */
411
+ public function __construct()
412
+ {
413
+ parent::__construct("No articles in goodslist!", 50034);
414
+ }
415
+ }
416
+
417
+ /**
418
+ * Exception for invalid price
419
+ *
420
+ * @category Payment
421
+ * @package KlarnaAPI
422
+ * @author MS Dev <ms.modules@klarna.com>
423
+ * @copyright 2012 Klarna AB (http://klarna.com)
424
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
425
+ * @link http://integration.klarna.com/
426
+ */
427
+ class Klarna_InvalidPriceException extends KlarnaException
428
+ {
429
+ /**
430
+ * Constructor
431
+ *
432
+ * @param mixed $price price
433
+ */
434
+ public function __construct($price)
435
+ {
436
+ parent::__construct(
437
+ "price/amount must be an integer and greater than 0! ($price)",
438
+ 50039
439
+ );
440
+ }
441
+ }
442
+
443
+
444
+ /**
445
+ * Exception for invalid pcstorage class
446
+ *
447
+ * @category Payment
448
+ * @package KlarnaAPI
449
+ * @author MS Dev <ms.modules@klarna.com>
450
+ * @copyright 2012 Klarna AB (http://klarna.com)
451
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
452
+ * @link http://integration.klarna.com/
453
+ */
454
+ class Klarna_PCStorageInvalidException extends KlarnaException
455
+ {
456
+ /**
457
+ * Constructor
458
+ *
459
+ * @param string $className classname
460
+ * @param string $pclassStorage pcstorage class file
461
+ */
462
+ public function __construct($className, $pclassStorage)
463
+ {
464
+ parent::__construct(
465
+ "$className located in $pclassStorage is not a PCStorage instance.",
466
+ 50052
467
+ );
468
+ }
469
+ }
470
+
471
+ /**
472
+ * Exception for invalid type
473
+ *
474
+ * @category Payment
475
+ * @package KlarnaAPI
476
+ * @author MS Dev <ms.modules@klarna.com>
477
+ * @copyright 2012 Klarna AB (http://klarna.com)
478
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
479
+ * @link http://integration.klarna.com/
480
+ */
481
+ class Klarna_InvalidTypeException extends KlarnaException
482
+ {
483
+ /**
484
+ * Constructor
485
+ *
486
+ * @param string $param parameter
487
+ * @param string $type type
488
+ */
489
+ public function __construct($param, $type)
490
+ {
491
+ parent::__construct(
492
+ "$param is not of the expected type. Expected: $type.",
493
+ 50062
494
+ );
495
+ }
496
+ }
497
+
498
+ /**
499
+ * Exception for invalid PNO
500
+ *
501
+ * @category Payment
502
+ * @package KlarnaAPI
503
+ * @author MS Dev <ms.modules@klarna.com>
504
+ * @copyright 2012 Klarna AB (http://klarna.com)
505
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
506
+ * @link http://integration.klarna.com/
507
+ */
508
+ class Klarna_InvalidPNOException extends KlarnaException
509
+ {
510
+ /**
511
+ * Constructor
512
+ */
513
+ public function __construct()
514
+ {
515
+ parent::__construct("PNO/SSN is not valid!", 50078);
516
+ }
517
+ }
518
+
519
+
520
+ /**
521
+ * Exception for invalid Email
522
+ *
523
+ * @category Payment
524
+ * @package KlarnaAPI
525
+ * @author MS Dev <ms.modules@klarna.com>
526
+ * @copyright 2012 Klarna AB (http://klarna.com)
527
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
528
+ * @link http://integration.klarna.com/
529
+ */
530
+ class Klarna_InvalidEmailException extends KlarnaException
531
+ {
532
+ /**
533
+ * Constructor
534
+ */
535
+ public function __construct()
536
+ {
537
+ parent::__construct("Email is not valid!", 50017);
538
+ }
539
+ }
540
+
541
+ /**
542
+ * Exception for invalid Email
543
+ *
544
+ * @category Payment
545
+ * @package KlarnaAPI
546
+ * @author MS Dev <ms.modules@klarna.com>
547
+ * @copyright 2012 Klarna AB (http://klarna.com)
548
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
549
+ * @link http://integration.klarna.com/
550
+ */
551
+ class Klarna_UnsupportedMarketException extends KlarnaException
552
+ {
553
+ /**
554
+ * Constructor
555
+ *
556
+ * @param string|array $countries allowed countries
557
+ */
558
+ public function __construct($countries)
559
+ {
560
+ if (is_array($countries)) {
561
+ $countries = implode(", ", $countries);
562
+ }
563
+ parent::__construct(
564
+ "This method is only available for customers from: {$countries}",
565
+ 50025
566
+ );
567
+ }
568
+ }
569
+ /**
570
+ * Exception for invalid Locale
571
+ *
572
+ * @category Payment
573
+ * @package KlarnaAPI
574
+ * @author MS Dev <ms.modules@klarna.com>
575
+ * @copyright 2012 Klarna AB (http://klarna.com)
576
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
577
+ * @link http://integration.klarna.com/
578
+ */
579
+ class Klarna_InvalidLocaleException extends KlarnaException
580
+ {
581
+ /**
582
+ * Constructor
583
+ */
584
+ public function __construct()
585
+ {
586
+ parent::__construct(
587
+ "You must set country, language and currency!",
588
+ 50023
589
+ );
590
+ }
591
+ }
592
+
593
+ /**
594
+ * Exception for Missing Address Fields
595
+ *
596
+ * @category Payment
597
+ * @package KlarnaAPI
598
+ * @author MS Dev <ms.modules@klarna.com>
599
+ * @copyright 2012 Klarna AB (http://klarna.com)
600
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
601
+ * @link http://integration.klarna.com/
602
+ */
603
+ class Klarna_AddressFieldMissingException extends KlarnaException
604
+ {
605
+ /**
606
+ * Constructor
607
+ *
608
+ * @param string $argument argument
609
+ */
610
+ public function __construct($argument)
611
+ {
612
+ parent::__construct("'{$argument}' not set!", 50015);
613
+ }
614
+ }
615
+
616
+ /**
617
+ * Exception for File Not Writable
618
+ *
619
+ * @category Payment
620
+ * @package KlarnaAPI
621
+ * @author MS Dev <ms.modules@klarna.com>
622
+ * @copyright 2012 Klarna AB (http://klarna.com)
623
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
624
+ * @link http://integration.klarna.com/
625
+ */
626
+ class Klarna_FileNotWritableException extends KlarnaException
627
+ {
628
+ /**
629
+ * Constructor
630
+ *
631
+ * @param string $file filename
632
+ */
633
+ public function __construct($file)
634
+ {
635
+ parent::__construct("Unable to write to {$file}!");
636
+ }
637
+ }
638
+
639
+ /**
640
+ * Exception for File Not Readable
641
+ *
642
+ * @category Payment
643
+ * @package KlarnaAPI
644
+ * @author MS Dev <ms.modules@klarna.com>
645
+ * @copyright 2012 Klarna AB (http://klarna.com)
646
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
647
+ * @link http://integration.klarna.com/
648
+ */
649
+ class Klarna_FileNotReadableException extends KlarnaException
650
+ {
651
+ /**
652
+ * Constructor
653
+ *
654
+ * @param string $file filename
655
+ */
656
+ public function __construct($file)
657
+ {
658
+ parent::__construct("Unable to read from {$file}!");
659
+ }
660
+ }
661
+
662
+ /**
663
+ * Exception for File Not Readable
664
+ *
665
+ * @category Payment
666
+ * @package KlarnaAPI
667
+ * @author MS Dev <ms.modules@klarna.com>
668
+ * @copyright 2012 Klarna AB (http://klarna.com)
669
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
670
+ * @link http://integration.klarna.com/
671
+ */
672
+ class Klarna_FileNotFoundException extends KlarnaException
673
+ {
674
+ /**
675
+ * Constructor
676
+ *
677
+ * @param string $file filename
678
+ */
679
+ public function __construct($file)
680
+ {
681
+ parent::__construct("Unable to find file: {$file}!");
682
+ }
683
+ }
684
+
685
+ /**
686
+ * Exception for Database Errors
687
+ *
688
+ * @category Payment
689
+ * @package KlarnaAPI
690
+ * @author MS Dev <ms.modules@klarna.com>
691
+ * @copyright 2012 Klarna AB (http://klarna.com)
692
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
693
+ * @link http://integration.klarna.com/
694
+ */
695
+
696
+ class Klarna_DatabaseException extends KlarnaException
697
+ {
698
+ }
699
+
700
+ /**
701
+ * Exception for PClass Errors
702
+ *
703
+ * @category Payment
704
+ * @package KlarnaAPI
705
+ * @author MS Dev <ms.modules@klarna.com>
706
+ * @copyright 2012 Klarna AB (http://klarna.com)
707
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
708
+ * @link http://integration.klarna.com/
709
+ */
710
+ class Klarna_PClassException extends KlarnaException
711
+ {
712
+ }
713
+
714
+ /**
715
+ * Exception for XML Parse errors
716
+ *
717
+ * @category Payment
718
+ * @package KlarnaAPI
719
+ * @author MS Dev <ms.modules@klarna.com>
720
+ * @copyright 2012 Klarna AB (http://klarna.com)
721
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
722
+ * @link http://integration.klarna.com/
723
+ */
724
+ class Klarna_XMLParseException extends KlarnaException
725
+ {
726
+ /**
727
+ * Constructor
728
+ *
729
+ * @param string $file filename
730
+ */
731
+ public function __construct($file)
732
+ {
733
+ parent::__construct("Unable to parse XML file: {$file}!");
734
+ }
735
+ }
lib/Klarna/Flags.php ADDED
@@ -0,0 +1,319 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * KlarnaFlags
4
+ *
5
+ * PHP Version 5.3
6
+ *
7
+ * @category Payment
8
+ * @package KlarnaAPI
9
+ * @author MS Dev <ms.modules@klarna.com>
10
+ * @copyright 2012 Klarna AB (http://klarna.com)
11
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
12
+ * @link http://integration.klarna.com/
13
+ */
14
+
15
+ /**
16
+ * Flag Constants class
17
+ *
18
+ * @category Payment
19
+ * @package KlarnaAPI
20
+ * @author MS Dev <ms.modules@klarna.com>
21
+ * @copyright 2012 Klarna AB (http://klarna.com)
22
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
23
+ * @link http://integration.klarna.com/
24
+ */
25
+ class KlarnaFlags
26
+ {
27
+
28
+ /**
29
+ * Specifies that no flag is to be used.
30
+ *
31
+ * @var int
32
+ */
33
+ const NO_FLAG = 0;
34
+
35
+ //Gender flags
36
+ /**
37
+ * Indicates that the person is a female.<br>
38
+ * Use "" or null when unspecified.<br>
39
+ *
40
+ * @var int
41
+ */
42
+ const FEMALE = 0;
43
+
44
+ /**
45
+ * Indicates that the person is a male.<br>
46
+ * Use "" or null when unspecified.<br>
47
+ *
48
+ * @var int
49
+ */
50
+ const MALE = 1;
51
+
52
+ //Order status constants
53
+ /**
54
+ * This signifies that the invoice or reservation is accepted.
55
+ *
56
+ * @var int
57
+ */
58
+ const ACCEPTED = 1;
59
+
60
+ /**
61
+ * This signifies that the invoice or reservation is pending, will be set
62
+ * to accepted or denied.
63
+ *
64
+ * @var int
65
+ */
66
+ const PENDING = 2;
67
+
68
+ /**
69
+ * This signifies that the invoice or reservation is <b>denied</b>.
70
+ *
71
+ * @var int
72
+ */
73
+ const DENIED = 3;
74
+
75
+ //Get_address constants
76
+ /**
77
+ * A code which indicates that all first names should be returned with the
78
+ * address.
79
+ *
80
+ * Formerly refered to as GA_OLD.
81
+ *
82
+ * @var int
83
+ */
84
+ const GA_ALL = 1;
85
+
86
+ /**
87
+ * A code which indicates that only the last name should be returned with
88
+ * the address.
89
+ *
90
+ * Formerly referd to as GA_NEW.
91
+ *
92
+ * @var int
93
+ */
94
+ const GA_LAST = 2;
95
+
96
+ /**
97
+ * A code which indicates that the given name should be returned with
98
+ * the address. If no given name is registered, this will behave as
99
+ * {@link KlarnaFlags::GA_ALL GA_ALL}.
100
+ *
101
+ */
102
+ const GA_GIVEN = 5;
103
+
104
+ //Article/goods constants
105
+ /**
106
+ * Quantity measured in 1/1000s.
107
+ *
108
+ * @var int
109
+ */
110
+ const PRINT_1000 = 1;
111
+
112
+ /**
113
+ * Quantity measured in 1/100s.
114
+ *
115
+ * @var int
116
+ */
117
+ const PRINT_100 = 2;
118
+
119
+ /**
120
+ * Quantity measured in 1/10s.
121
+ *
122
+ * @var int
123
+ */
124
+ const PRINT_10 = 4;
125
+
126
+ /**
127
+ * Indicates that the item is a shipment fee.
128
+ *
129
+ * Update_charge_amount (1)
130
+ *
131
+ * @var int
132
+ */
133
+ const IS_SHIPMENT = 8;
134
+
135
+ /**
136
+ * Indicates that the item is a handling fee.
137
+ *
138
+ * Update_charge_amount (2)
139
+ *
140
+ * @var int
141
+ */
142
+ const IS_HANDLING = 16;
143
+
144
+ /**
145
+ * Article price including VAT.
146
+ *
147
+ * @var int
148
+ */
149
+ const INC_VAT = 32;
150
+
151
+ //Miscellaneous
152
+ /**
153
+ * Signifies that this is to be displayed in the checkout.<br>
154
+ * Used for part payment.<br>
155
+ *
156
+ * @var int
157
+ */
158
+ const CHECKOUT_PAGE = 0;
159
+
160
+ /**
161
+ * Signifies that this is to be displayed in the product page.<br>
162
+ * Used for part payment.<br>
163
+ *
164
+ * @var int
165
+ */
166
+ const PRODUCT_PAGE = 1;
167
+
168
+ /**
169
+ * Signifies that the specified address is billing address.
170
+ *
171
+ * @var int
172
+ */
173
+ const IS_BILLING = 100;
174
+
175
+ /**
176
+ * Signifies that the specified address is shipping address.
177
+ *
178
+ * @var int
179
+ */
180
+ const IS_SHIPPING = 101;
181
+
182
+ //Invoice and Reservation
183
+ /**
184
+ * Indicates that the purchase is a test invoice/part payment.
185
+ *
186
+ * @var int
187
+ */
188
+ const TEST_MODE = 2;
189
+
190
+ /**
191
+ * PClass id/value for invoices.
192
+ *
193
+ * @see KlarnaPClass::INVOICE.
194
+ * @var int
195
+ */
196
+ const PCLASS_INVOICE = -1;
197
+
198
+ //Invoice
199
+ /**
200
+ * Activates an invoices automatically, requires setting in Klarna Online.
201
+ *
202
+ * If you designate this flag an invoice is created directly in the active
203
+ * state, i.e. Klarna will buy the invoice immediately.
204
+ *
205
+ * @var int
206
+ */
207
+ const AUTO_ACTIVATE = 1;
208
+
209
+ /**
210
+ * Creates a pre-pay invoice.
211
+ *
212
+ * @var int
213
+ *
214
+ * @deprecated Do not use.
215
+ */
216
+ const PRE_PAY = 8;
217
+
218
+ /**
219
+ * Used to flag a purchase as sensitive order.
220
+ *
221
+ * @var int
222
+ */
223
+ const SENSITIVE_ORDER = 1024;
224
+
225
+ /**
226
+ * Used to return an array with long and short ocr number.
227
+ *
228
+ * @see Klarna::addTransaction()
229
+ * @var int
230
+ */
231
+ const RETURN_OCR = 8192;
232
+
233
+ /**
234
+ * Specifies the shipment type as normal.
235
+ *
236
+ * @var int
237
+ */
238
+ const NORMAL_SHIPMENT = 1;
239
+
240
+ /**
241
+ * Specifies the shipment type as express.
242
+ *
243
+ * @var int
244
+ */
245
+ const EXPRESS_SHIPMENT = 2;
246
+
247
+ //Mobile (Invoice) flags
248
+ /**
249
+ * Marks the transaction as Klarna mobile.
250
+ *
251
+ * @var int
252
+ */
253
+ const M_PHONE_TRANSACTION = 262144;
254
+
255
+ /**
256
+ * Sends a pin code to the phone sent in pno.
257
+ *
258
+ * @var int
259
+ */
260
+ const M_SEND_PHONE_PIN = 524288;
261
+
262
+ //Reservation flags
263
+ /**
264
+ * Signifies that the amount specified is the new amount.
265
+ *
266
+ * @var int
267
+ */
268
+ const NEW_AMOUNT = 0;
269
+
270
+ /**
271
+ * Signifies that the amount specified is to be added.
272
+ *
273
+ * @var int
274
+ */
275
+ const ADD_AMOUNT = 1;
276
+
277
+ /**
278
+ * Sends the invoice by mail when activating a reservation.
279
+ *
280
+ * @var int
281
+ */
282
+ const RSRV_SEND_BY_MAIL = 4;
283
+
284
+ /**
285
+ * Sends the invoice by e-mail when activating a reservation.
286
+ *
287
+ * @var int
288
+ */
289
+ const RSRV_SEND_BY_EMAIL = 8;
290
+
291
+ /**
292
+ * Used for partial deliveries, this flag saves the reservation number so
293
+ * it can be used again.
294
+ *
295
+ * @var int
296
+ */
297
+ const RSRV_PRESERVE_RESERVATION = 16;
298
+
299
+ /**
300
+ * Used to flag a purchase as sensitive order.
301
+ *
302
+ * @var int
303
+ */
304
+ const RSRV_SENSITIVE_ORDER = 32;
305
+
306
+ /**
307
+ * Marks the transaction as Klarna mobile.
308
+ *
309
+ * @var int
310
+ */
311
+ const RSRV_PHONE_TRANSACTION = 512;
312
+
313
+ /**
314
+ * Sends a pin code to the mobile number.
315
+ *
316
+ * @var int
317
+ */
318
+ const RSRV_SEND_PHONE_PIN = 1024;
319
+ }
lib/Klarna/Klarna.php ADDED
@@ -0,0 +1,4611 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Klarna API
4
+ *
5
+ * PHP Version 5.3
6
+ *
7
+ * @category Payment
8
+ * @package KlarnaAPI
9
+ * @author MS Dev <ms.modules@klarna.com>
10
+ * @copyright 2012 Klarna AB (http://klarna.com)
11
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
12
+ * @link http://integration.klarna.com/
13
+ */
14
+
15
+ /**
16
+ * This API provides a way to integrate with Klarna's services over the
17
+ * XMLRPC protocol.
18
+ *
19
+ * All strings inputted need to be encoded with ISO-8859-1.<br>
20
+ * In addition you need to decode HTML entities, if they exist.<br>
21
+ *
22
+ * For more information see our
23
+ * {@link http://integration.klarna.com/en/api/step-by-step step by step} guide.
24
+ *
25
+ * Dependencies:
26
+ *
27
+ * xmlrpc-3.0.0.beta/lib/xmlrpc.inc
28
+ * from {@link http://phpxmlrpc.sourceforge.net/}
29
+ *
30
+ * xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc
31
+ * from {@link http://phpxmlrpc.sourceforge.net/}
32
+ *
33
+ * @category Payment
34
+ * @package KlarnaAPI
35
+ * @author MS Dev <ms.modules@klarna.com>
36
+ * @copyright 2012 Klarna AB (http://klarna.com)
37
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
38
+ * @link http://integration.klarna.com/
39
+ */
40
+ class Klarna
41
+ {
42
+ /**
43
+ * Klarna PHP API version identifier.
44
+ *
45
+ * @var string
46
+ */
47
+ protected $VERSION = 'php:api:2.4.2';
48
+
49
+ /**
50
+ * Klarna protocol identifier.
51
+ *
52
+ * @var string
53
+ */
54
+ protected $PROTO = '4.1';
55
+
56
+ /**
57
+ * Flag to indicate use of the report server Candice.
58
+ *
59
+ * @var bool
60
+ */
61
+ private static $_candice = true;
62
+
63
+ /**
64
+ * URL/Address to the Candice server.
65
+ * Port used is 80.
66
+ *
67
+ * @var string
68
+ */
69
+ private static $_c_addr = "clientstat.klarna.com";
70
+
71
+ /**
72
+ * Constants used with LIVE mode for the communications with Klarna.
73
+ *
74
+ * @var int
75
+ */
76
+ const LIVE = 0;
77
+
78
+ /**
79
+ * URL/Address to the live Klarna Online server.
80
+ * Port used is 443 for SSL and 80 without.
81
+ *
82
+ * @var string
83
+ */
84
+ private static $_live_addr = 'payment.klarna.com';
85
+
86
+ /**
87
+ * Constants used with BETA mode for the communications with Klarna.
88
+ *
89
+ * @var int
90
+ */
91
+ const BETA = 1;
92
+
93
+ /**
94
+ * URL/Address to the beta test Klarna Online server.
95
+ * Port used is 443 for SSL and 80 without.
96
+ *
97
+ * @var string
98
+ */
99
+ private static $_beta_addr = 'payment.testdrive.klarna.com';
100
+
101
+ /**
102
+ * Indicates whether the communications is over SSL or not.
103
+ *
104
+ * @var bool
105
+ */
106
+ protected $ssl = false;
107
+
108
+ /**
109
+ * An object of xmlrpc_client, used to communicate with Klarna.
110
+ *
111
+ * @link http://phpxmlrpc.sourceforge.net/
112
+ *
113
+ * @var xmlrpc_client
114
+ */
115
+ protected $xmlrpc;
116
+
117
+ /**
118
+ * Which server the Klarna API is using, LIVE or BETA (TESTING).
119
+ *
120
+ * @see Klarna::LIVE
121
+ * @see Klarna::BETA
122
+ *
123
+ * @var int
124
+ */
125
+ protected $mode;
126
+
127
+ /**
128
+ * Associative array holding url information.
129
+ *
130
+ * @var array
131
+ */
132
+ private $_url;
133
+
134
+ /**
135
+ * The estore's identifier received from Klarna.
136
+ *
137
+ * @var int
138
+ */
139
+ private $_eid;
140
+
141
+ /**
142
+ * The estore's shared secret received from Klarna.
143
+ *
144
+ * <b>Note</b>:<br>
145
+ * DO NOT SHARE THIS WITH ANYONE!
146
+ *
147
+ * @var string
148
+ */
149
+ private $_secret;
150
+
151
+ /**
152
+ * KlarnaCountry constant.
153
+ *
154
+ * @see KlarnaCountry
155
+ *
156
+ * @var int
157
+ */
158
+ private $_country;
159
+
160
+ /**
161
+ * KlarnaCurrency constant.
162
+ *
163
+ * @see KlarnaCurrency
164
+ *
165
+ * @var int
166
+ */
167
+ private $_currency;
168
+
169
+ /**
170
+ * KlarnaLanguage constant.
171
+ *
172
+ * @see KlarnaLanguage
173
+ *
174
+ * @var int
175
+ */
176
+ private $_language;
177
+
178
+ /**
179
+ * An array of articles for the current order.
180
+ *
181
+ * @var array
182
+ */
183
+ protected $goodsList;
184
+
185
+ /**
186
+ * An array of article numbers and quantity.
187
+ *
188
+ * @var array
189
+ */
190
+ protected $artNos;
191
+
192
+ /**
193
+ * An KlarnaAddr object containing the billing address.
194
+ *
195
+ * @var KlarnaAddr
196
+ */
197
+ protected $billing;
198
+
199
+ /**
200
+ * An KlarnaAddr object containing the shipping address.
201
+ *
202
+ * @var KlarnaAddr
203
+ */
204
+ protected $shipping;
205
+
206
+ /**
207
+ * Estore's user(name) or identifier.
208
+ * Only used in {@link Klarna::addTransaction()}.
209
+ *
210
+ * @var string
211
+ */
212
+ protected $estoreUser = "";
213
+
214
+ /**
215
+ * External order numbers from other systems.
216
+ *
217
+ * @var string
218
+ */
219
+ protected $orderid = array("", "");
220
+
221
+ /**
222
+ * Reference (person) parameter.
223
+ *
224
+ * @var string
225
+ */
226
+ protected $reference = "";
227
+
228
+ /**
229
+ * Reference code parameter.
230
+ *
231
+ * @var string
232
+ */
233
+ protected $reference_code = "";
234
+
235
+ /**
236
+ * An array of named extra info.
237
+ *
238
+ * @var array
239
+ */
240
+ protected $extraInfo = array();
241
+
242
+ /**
243
+ * An array of named bank info.
244
+ *
245
+ * @var array
246
+ */
247
+ protected $bankInfo = array();
248
+
249
+ /**
250
+ * An array of named income expense info.
251
+ *
252
+ * @var array
253
+ */
254
+ protected $incomeInfo = array();
255
+
256
+ /**
257
+ * An array of named shipment info.
258
+ *
259
+ * @var array
260
+ */
261
+ protected $shipInfo = array();
262
+
263
+ /**
264
+ * An array of named travel info.
265
+ *
266
+ * @ignore Do not show this in PHPDoc.
267
+ * @var array
268
+ */
269
+ protected $travelInfo = array();
270
+
271
+ /**
272
+ * An array of named activate info
273
+ *
274
+ * @ignore
275
+ * @var array
276
+ */
277
+ protected $activateInfo = array();
278
+
279
+ /**
280
+ * An array of named session id's.<br>
281
+ * E.g. "dev_id_1" => ...<br>
282
+ *
283
+ * @var array
284
+ */
285
+ protected $sid = array();
286
+
287
+ /**
288
+ * A comment sent in the XMLRPC communications.
289
+ * This is resetted using clear().
290
+ *
291
+ * @var string
292
+ */
293
+ protected $comment = "";
294
+
295
+ /**
296
+ * An array with all the checkoutHTML objects.
297
+ *
298
+ * @var array
299
+ */
300
+ protected $coObjects = array();
301
+
302
+ /**
303
+ * Flag to indicate if the API should output verbose
304
+ * debugging information.
305
+ *
306
+ * @var bool
307
+ */
308
+ public static $debug = false;
309
+
310
+ /**
311
+ * Turns on the internal XMLRPC debugging.
312
+ *
313
+ * @var bool
314
+ */
315
+ public static $xmlrpcDebug = false;
316
+
317
+ /**
318
+ * If this is set to true, XMLRPC invocation is disabled.
319
+ *
320
+ * @var bool
321
+ */
322
+ public static $disableXMLRPC = false;
323
+
324
+ /**
325
+ * If the estore is using a proxy which populates the clients IP to
326
+ * x_forwarded_for
327
+ * then and only then should this be set to true.
328
+ *
329
+ * <b>Note</b>:<br>
330
+ * USE WITH CARE!
331
+ *
332
+ * @var bool
333
+ */
334
+ public static $x_forwarded_for = false;
335
+
336
+ /**
337
+ * Array of HTML entities, used to create numeric htmlentities.
338
+ *
339
+ * @ignore Do not show this in PHPDoc.
340
+ * @var array
341
+ */
342
+ protected static $htmlentities = false;
343
+
344
+ /**
345
+ * Populated with possible proxy information.
346
+ * A comma separated list of IP addresses.
347
+ *
348
+ * @var string
349
+ */
350
+ private $_x_fwd;
351
+
352
+ /**
353
+ * The storage class for PClasses.
354
+ *
355
+ * Use 'xml' for xmlstorage.class.php.<br>
356
+ * Use 'mysql' for mysqlstorage.class.php.<br>
357
+ * Use 'json' for jsonstorage.class.php.<br>
358
+ *
359
+ * @var string
360
+ */
361
+ protected $pcStorage;
362
+
363
+ /**
364
+ * The storage URI for PClasses.
365
+ *
366
+ * Use the absolute or relative URI to a file if
367
+ * {@link Klarna::$pcStorage} is set as 'xml' or 'json'.<br>
368
+ * Use a HTTP-auth similar URL if {@link Klarna::$pcStorage} is set
369
+ * as 'mysql', <br>
370
+ * e.g. user:passwd@addr:port/dbName.dbTable.<br>
371
+ * Or an associative array (recommended) {@see MySQLStorage}
372
+ *
373
+ * @var mixed
374
+ */
375
+ protected $pcURI;
376
+
377
+ /**
378
+ * PCStorage instance.
379
+ *
380
+ * @ignore Do not show this in PHPDoc.
381
+ * @var PCStorage
382
+ */
383
+ protected $pclasses;
384
+
385
+ /**
386
+ * ArrayAccess instance.
387
+ *
388
+ * @ignore Do not show this in PHPDoc.
389
+ * @var ArrayAccess
390
+ */
391
+ protected $config;
392
+
393
+ /**
394
+ * Empty constructor, because sometimes it's needed.
395
+ */
396
+ public function __construct()
397
+ {
398
+ }
399
+
400
+ /**
401
+ * Checks if the config has fields described in argument.<br>
402
+ * Missing field(s) is in the exception message.
403
+ *
404
+ * To check that the config has eid and secret:<br>
405
+ * <code>
406
+ * try {
407
+ * $this->hasFields('eid', 'secret');
408
+ * }
409
+ * catch(Exception $e) {
410
+ * echo "Missing fields: " . $e->getMessage();
411
+ * }
412
+ * </code>
413
+ *
414
+ * @throws Exception
415
+ * @return void
416
+ */
417
+ protected function hasFields(/*variable arguments*/)
418
+ {
419
+ $missingFields = array();
420
+ $args = func_get_args();
421
+ foreach ($args as $field) {
422
+ if (!isset($this->config[$field])) {
423
+ $missingFields[] = $field;
424
+ }
425
+ }
426
+ if (count($missingFields) > 0) {
427
+ throw new Klarna_ConfigFieldMissingException(
428
+ implode(', ', $missingFields)
429
+ );
430
+ }
431
+ }
432
+
433
+ /**
434
+ * Initializes the Klarna object accordingly to the set config object.
435
+ *
436
+ * @throws KlarnaException
437
+ * @return void
438
+ */
439
+ protected function init()
440
+ {
441
+ $this->hasFields('eid', 'secret', 'mode', 'pcStorage', 'pcURI');
442
+
443
+ if (!is_int($this->config['eid'])) {
444
+ $this->config['eid'] = intval($this->config['eid']);
445
+ }
446
+
447
+ if ($this->config['eid'] <= 0) {
448
+ throw new Klarna_ConfigFieldMissingException('eid');
449
+ }
450
+
451
+ if (!is_string($this->config['secret'])) {
452
+ $this->config['secret'] = strval($this->config['secret']);
453
+ }
454
+
455
+ if (strlen($this->config['secret']) == 0) {
456
+ throw new Klarna_ConfigFieldMissingException('secret');
457
+ }
458
+
459
+ //Set the shop id and secret.
460
+ $this->_eid = $this->config['eid'];
461
+ $this->_secret = $this->config['secret'];
462
+
463
+ //Set the country specific attributes.
464
+ try {
465
+ $this->hasFields('country', 'language', 'currency');
466
+
467
+ //If hasFields doesn't throw exception we can set them all.
468
+ $this->setCountry($this->config['country']);
469
+ $this->setLanguage($this->config['language']);
470
+ $this->setCurrency($this->config['currency']);
471
+ } catch(Exception $e) {
472
+ //fields missing for country, language or currency
473
+ $this->_country = $this->_language = $this->_currency = null;
474
+ }
475
+
476
+ //Set addr and port according to mode.
477
+ $this->mode = (int)$this->config['mode'];
478
+
479
+ $this->_url = array();
480
+
481
+ // If a custom url has been added to the config, use that as xmlrpc
482
+ // recipient.
483
+ if (isset($this->config['url'])) {
484
+ $this->_url = parse_url($this->config['url']);
485
+ if ($this->_url === false) {
486
+ $message = "Configuration value 'url' could not be parsed. " .
487
+ "(Was: '{$this->config['url']}')";
488
+ Klarna::printDebug(__METHOD__, $message);
489
+ throw new InvalidArgumentException($message);
490
+ }
491
+ } else {
492
+
493
+ $this->_url['scheme'] = 'https';
494
+
495
+ if ($this->mode === self::LIVE) {
496
+ $this->_url['host'] = self::$_live_addr;
497
+ } else {
498
+ $this->_url['host'] = self::$_beta_addr;
499
+ }
500
+
501
+ if (isset($this->config['ssl'])
502
+ && (bool)$this->config['ssl'] === false
503
+ ) {
504
+ $this->_url['scheme'] = 'http';
505
+ }
506
+ }
507
+
508
+ // If no port has been specified, deduce from url scheme
509
+ if (!array_key_exists('port', $this->_url)) {
510
+ if ($this->_url['scheme'] === 'https') {
511
+ $this->_url['port'] = 443;
512
+ } else {
513
+ $this->_url['port'] = 80;
514
+ }
515
+ }
516
+
517
+ try {
518
+ $this->hasFields('candice');
519
+ self::$_candice = (bool)$this->config['candice'];
520
+ } catch(Exception $e) {
521
+ //No 'candice' field ignore it...
522
+ }
523
+
524
+ try {
525
+ $this->hasFields('xmlrpcDebug');
526
+ Klarna::$xmlrpcDebug = $this->config['xmlrpcDebug'];
527
+ } catch(Exception $e) {
528
+ //No 'xmlrpcDebug' field ignore it...
529
+ }
530
+
531
+ try {
532
+ $this->hasFields('debug');
533
+ Klarna::$debug = $this->config['debug'];
534
+ } catch(Exception $e) {
535
+ //No 'debug' field ignore it...
536
+ }
537
+
538
+ $this->pcStorage = $this->config['pcStorage'];
539
+ $this->pcURI = $this->config['pcURI'];
540
+
541
+ // Default path to '/' if not set.
542
+ if (!array_key_exists('path', $this->_url)) {
543
+ $this->_url['path'] = '/';
544
+ }
545
+
546
+ $this->xmlrpc = new xmlrpc_client(
547
+ $this->_url['path'],
548
+ $this->_url['host'],
549
+ $this->_url['port'],
550
+ $this->_url['scheme']
551
+ );
552
+
553
+ $this->xmlrpc->request_charset_encoding = 'ISO-8859-1';
554
+ }
555
+
556
+ /**
557
+ * Method of ease for setting common config fields.
558
+ *
559
+ * The storage module for PClasses:<br>
560
+ * Use 'xml' for xmlstorage.class.php.<br>
561
+ * Use 'mysql' for mysqlstorage.class.php.<br>
562
+ * Use 'json' for jsonstorage.class.php.<br>
563
+ *
564
+ * The storage URI for PClasses:<br>
565
+ * Use the absolute or relative URI to a file if {@link Klarna::$pcStorage}
566
+ * is set as 'xml' or 'json'.<br>
567
+ * Use a HTTP-auth similar URL if {@link Klarna::$pcStorage} is set as
568
+ * mysql', e.g. user:passwd@addr:port/dbName.dbTable.
569
+ * Or an associative array (recommended) {@see MySQLStorage}
570
+ *
571
+ * <b>Note</b>:<br>
572
+ * This disables the config file storage.<br>
573
+ *
574
+ * @param int $eid Merchant ID/EID
575
+ * @param string $secret Secret key/Shared key
576
+ * @param int $country {@link KlarnaCountry}
577
+ * @param int $language {@link KlarnaLanguage}
578
+ * @param int $currency {@link KlarnaCurrency}
579
+ * @param int $mode {@link Klarna::LIVE} or {@link Klarna::BETA}
580
+ * @param string $pcStorage PClass storage module.
581
+ * @param string $pcURI PClass URI.
582
+ * @param bool $ssl Whether HTTPS (HTTP over SSL) or HTTP is used.
583
+ * @param bool $candice Error reporting to Klarna.
584
+ *
585
+ * @see Klarna::setConfig()
586
+ * @see KlarnaConfig
587
+ *
588
+ * @throws KlarnaException
589
+ * @return void
590
+ */
591
+ public function config(
592
+ $eid, $secret, $country, $language, $currency,
593
+ $mode = Klarna::LIVE, $pcStorage = 'json', $pcURI = 'pclasses.json',
594
+ $ssl = true, $candice = true
595
+ ) {
596
+ try {
597
+ KlarnaConfig::$store = false;
598
+ $this->config = new KlarnaConfig(null);
599
+
600
+ $this->config['eid'] = $eid;
601
+ $this->config['secret'] = $secret;
602
+ $this->config['country'] = $country;
603
+ $this->config['language'] = $language;
604
+ $this->config['currency'] = $currency;
605
+ $this->config['mode'] = $mode;
606
+ $this->config['ssl'] = $ssl;
607
+ $this->config['candice'] = $candice;
608
+ $this->config['pcStorage'] = $pcStorage;
609
+ $this->config['pcURI'] = $pcURI;
610
+
611
+ $this->init();
612
+ } catch(Exception $e) {
613
+ $this->config = null;
614
+ throw new KlarnaException(
615
+ $e->getMessage(),
616
+ $e->getCode()
617
+ );
618
+ }
619
+ }
620
+
621
+ /**
622
+ * Sets and initializes this Klarna object using the supplied config object.
623
+ *
624
+ * @param KlarnaConfig &$config Config object.
625
+ *
626
+ * @see KlarnaConfig
627
+ * @throws KlarnaException
628
+ * @return void
629
+ */
630
+ public function setConfig(&$config)
631
+ {
632
+ $this->_checkConfig($config);
633
+
634
+ $this->config = $config;
635
+ $this->init();
636
+ }
637
+
638
+ /**
639
+ * Get the complete locale (country, language, currency) to use for the
640
+ * values passed, or the configured value if passing null.
641
+ *
642
+ * @param mixed $country country constant or code
643
+ * @param mixed $language language constant or code
644
+ * @param mixed $currency currency constant or code
645
+ *
646
+ * @throws KlarnaException
647
+ * @return array
648
+ */
649
+ public function getLocale(
650
+ $country = null, $language = null, $currency = null
651
+ ) {
652
+ $locale = array(
653
+ 'country' => null,
654
+ 'language' => null,
655
+ 'currency' => null
656
+ );
657
+
658
+ if ($country === null) {
659
+ // Use the configured country / language / currency
660
+ $locale['country'] = $this->_country;
661
+ if ($this->_language !== null) {
662
+ $locale['language'] = $this->_language;
663
+ }
664
+
665
+ if ($this->_currency !== null) {
666
+ $locale['currency'] = $this->_currency;
667
+ }
668
+ } else {
669
+ // Use the given country / language / currency
670
+ if (!is_numeric($country)) {
671
+ $country = KlarnaCountry::fromCode($country);
672
+ }
673
+ $locale['country'] = intval($country);
674
+
675
+ if ($language !== null) {
676
+ if (!is_numeric($language)) {
677
+ $language = KlarnaLanguage::fromCode($language);
678
+ }
679
+ $locale['language'] = intval($language);
680
+ }
681
+
682
+ if ($currency !== null) {
683
+ if (!is_numeric($currency)) {
684
+ $currency = KlarnaCurrency::fromCode($currency);
685
+ }
686
+ $locale['currency'] = intval($currency);
687
+ }
688
+ }
689
+
690
+ // Complete partial structure with defaults
691
+ if ($locale['currency'] === null) {
692
+ $locale['currency'] = $this->getCurrencyForCountry(
693
+ $locale['country']
694
+ );
695
+ }
696
+
697
+ if ($locale['language'] === null) {
698
+ $locale['language'] = $this->getLanguageForCountry(
699
+ $locale['country']
700
+ );
701
+ }
702
+
703
+ $this->_checkCountry($locale['country']);
704
+ $this->_checkCurrency($locale['currency']);
705
+ $this->_checkLanguage($locale['language']);
706
+
707
+ return $locale;
708
+ }
709
+
710
+ /**
711
+ * Sets the country used.
712
+ *
713
+ * <b>Note</b>:<br>
714
+ * If you input 'dk', 'fi', 'de', 'nl', 'no' or 'se', <br>
715
+ * then currency and language will be set to mirror that country.<br>
716
+ *
717
+ * @param string|int $country {@link KlarnaCountry}
718
+ *
719
+ * @see KlarnaCountry
720
+ *
721
+ * @throws KlarnaException
722
+ * @return void
723
+ */
724
+ public function setCountry($country)
725
+ {
726
+ if (!is_numeric($country)
727
+ && (strlen($country) == 2 || strlen($country) == 3)
728
+ ) {
729
+ $country = KlarnaCountry::fromCode($country);
730
+ }
731
+ $this->_checkCountry($country);
732
+ $this->_country = $country;
733
+ }
734
+
735
+ /**
736
+ * Returns the country code for the set country constant.
737
+ *
738
+ * @param int $country {@link KlarnaCountry Country} constant.
739
+ *
740
+ * @return string Two letter code, e.g. "se", "no", etc.
741
+ */
742
+ public function getCountryCode($country = null)
743
+ {
744
+ if ($country === null) {
745
+ $country = $this->_country;
746
+ }
747
+
748
+ $code = KlarnaCountry::getCode($country);
749
+ return (string) $code;
750
+ }
751
+
752
+ /**
753
+ * Returns the {@link KlarnaCountry country} constant from the country code.
754
+ *
755
+ * @param string $code Two letter code, e.g. "se", "no", etc.
756
+ *
757
+ * @throws KlarnaException
758
+ * @return int {@link KlarnaCountry Country} constant.
759
+ */
760
+ public static function getCountryForCode($code)
761
+ {
762
+ $country = KlarnaCountry::fromCode($code);
763
+ if ($country === null) {
764
+ throw new Klarna_UnknownCountryException($code);
765
+ }
766
+ return $country;
767
+ }
768
+
769
+ /**
770
+ * Returns the country constant.
771
+ *
772
+ * @return int {@link KlarnaCountry}
773
+ */
774
+ public function getCountry()
775
+ {
776
+ return $this->_country;
777
+ }
778
+
779
+ /**
780
+ * Sets the language used.
781
+ *
782
+ * <b>Note</b>:<br>
783
+ * You can use the two letter language code instead of the constant.<br>
784
+ * E.g. 'da' instead of using {@link KlarnaLanguage::DA}.<br>
785
+ *
786
+ * @param string|int $language {@link KlarnaLanguage}
787
+ *
788
+ * @see KlarnaLanguage
789
+ *
790
+ * @throws KlarnaException
791
+ * @return void
792
+ */
793
+ public function setLanguage($language)
794
+ {
795
+ if (!is_numeric($language) && strlen($language) == 2) {
796
+ $this->setLanguage(self::getLanguageForCode($language));
797
+ } else {
798
+ $this->_checkLanguage($language);
799
+ $this->_language = $language;
800
+ }
801
+ }
802
+
803
+ /**
804
+ * Returns the language code for the set language constant.
805
+ *
806
+ * @param int $language {@link KlarnaLanguage Language} constant.
807
+ *
808
+ * @return string Two letter code, e.g. "da", "de", etc.
809
+ */
810
+ public function getLanguageCode($language = null)
811
+ {
812
+ if ($language === null) {
813
+ $language = $this->_language;
814
+ }
815
+ $code = KlarnaLanguage::getCode($language);
816
+
817
+ return (string) $code;
818
+ }
819
+
820
+ /**
821
+ * Returns the {@link KlarnaLanguage language} constant from the language code.
822
+ *
823
+ * @param string $code Two letter code, e.g. "da", "de", etc.
824
+ *
825
+ * @throws KlarnaException
826
+ * @return int {@link KlarnaLanguage Language} constant.
827
+ */
828
+ public static function getLanguageForCode($code)
829
+ {
830
+ $language = KlarnaLanguage::fromCode($code);
831
+
832
+ if ($language === null) {
833
+ throw new Klarna_UnknownLanguageException($code);
834
+ }
835
+ return $language;
836
+ }
837
+
838
+ /**
839
+ * Returns the language constant.
840
+ *
841
+ * @return int {@link KlarnaLanguage}
842
+ */
843
+ public function getLanguage()
844
+ {
845
+ return $this->_language;
846
+ }
847
+
848
+ /**
849
+ * Sets the currency used.
850
+ *
851
+ * <b>Note</b>:<br>
852
+ * You can use the three letter shortening of the currency.<br>
853
+ * E.g. "dkk", "eur", "nok" or "sek" instead of the constant.<br>
854
+ *
855
+ * @param string|int $currency {@link KlarnaCurrency}
856
+ *
857
+ * @see KlarnaCurrency
858
+ *
859
+ * @throws KlarnaException
860
+ * @return void
861
+ */
862
+ public function setCurrency($currency)
863
+ {
864
+ if (!is_numeric($currency) && strlen($currency) == 3) {
865
+ $this->setCurrency(self::getCurrencyForCode($currency));
866
+ } else {
867
+ $this->_checkCurrency($currency);
868
+ $this->_currency = $currency;
869
+ }
870
+ }
871
+
872
+ /**
873
+ * Returns the {@link KlarnaCurrency currency} constant from the currency
874
+ * code.
875
+ *
876
+ * @param string $code Two letter code, e.g. "dkk", "eur", etc.
877
+ *
878
+ * @throws KlarnaException
879
+ * @return int {@link KlarnaCurrency Currency} constant.
880
+ */
881
+ public static function getCurrencyForCode($code)
882
+ {
883
+ $currency = KlarnaCurrency::fromCode($code);
884
+ if ($currency === null) {
885
+ throw new Klarna_UnknownCurrencyException($code);
886
+ }
887
+ return $currency;
888
+ }
889
+
890
+ /**
891
+ * Returns the the currency code for the set currency constant.
892
+ *
893
+ * @param int $currency {@link KlarnaCurrency Currency} constant.
894
+ *
895
+ * @return string Three letter currency code.
896
+ */
897
+ public function getCurrencyCode($currency = null)
898
+ {
899
+ if ($currency === null) {
900
+ $currency = $this->_currency;
901
+ }
902
+
903
+ $code = KlarnaCurrency::getCode($currency);
904
+ return (string) $code;
905
+ }
906
+
907
+ /**
908
+ * Returns the set currency constant.
909
+ *
910
+ * @return int {@link KlarnaCurrency}
911
+ */
912
+ public function getCurrency()
913
+ {
914
+ return $this->_currency;
915
+ }
916
+
917
+ /**
918
+ * Returns the {@link KlarnaLanguage language} constant for the specified
919
+ * or set country.
920
+ *
921
+ * @param int $country {@link KlarnaCountry Country} constant.
922
+ *
923
+ * @deprecated Do not use.
924
+ *
925
+ * @return int|false if no match otherwise KlarnaLanguage constant.
926
+ */
927
+ public function getLanguageForCountry($country = null)
928
+ {
929
+ if ($country === null) {
930
+ $country = $this->_country;
931
+ }
932
+ // Since getLanguage defaults to EN, check so we actually have a match
933
+ $language = KlarnaCountry::getLanguage($country);
934
+ if (KlarnaCountry::checkLanguage($country, $language)) {
935
+ return $language;
936
+ }
937
+ return false;
938
+ }
939
+
940
+ /**
941
+ * Returns the {@link KlarnaCurrency currency} constant for the specified
942
+ * or set country.
943
+ *
944
+ * @param int $country {@link KlarnaCountry country} constant.
945
+ *
946
+ * @deprecated Do not use.
947
+ *
948
+ * @return int|false {@link KlarnaCurrency currency} constant.
949
+ */
950
+ public function getCurrencyForCountry($country = null)
951
+ {
952
+ if ($country === null) {
953
+ $country = $this->_country;
954
+ }
955
+ return KlarnaCountry::getCurrency($country);
956
+ }
957
+
958
+ /**
959
+ * Sets the session id's for various device identification,
960
+ * behaviour identification software.
961
+ *
962
+ * <b>Available named session id's</b>:<br>
963
+ * string - dev_id_1<br>
964
+ * string - dev_id_2<br>
965
+ * string - dev_id_3<br>
966
+ * string - beh_id_1<br>
967
+ * string - beh_id_2<br>
968
+ * string - beh_id_3<br>
969
+ *
970
+ * @param string $name Session ID identifier, e.g. 'dev_id_1'.
971
+ * @param string $sid Session ID.
972
+ *
973
+ * @throws KlarnaException
974
+ * @return void
975
+ */
976
+ public function setSessionID($name, $sid)
977
+ {
978
+ $this->_checkArgument($name, "name");
979
+ $this->_checkArgument($sid, "sid");
980
+
981
+ $this->sid[$name] = $sid;
982
+ }
983
+
984
+ /**
985
+ * Sets the shipment information for the upcoming transaction.<br>
986
+ *
987
+ * Using this method is optional.
988
+ *
989
+ * <b>Available named values are</b>:<br>
990
+ * int - delay_adjust<br>
991
+ * string - shipping_company<br>
992
+ * string - shipping_product<br>
993
+ * string - tracking_no<br>
994
+ * array - warehouse_addr<br>
995
+ *
996
+ * "warehouse_addr" is sent using {@link KlarnaAddr::toArray()}.
997
+ *
998
+ * Make sure you send in the values as the right data type.<br>
999
+ * Use strval, intval or similar methods to ensure the right type is sent.
1000
+ *
1001
+ * @param string $name key
1002
+ * @param mixed $value value
1003
+ *
1004
+ * @throws KlarnaException
1005
+ * @return void
1006
+ */
1007
+ public function setShipmentInfo($name, $value)
1008
+ {
1009
+ $this->_checkArgument($name, "name");
1010
+
1011
+ $this->shipInfo[$name] = $value;
1012
+ }
1013
+
1014
+ /**
1015
+ * Sets the Activation information for the upcoming transaction.<br>
1016
+ *
1017
+ * Using this method is optional.
1018
+ *
1019
+ * <b>Available named values are</b>:<br>
1020
+ * int - flags<br>
1021
+ * int - bclass<br>
1022
+ * string - orderid1<br>
1023
+ * string - orderid2<br>
1024
+ * string - ocr<br>
1025
+ * string - reference<br>
1026
+ * string - reference_code<br>
1027
+ * string - cust_no<br>
1028
+ *
1029
+ * Make sure you send in the values as the right data type.<br>
1030
+ * Use strval, intval or similar methods to ensure the right type is sent.
1031
+ *
1032
+ * @param string $name key
1033
+ * @param mixed $value value
1034
+ *
1035
+ * @see setShipmentInfo
1036
+ *
1037
+ * @return void
1038
+ */
1039
+ public function setActivateInfo($name, $value)
1040
+ {
1041
+ $this->activateInfo[$name] = $value;
1042
+ }
1043
+
1044
+ /**
1045
+ * Sets the extra information for the upcoming transaction.<br>
1046
+ *
1047
+ * Using this method is optional.
1048
+ *
1049
+ * <b>Available named values are</b>:<br>
1050
+ * string - cust_no<br>
1051
+ * string - estore_user<br>
1052
+ * string - maiden_name<br>
1053
+ * string - place_of_birth<br>
1054
+ * string - password<br>
1055
+ * string - new_password<br>
1056
+ * string - captcha<br>
1057
+ * int - poa_group<br>
1058
+ * string - poa_pno<br>
1059
+ * string - ready_date<br>
1060
+ * string - rand_string<br>
1061
+ * int - bclass<br>
1062
+ * string - pin<br>
1063
+ *
1064
+ * Make sure you send in the values as the right data type.<br>
1065
+ * Use strval, intval or similar methods to ensure the right type is sent.
1066
+ *
1067
+ * @param string $name key
1068
+ * @param mixed $value value
1069
+ *
1070
+ * @throws KlarnaException
1071
+ * @return void
1072
+ */
1073
+ public function setExtraInfo($name, $value)
1074
+ {
1075
+ $this->_checkArgument($name, "name");
1076
+
1077
+ $this->extraInfo[$name] = $value;
1078
+ }
1079
+
1080
+ /**
1081
+ * Sets the income expense information for the upcoming transaction.<br>
1082
+ *
1083
+ * Using this method is optional.
1084
+ *
1085
+ * <b>Available named values are</b>:<br>
1086
+ * int - yearly_salary<br>
1087
+ * int - no_people_in_household<br>
1088
+ * int - no_children_below_18<br>
1089
+ * int - net_monthly_household_income<br>
1090
+ * int - monthly_cost_accommodation<br>
1091
+ * int - monthly_cost_other_loans<br>
1092
+ *
1093
+ * Make sure you send in the values as the right data type.<br>
1094
+ * Use strval, intval or similar methods to ensure the right type is sent.
1095
+ *
1096
+ * @param string $name key
1097
+ * @param mixed $value value
1098
+ *
1099
+ * @throws KlarnaException
1100
+ * @return void
1101
+ */
1102
+ public function setIncomeInfo($name, $value)
1103
+ {
1104
+ $this->_checkArgument($name, "name");
1105
+
1106
+ $this->incomeInfo[$name] = $value;
1107
+ }
1108
+
1109
+ /**
1110
+ * Sets the bank information for the upcoming transaction.<br>
1111
+ *
1112
+ * Using this method is optional.
1113
+ *
1114
+ * <b>Available named values are</b>:<br>
1115
+ * int - bank_acc_bic<br>
1116
+ * int - bank_acc_no<br>
1117
+ * int - bank_acc_pin<br>
1118
+ * int - bank_acc_tan<br>
1119
+ * string - bank_name<br>
1120
+ * string - bank_city<br>
1121
+ * string - iban<br>
1122
+ *
1123
+ * Make sure you send in the values as the right data type.<br>
1124
+ * Use strval, intval or similar methods to ensure the right type is sent.
1125
+ *
1126
+ * @param string $name key
1127
+ * @param mixed $value value
1128
+ *
1129
+ * @throws KlarnaException
1130
+ * @return void
1131
+ */
1132
+ public function setBankInfo($name, $value)
1133
+ {
1134
+ $this->_checkArgument($name, "name");
1135
+
1136
+ $this->bankInfo[$name] = $value;
1137
+ }
1138
+
1139
+ /**
1140
+ * Sets the travel information for the upcoming transaction.<br>
1141
+ *
1142
+ * Using this method is optional.
1143
+ *
1144
+ * <b>Available named values are</b>:<br>
1145
+ * string - travel_company<br>
1146
+ * string - reseller_company<br>
1147
+ * string - departure_date<br>
1148
+ * string - return_date<br>
1149
+ * array - destinations<br>
1150
+ * array - passenger_list<br>
1151
+ * array - passport_no<br>
1152
+ * array - driver_license_no<br>
1153
+ *
1154
+ * Make sure you send in the values as the right data type.<br>
1155
+ * Use strval, intval or similar methods to ensure the right type is sent.
1156
+ *
1157
+ * @param string $name key
1158
+ * @param mixed $value value
1159
+ *
1160
+ * @throws KlarnaException
1161
+ * @return void
1162
+ */
1163
+ public function setTravelInfo($name, $value)
1164
+ {
1165
+ $this->_checkArgument($name, "name");
1166
+
1167
+ $this->travelInfo[$name] = $value;
1168
+ }
1169
+
1170
+ /**
1171
+ * Returns the clients IP address.
1172
+ *
1173
+ * @return string
1174
+ */
1175
+ public function getClientIP()
1176
+ {
1177
+ $tmp_ip = '';
1178
+ $x_fwd = null;
1179
+
1180
+ //Proxy handling.
1181
+ if (array_key_exists('REMOTE_ADDR', $_SERVER)) {
1182
+ $tmp_ip = $_SERVER['REMOTE_ADDR'];
1183
+ }
1184
+
1185
+ if (isset($_SERVER["HTTP_X_FORWARDED_FOR"])) {
1186
+ $x_fwd = $_SERVER["HTTP_X_FORWARDED_FOR"];
1187
+ }
1188
+
1189
+ if (self::$x_forwarded_for && ($x_fwd !== null)) {
1190
+ $forwarded = explode(",", $x_fwd);
1191
+ return trim($forwarded[0]);
1192
+ }
1193
+
1194
+ return $tmp_ip;
1195
+ }
1196
+
1197
+ /**
1198
+ * Sets the specified address for the current order.
1199
+ *
1200
+ * <b>Address type can be</b>:<br>
1201
+ * {@link KlarnaFlags::IS_SHIPPING}<br>
1202
+ * {@link KlarnaFlags::IS_BILLING}<br>
1203
+ *
1204
+ * @param int $type Address type.
1205
+ * @param KlarnaAddr $addr Specified address.
1206
+ *
1207
+ * @throws KlarnaException
1208
+ * @return void
1209
+ */
1210
+ public function setAddress($type, $addr)
1211
+ {
1212
+ if (!($addr instanceof KlarnaAddr)) {
1213
+ throw new Klarna_InvalidKlarnaAddrException;
1214
+ }
1215
+
1216
+ if ($addr->isCompany === null) {
1217
+ $addr->isCompany = false;
1218
+ }
1219
+
1220
+ if ($type === KlarnaFlags::IS_SHIPPING) {
1221
+ $this->shipping = $addr;
1222
+ self::printDebug("shipping address array", $this->shipping);
1223
+ return;
1224
+ }
1225
+
1226
+ if ($type === KlarnaFlags::IS_BILLING) {
1227
+ $this->billing = $addr;
1228
+ self::printDebug("billing address array", $this->billing);
1229
+ return;
1230
+ }
1231
+ throw new Klarna_UnknownAddressTypeException($type);
1232
+ }
1233
+
1234
+ /**
1235
+ * Sets order id's from other systems for the upcoming transaction.<br>
1236
+ * User is only sent with {@link Klarna::addTransaction()}.<br>
1237
+ *
1238
+ * @param string $orderid1 order id 1
1239
+ * @param string $orderid2 order id 2
1240
+ * @param string $user username
1241
+ *
1242
+ * @see Klarna::setExtraInfo()
1243
+ *
1244
+ * @throws KlarnaException
1245
+ * @return void
1246
+ */
1247
+ public function setEstoreInfo($orderid1 = "", $orderid2 = "", $user = "")
1248
+ {
1249
+ if (!is_string($orderid1)) {
1250
+ $orderid1 = strval($orderid1);
1251
+ }
1252
+
1253
+ if (!is_string($orderid2)) {
1254
+ $orderid2 = strval($orderid2);
1255
+ }
1256
+
1257
+ if (!is_string($user)) {
1258
+ $user = strval($user);
1259
+ }
1260
+
1261
+ if (strlen($user) > 0 ) {
1262
+ $this->setExtraInfo('estore_user', $user);
1263
+ }
1264
+
1265
+ $this->orderid[0] = $orderid1;
1266
+ $this->orderid[1] = $orderid2;
1267
+ }
1268
+
1269
+ /**
1270
+ * Sets the reference (person) and reference code, for the upcoming
1271
+ * transaction.
1272
+ *
1273
+ * If this is omitted, it can grab first name, last name from the address
1274
+ * and use that as a reference person.
1275
+ *
1276
+ * @param string $ref Reference person / message to customer on invoice.
1277
+ * @param string $code Reference code / message to customer on invoice.
1278
+ *
1279
+ * @return void
1280
+ */
1281
+ public function setReference($ref, $code)
1282
+ {
1283
+ $this->_checkRef($ref, $code);
1284
+ $this->reference = $ref;
1285
+ $this->reference_code = $code;
1286
+ }
1287
+
1288
+ /**
1289
+ * Returns the reference (person).
1290
+ *
1291
+ * @return string
1292
+ */
1293
+ public function getReference()
1294
+ {
1295
+ return $this->reference;
1296
+ }
1297
+
1298
+ /**
1299
+ * Returns an associative array used to send the address to Klarna.
1300
+ * TODO: Kill it all
1301
+ *
1302
+ * @param KlarnaAddr $addr Address object to assemble.
1303
+ *
1304
+ * @throws KlarnaException
1305
+ * @return array The address for the specified method.
1306
+ */
1307
+ protected function assembleAddr($addr)
1308
+ {
1309
+ if (!($addr instanceof KlarnaAddr)) {
1310
+ throw new Klarna_InvalidKlarnaAddrException;
1311
+ }
1312
+
1313
+ return $addr->toArray();
1314
+ }
1315
+
1316
+ /**
1317
+ * Sets the comment field, which can be shown in the invoice.
1318
+ *
1319
+ * @param string $data comment to set
1320
+ *
1321
+ * @return void
1322
+ */
1323
+ public function setComment($data)
1324
+ {
1325
+ $this->comment = $data;
1326
+ }
1327
+
1328
+ /**
1329
+ * Adds an additional comment to the comment field. Appends with a newline.
1330
+ *
1331
+ * @param string $data comment to add
1332
+ *
1333
+ * @see Klarna::setComment()
1334
+ *
1335
+ * @return void
1336
+ */
1337
+ public function addComment($data)
1338
+ {
1339
+ $this->comment .= "\n".$data;
1340
+ }
1341
+
1342
+ /**
1343
+ * Returns the PNO/SSN encoding constant for currently set country.
1344
+ *
1345
+ * <b>Note</b>:<br>
1346
+ * Country, language and currency needs to match!
1347
+ *
1348
+ * @throws KlarnaException
1349
+ * @return int {@link KlarnaEncoding} constant.
1350
+ */
1351
+ public function getPNOEncoding()
1352
+ {
1353
+ $this->_checkLocale();
1354
+
1355
+ $country = KlarnaCountry::getCode($this->_country);
1356
+
1357
+ return KlarnaEncoding::get($country);
1358
+ }
1359
+
1360
+ /**
1361
+ * Purpose: The get_addresses function is used to retrieve a customer's
1362
+ * address(es). Using this, the customer is not required to enter any
1363
+ * information, only confirm the one presented to him/her.<br>
1364
+ *
1365
+ * The get_addresses function can also be used for companies.<br>
1366
+ * If the customer enters a company number, it will return all the
1367
+ * addresses where the company is registered at.<br>
1368
+ *
1369
+ * The get_addresses function is ONLY allowed to be used for Swedish
1370
+ * persons with the following conditions:
1371
+ * <ul>
1372
+ * <li>
1373
+ * It can be only used if invoice or part payment is
1374
+ * the default payment method
1375
+ * </li>
1376
+ * <li>
1377
+ * It has to disappear if the customer chooses another
1378
+ * payment method
1379
+ * </li>
1380
+ * <li>
1381
+ * The button is not allowed to be called "get address", but
1382
+ * "continue" or<br>
1383
+ * it can be picked up automatically when all the numbers have
1384
+ * been typed.
1385
+ * </li>
1386
+ * </ul>
1387
+ *
1388
+ * <b>Type can be one of these</b>:<br>
1389
+ * {@link KlarnaFlags::GA_ALL},<br>
1390
+ * {@link KlarnaFlags::GA_LAST},<br>
1391
+ * {@link KlarnaFlags::GA_GIVEN}.<br>
1392
+ *
1393
+ * @param string $pno Social security number, personal number, ...
1394
+ * @param int $encoding {@link KlarnaEncoding PNO Encoding} constant.
1395
+ * @param int $type Specifies returned information.
1396
+ *
1397
+ * @link http://integration.klarna.com/en/api/standard-integration/functions
1398
+ * /getaddresses
1399
+ * @throws KlarnaException
1400
+ * @return array An array of {@link KlarnaAddr} objects.
1401
+ */
1402
+ public function getAddresses(
1403
+ $pno, $encoding = null, $type = KlarnaFlags::GA_GIVEN
1404
+ ) {
1405
+ if ($this->_country !== KlarnaCountry::SE) {
1406
+ throw new Klarna_UnsupportedMarketException("Sweden");
1407
+ }
1408
+
1409
+ //Get the PNO/SSN encoding constant.
1410
+ if ($encoding === null) {
1411
+ $encoding = $this->getPNOEncoding();
1412
+ }
1413
+
1414
+ $this->_checkPNO($pno, $encoding);
1415
+
1416
+ $digestSecret = self::digest(
1417
+ $this->colon(
1418
+ $this->_eid, $pno, $this->_secret
1419
+ )
1420
+ );
1421
+
1422
+ $paramList = array(
1423
+ $pno,
1424
+ $this->_eid,
1425
+ $digestSecret,
1426
+ $encoding,
1427
+ $type,
1428
+ $this->getClientIP()
1429
+ );
1430
+
1431
+ self::printDebug("get_addresses array", $paramList);
1432
+
1433
+ $result = $this->xmlrpc_call('get_addresses', $paramList);
1434
+
1435
+ self::printDebug("get_addresses result array", $result);
1436
+
1437
+ $addrs = array();
1438
+ foreach ($result as $tmpAddr) {
1439
+ try {
1440
+ $addr = new KlarnaAddr();
1441
+ if ($type === KlarnaFlags::GA_GIVEN) {
1442
+ $addr->isCompany = (count($tmpAddr) == 5) ? true : false;
1443
+ if ($addr->isCompany) {
1444
+ $addr->setCompanyName($tmpAddr[0]);
1445
+ $addr->setStreet($tmpAddr[1]);
1446
+ $addr->setZipCode($tmpAddr[2]);
1447
+ $addr->setCity($tmpAddr[3]);
1448
+ $addr->setCountry($tmpAddr[4]);
1449
+ } else {
1450
+ $addr->setFirstName($tmpAddr[0]);
1451
+ $addr->setLastName($tmpAddr[1]);
1452
+ $addr->setStreet($tmpAddr[2]);
1453
+ $addr->setZipCode($tmpAddr[3]);
1454
+ $addr->setCity($tmpAddr[4]);
1455
+ $addr->setCountry($tmpAddr[5]);
1456
+ }
1457
+ } else if ($type === KlarnaFlags::GA_LAST) {
1458
+ // Here we cannot decide if it is a company or not?
1459
+ // Assume private person.
1460
+ $addr->setLastName($tmpAddr[0]);
1461
+ $addr->setStreet($tmpAddr[1]);
1462
+ $addr->setZipCode($tmpAddr[2]);
1463
+ $addr->setCity($tmpAddr[3]);
1464
+ $addr->setCountry($tmpAddr[4]);
1465
+ } else if ($type === KlarnaFlags::GA_ALL) {
1466
+ if (strlen($tmpAddr[0]) > 0) {
1467
+ $addr->setFirstName($tmpAddr[0]);
1468
+ $addr->setLastName($tmpAddr[1]);
1469
+ } else {
1470
+ $addr->isCompany = true;
1471
+ $addr->setCompanyName($tmpAddr[1]);
1472
+ }
1473
+ $addr->setStreet($tmpAddr[2]);
1474
+ $addr->setZipCode($tmpAddr[3]);
1475
+ $addr->setCity($tmpAddr[4]);
1476
+ $addr->setCountry($tmpAddr[5]);
1477
+ } else {
1478
+ continue;
1479
+ }
1480
+ $addrs[] = $addr;
1481
+ } catch(Exception $e) {
1482
+ //Silently fail
1483
+ }
1484
+ }
1485
+
1486
+ return $addrs;
1487
+ }
1488
+
1489
+ /**
1490
+ * Adds an article to the current goods list for the current order.
1491
+ *
1492
+ * <b>Note</b>:<br>
1493
+ * It is recommended that you use {@link KlarnaFlags::INC_VAT}.<br>
1494
+ *
1495
+ * <b>Flags can be</b>:<br>
1496
+ * {@link KlarnaFlags::INC_VAT}<br>
1497
+ * {@link KlarnaFlags::IS_SHIPMENT}<br>
1498
+ * {@link KlarnaFlags::IS_HANDLING}<br>
1499
+ * {@link KlarnaFlags::PRINT_1000}<br>
1500
+ * {@link KlarnaFlags::PRINT_100}<br>
1501
+ * {@link KlarnaFlags::PRINT_10}<br>
1502
+ * {@link KlarnaFlags::NO_FLAG}<br>
1503
+ *
1504
+ * Some flags can be added to each other for multiple options.
1505
+ *
1506
+ * @param int $qty Quantity.
1507
+ * @param string $artNo Article number.
1508
+ * @param string $title Article title.
1509
+ * @param int $price Article price.
1510
+ * @param float $vat VAT in percent, e.g. 25% is inputted as 25.
1511
+ * @param float $discount Possible discount on article.
1512
+ * @param int $flags Options which specify the article
1513
+ * ({@link KlarnaFlags::IS_HANDLING}) and it's price
1514
+ * ({@link KlarnaFlags::INC_VAT})
1515
+ *
1516
+ * @see Klarna::addTransaction()
1517
+ * @see Klarna::reserveAmount()
1518
+ * @see Klarna::activateReservation()
1519
+ *
1520
+ * @throws KlarnaException
1521
+ * @return void
1522
+ */
1523
+ public function addArticle(
1524
+ $qty, $artNo, $title, $price, $vat, $discount = 0,
1525
+ $flags = KlarnaFlags::INC_VAT
1526
+ ) {
1527
+ $this->_checkQty($qty);
1528
+
1529
+ // Either artno or title has to be set
1530
+ if ((($artNo === null ) || ($artNo == ""))
1531
+ && (($title === null ) || ($title == ""))
1532
+ ) {
1533
+ throw new Klarna_ArgumentNotSetException('Title and ArtNo', 50026);
1534
+ }
1535
+
1536
+ $this->_checkPrice($price);
1537
+ $this->_checkVAT($vat);
1538
+ $this->_checkDiscount($discount);
1539
+ $this->_checkInt($flags, 'flags');
1540
+
1541
+ //Create goodsList array if not set.
1542
+ if (!$this->goodsList || !is_array($this->goodsList)) {
1543
+ $this->goodsList = array();
1544
+ }
1545
+
1546
+ //Populate a temp array with the article details.
1547
+ $tmpArr = array(
1548
+ "artno" => $artNo,
1549
+ "title" => $title,
1550
+ "price" => $price,
1551
+ "vat" => $vat,
1552
+ "discount" => $discount,
1553
+ "flags" => $flags
1554
+ );
1555
+
1556
+ //Add the temp array and quantity field to the internal goods list.
1557
+ $this->goodsList[] = array(
1558
+ "goods" => $tmpArr,
1559
+ "qty" => $qty
1560
+ );
1561
+
1562
+ if (count($this->goodsList) > 0) {
1563
+ self::printDebug(
1564
+ "article added",
1565
+ $this->goodsList[count($this->goodsList)-1]
1566
+ );
1567
+ }
1568
+ }
1569
+
1570
+ /**
1571
+ * Assembles and sends the current order to Klarna.<br>
1572
+ * This clears all relevant data if $clear is set to true.<br>
1573
+ *
1574
+ * <b>This method returns an array with</b>:<br>
1575
+ * Invoice number<br>
1576
+ * Order status flag<br>
1577
+ *
1578
+ * If the flag {@link KlarnaFlags::RETURN_OCR} is used:<br>
1579
+ * Invoice number<br>
1580
+ * OCR number <br>
1581
+ * Order status flag<br>
1582
+ *
1583
+ * <b>Order status can be</b>:<br>
1584
+ * {@link KlarnaFlags::ACCEPTED}<br>
1585
+ * {@link KlarnaFlags::PENDING}<br>
1586
+ * {@link KlarnaFlags::DENIED}<br>
1587
+ *
1588
+ * Gender is only required for Germany and Netherlands.<br>
1589
+ *
1590
+ * <b>Flags can be</b>:<br>
1591
+ * {@link KlarnaFlags::NO_FLAG}<br>
1592
+ * {@link KlarnaFlags::TEST_MODE}<br>
1593
+ * {@link KlarnaFlags::AUTO_ACTIVATE}<br>
1594
+ * {@link KlarnaFlags::SENSITIVE_ORDER}<br>
1595
+ * {@link KlarnaFlags::RETURN_OCR}<br>
1596
+ * {@link KlarnaFlags::M_PHONE_TRANSACTION}<br>
1597
+ * {@link KlarnaFlags::M_SEND_PHONE_PIN}<br>
1598
+ *
1599
+ * Some flags can be added to each other for multiple options.
1600
+ *
1601
+ * <b>Note</b>:<br>
1602
+ * Normal shipment type is assumed unless otherwise specified,
1603
+ * ou can do this by calling:<br>
1604
+ * {@link Klarna::setShipmentInfo() setShipmentInfo('delay_adjust', ...)}
1605
+ * with either:<br>
1606
+ * {@link KlarnaFlags::NORMAL_SHIPMENT NORMAL_SHIPMENT} or
1607
+ * {@link KlarnaFlags::EXPRESS_SHIPMENT EXPRESS_SHIPMENT}<br>
1608
+ *
1609
+ * @param string $pno Personal number, SSN, date of birth, etc.
1610
+ * @param int $gender {@link KlarnaFlags::FEMALE} or
1611
+ * {@link KlarnaFlags::MALE},
1612
+ * null or "" for unspecified.
1613
+ * @param int $flags Options which affect the behaviour.
1614
+ * @param int $pclass PClass id used for this invoice.
1615
+ * @param int $encoding {@link KlarnaEncoding Encoding} constant for the
1616
+ * PNO parameter.
1617
+ * @param bool $clear Whether customer info should be cleared after
1618
+ * this call or not.
1619
+ *
1620
+ * @link http://integration.klarna.com/en/api/standard-integration/functions/
1621
+ * addtransaction
1622
+ *
1623
+ * @throws KlarnaException
1624
+ * @return array An array with invoice number and order status. [string, int]
1625
+ */
1626
+ public function addTransaction(
1627
+ $pno, $gender, $flags = KlarnaFlags::NO_FLAG,
1628
+ $pclass = KlarnaPClass::INVOICE, $encoding = null, $clear = true
1629
+ ) {
1630
+ $this->_checkLocale(50023);
1631
+
1632
+ //Get the PNO/SSN encoding constant.
1633
+ if ($encoding === null) {
1634
+ $encoding = $this->getPNOEncoding();
1635
+ }
1636
+
1637
+ if (!($flags & KlarnaFlags::PRE_PAY)) {
1638
+ $this->_checkPNO($pno, $encoding);
1639
+ }
1640
+
1641
+ if ($gender === 'm') {
1642
+ $gender = KlarnaFlags::MALE;
1643
+ } else if ($gender === 'f') {
1644
+ $gender = KlarnaFlags::FEMALE;
1645
+ }
1646
+
1647
+ if ($gender !== null && strlen($gender) > 0) {
1648
+ $this->_checkInt($gender, 'gender');
1649
+ }
1650
+
1651
+ $this->_checkInt($flags, 'flags');
1652
+ $this->_checkInt($pclass, 'pclass');
1653
+
1654
+ //Check so required information is set.
1655
+ $this->_checkGoodslist();
1656
+
1657
+ //We need at least one address set
1658
+ if (!($this->billing instanceof KlarnaAddr)
1659
+ && !($this->shipping instanceof KlarnaAddr)
1660
+ ) {
1661
+ throw new Klarna_MissingAddressException;
1662
+ }
1663
+
1664
+ //If only one address is set, copy to the other address.
1665
+ if (!($this->shipping instanceof KlarnaAddr)
1666
+ && ($this->billing instanceof KlarnaAddr)
1667
+ ) {
1668
+ $this->shipping = $this->billing;
1669
+ } else if (!($this->billing instanceof KlarnaAddr)
1670
+ && ($this->shipping instanceof KlarnaAddr)
1671
+ ) {
1672
+ $this->billing = $this->shipping;
1673
+ }
1674
+
1675
+ //Assume normal shipment unless otherwise specified.
1676
+ if (!isset($this->shipInfo['delay_adjust'])) {
1677
+ $this->setShipmentInfo('delay_adjust', KlarnaFlags::NORMAL_SHIPMENT);
1678
+ }
1679
+
1680
+ //Make sure we get any session ID's or similar
1681
+ $this->initCheckout();
1682
+
1683
+ //function add_transaction_digest
1684
+ $string = "";
1685
+ foreach ($this->goodsList as $goods) {
1686
+ $string .= $goods['goods']['title'] .':';
1687
+ }
1688
+ $digestSecret = self::digest($string . $this->_secret);
1689
+ //end function add_transaction_digest
1690
+
1691
+ $billing = $this->assembleAddr($this->billing);
1692
+ $shipping = $this->assembleAddr($this->shipping);
1693
+
1694
+ //Shipping country must match specified country!
1695
+ if (strlen($shipping['country']) > 0
1696
+ && ($shipping['country'] !== $this->_country)
1697
+ ) {
1698
+ throw new Klarna_ShippingCountryException;
1699
+ }
1700
+
1701
+ $paramList = array(
1702
+ $pno,
1703
+ $gender,
1704
+ $this->reference,
1705
+ $this->reference_code,
1706
+ $this->orderid[0],
1707
+ $this->orderid[1],
1708
+ $shipping,
1709
+ $billing,
1710
+ $this->getClientIP(),
1711
+ $flags,
1712
+ $this->_currency,
1713
+ $this->_country,
1714
+ $this->_language,
1715
+ $this->_eid,
1716
+ $digestSecret,
1717
+ $encoding,
1718
+ $pclass,
1719
+ $this->goodsList,
1720
+ $this->comment,
1721
+ $this->shipInfo,
1722
+ $this->travelInfo,
1723
+ $this->incomeInfo,
1724
+ $this->bankInfo,
1725
+ $this->sid,
1726
+ $this->extraInfo
1727
+ );
1728
+
1729
+ self::printDebug('add_invoice', $paramList);
1730
+
1731
+ $result = $this->xmlrpc_call('add_invoice', $paramList);
1732
+
1733
+ if ($clear === true) {
1734
+ //Make sure any stored values that need to be unique between
1735
+ //purchases are cleared.
1736
+ foreach ($this->coObjects as $co) {
1737
+ $co->clear();
1738
+ }
1739
+ $this->clear();
1740
+ }
1741
+
1742
+ self::printDebug('add_invoice result', $result);
1743
+
1744
+ return $result;
1745
+ }
1746
+
1747
+
1748
+ /**
1749
+ * Activates previously created invoice
1750
+ * (from {@link Klarna::addTransaction()}).
1751
+ *
1752
+ * <b>Note</b>:<br>
1753
+ * If you want to change the shipment type, you can specify it using:
1754
+ * {@link Klarna::setShipmentInfo() setShipmentInfo('delay_adjust', ...)}
1755
+ * with either: {@link KlarnaFlags::NORMAL_SHIPMENT NORMAL_SHIPMENT} or
1756
+ * {@link KlarnaFlags::EXPRESS_SHIPMENT EXPRESS_SHIPMENT}
1757
+ *
1758
+ * @param string $invNo Invoice number.
1759
+ * @param int $pclass PClass id used for this invoice.
1760
+ * @param bool $clear Whether customer info should be cleared after this
1761
+ * call.
1762
+ *
1763
+ * @see Klarna::setShipmentInfo()
1764
+ * @link http://integration.klarna.com/en/api/standard-integration/functions
1765
+ * /activateinvoice
1766
+ *
1767
+ * @throws KlarnaException
1768
+ * @return string An URL to the PDF invoice.
1769
+ */
1770
+ public function activateInvoice(
1771
+ $invNo, $pclass = KlarnaPClass::INVOICE, $clear = true
1772
+ ) {
1773
+ $this->_checkInvNo($invNo);
1774
+
1775
+ $digestSecret = self::digest(
1776
+ $this->colon($this->_eid, $invNo, $this->_secret)
1777
+ );
1778
+
1779
+ $paramList = array(
1780
+ $this->_eid,
1781
+ $invNo,
1782
+ $digestSecret,
1783
+ $pclass,
1784
+ $this->shipInfo
1785
+ );
1786
+
1787
+ self::printDebug('activate_invoice', $paramList);
1788
+
1789
+ $result = $this->xmlrpc_call('activate_invoice', $paramList);
1790
+
1791
+ if ($clear === true) {
1792
+ $this->clear();
1793
+ }
1794
+
1795
+ self::printDebug('activate_invoice result', $result);
1796
+
1797
+ return $result;
1798
+ }
1799
+
1800
+ /**
1801
+ * Removes a passive invoices which has previously been created with
1802
+ * {@link Klarna::addTransaction()}.
1803
+ * True is returned if the invoice was successfully removed, otherwise an
1804
+ * exception is thrown.<br>
1805
+ *
1806
+ * @param string $invNo Invoice number.
1807
+ *
1808
+ * @throws KlarnaException
1809
+ * @return bool
1810
+ */
1811
+ public function deleteInvoice($invNo)
1812
+ {
1813
+ $this->_checkInvNo($invNo);
1814
+
1815
+ $digestSecret = self::digest(
1816
+ $this->colon($this->_eid, $invNo, $this->_secret)
1817
+ );
1818
+
1819
+ $paramList = array(
1820
+ $this->_eid,
1821
+ $invNo,
1822
+ $digestSecret
1823
+ );
1824
+
1825
+ self::printDebug('delete_invoice', $paramList);
1826
+
1827
+ $result = $this->xmlrpc_call('delete_invoice', $paramList);
1828
+
1829
+ return ($result == 'ok') ? true : false;
1830
+ }
1831
+
1832
+ /**
1833
+ * Summarizes the prices of the held goods list
1834
+ *
1835
+ * @return int total amount
1836
+ */
1837
+ public function summarizeGoodsList()
1838
+ {
1839
+ $amount = 0;
1840
+ if (!is_array($this->goodsList)) {
1841
+ return $amount;
1842
+ }
1843
+ foreach ($this->goodsList as $goods) {
1844
+ $price = $goods['goods']['price'];
1845
+
1846
+ // Add VAT if price is Excluding VAT
1847
+ if (($goods['goods']['flags'] & KlarnaFlags::INC_VAT) === 0) {
1848
+ $vat = $goods['goods']['vat'] / 100.0;
1849
+ $price *= (1.0 + $vat);
1850
+ }
1851
+
1852
+ // Reduce discounts
1853
+ if ($goods['goods']['discount'] > 0) {
1854
+ $discount = $goods['goods']['discount'] / 100.0;
1855
+ $price *= (1.0 - $discount);
1856
+ }
1857
+
1858
+ $amount += $price * (int)$goods['qty'];
1859
+ }
1860
+ return $amount;
1861
+ }
1862
+
1863
+ /**
1864
+ * Reserves a purchase amount for a specific customer. <br>
1865
+ * The reservation is valid, by default, for 7 days.<br>
1866
+ *
1867
+ * <b>This method returns an array with</b>:<br>
1868
+ * A reservation number (rno)<br>
1869
+ * Order status flag<br>
1870
+ *
1871
+ * <b>Order status can be</b>:<br>
1872
+ * {@link KlarnaFlags::ACCEPTED}<br>
1873
+ * {@link KlarnaFlags::PENDING}<br>
1874
+ * {@link KlarnaFlags::DENIED}<br>
1875
+ *
1876
+ * <b>Please note</b>:<br>
1877
+ * Activation must be done with activate_reservation, i.e. you cannot
1878
+ * activate through Klarna Online.
1879
+ *
1880
+ * Gender is only required for Germany and Netherlands.<br>
1881
+ *
1882
+ * <b>Flags can be set to</b>:<br>
1883
+ * {@link KlarnaFlags::NO_FLAG}<br>
1884
+ * {@link KlarnaFlags::TEST_MODE}<br>
1885
+ * {@link KlarnaFlags::RSRV_SENSITIVE_ORDER}<br>
1886
+ * {@link KlarnaFlags::RSRV_PHONE_TRANSACTION}<br>
1887
+ * {@link KlarnaFlags::RSRV_SEND_PHONE_PIN}<br>
1888
+ *
1889
+ * Some flags can be added to each other for multiple options.
1890
+ *
1891
+ * <b>Note</b>:<br>
1892
+ * Normal shipment type is assumed unless otherwise specified, you can do
1893
+ * this by calling:<br>
1894
+ * {@link Klarna::setShipmentInfo() setShipmentInfo('delay_adjust', ...)}
1895
+ * with either: {@link KlarnaFlags::NORMAL_SHIPMENT NORMAL_SHIPMENT} or
1896
+ * {@link KlarnaFlags::EXPRESS_SHIPMENT EXPRESS_SHIPMENT}<br>
1897
+ *
1898
+ * @param string $pno Personal number, SSN, date of birth, etc.
1899
+ * @param int $gender {@link KlarnaFlags::FEMALE} or
1900
+ * {@link KlarnaFlags::MALE}, null for unspecified.
1901
+ * @param int $amount Amount to be reserved, including VAT.
1902
+ * @param int $flags Options which affect the behaviour.
1903
+ * @param int $pclass {@link KlarnaPClass::getId() PClass ID}.
1904
+ * @param int $encoding {@link KlarnaEncoding PNO Encoding} constant.
1905
+ * @param bool $clear Whether customer info should be cleared after
1906
+ * this call.
1907
+ *
1908
+ * @link http://integration.klarna.com/en/api/advanced-integration
1909
+ * /functions/reserveamount
1910
+ *
1911
+ * @throws KlarnaException
1912
+ * @return array An array with reservation number and order
1913
+ * status. [string, int]
1914
+ */
1915
+ public function reserveAmount(
1916
+ $pno, $gender, $amount, $flags = 0, $pclass = KlarnaPClass::INVOICE,
1917
+ $encoding = null, $clear = true
1918
+ ) {
1919
+ $this->_checkLocale();
1920
+
1921
+ //Get the PNO/SSN encoding constant.
1922
+ if ($encoding === null) {
1923
+ $encoding = $this->getPNOEncoding();
1924
+ }
1925
+
1926
+ $this->_checkPNO($pno, $encoding);
1927
+
1928
+ if ($gender === 'm') {
1929
+ $gender = KlarnaFlags::MALE;
1930
+ } else if ($gender === 'f') {
1931
+ $gender = KlarnaFlags::FEMALE;
1932
+ }
1933
+ if ($gender !== null && strlen($gender) > 0) {
1934
+ $this->_checkInt($gender, 'gender');
1935
+ }
1936
+
1937
+ $this->_checkInt($flags, 'flags');
1938
+ $this->_checkInt($pclass, 'pclass');
1939
+
1940
+ //Check so required information is set.
1941
+ $this->_checkGoodslist();
1942
+
1943
+
1944
+ //Calculate automatically the amount from goodsList.
1945
+ if ($amount === -1) {
1946
+ $amount = (int)round($this->summarizeGoodsList());
1947
+ } else {
1948
+ $this->_checkAmount($amount);
1949
+ }
1950
+
1951
+ if ($amount < 0) {
1952
+ throw new Klarna_InvalidPriceException($amount);
1953
+ }
1954
+
1955
+ //No addresses used for phone transactions
1956
+ if ($flags & KlarnaFlags::RSRV_PHONE_TRANSACTION) {
1957
+ $billing = $shipping = '';
1958
+ } else {
1959
+ $billing = $this->assembleAddr($this->billing);
1960
+ $shipping = $this->assembleAddr($this->shipping);
1961
+
1962
+ if (strlen($shipping['country']) > 0
1963
+ && ($shipping['country'] !== $this->_country)
1964
+ ) {
1965
+ throw new Klarna_ShippingCountryException;
1966
+ }
1967
+ }
1968
+
1969
+ //Assume normal shipment unless otherwise specified.
1970
+ if (!isset($this->shipInfo['delay_adjust'])) {
1971
+ $this->setShipmentInfo('delay_adjust', KlarnaFlags::NORMAL_SHIPMENT);
1972
+ }
1973
+
1974
+ //Make sure we get any session ID's or similar
1975
+ $this->initCheckout($this, $this->_eid);
1976
+
1977
+ $digestSecret = self::digest(
1978
+ "{$this->_eid}:{$pno}:{$amount}:{$this->_secret}"
1979
+ );
1980
+
1981
+ $paramList = array(
1982
+ $pno,
1983
+ $gender,
1984
+ $amount,
1985
+ $this->reference,
1986
+ $this->reference_code,
1987
+ $this->orderid[0],
1988
+ $this->orderid[1],
1989
+ $shipping,
1990
+ $billing,
1991
+ $this->getClientIP(),
1992
+ $flags,
1993
+ $this->_currency,
1994
+ $this->_country,
1995
+ $this->_language,
1996
+ $this->_eid,
1997
+ $digestSecret,
1998
+ $encoding, $pclass,
1999
+ $this->goodsList,
2000
+ $this->comment,
2001
+ $this->shipInfo,
2002
+ $this->travelInfo,
2003
+ $this->incomeInfo,
2004
+ $this->bankInfo,
2005
+ $this->sid,
2006
+ $this->extraInfo
2007
+ );
2008
+
2009
+ self::printDebug('reserve_amount', $paramList);
2010
+
2011
+ $result = $this->xmlrpc_call('reserve_amount', $paramList);
2012
+
2013
+ if ($clear === true) {
2014
+ //Make sure any stored values that need to be unique between
2015
+ //purchases are cleared.
2016
+ foreach ($this->coObjects as $co) {
2017
+ $co->clear();
2018
+ }
2019
+ $this->clear();
2020
+ }
2021
+
2022
+ self::printDebug('reserve_amount result', $result);
2023
+
2024
+ return $result;
2025
+ }
2026
+
2027
+ /**
2028
+ * Cancels a reservation.
2029
+ *
2030
+ * @param string $rno Reservation number.
2031
+ *
2032
+ * @link http://integration.klarna.com/en/api/advanced-integration/functions
2033
+ * /cancelreservation
2034
+ *
2035
+ * @throws KlarnaException
2036
+ * @return bool True, if the cancellation was successful.
2037
+ */
2038
+ public function cancelReservation($rno)
2039
+ {
2040
+ $this->_checkRNO($rno);
2041
+
2042
+ $digestSecret = self::digest(
2043
+ $this->colon($this->_eid, $rno, $this->_secret)
2044
+ );
2045
+ $paramList = array(
2046
+ $rno,
2047
+ $this->_eid,
2048
+ $digestSecret
2049
+ );
2050
+
2051
+ self::printDebug('cancel_reservation', $paramList);
2052
+
2053
+ $result = $this->xmlrpc_call('cancel_reservation', $paramList);
2054
+
2055
+ return ($result == 'ok');
2056
+ }
2057
+
2058
+ /**
2059
+ * Changes specified reservation to a new amount.
2060
+ *
2061
+ * <b>Flags can be either of these</b>:<br>
2062
+ * {@link KlarnaFlags::NEW_AMOUNT}<br>
2063
+ * {@link KlarnaFlags::ADD_AMOUNT}<br>
2064
+ *
2065
+ * @param string $rno Reservation number.
2066
+ * @param int $amount Amount including VAT.
2067
+ * @param int $flags Options which affect the behaviour.
2068
+ *
2069
+ * @link http://integration.klarna.com/en/api/advanced-integration/functions
2070
+ * /changereservation
2071
+ *
2072
+ * @throws KlarnaException
2073
+ * @return bool True, if the change was successful.
2074
+ */
2075
+ public function changeReservation(
2076
+ $rno, $amount, $flags = KlarnaFlags::NEW_AMOUNT
2077
+ ) {
2078
+ $this->_checkRNO($rno);
2079
+ $this->_checkAmount($amount);
2080
+ $this->_checkInt($flags, 'flags');
2081
+
2082
+ $digestSecret = self::digest(
2083
+ $this->colon($this->_eid, $rno, $amount, $this->_secret)
2084
+ );
2085
+ $paramList = array(
2086
+ $rno,
2087
+ $amount,
2088
+ $this->_eid,
2089
+ $digestSecret,
2090
+ $flags
2091
+ );
2092
+
2093
+ self::printDebug('change_reservation', $paramList);
2094
+
2095
+ $result = $this->xmlrpc_call('change_reservation', $paramList);
2096
+
2097
+ return ($result == 'ok') ? true : false;
2098
+ }
2099
+
2100
+ /**
2101
+ * Update the reservation matching the given reservation number.
2102
+ *
2103
+ * @param string $rno Reservation number
2104
+ * @param boolean $clear clear set data aftre updating. Defaulted to true.
2105
+ *
2106
+ * @throws KlarnaException if no RNO is given, or if an error is recieved
2107
+ * from Klarna Online.
2108
+ *
2109
+ * @return true if the update was successful
2110
+ */
2111
+ public function update($rno, $clear = true)
2112
+ {
2113
+ $rno = strval($rno);
2114
+
2115
+ // All info that is sent in is part of the digest secret, in this order:
2116
+ // [
2117
+ // proto_vsn, client_vsn, eid, rno, careof, street, zip, city,
2118
+ // country, fname, lname, careof, street, zip, city, country,
2119
+ // fname, lname, artno, qty, orderid1, orderid2
2120
+ // ].
2121
+ // The address part appears twice, that is one per address that
2122
+ // changes. If no value is sent in for an optional field, there
2123
+ // is no entry for this field in the digest secret. Shared secret
2124
+ // is added at the end of the digest secret.
2125
+ $digestArray = array(
2126
+ str_replace('.', ':', $this->PROTO),
2127
+ $this->VERSION,
2128
+ $this->_eid,
2129
+ $rno
2130
+ );
2131
+ $digestArray = array_merge(
2132
+ $digestArray, $this->_addressDigestPart($this->shipping)
2133
+ );
2134
+ $digestArray = array_merge(
2135
+ $digestArray, $this->_addressDigestPart($this->billing)
2136
+ );
2137
+ if (is_array($this->goodsList) && $this->goodsList !== array()) {
2138
+ foreach ($this->goodsList as $goods) {
2139
+ if (strlen($goods["goods"]["artno"]) > 0) {
2140
+ $digestArray[] = $goods["goods"]["artno"];
2141
+ } else {
2142
+ $digestArray[] = $goods["goods"]["title"];
2143
+ }
2144
+ $digestArray[] = $goods["qty"];
2145
+ }
2146
+ }
2147
+ foreach ($this->orderid as $orderid) {
2148
+ $digestArray[] = $orderid;
2149
+ }
2150
+ $digestArray[] = $this->_secret;
2151
+
2152
+ $digestSecret = $this->digest(
2153
+ call_user_func_array(
2154
+ array('self', 'colon'), $digestArray
2155
+ )
2156
+ );
2157
+
2158
+ $shipping = array();
2159
+ $billing = array();
2160
+ if ($this->shipping !== null && $this->shipping instanceof KlarnaAddr) {
2161
+ $shipping = $this->shipping->toArray();
2162
+ }
2163
+ if ($this->billing !== null && $this->billing instanceof KlarnaAddr) {
2164
+ $billing = $this->billing->toArray();
2165
+ }
2166
+ $paramList = array(
2167
+ $this->_eid,
2168
+ $digestSecret,
2169
+ $rno,
2170
+ array(
2171
+ 'goods_list' => $this->goodsList,
2172
+ 'dlv_addr' => $shipping,
2173
+ 'bill_addr' => $billing,
2174
+ 'orderid1' => $this->orderid[0],
2175
+ 'orderid2' => $this->orderid[1]
2176
+ )
2177
+ );
2178
+
2179
+ self::printDebug('update array', $paramList);
2180
+
2181
+ $result = $this->xmlrpc_call('update', $paramList);
2182
+
2183
+ self::printDebug('update result', $result);
2184
+
2185
+ return ($result === 'ok');
2186
+ }
2187
+
2188
+ /**
2189
+ * Help function to sort the address for update digest.
2190
+ *
2191
+ * @param KlarnaAddr|null $address KlarnaAddr object or null
2192
+ *
2193
+ * @return array
2194
+ */
2195
+ private function _addressDigestPart(KlarnaAddr $address = null)
2196
+ {
2197
+ if ($address === null) {
2198
+ return array();
2199
+ }
2200
+
2201
+ $keyOrder = array(
2202
+ 'careof', 'street', 'zip', 'city', 'country', 'fname', 'lname'
2203
+ );
2204
+
2205
+ $holder = $address->toArray();
2206
+ $digest = array();
2207
+
2208
+ foreach ($keyOrder as $key) {
2209
+ if ($holder[$key] != "") {
2210
+ $digest[] = $holder[$key];
2211
+ }
2212
+ }
2213
+
2214
+ return $digest;
2215
+ }
2216
+
2217
+ /**
2218
+ * Activate the reservation matching the given reservation number.
2219
+ * Optional information should be set in ActivateInfo.
2220
+ *
2221
+ * To perform a partial activation, use the addArtNo function to specify
2222
+ * which items in the reservation to include in the activation.
2223
+ *
2224
+ * @param string $rno Reservation number
2225
+ * @param string $ocr optional OCR number to attach to the reservation when
2226
+ * activating. Overrides OCR specified in activateInfo.
2227
+ * @param string $flags optional flags to affect behavior. If specified it
2228
+ * will overwrite any flag set in activateInfo.
2229
+ * @param boolean $clear clear set data after activating. Defaulted to true.
2230
+ *
2231
+ * @throws KlarnaException when the RNO is not specified, or if an error
2232
+ * is recieved from Klarna Online.
2233
+ * @return A string array with risk status and reservation number.
2234
+ */
2235
+ public function activate(
2236
+ $rno, $ocr = null, $flags = null, $clear = true
2237
+ ) {
2238
+ $this->_checkRNO($rno);
2239
+
2240
+ // Overwrite any OCR set on activateInfo if supplied here since this
2241
+ // method call is more specific.
2242
+ if ($ocr !== null) {
2243
+ $this->setActivateInfo('ocr', $ocr);
2244
+ }
2245
+
2246
+ // If flags is specified set the flag supplied here to activateInfo.
2247
+ if ($flags !== null) {
2248
+ $this->setActivateInfo('flags', $flags);
2249
+ }
2250
+
2251
+ //Assume normal shipment unless otherwise specified.
2252
+ if (!array_key_exists('delay_adjust', $this->shipInfo)) {
2253
+ $this->setShipmentInfo('delay_adjust', KlarnaFlags::NORMAL_SHIPMENT);
2254
+ }
2255
+
2256
+ // Append shipment info to activateInfo
2257
+ $this->activateInfo['shipment_info'] = $this->shipInfo;
2258
+
2259
+ // Unlike other calls, if NO_FLAG is specified it should not be sent in
2260
+ // at all.
2261
+ if (array_key_exists('flags', $this->activateInfo)
2262
+ && $this->activateInfo['flags'] === KlarnaFlags::NO_FLAG
2263
+ ) {
2264
+ unset($this->activateInfo['flags']);
2265
+ }
2266
+
2267
+ // Build digest. Any field in activateInfo that is set is included in
2268
+ // the digest.
2269
+ $digestArray = array(
2270
+ str_replace('.', ':', $this->PROTO),
2271
+ $this->VERSION,
2272
+ $this->_eid,
2273
+ $rno
2274
+ );
2275
+
2276
+ $optionalDigestKeys = array(
2277
+ 'bclass',
2278
+ 'cust_no',
2279
+ 'flags',
2280
+ 'ocr',
2281
+ 'orderid1',
2282
+ 'orderid2',
2283
+ 'reference',
2284
+ 'reference_code'
2285
+ );
2286
+
2287
+ foreach ($optionalDigestKeys as $key) {
2288
+ if (array_key_exists($key, $this->activateInfo)) {
2289
+ $digestArray[] = $this->activateInfo[$key];
2290
+ }
2291
+ }
2292
+
2293
+ if (array_key_exists('delay_adjust', $this->activateInfo['shipment_info'])) {
2294
+ $digestArray[] = $this->activateInfo['shipment_info']['delay_adjust'];
2295
+ }
2296
+
2297
+ // If there are any artnos added with addArtNo, add them to the digest
2298
+ // and to the activateInfo
2299
+ if (is_array($this->artNos)) {
2300
+ foreach ($this->artNos as $artNo) {
2301
+ $digestArray[] = $artNo['artno'];
2302
+ $digestArray[] = $artNo['qty'];
2303
+ }
2304
+ $this->setActivateInfo('artnos', $this->artNos);
2305
+ }
2306
+
2307
+ $digestArray[] = $this->_secret;
2308
+ $digestSecret = self::digest(
2309
+ call_user_func_array(
2310
+ array('self', 'colon'), $digestArray
2311
+ )
2312
+ );
2313
+
2314
+ // Create the parameter list.
2315
+ $paramList = array(
2316
+ $this->_eid,
2317
+ $digestSecret,
2318
+ $rno,
2319
+ $this->activateInfo
2320
+ );
2321
+
2322
+ self::printDebug('activate array', $paramList);
2323
+
2324
+ $result = $this->xmlrpc_call('activate', $paramList);
2325
+
2326
+ self::printDebug('activate result', $result);
2327
+
2328
+ // Clear the state if specified.
2329
+ if ($clear) {
2330
+ $this->clear();
2331
+ }
2332
+
2333
+ return $result;
2334
+ }
2335
+
2336
+ /**
2337
+ * Activates a previously created reservation.
2338
+ *
2339
+ * <b>This method returns an array with</b>:<br>
2340
+ * Risk status ("no_risk", "ok")<br>
2341
+ * Invoice number<br>
2342
+ *
2343
+ * Gender is only required for Germany and Netherlands.<br>
2344
+ *
2345
+ * Use of the OCR parameter is optional.
2346
+ * An OCR number can be retrieved by using:
2347
+ * {@link Klarna::reserveOCR()} or {@link Klarna::reserveOCRemail()}.
2348
+ *
2349
+ * <b>Flags can be set to</b>:<br>
2350
+ * {@link KlarnaFlags::NO_FLAG}<br>
2351
+ * {@link KlarnaFlags::TEST_MODE}<br>
2352
+ * {@link KlarnaFlags::RSRV_SEND_BY_MAIL}<br>
2353
+ * {@link KlarnaFlags::RSRV_SEND_BY_EMAIL}<br>
2354
+ * {@link KlarnaFlags::RSRV_PRESERVE_RESERVATION}<br>
2355
+ * {@link KlarnaFlags::RSRV_SENSITIVE_ORDER}<br>
2356
+ *
2357
+ * Some flags can be added to each other for multiple options.
2358
+ *
2359
+ * <b>Note</b>:<br>
2360
+ * Normal shipment type is assumed unless otherwise specified, you can
2361
+ * do this by calling:
2362
+ * {@link Klarna::setShipmentInfo() setShipmentInfo('delay_adjust', ...)}
2363
+ * with either: {@link KlarnaFlags::NORMAL_SHIPMENT NORMAL_SHIPMENT} or
2364
+ * {@link KlarnaFlags::EXPRESS_SHIPMENT EXPRESS_SHIPMENT}<br>
2365
+ *
2366
+ * @param string $pno Personal number, SSN, date of birth, etc.
2367
+ * @param string $rno Reservation number.
2368
+ * @param int $gender {@link KlarnaFlags::FEMALE} or
2369
+ * {@link KlarnaFlags::MALE}, null for unspecified.
2370
+ * @param string $ocr A OCR number.
2371
+ * @param int $flags Options which affect the behaviour.
2372
+ * @param int $pclass {@link KlarnaPClass::getId() PClass ID}.
2373
+ * @param int $encoding {@link KlarnaEncoding PNO Encoding} constant.
2374
+ * @param bool $clear Whether customer info should be cleared after
2375
+ * this call.
2376
+ *
2377
+ * @link http://integration.klarna.com/en/api/advanced-integration/functions
2378
+ * /activatereservation
2379
+ * @see Klarna::reserveAmount()
2380
+ *
2381
+ * @throws KlarnaException
2382
+ * @return array An array with risk status and invoice number [string, string].
2383
+ */
2384
+ public function activateReservation(
2385
+ $pno, $rno, $gender, $ocr = "", $flags = KlarnaFlags::NO_FLAG,
2386
+ $pclass = KlarnaPClass::INVOICE, $encoding = null, $clear = true
2387
+ ) {
2388
+ $this->_checkLocale();
2389
+
2390
+ //Get the PNO/SSN encoding constant.
2391
+ if ($encoding === null) {
2392
+ $encoding = $this->getPNOEncoding();
2393
+ }
2394
+
2395
+ // Only check PNO if it is not explicitly null.
2396
+ if ($pno !== null) {
2397
+ $this->_checkPNO($pno, $encoding);
2398
+ }
2399
+
2400
+ $this->_checkRNO($rno);
2401
+
2402
+ if ($gender !== null && strlen($gender) > 0) {
2403
+ $this->_checkInt($gender, 'gender');
2404
+ }
2405
+
2406
+ $this->_checkOCR($ocr);
2407
+ $this->_checkRef($this->reference, $this->reference_code);
2408
+
2409
+ $this->_checkGoodslist();
2410
+
2411
+ //No addresses used for phone transactions
2412
+ $billing = $shipping = '';
2413
+ if ( !($flags & KlarnaFlags::RSRV_PHONE_TRANSACTION) ) {
2414
+ $billing = $this->assembleAddr($this->billing);
2415
+ $shipping = $this->assembleAddr($this->shipping);
2416
+
2417
+ if (strlen($shipping['country']) > 0
2418
+ && ($shipping['country'] !== $this->_country)
2419
+ ) {
2420
+ throw new Klarna_ShippingCountryException;
2421
+ }
2422
+ }
2423
+
2424
+ //activate digest
2425
+ $string = $this->_eid . ":" . $pno . ":";
2426
+ foreach ($this->goodsList as $goods) {
2427
+ $string .= $goods["goods"]["artno"] . ":" . $goods["qty"] . ":";
2428
+ }
2429
+ $digestSecret = self::digest($string . $this->_secret);
2430
+ //end digest
2431
+
2432
+ //Assume normal shipment unless otherwise specified.
2433
+ if (!isset($this->shipInfo['delay_adjust'])) {
2434
+ $this->setShipmentInfo('delay_adjust', KlarnaFlags::NORMAL_SHIPMENT);
2435
+ }
2436
+
2437
+ $paramList = array(
2438
+ $rno,
2439
+ $ocr,
2440
+ $pno,
2441
+ $gender,
2442
+ $this->reference,
2443
+ $this->reference_code,
2444
+ $this->orderid[0],
2445
+ $this->orderid[1],
2446
+ $shipping,
2447
+ $billing,
2448
+ "0.0.0.0",
2449
+ $flags,
2450
+ $this->_currency,
2451
+ $this->_country,
2452
+ $this->_language,
2453
+ $this->_eid,
2454
+ $digestSecret,
2455
+ $encoding,
2456
+ $pclass,
2457
+ $this->goodsList,
2458
+ $this->comment,
2459
+ $this->shipInfo,
2460
+ $this->travelInfo,
2461
+ $this->incomeInfo,
2462
+ $this->bankInfo,
2463
+ $this->extraInfo
2464
+ );
2465
+
2466
+ self::printDebug('activate_reservation', $paramList);
2467
+
2468
+ $result = $this->xmlrpc_call('activate_reservation', $paramList);
2469
+
2470
+ if ($clear === true) {
2471
+ $this->clear();
2472
+ }
2473
+
2474
+ self::printDebug('activate_reservation result', $result);
2475
+
2476
+ return $result;
2477
+ }
2478
+
2479
+
2480
+ /**
2481
+ * Splits a reservation due to for example outstanding articles.
2482
+ *
2483
+ * <b>For flags usage see</b>:<br>
2484
+ * {@link Klarna::reserveAmount()}<br>
2485
+ *
2486
+ * @param string $rno Reservation number.
2487
+ * @param int $amount The amount to be subtracted from the reservation.
2488
+ * @param int $flags Options which affect the behaviour.
2489
+ *
2490
+ * @link http://integration.klarna.com/en/api/advanced-integration/functions
2491
+ * /splitreservation
2492
+ *
2493
+ * @throws KlarnaException
2494
+ * @return string A new reservation number.
2495
+ */
2496
+ public function splitReservation(
2497
+ $rno, $amount, $flags = KlarnaFlags::NO_FLAG
2498
+ ) {
2499
+ //Check so required information is set.
2500
+ $this->_checkRNO($rno);
2501
+ $this->_checkAmount($amount);
2502
+
2503
+ if ($amount <= 0) {
2504
+ throw new Klarna_InvalidPriceException($amount);
2505
+ }
2506
+
2507
+ $digestSecret = self::digest(
2508
+ $this->colon($this->_eid, $rno, $amount, $this->_secret)
2509
+ );
2510
+ $paramList = array(
2511
+ $rno,
2512
+ $amount,
2513
+ $this->orderid[0],
2514
+ $this->orderid[1],
2515
+ $flags,
2516
+ $this->_eid,
2517
+ $digestSecret
2518
+ );
2519
+
2520
+ self::printDebug('split_reservation array', $paramList);
2521
+
2522
+ $result = $this->xmlrpc_call('split_reservation', $paramList);
2523
+
2524
+ self::printDebug('split_reservation result', $result);
2525
+
2526
+ return $result;
2527
+ }
2528
+
2529
+ /**
2530
+ * Reserves a specified number of OCR numbers.<br>
2531
+ * For the specified country or the {@link Klarna::setCountry() set country}.<br>
2532
+ *
2533
+ * @param int $no The number of OCR numbers to reserve.
2534
+ * @param int $country {@link KlarnaCountry} constant.
2535
+ *
2536
+ * @link http://integration.klarna.com/en/api/advanced-integration/functions
2537
+ * /reserveocrnums
2538
+ *
2539
+ * @throws KlarnaException
2540
+ * @return array An array of OCR numbers.
2541
+ */
2542
+ public function reserveOCR($no, $country = null)
2543
+ {
2544
+ $this->_checkNo($no);
2545
+ if ($country === null) {
2546
+ if (!$this->_country) {
2547
+ throw new Klarna_MissingCountryException;
2548
+ }
2549
+ $country = $this->_country;
2550
+ } else {
2551
+ $this->_checkCountry($country);
2552
+ }
2553
+
2554
+ $digestSecret = self::digest(
2555
+ $this->colon($this->_eid, $no, $this->_secret)
2556
+ );
2557
+ $paramList = array(
2558
+ $no,
2559
+ $this->_eid,
2560
+ $digestSecret,
2561
+ $country
2562
+ );
2563
+
2564
+ self::printDebug('reserve_ocr_nums array', $paramList);
2565
+
2566
+ return $this->xmlrpc_call('reserve_ocr_nums', $paramList);
2567
+ }
2568
+
2569
+ /**
2570
+ * Reserves the number of OCRs specified and sends them to the given email.
2571
+ *
2572
+ * @param int $no Number of OCR numbers to reserve.
2573
+ * @param string $email address.
2574
+ * @param int $country {@link KlarnaCountry} constant.
2575
+ *
2576
+ * @return bool True, if the OCRs were reserved and sent.
2577
+ */
2578
+ public function reserveOCRemail($no, $email, $country = null)
2579
+ {
2580
+ $this->_checkNo($no);
2581
+ $this->_checkPNO($email, KlarnaEncoding::EMAIL);
2582
+
2583
+ if ($country === null) {
2584
+ if (!$this->_country) {
2585
+ throw new Klarna_MissingCountryException;
2586
+ }
2587
+ $country = $this->_country;
2588
+ } else {
2589
+ $this->_checkCountry($country);
2590
+ }
2591
+
2592
+ $digestSecret = self::digest(
2593
+ $this->colon($this->_eid, $no, $this->_secret)
2594
+ );
2595
+ $paramList = array(
2596
+ $no,
2597
+ $email,
2598
+ $this->_eid,
2599
+ $digestSecret,
2600
+ $country
2601
+ );
2602
+
2603
+ self::printDebug('reserve_ocr_nums_email array', $paramList);
2604
+
2605
+ $result = $this->xmlrpc_call('reserve_ocr_nums_email', $paramList);
2606
+
2607
+ return ($result == 'ok');
2608
+ }
2609
+
2610
+ /**
2611
+ * Checks if the specified SSN/PNO has an part payment account with Klarna.
2612
+ *
2613
+ * @param string $pno Social security number, Personal number, ...
2614
+ * @param int $encoding {@link KlarnaEncoding PNO Encoding} constant.
2615
+ *
2616
+ * @link http://integration.klarna.com/en/api/standard-integration/functions
2617
+ * /hasaccount
2618
+ *
2619
+ * @throws KlarnaException
2620
+ * @return bool True, if customer has an account.
2621
+ */
2622
+ public function hasAccount($pno, $encoding = null)
2623
+ {
2624
+ //Get the PNO/SSN encoding constant.
2625
+ if ($encoding === null) {
2626
+ $encoding = $this->getPNOEncoding();
2627
+ }
2628
+
2629
+ $this->_checkPNO($pno, $encoding);
2630
+
2631
+ $digest = self::digest(
2632
+ $this->colon($this->_eid, $pno, $this->_secret)
2633
+ );
2634
+
2635
+ $paramList = array(
2636
+ $this->_eid,
2637
+ $pno,
2638
+ $digest,
2639
+ $encoding
2640
+ );
2641
+
2642
+ self::printDebug('has_account', $paramList);
2643
+
2644
+ $result = $this->xmlrpc_call('has_account', $paramList);
2645
+
2646
+ return ($result === 'true');
2647
+ }
2648
+
2649
+ /**
2650
+ * Adds an article number and quantity to be used in
2651
+ * {@link Klarna::activatePart()}, {@link Klarna::creditPart()}
2652
+ * and {@link Klarna::invoicePartAmount()}.
2653
+ *
2654
+ * @param int $qty Quantity of specified article.
2655
+ * @param string $artNo Article number.
2656
+ *
2657
+ * @link http://integration.klarna.com/en/api/invoice-handling-functions/
2658
+ * functions/mkartno
2659
+ *
2660
+ * @throws KlarnaException
2661
+ * @return void
2662
+ */
2663
+ public function addArtNo($qty, $artNo)
2664
+ {
2665
+ $this->_checkQty($qty);
2666
+ $this->_checkArtNo($artNo);
2667
+
2668
+ if (!is_array($this->artNos)) {
2669
+ $this->artNos = array();
2670
+ }
2671
+
2672
+ $this->artNos[] = array('artno' => $artNo, 'qty' => $qty);
2673
+ }
2674
+
2675
+ /**
2676
+ * Partially activates a passive invoice.
2677
+ *
2678
+ * Returned array contains index "url" and "invno".<br>
2679
+ * The value of "url" is a URL pointing to a temporary PDF-version of the
2680
+ * activated invoice.<br>
2681
+ * The value of "invno" is either 0 if the entire invoice was activated or
2682
+ * the number on the new passive invoice.<br>
2683
+ *
2684
+ * <b>Note</b>:<br>
2685
+ * You need to call {@link Klarna::addArtNo()} first, to specify which
2686
+ * articles and how many you want to partially activate.<br>
2687
+ * If you want to change the shipment type, you can specify it using:
2688
+ * {@link Klarna::setShipmentInfo() setShipmentInfo('delay_adjust', ...)}
2689
+ * with either: {@link KlarnaFlags::NORMAL_SHIPMENT NORMAL_SHIPMENT}
2690
+ * or {@link KlarnaFlags::EXPRESS_SHIPMENT EXPRESS_SHIPMENT}
2691
+ *
2692
+ * @param string $invNo Invoice numbers.
2693
+ * @param int $pclass PClass id used for this invoice.
2694
+ * @param bool $clear Whether customer info should be cleared after
2695
+ * this call.
2696
+ *
2697
+ * @see Klarna::addArtNo()
2698
+ * @see Klarna::activateInvoice()
2699
+ * @link http://integration.klarna.com/en/api/standard-integration/functions
2700
+ * /activatepart
2701
+ *
2702
+ * @throws KlarnaException
2703
+ * @return array An array with invoice URL and invoice number.
2704
+ * ['url' => val, 'invno' => val]
2705
+ */
2706
+ public function activatePart(
2707
+ $invNo, $pclass = KlarnaPClass::INVOICE, $clear = true
2708
+ ) {
2709
+ $this->_checkInvNo($invNo);
2710
+ $this->_checkArtNos($this->artNos);
2711
+
2712
+ self::printDebug('activate_part artNos array', $this->artNos);
2713
+
2714
+ //function activate_part_digest
2715
+ $string = $this->_eid . ":" . $invNo . ":";
2716
+ foreach ($this->artNos as $artNo) {
2717
+ $string .= $artNo["artno"] . ":". $artNo["qty"] . ":";
2718
+ }
2719
+ $digestSecret = self::digest($string . $this->_secret);
2720
+ //end activate_part_digest
2721
+
2722
+ $paramList = array(
2723
+ $this->_eid,
2724
+ $invNo,
2725
+ $this->artNos,
2726
+ $digestSecret,
2727
+ $pclass,
2728
+ $this->shipInfo
2729
+ );
2730
+
2731
+ self::printDebug('activate_part array', $paramList);
2732
+
2733
+ $result = $this->xmlrpc_call('activate_part', $paramList);
2734
+
2735
+ if ($clear === true) {
2736
+ $this->clear();
2737
+ }
2738
+
2739
+ self::printDebug('activate_part result', $result);
2740
+
2741
+ return $result;
2742
+ }
2743
+
2744
+ /**
2745
+ * Retrieves the total amount for an active invoice.
2746
+ *
2747
+ * @param string $invNo Invoice number.
2748
+ *
2749
+ * @link http://integration.klarna.com/en/api/other-functions/functions
2750
+ * /invoiceamount
2751
+ *
2752
+ * @throws KlarnaException
2753
+ * @return float The total amount.
2754
+ */
2755
+ public function invoiceAmount($invNo)
2756
+ {
2757
+ $this->_checkInvNo($invNo);
2758
+
2759
+ $digestSecret = self::digest(
2760
+ $this->colon($this->_eid, $invNo, $this->_secret)
2761
+ );
2762
+
2763
+ $paramList = array(
2764
+ $this->_eid,
2765
+ $invNo,
2766
+ $digestSecret
2767
+ );
2768
+
2769
+ self::printDebug('invoice_amount array', $paramList);
2770
+
2771
+ $result = $this->xmlrpc_call('invoice_amount', $paramList);
2772
+
2773
+ //Result is in cents, fix it.
2774
+ return ($result / 100);
2775
+ }
2776
+
2777
+ /**
2778
+ * Changes the order number of a purchase that was set when the order was
2779
+ * made online.
2780
+ *
2781
+ * @param string $invNo Invoice number.
2782
+ * @param string $orderid Estores order number.
2783
+ *
2784
+ * @link http://integration.klarna.com/en/api/other-functions/functions
2785
+ * /updateorderno
2786
+ *
2787
+ * @throws KlarnaException
2788
+ * @return string Invoice number.
2789
+ */
2790
+ public function updateOrderNo($invNo, $orderid)
2791
+ {
2792
+ $this->_checkInvNo($invNo);
2793
+ $this->_checkEstoreOrderNo($orderid);
2794
+
2795
+ $digestSecret = self::digest(
2796
+ $this->colon($invNo, $orderid, $this->_secret)
2797
+ );
2798
+
2799
+ $paramList = array(
2800
+ $this->_eid,
2801
+ $digestSecret,
2802
+ $invNo,
2803
+ $orderid
2804
+ );
2805
+
2806
+ self::printDebug('update_orderno array', $paramList);
2807
+
2808
+ $result = $this->xmlrpc_call('update_orderno', $paramList);
2809
+
2810
+ return $result;
2811
+ }
2812
+
2813
+ /**
2814
+ * Sends an activated invoice to the customer via e-mail. <br>
2815
+ * The email is sent in plain text format and contains a link to a
2816
+ * PDF-invoice.<br>
2817
+ *
2818
+ * <b>Please note!</b><br>
2819
+ * Regular postal service is used if the customer has not entered his/her
2820
+ * e-mail address when making the purchase (charges may apply).<br>
2821
+ *
2822
+ * @param string $invNo Invoice number.
2823
+ *
2824
+ * @link http://integration.klarna.com/en/api/invoice-handling-functions
2825
+ * /functions/emailinvoice
2826
+ *
2827
+ * @throws KlarnaException
2828
+ * @return string Invoice number.
2829
+ */
2830
+ public function emailInvoice($invNo)
2831
+ {
2832
+ $this->_checkInvNo($invNo);
2833
+
2834
+ $digestSecret = self::digest(
2835
+ $this->colon($this->_eid, $invNo, $this->_secret)
2836
+ );
2837
+ $paramList = array(
2838
+ $this->_eid,
2839
+ $invNo,
2840
+ $digestSecret
2841
+ );
2842
+
2843
+ self::printDebug('email_invoice array', $paramList);
2844
+
2845
+ return $this->xmlrpc_call('email_invoice', $paramList);
2846
+ }
2847
+
2848
+ /**
2849
+ * Requests a postal send-out of an activated invoice to a customer by
2850
+ * Klarna (charges may apply).
2851
+ *
2852
+ * @param string $invNo Invoice number.
2853
+ *
2854
+ * @link http://integration.klarna.com/en/api/invoice-handling-functions
2855
+ * /functions/sendinvoice
2856
+ *
2857
+ * @throws KlarnaException
2858
+ * @return string Invoice number.
2859
+ */
2860
+ public function sendInvoice($invNo)
2861
+ {
2862
+ $this->_checkInvNo($invNo);
2863
+
2864
+ $digestSecret = self::digest(
2865
+ $this->colon($this->_eid, $invNo, $this->_secret)
2866
+ );
2867
+ $paramList = array(
2868
+ $this->_eid,
2869
+ $invNo,
2870
+ $digestSecret
2871
+ );
2872
+
2873
+ self::printDebug('send_invoice array', $paramList);
2874
+
2875
+ return $this->xmlrpc_call('send_invoice', $paramList);
2876
+ }
2877
+
2878
+ /**
2879
+ * Gives discounts on invoices.<br>
2880
+ * If you are using standard integration and the purchase is not yet
2881
+ * activated (you have not yet delivered the goods), <br>
2882
+ * just change the article list in our online interface Klarna Online.<br>
2883
+ *
2884
+ * <b>Flags can be</b>:<br>
2885
+ * {@link KlarnaFlags::INC_VAT}<br>
2886
+ * {@link KlarnaFlags::NO_FLAG}, <b>NOT RECOMMENDED!</b><br>
2887
+ *
2888
+ * @param string $invNo Invoice number.
2889
+ * @param int $amount The amount given as a discount.
2890
+ * @param float $vat VAT in percent, e.g. 22.2 for 22.2%.
2891
+ * @param int $flags If amount is
2892
+ * {@link KlarnaFlags::INC_VAT including} or
2893
+ * {@link KlarnaFlags::NO_FLAG excluding} VAT.
2894
+ * @param string $description Optional custom text to present as discount
2895
+ * in the invoice.
2896
+ *
2897
+ * @link http://integration.klarna.com/en/api/invoice-handling-functions
2898
+ * /functions/returnamount
2899
+ *
2900
+ * @throws KlarnaException
2901
+ * @return string Invoice number.
2902
+ */
2903
+ public function returnAmount(
2904
+ $invNo, $amount, $vat, $flags = KlarnaFlags::INC_VAT, $description = ""
2905
+ ) {
2906
+ $this->_checkInvNo($invNo);
2907
+ $this->_checkAmount($amount);
2908
+ $this->_checkVAT($vat);
2909
+ $this->_checkInt($flags, 'flags');
2910
+
2911
+ if ($description == null) {
2912
+ $description = "";
2913
+ }
2914
+
2915
+ $digestSecret = self::digest(
2916
+ $this->colon($this->_eid, $invNo, $this->_secret)
2917
+ );
2918
+ $paramList = array(
2919
+ $this->_eid,
2920
+ $invNo,
2921
+ $amount,
2922
+ $vat,
2923
+ $digestSecret,
2924
+ $flags,
2925
+ $description
2926
+ );
2927
+
2928
+ self::printDebug('return_amount', $paramList);
2929
+
2930
+ return $this->xmlrpc_call('return_amount', $paramList);
2931
+ }
2932
+
2933
+ /**
2934
+ * Performs a complete refund on an invoice, part payment and mobile
2935
+ * purchase.
2936
+ *
2937
+ * @param string $invNo Invoice number.
2938
+ * @param string $credNo Credit number.
2939
+ *
2940
+ * @link http://integration.klarna.com/en/api/invoice-handling-functions
2941
+ * /functions/creditinvoice
2942
+ *
2943
+ * @throws KlarnaException
2944
+ * @return string Invoice number.
2945
+ */
2946
+ public function creditInvoice($invNo, $credNo = "")
2947
+ {
2948
+ $this->_checkInvNo($invNo);
2949
+ $this->_checkCredNo($credNo);
2950
+
2951
+ $digestSecret = self::digest(
2952
+ $this->colon($this->_eid, $invNo, $this->_secret)
2953
+ );
2954
+ $paramList = array(
2955
+ $this->_eid,
2956
+ $invNo,
2957
+ $credNo,
2958
+ $digestSecret
2959
+ );
2960
+
2961
+ self::printDebug('credit_invoice', $paramList);
2962
+
2963
+ return $this->xmlrpc_call('credit_invoice', $paramList);
2964
+ }
2965
+
2966
+ /**
2967
+ * Performs a partial refund on an invoice, part payment or mobile purchase.
2968
+ *
2969
+ * <b>Note</b>:<br>
2970
+ * You need to call {@link Klarna::addArtNo()} first.<br>
2971
+ *
2972
+ * @param string $invNo Invoice number.
2973
+ * @param string $credNo Credit number.
2974
+ *
2975
+ * @see Klarna::addArtNo()
2976
+ * @link http://integration.klarna.com/en/api/invoice-handling-functions
2977
+ * /functions/creditpart
2978
+ *
2979
+ * @throws KlarnaException
2980
+ * @return string Invoice number.
2981
+ */
2982
+ public function creditPart($invNo, $credNo = "")
2983
+ {
2984
+ $this->_checkInvNo($invNo);
2985
+ $this->_checkCredNo($credNo);
2986
+
2987
+ if ($this->goodsList === null || empty($this->goodsList)) {
2988
+ $this->_checkArtNos($this->artNos);
2989
+ }
2990
+
2991
+ //function activate_part_digest
2992
+ $string = $this->_eid . ":" . $invNo . ":";
2993
+
2994
+ if ($this->artNos !== null && !empty($this->artNos)) {
2995
+ foreach ($this->artNos as $artNo) {
2996
+ $string .= $artNo["artno"] . ":". $artNo["qty"] . ":";
2997
+ }
2998
+ }
2999
+
3000
+ $digestSecret = self::digest($string . $this->_secret);
3001
+ //end activate_part_digest
3002
+
3003
+ $paramList = array(
3004
+ $this->_eid,
3005
+ $invNo,
3006
+ $this->artNos,
3007
+ $credNo,
3008
+ $digestSecret
3009
+ );
3010
+
3011
+ if ($this->goodsList !== null && !empty($this->goodsList)) {
3012
+ $paramList[] = 0;
3013
+ $paramList[] = $this->goodsList;
3014
+ }
3015
+
3016
+ $this->artNos = array();
3017
+
3018
+ self::printDebug('credit_part', $paramList);
3019
+
3020
+ return $this->xmlrpc_call('credit_part', $paramList);
3021
+ }
3022
+
3023
+ /**
3024
+ * Changes the quantity of a specific item in a passive invoice.
3025
+ *
3026
+ * @param string $invNo Invoice number.
3027
+ * @param string $artNo Article number.
3028
+ * @param int $qty Quantity of specified article.
3029
+ *
3030
+ * @link http://integration.klarna.com/en/api/other-functions/functions
3031
+ * /updategoodsqty
3032
+ *
3033
+ * @throws KlarnaException
3034
+ * @return string Invoice number.
3035
+ */
3036
+ public function updateGoodsQty($invNo, $artNo, $qty)
3037
+ {
3038
+ $this->_checkInvNo($invNo);
3039
+ $this->_checkQty($qty);
3040
+ $this->_checkArtNo($artNo);
3041
+
3042
+ $digestSecret = self::digest(
3043
+ $this->colon($invNo, $artNo, $qty, $this->_secret)
3044
+ );
3045
+
3046
+ $paramList = array(
3047
+ $this->_eid,
3048
+ $digestSecret,
3049
+ $invNo,
3050
+ $artNo,
3051
+ $qty
3052
+ );
3053
+
3054
+ self::printDebug('update_goods_qty', $paramList);
3055
+
3056
+ return $this->xmlrpc_call('update_goods_qty', $paramList);
3057
+ }
3058
+
3059
+ /**
3060
+ * Changes the amount of a fee (e.g. the invoice fee) in a passive invoice.
3061
+ *
3062
+ * <b>Type can be</b>:<br>
3063
+ * {@link KlarnaFlags::IS_SHIPMENT}<br>
3064
+ * {@link KlarnaFlags::IS_HANDLING}<br>
3065
+ *
3066
+ * @param string $invNo Invoice number.
3067
+ * @param int $type Charge type.
3068
+ * @param int $newAmount The new amount for the charge.
3069
+ *
3070
+ * @link http://integration.klarna.com/en/api/other-functions/functions
3071
+ * /updatechargeamount
3072
+ *
3073
+ * @throws KlarnaException
3074
+ * @return string Invoice number.
3075
+ */
3076
+ public function updateChargeAmount($invNo, $type, $newAmount)
3077
+ {
3078
+ $this->_checkInvNo($invNo);
3079
+ $this->_checkInt($type, 'type');
3080
+ $this->_checkAmount($newAmount);
3081
+
3082
+ if ($type === KlarnaFlags::IS_SHIPMENT) {
3083
+ $type = 1;
3084
+ } else if ($type === KlarnaFlags::IS_HANDLING) {
3085
+ $type = 2;
3086
+ }
3087
+
3088
+ $digestSecret = self::digest(
3089
+ $this->colon($invNo, $type, $newAmount, $this->_secret)
3090
+ );
3091
+
3092
+ $paramList = array(
3093
+ $this->_eid,
3094
+ $digestSecret,
3095
+ $invNo,
3096
+ $type,
3097
+ $newAmount
3098
+ );
3099
+
3100
+ self::printDebug('update_charge_amount', $paramList);
3101
+
3102
+ return $this->xmlrpc_call('update_charge_amount', $paramList);
3103
+ }
3104
+
3105
+ /**
3106
+ * The invoice_address function is used to retrieve the address of a
3107
+ * purchase.
3108
+ *
3109
+ * @param string $invNo Invoice number.
3110
+ *
3111
+ * @link http://integration.klarna.com/en/api/other-functions/functions
3112
+ * /invoiceaddress
3113
+ *
3114
+ * @throws KlarnaException
3115
+ * @return KlarnaAddr
3116
+ */
3117
+ public function invoiceAddress($invNo)
3118
+ {
3119
+ $this->_checkInvNo($invNo);
3120
+
3121
+ $digestSecret = self::digest(
3122
+ $this->colon($this->_eid, $invNo, $this->_secret)
3123
+ );
3124
+ $paramList = array(
3125
+ $this->_eid,
3126
+ $invNo,
3127
+ $digestSecret
3128
+ );
3129
+
3130
+ self::printDebug('invoice_address', $paramList);
3131
+
3132
+ $result = $this->xmlrpc_call('invoice_address', $paramList);
3133
+
3134
+ $addr = new KlarnaAddr();
3135
+ if (strlen($result[0]) > 0) {
3136
+ $addr->isCompany = false;
3137
+ $addr->setFirstName($result[0]);
3138
+ $addr->setLastName($result[1]);
3139
+ } else {
3140
+ $addr->isCompany = true;
3141
+ $addr->setCompanyName($result[1]);
3142
+ }
3143
+ $addr->setStreet($result[2]);
3144
+ $addr->setZipCode($result[3]);
3145
+ $addr->setCity($result[4]);
3146
+ $addr->setCountry($result[5]);
3147
+
3148
+ return $addr;
3149
+ }
3150
+
3151
+ /**
3152
+ * Retrieves the amount of a specific goods from a purchase.
3153
+ *
3154
+ * <b>Note</b>:<br>
3155
+ * You need to call {@link Klarna::addArtNo()} first.<br>
3156
+ *
3157
+ * @param string $invNo Invoice number.
3158
+ *
3159
+ * @link http://integration.klarna.com/en/api/other-functions/functions
3160
+ * /invoicepartamount
3161
+ * @see Klarna::addArtNo()
3162
+ *
3163
+ * @throws KlarnaException
3164
+ * @return float The amount of the goods.
3165
+ */
3166
+ public function invoicePartAmount($invNo)
3167
+ {
3168
+ $this->_checkInvNo($invNo);
3169
+ $this->_checkArtNos($this->artNos);
3170
+
3171
+ //function activate_part_digest
3172
+ $string = $this->_eid . ":" . $invNo . ":";
3173
+ foreach ($this->artNos as $artNo) {
3174
+ $string .= $artNo["artno"] . ":". $artNo["qty"] . ":";
3175
+ }
3176
+ $digestSecret = self::digest($string . $this->_secret);
3177
+ //end activate_part_digest
3178
+
3179
+ $paramList = array(
3180
+ $this->_eid,
3181
+ $invNo,
3182
+ $this->artNos,
3183
+ $digestSecret
3184
+ );
3185
+ $this->artNos = array();
3186
+
3187
+ self::printDebug('invoice_part_amount', $paramList);
3188
+
3189
+ $result = $this->xmlrpc_call('invoice_part_amount', $paramList);
3190
+
3191
+ return ($result / 100);
3192
+ }
3193
+
3194
+ /**
3195
+ * Returns the current order status for a specific reservation or invoice.
3196
+ * Use this when {@link Klarna::addTransaction()} or
3197
+ * {@link Klarna::reserveAmount()} returns a {@link KlarnaFlags::PENDING}
3198
+ * status.
3199
+ *
3200
+ * <b>Order status can be</b>:<br>
3201
+ * {@link KlarnaFlags::ACCEPTED}<br>
3202
+ * {@link KlarnaFlags::PENDING}<br>
3203
+ * {@link KlarnaFlags::DENIED}<br>
3204
+ *
3205
+ * @param string $id Reservation number or invoice number.
3206
+ * @param int $type 0 if $id is an invoice or reservation, 1 for order id
3207
+ *
3208
+ * @link http://integration.klarna.com/en/api/other-functions/functions
3209
+ * /checkorderstatus
3210
+ *
3211
+ * @throws KlarnaException
3212
+ * @return string The order status.
3213
+ */
3214
+ public function checkOrderStatus($id, $type = 0)
3215
+ {
3216
+ $this->_checkArgument($id, "id");
3217
+
3218
+ $this->_checkInt($type, 'type');
3219
+ if ($type !== 0 && $type !== 1) {
3220
+ throw new Klarna_InvalidTypeException(
3221
+ 'type', "0 or 1"
3222
+ );
3223
+ }
3224
+
3225
+ $digestSecret = self::digest(
3226
+ $this->colon($this->_eid, $id, $this->_secret)
3227
+ );
3228
+ $paramList = array(
3229
+ $this->_eid,
3230
+ $digestSecret,
3231
+ $id,
3232
+ $type
3233
+ );
3234
+
3235
+ self::printDebug('check_order_status', $paramList);
3236
+
3237
+ return $this->xmlrpc_call('check_order_status', $paramList);
3238
+ }
3239
+
3240
+ /**
3241
+ * Retrieves a list of all the customer numbers associated with the
3242
+ * specified pno.
3243
+ *
3244
+ * @param string $pno Social security number, Personal number, ...
3245
+ * @param int $encoding {@link KlarnaEncoding PNO Encoding} constant.
3246
+ *
3247
+ * @throws KlarnaException
3248
+ * @return array An array containing all customer numbers associated
3249
+ * with that pno.
3250
+ */
3251
+ public function getCustomerNo($pno, $encoding = null)
3252
+ {
3253
+ //Get the PNO/SSN encoding constant.
3254
+ if ($encoding === null) {
3255
+ $encoding = $this->getPNOEncoding();
3256
+ }
3257
+ $this->_checkPNO($pno, $encoding);
3258
+
3259
+ $digestSecret = self::digest(
3260
+ $this->colon($this->_eid, $pno, $this->_secret)
3261
+ );
3262
+ $paramList = array(
3263
+ $pno,
3264
+ $this->_eid,
3265
+ $digestSecret,
3266
+ $encoding
3267
+ );
3268
+
3269
+ self::printDebug('get_customer_no', $paramList);
3270
+
3271
+ return $this->xmlrpc_call('get_customer_no', $paramList);
3272
+ }
3273
+
3274
+ /**
3275
+ * Associates a pno with a customer number when you want to make future
3276
+ * purchases without a pno.
3277
+ *
3278
+ * @param string $pno Social security number, Personal number, ...
3279
+ * @param string $custNo The customer number.
3280
+ * @param int $encoding {@link KlarnaEncoding PNO Encoding} constant.
3281
+ *
3282
+ * @throws KlarnaException
3283
+ * @return bool True, if the customer number was associated with the pno.
3284
+ */
3285
+ public function setCustomerNo($pno, $custNo, $encoding = null)
3286
+ {
3287
+ //Get the PNO/SSN encoding constant.
3288
+ if ($encoding === null) {
3289
+ $encoding = $this->getPNOEncoding();
3290
+ }
3291
+ $this->_checkPNO($pno, $encoding);
3292
+
3293
+ $this->_checkArgument($custNo, 'custNo');
3294
+
3295
+ $digestSecret = self::digest(
3296
+ $this->colon($this->_eid, $pno, $custNo, $this->_secret)
3297
+ );
3298
+ $paramList = array(
3299
+ $pno,
3300
+ $custNo,
3301
+ $this->_eid,
3302
+ $digestSecret,
3303
+ $encoding
3304
+ );
3305
+
3306
+ self::printDebug('set_customer_no', $paramList);
3307
+
3308
+ $result = $this->xmlrpc_call('set_customer_no', $paramList);
3309
+
3310
+ return ($result == 'ok');
3311
+ }
3312
+
3313
+ /**
3314
+ * Removes a customer number from association with a pno.
3315
+ *
3316
+ * @param string $custNo The customer number.
3317
+ *
3318
+ * @throws KlarnaException
3319
+ * @return bool True, if the customer number association was removed.
3320
+ */
3321
+ public function removeCustomerNo($custNo)
3322
+ {
3323
+ $this->_checkArgument($custNo, 'custNo');
3324
+
3325
+ $digestSecret = self::digest(
3326
+ $this->colon($this->_eid, $custNo, $this->_secret)
3327
+ );
3328
+
3329
+ $paramList = array(
3330
+ $custNo,
3331
+ $this->_eid,
3332
+ $digestSecret
3333
+ );
3334
+
3335
+ self::printDebug('remove_customer_no', $paramList);
3336
+
3337
+ $result = $this->xmlrpc_call('remove_customer_no', $paramList);
3338
+
3339
+ return ($result == 'ok');
3340
+ }
3341
+
3342
+ /**
3343
+ * Sets notes/log information for the specified invoice number.
3344
+ *
3345
+ * @param string $invNo Invoice number.
3346
+ * @param string $notes Note(s) to be associated with the invoice.
3347
+ *
3348
+ * @throws KlarnaException
3349
+ * @return string Invoice number.
3350
+ */
3351
+ public function updateNotes($invNo, $notes)
3352
+ {
3353
+ $this->_checkInvNo($invNo);
3354
+
3355
+ if (!is_string($notes)) {
3356
+ $notes = strval($notes);
3357
+ }
3358
+
3359
+ $digestSecret = self::digest(
3360
+ $this->colon($invNo, $notes, $this->_secret)
3361
+ );
3362
+
3363
+ $paramList = array(
3364
+ $this->_eid,
3365
+ $digestSecret,
3366
+ $invNo,
3367
+ $notes
3368
+ );
3369
+
3370
+ self::printDebug('update_notes', $paramList);
3371
+
3372
+ return $this->xmlrpc_call('update_notes', $paramList);
3373
+ }
3374
+
3375
+ /**
3376
+ * Returns the configured PCStorage object.
3377
+ *
3378
+ * @throws Exception|KlarnaException
3379
+ * @return PCStorage
3380
+ */
3381
+ public function getPCStorage()
3382
+ {
3383
+ if (isset($this->pclasses)) {
3384
+ return $this->pclasses;
3385
+ }
3386
+
3387
+ include_once 'pclasses/storage.intf.php';
3388
+ $className = $this->pcStorage.'storage';
3389
+ $pclassStorage = dirname(__FILE__) . "/pclasses/{$className}.class.php";
3390
+
3391
+ include_once $pclassStorage;
3392
+ $storage = new $className;
3393
+
3394
+ if (!($storage instanceof PCStorage)) {
3395
+ throw new Klarna_PCStorageInvalidException(
3396
+ $className, $pclassStorage
3397
+ );
3398
+ }
3399
+ return $storage;
3400
+ }
3401
+
3402
+ /**
3403
+ * Fetch pclasses
3404
+ *
3405
+ * @param PCStorage $storage PClass Storage
3406
+ * @param int $country KlarnaCountry constant
3407
+ * @param int $language KlarnaLanguage constant
3408
+ * @param int $currency KlarnaCurrency constant
3409
+ *
3410
+ * @return void
3411
+ */
3412
+ private function _fetchPClasses($storage, $country, $language, $currency)
3413
+ {
3414
+ $digestSecret = self::digest(
3415
+ $this->_eid . ":" . $currency . ":" . $this->_secret
3416
+ );
3417
+ $paramList = array(
3418
+ $this->_eid,
3419
+ $currency,
3420
+ $digestSecret,
3421
+ $country,
3422
+ $language
3423
+ );
3424
+
3425
+ self::printDebug('get_pclasses array', $paramList);
3426
+
3427
+ $result = $this->xmlrpc_call('get_pclasses', $paramList);
3428
+
3429
+ self::printDebug('get_pclasses result', $result);
3430
+
3431
+ foreach ($result as &$pclass) {
3432
+ //numeric htmlentities
3433
+ $pclass[1] = Klarna::num_htmlentities($pclass[1]);
3434
+
3435
+ //Below values are in "cents", fix them.
3436
+ $pclass[3] /= 100; //divide start fee with 100
3437
+ $pclass[4] /= 100; //divide invoice fee with 100
3438
+ $pclass[5] /= 100; //divide interest rate with 100
3439
+ $pclass[6] /= 100; //divide min amount with 100
3440
+
3441
+ if ($pclass[9] != '-') {
3442
+ //unix timestamp instead of yyyy-mm-dd
3443
+ $pclass[9] = strtotime($pclass[9]);
3444
+ }
3445
+
3446
+ //Associate the PClass with this estore.
3447
+ array_unshift($pclass, $this->_eid);
3448
+
3449
+ $storage->addPClass(new KlarnaPClass($pclass));
3450
+ }
3451
+ }
3452
+
3453
+ /**
3454
+ * Fetches the PClasses from Klarna Online.<br>
3455
+ * Removes the cached/stored pclasses and updates.<br>
3456
+ * You are only allowed to call this once, or once per update of PClasses
3457
+ * in KO.<br>
3458
+ *
3459
+ * <b>Note</b>:<br>
3460
+ * If language and/or currency is null, then they will be set to mirror
3461
+ * the specified country.<br/>
3462
+ * Short codes like DE, SV or EUR can also be used instead of the constants.
3463
+ *
3464
+ * @param string|int $country {@link KlarnaCountry Country} constant,
3465
+ * or two letter code.
3466
+ * @param mixed $language {@link KlarnaLanguage Language} constant,
3467
+ * or two letter code.
3468
+ * @param mixed $currency {@link KlarnaCurrency Currency} constant,
3469
+ * or three letter code.
3470
+ *
3471
+ * @throws KlarnaException
3472
+ * @return void
3473
+ */
3474
+ public function fetchPClasses(
3475
+ $country = null, $language = null, $currency = null
3476
+ ) {
3477
+ extract(
3478
+ $this->getLocale($country, $language, $currency),
3479
+ EXTR_OVERWRITE
3480
+ );
3481
+
3482
+ $this->_checkConfig();
3483
+
3484
+ $pclasses = $this->getPCStorage();
3485
+ try {
3486
+ //Attempt to load previously stored pclasses, so they aren't
3487
+ // accidentially removed.
3488
+ $pclasses->load($this->pcURI);
3489
+ }
3490
+ catch(Exception $e) {
3491
+ self::printDebug('load pclasses', $e->getMessage());
3492
+ }
3493
+
3494
+ $this->_fetchPClasses($pclasses, $country, $language, $currency);
3495
+
3496
+ $pclasses->save($this->pcURI);
3497
+ $this->pclasses = $pclasses;
3498
+ }
3499
+
3500
+ /**
3501
+ * Removes the stored PClasses, if you need to update them.
3502
+ *
3503
+ * @throws KlarnaException
3504
+ * @return void
3505
+ */
3506
+ public function clearPClasses()
3507
+ {
3508
+ $this->_checkConfig();
3509
+
3510
+ $pclasses = $this->getPCStorage();
3511
+ $pclasses->clear($this->pcURI);
3512
+ }
3513
+
3514
+ /**
3515
+ * Retrieves the specified PClasses.
3516
+ *
3517
+ * <b>Type can be</b>:<br>
3518
+ * {@link KlarnaPClass::CAMPAIGN}<br>
3519
+ * {@link KlarnaPClass::ACCOUNT}<br>
3520
+ * {@link KlarnaPClass::SPECIAL}<br>
3521
+ * {@link KlarnaPClass::FIXED}<br>
3522
+ * {@link KlarnaPClass::DELAY}<br>
3523
+ * {@link KlarnaPClass::MOBILE}<br>
3524
+ *
3525
+ * @param int $type PClass type identifier.
3526
+ *
3527
+ * @throws KlarnaException
3528
+ * @return array An array of PClasses. [KlarnaPClass]
3529
+ */
3530
+ public function getPClasses($type = null)
3531
+ {
3532
+ $this->_checkConfig();
3533
+
3534
+ if (!$this->pclasses) {
3535
+ $this->pclasses = $this->getPCStorage();
3536
+ $this->pclasses->load($this->pcURI);
3537
+ }
3538
+ $tmp = $this->pclasses->getPClasses(
3539
+ $this->_eid, $this->_country, $type
3540
+ );
3541
+ $this->sortPClasses($tmp[$this->_eid]);
3542
+ return $tmp[$this->_eid];
3543
+ }
3544
+
3545
+ /**
3546
+ * Retrieve a flattened array of all pclasses stored in the configured
3547
+ * pclass storage.
3548
+ *
3549
+ * @return array
3550
+ */
3551
+ public function getAllPClasses()
3552
+ {
3553
+ if (!$this->pclasses) {
3554
+ $this->pclasses = $this->getPCStorage();
3555
+ $this->pclasses->load($this->pcURI);
3556
+ }
3557
+ return $this->pclasses->getAllPClasses();
3558
+ }
3559
+
3560
+ /**
3561
+ * Returns the specified PClass.
3562
+ *
3563
+ * @param int $id The PClass ID.
3564
+ *
3565
+ * @return KlarnaPClass
3566
+ */
3567
+ public function getPClass($id)
3568
+ {
3569
+ if (!is_numeric($id)) {
3570
+ throw new Klarna_InvalidTypeException('id', 'integer');
3571
+ }
3572
+
3573
+ $this->_checkConfig();
3574
+
3575
+ if (!$this->pclasses || !($this->pclasses instanceof PCStorage)) {
3576
+ $this->pclasses = $this->getPCStorage();
3577
+ $this->pclasses->load($this->pcURI);
3578
+ }
3579
+ return $this->pclasses->getPClass(
3580
+ intval($id), $this->_eid, $this->_country
3581
+ );
3582
+ }
3583
+
3584
+ /**
3585
+ * Sorts the specified array of KlarnaPClasses.
3586
+ *
3587
+ * @param array &$array An array of {@link KlarnaPClass PClasses}.
3588
+ *
3589
+ * @return void
3590
+ */
3591
+ public function sortPClasses(&$array)
3592
+ {
3593
+ if (!is_array($array)) {
3594
+ //Input is not an array!
3595
+ $array = array();
3596
+ return;
3597
+ }
3598
+ //Sort pclasses array after natural sort (natcmp)
3599
+ if (!function_exists('pcCmp')) {
3600
+ /**
3601
+ * Comparison function
3602
+ *
3603
+ * @param KlarnaPClass $a object 1
3604
+ * @param KlarnaPClass $b object 2
3605
+ *
3606
+ * @return int
3607
+ */
3608
+ function pcCmp($a, $b)
3609
+ {
3610
+ if ($a->getDescription() == null
3611
+ && $b->getDescription() == null
3612
+ ) {
3613
+ return 0;
3614
+ } else if ($a->getDescription() == null) {
3615
+ return 1;
3616
+ } else if ($b->getDescription() == null) {
3617
+ return -1;
3618
+ } else if ($b->getType() === 2 && $a->getType() !== 2) {
3619
+ return 1;
3620
+ } else if ($b->getType() !== 2 && $a->getType() === 2) {
3621
+ return -1;
3622
+ }
3623
+
3624
+ return strnatcmp($a->getDescription(), $b->getDescription())*-1;
3625
+ }
3626
+ }
3627
+ usort($array, "pcCmp");
3628
+ }
3629
+
3630
+ /**
3631
+ * Returns the cheapest, per month, PClass related to the specified sum.
3632
+ *
3633
+ * <b>Note</b>: This choose the cheapest PClass for the current country.<br>
3634
+ * {@link Klarna::setCountry()}
3635
+ *
3636
+ * <b>Flags can be</b>:<br>
3637
+ * {@link KlarnaFlags::CHECKOUT_PAGE}<br>
3638
+ * {@link KlarnaFlags::PRODUCT_PAGE}<br>
3639
+ *
3640
+ * @param float $sum The product cost, or total sum of the cart.
3641
+ * @param int $flags Which type of page the info will be displayed on.
3642
+ *
3643
+ * @throws KlarnaException
3644
+ * @return KlarnaPClass or false if none was found.
3645
+ */
3646
+ public function getCheapestPClass($sum, $flags)
3647
+ {
3648
+ if (!is_numeric($sum)) {
3649
+ throw new Klarna_InvalidPriceException($sum);
3650
+ }
3651
+
3652
+ if (!is_numeric($flags)
3653
+ || !in_array(
3654
+ $flags, array(
3655
+ KlarnaFlags::CHECKOUT_PAGE, KlarnaFlags::PRODUCT_PAGE)
3656
+ )
3657
+ ) {
3658
+ throw new Klarna_InvalidTypeException(
3659
+ 'flags',
3660
+ KlarnaFlags::CHECKOUT_PAGE . ' or ' . KlarnaFlags::PRODUCT_PAGE
3661
+ );
3662
+ }
3663
+
3664
+ $lowest_pp = $lowest = false;
3665
+
3666
+ foreach ($this->getPClasses() as $pclass) {
3667
+ $lowest_payment = KlarnaCalc::get_lowest_payment_for_account(
3668
+ $pclass->getCountry()
3669
+ );
3670
+ if ($pclass->getType() < 2 && $sum >= $pclass->getMinAmount()) {
3671
+ $minpay = KlarnaCalc::calc_monthly_cost(
3672
+ $sum, $pclass, $flags
3673
+ );
3674
+
3675
+ if ($minpay < $lowest_pp || $lowest_pp === false) {
3676
+ if ($pclass->getType() == KlarnaPClass::ACCOUNT
3677
+ || $minpay >= $lowest_payment
3678
+ ) {
3679
+ $lowest_pp = $minpay;
3680
+ $lowest = $pclass;
3681
+ }
3682
+ }
3683
+ }
3684
+ }
3685
+
3686
+ return $lowest;
3687
+ }
3688
+
3689
+ /**
3690
+ * Initializes the checkoutHTML objects.
3691
+ *
3692
+ * @see Klarna::checkoutHTML()
3693
+ * @return void
3694
+ */
3695
+ protected function initCheckout()
3696
+ {
3697
+ $dir = dirname(__FILE__);
3698
+
3699
+ //Require the CheckoutHTML interface/abstract class
3700
+ include_once $dir.'/checkout/checkouthtml.intf.php';
3701
+
3702
+ //Iterate over all .class.php files in checkout/
3703
+ foreach (glob($dir.'/checkout/*.class.php') as $checkout) {
3704
+ if (!self::$debug) {
3705
+ ob_start();
3706
+ }
3707
+ include_once $checkout;
3708
+
3709
+ $className = basename($checkout, '.class.php');
3710
+ $cObj = new $className;
3711
+
3712
+ if ($cObj instanceof CheckoutHTML) {
3713
+ $cObj->init($this, $this->_eid);
3714
+ $this->coObjects[$className] = $cObj;
3715
+ }
3716
+
3717
+ if (!self::$debug) {
3718
+ ob_end_clean();
3719
+ }
3720
+ }
3721
+ }
3722
+
3723
+ /**
3724
+ * Returns the checkout page HTML from the checkout classes.
3725
+ *
3726
+ * <b>Note</b>:<br>
3727
+ * This method uses output buffering to silence unwanted echoes.<br>
3728
+ *
3729
+ * @see CheckoutHTML
3730
+ *
3731
+ * @return string A HTML string.
3732
+ */
3733
+ public function checkoutHTML()
3734
+ {
3735
+ if (empty($this->coObjects)) {
3736
+ $this->initCheckout();
3737
+ }
3738
+ $dir = dirname(__FILE__);
3739
+
3740
+ //Require the CheckoutHTML interface/abstract class
3741
+ include_once $dir.'/checkout/checkouthtml.intf.php';
3742
+
3743
+ //Iterate over all .class.php files in
3744
+ $html = "\n";
3745
+ foreach ($this->coObjects as $cObj) {
3746
+ if (!self::$debug) {
3747
+ ob_start();
3748
+ }
3749
+ if ($cObj instanceof CheckoutHTML) {
3750
+ $html .= $cObj->toHTML() . "\n";
3751
+ }
3752
+ if (!self::$debug) {
3753
+ ob_end_clean();
3754
+ }
3755
+ }
3756
+
3757
+ return $html;
3758
+ }
3759
+
3760
+ /**
3761
+ * Creates a XMLRPC call with specified XMLRPC method and parameters from array.
3762
+ *
3763
+ * @param string $method XMLRPC method.
3764
+ * @param array $array XMLRPC parameters.
3765
+ *
3766
+ * @throws KlarnaException
3767
+ * @return mixed
3768
+ */
3769
+ protected function xmlrpc_call($method, $array)
3770
+ {
3771
+ $this->_checkConfig();
3772
+
3773
+ if (!isset($method) || !is_string($method)) {
3774
+ throw new Klarna_InvalidTypeException('method', 'string');
3775
+ }
3776
+ if ($array === null || count($array) === 0) {
3777
+ throw new KlarnaException("Parameterlist is empty or null!", 50067);
3778
+ }
3779
+ if (self::$disableXMLRPC) {
3780
+ return true;
3781
+ }
3782
+ try {
3783
+ /*
3784
+ * Disable verifypeer for CURL, so below error is avoided.
3785
+ * CURL error: SSL certificate problem, verify that the CA
3786
+ * cert is OK.
3787
+ * Details: error:14090086:SSL
3788
+ * routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (#8)
3789
+ */
3790
+ $this->xmlrpc->verifypeer = false;
3791
+
3792
+ $timestart = microtime(true);
3793
+
3794
+ //Create the XMLRPC message.
3795
+ $msg = new xmlrpcmsg($method);
3796
+ $params = array_merge(
3797
+ array(
3798
+ $this->PROTO, $this->VERSION
3799
+ ), $array
3800
+ );
3801
+
3802
+ $msg = new xmlrpcmsg($method);
3803
+ foreach ($params as $p) {
3804
+ if (!$msg->addParam(
3805
+ php_xmlrpc_encode($p, array('extension_api'))
3806
+ )
3807
+ ) {
3808
+ throw new KlarnaException(
3809
+ "Failed to add parameters to XMLRPC message.",
3810
+ 50068
3811
+ );
3812
+ }
3813
+ }
3814
+
3815
+ //Send the message.
3816
+ $selectDateTime = microtime(true);
3817
+ if (self::$xmlrpcDebug) {
3818
+ $this->xmlrpc->setDebug(2);
3819
+ }
3820
+ $xmlrpcresp = $this->xmlrpc->send($msg);
3821
+
3822
+ //Calculate time and selectTime.
3823
+ $timeend = microtime(true);
3824
+ $time = (int) (($selectDateTime - $timestart) * 1000);
3825
+ $selectTime = (int) (($timeend - $timestart) * 1000);
3826
+
3827
+ $status = $xmlrpcresp->faultCode();
3828
+
3829
+ //Send report to candice.
3830
+ if (self::$_candice === true) {
3831
+ $this->sendStat($method, $time, $selectTime, $status);
3832
+ }
3833
+
3834
+ if ($status !== 0) {
3835
+ throw new KlarnaException($xmlrpcresp->faultString(), $status);
3836
+ }
3837
+
3838
+ return php_xmlrpc_decode($xmlrpcresp->value());
3839
+ }
3840
+ catch(KlarnaException $e) {
3841
+ //Otherwise it is caught below, and rethrown.
3842
+ throw $e;
3843
+ }
3844
+ catch(Exception $e) {
3845
+ throw new KlarnaException($e->getMessage(), $e->getCode());
3846
+ }
3847
+ }
3848
+
3849
+ /**
3850
+ * Removes all relevant order/customer data from the internal structure.
3851
+ *
3852
+ * @return void
3853
+ */
3854
+ public function clear()
3855
+ {
3856
+ $this->goodsList = null;
3857
+ $this->comment = "";
3858
+
3859
+ $this->billing = null;
3860
+ $this->shipping = null;
3861
+
3862
+ $this->shipInfo = array();
3863
+ $this->extraInfo = array();
3864
+ $this->bankInfo = array();
3865
+ $this->incomeInfo = array();
3866
+ $this->activateInfo = array();
3867
+
3868
+ $this->reference = "";
3869
+ $this->reference_code = "";
3870
+
3871
+ $this->orderid[0] = "";
3872
+ $this->orderid[1] = "";
3873
+
3874
+ $this->artNos = array();
3875
+ $this->coObjects = array();
3876
+ }
3877
+
3878
+ /**
3879
+ * Sends a report to Candice.
3880
+ *
3881
+ * @param string $method XMLRPC method.
3882
+ * @param int $time Elapsed time of entire XMLRPC call.
3883
+ * @param int $selectTime Time to create the XMLRPC parameters.
3884
+ * @param int $status XMLRPC error code.
3885
+ *
3886
+ * @return void
3887
+ */
3888
+ protected function sendStat($method, $time, $selectTime, $status)
3889
+ {
3890
+ $fp = @fsockopen('udp://'.self::$_c_addr, 80, $errno, $errstr, 1500);
3891
+ if ($fp) {
3892
+ $uri = "{$this->_url['scheme']}://{$this->_url['host']}" .
3893
+ ":{$this->_url['port']}";
3894
+
3895
+ $data = $this->pipe(
3896
+ $this->_eid,
3897
+ $method,
3898
+ $time,
3899
+ $selectTime,
3900
+ $status,
3901
+ $uri
3902
+ );
3903
+ $digest = self::digest($this->pipe($data, $this->_secret));
3904
+
3905
+ self::printDebug("candice report", $data);
3906
+
3907
+ @fwrite($fp, $this->pipe($data, $digest));
3908
+ @fclose($fp);
3909
+ }
3910
+ }
3911
+
3912
+ /**
3913
+ * Implodes parameters with delimiter ':'.
3914
+ * Null and "" values are ignored by the colon function to
3915
+ * ensure there is not several colons in succession.
3916
+ *
3917
+ * @return string Colon separated string.
3918
+ */
3919
+ public static function colon(/* variable parameters */)
3920
+ {
3921
+ return implode(
3922
+ ':',
3923
+ array_filter(
3924
+ func_get_args(),
3925
+ array('self', 'filterDigest')
3926
+ )
3927
+ );
3928
+ }
3929
+
3930
+ /**
3931
+ * Implodes parameters with delimiter '|'.
3932
+ *
3933
+ * @return string Pipe separated string.
3934
+ */
3935
+ public static function pipe(/* variable parameters */)
3936
+ {
3937
+ $args = func_get_args();
3938
+ return implode('|', $args);
3939
+ }
3940
+
3941
+ /**
3942
+ * Check if the value has a string length larger than 0
3943
+ *
3944
+ * @param mixed $value The value to check.
3945
+ *
3946
+ * @return boolean True if string length is larger than 0
3947
+ */
3948
+ public static function filterDigest($value)
3949
+ {
3950
+ return strlen(strval($value)) > 0;
3951
+ }
3952
+
3953
+ /**
3954
+ * Creates a digest hash from the inputted string,
3955
+ * and the specified or the preferred hash algorithm.
3956
+ *
3957
+ * @param string $data Data to be hashed.
3958
+ * @param string $hash hash algoritm to use
3959
+ *
3960
+ * @throws KlarnaException
3961
+ * @return string Base64 encoded hash.
3962
+ */
3963
+ public static function digest($data, $hash = null)
3964
+ {
3965
+ if ($hash===null) {
3966
+ $preferred = array(
3967
+ 'sha512',
3968
+ 'sha384',
3969
+ 'sha256',
3970
+ 'sha224',
3971
+ 'md5'
3972
+ );
3973
+
3974
+ $hashes = array_intersect($preferred, hash_algos());
3975
+
3976
+ if (count($hashes) == 0) {
3977
+ throw new KlarnaException(
3978
+ "No available hash algorithm supported!"
3979
+ );
3980
+ }
3981
+ $hash = array_shift($hashes);
3982
+ }
3983
+ self::printDebug('digest() using hash', $hash);
3984
+
3985
+ return base64_encode(pack("H*", hash($hash, $data)));
3986
+ }
3987
+
3988
+ /**
3989
+ * Converts special characters to numeric htmlentities.
3990
+ *
3991
+ * <b>Note</b>:<br>
3992
+ * If supplied string is encoded with UTF-8, o umlaut ("ö") will become two
3993
+ * HTML entities instead of one.
3994
+ *
3995
+ * @param string $str String to be converted.
3996
+ *
3997
+ * @return string String converted to numeric HTML entities.
3998
+ */
3999
+ public static function num_htmlentities($str)
4000
+ {
4001
+ if (!self::$htmlentities) {
4002
+ self::$htmlentities = array();
4003
+ $table = get_html_translation_table(HTML_ENTITIES, ENT_QUOTES);
4004
+ foreach ($table as $char => $entity) {
4005
+ self::$htmlentities[$entity] = '&#' . ord($char) . ';';
4006
+ }
4007
+ }
4008
+
4009
+ return str_replace(
4010
+ array_keys(
4011
+ self::$htmlentities
4012
+ ), self::$htmlentities, htmlentities($str)
4013
+ );
4014
+ }
4015
+
4016
+ /**
4017
+ * Prints debug information if debug is set to true.
4018
+ * $msg is used as header/footer in the output.
4019
+ *
4020
+ * if FirePHP is available it will be used instead of
4021
+ * dumping the debug info into the document.
4022
+ *
4023
+ * It uses print_r and encapsulates it in HTML/XML comments.
4024
+ * (<!-- -->)
4025
+ *
4026
+ * @param string $msg Debug identifier, e.g. "my array".
4027
+ * @param mixed $mixed Object, type, etc, to be debugged.
4028
+ *
4029
+ * @return void
4030
+ */
4031
+ public static function printDebug($msg, $mixed)
4032
+ {
4033
+ if (self::$debug) {
4034
+ if (class_exists('FB', false)) {
4035
+ FB::send($mixed, $msg);
4036
+ } else {
4037
+ echo "\n<!-- ".$msg.": \n";
4038
+ print_r($mixed);
4039
+ echo "\n end ".$msg." -->\n";
4040
+ }
4041
+ }
4042
+ }
4043
+
4044
+ /**
4045
+ * Checks/fixes so the invNo input is valid.
4046
+ *
4047
+ * @param string &$invNo Invoice number.
4048
+ *
4049
+ * @throws KlarnaException
4050
+ * @return void
4051
+ */
4052
+ private function _checkInvNo(&$invNo)
4053
+ {
4054
+ if (!isset($invNo)) {
4055
+ throw new Klarna_ArgumentNotSetException("Invoice number");
4056
+ }
4057
+ if (!is_string($invNo)) {
4058
+ $invNo = strval($invNo);
4059
+ }
4060
+ if (strlen($invNo) == 0) {
4061
+ throw new Klarna_ArgumentNotSetException("Invoice number");
4062
+ }
4063
+ }
4064
+
4065
+ /**
4066
+ * Checks/fixes so the quantity input is valid.
4067
+ *
4068
+ * @param int &$qty Quantity.
4069
+ *
4070
+ * @throws KlarnaException
4071
+ * @return void
4072
+ */
4073
+ private function _checkQty(&$qty)
4074
+ {
4075
+ if (!isset($qty)) {
4076
+ throw new Klarna_ArgumentNotSetException("Quantity");
4077
+ }
4078
+ if (is_numeric($qty) && !is_int($qty)) {
4079
+ $qty = intval($qty);
4080
+ }
4081
+ if (!is_int($qty)) {
4082
+ throw new Klarna_InvalidTypeException("Quantity", "integer");
4083
+ }
4084
+ }
4085
+
4086
+ /**
4087
+ * Checks/fixes so the artTitle input is valid.
4088
+ *
4089
+ * @param string &$artTitle Article title.
4090
+ *
4091
+ * @throws KlarnaException
4092
+ * @return void
4093
+ */
4094
+ private function _checkArtTitle(&$artTitle)
4095
+ {
4096
+ if (!is_string($artTitle)) {
4097
+ $artTitle = strval($artTitle);
4098
+ }
4099
+ if (!isset($artTitle) || strlen($artTitle) == 0) {
4100
+ throw new Klarna_ArgumentNotSetException("artTitle", 50059);
4101
+ }
4102
+ }
4103
+
4104
+ /**
4105
+ * Checks/fixes so the artNo input is valid.
4106
+ *
4107
+ * @param int|string &$artNo Article number.
4108
+ *
4109
+ * @throws KlarnaException
4110
+ * @return void
4111
+ */
4112
+ private function _checkArtNo(&$artNo)
4113
+ {
4114
+ if (is_numeric($artNo) && !is_string($artNo)) {
4115
+ //Convert artNo to string if integer.
4116
+ $artNo = strval($artNo);
4117
+ }
4118
+ if (!isset($artNo) || strlen($artNo) == 0 || (!is_string($artNo))) {
4119
+ throw new Klarna_ArgumentNotSetException("artNo");
4120
+ }
4121
+ }
4122
+
4123
+ /**
4124
+ * Checks/fixes so the credNo input is valid.
4125
+ *
4126
+ * @param string &$credNo Credit number.
4127
+ *
4128
+ * @throws KlarnaException
4129
+ * @return void
4130
+ */
4131
+ private function _checkCredNo(&$credNo)
4132
+ {
4133
+ if (!isset($credNo)) {
4134
+ throw new Klarna_ArgumentNotSetException("Credit number");
4135
+ }
4136
+
4137
+ if ($credNo === false || $credNo === null) {
4138
+ $credNo = "";
4139
+ }
4140
+ if (!is_string($credNo)) {
4141
+ $credNo = strval($credNo);
4142
+ if (!is_string($credNo)) {
4143
+ throw new Klarna_InvalidTypeException("Credit number", "string");
4144
+ }
4145
+ }
4146
+ }
4147
+
4148
+ /**
4149
+ * Checks so that artNos is an array and is not empty.
4150
+ *
4151
+ * @param array &$artNos Array from {@link Klarna::addArtNo()}.
4152
+ *
4153
+ * @throws KlarnaException
4154
+ * @return void
4155
+ */
4156
+ private function _checkArtNos(&$artNos)
4157
+ {
4158
+ if (!is_array($artNos)) {
4159
+ throw new Klarna_InvalidTypeException("artNos", "array");
4160
+ }
4161
+ if (empty($artNos)) {
4162
+ throw new KlarnaException('ArtNo array is empty!', 50064);
4163
+ }
4164
+ }
4165
+
4166
+ /**
4167
+ * Checks/fixes so the integer input is valid.
4168
+ *
4169
+ * @param int &$int {@link KlarnaFlags flags} constant.
4170
+ * @param string $field Name of the field.
4171
+ *
4172
+ * @throws KlarnaException
4173
+ * @return void
4174
+ */
4175
+ private function _checkInt(&$int, $field)
4176
+ {
4177
+ if (!isset($int)) {
4178
+ throw new Klarna_ArgumentNotSetException($field);
4179
+ }
4180
+ if (is_numeric($int) && !is_int($int)) {
4181
+ $int = intval($int);
4182
+ }
4183
+ if (!is_numeric($int) || !is_int($int)) {
4184
+ throw new Klarna_InvalidTypeException($field, "integer");
4185
+ }
4186
+ }
4187
+
4188
+ /**
4189
+ * Checks/fixes so the VAT input is valid.
4190
+ *
4191
+ * @param float &$vat VAT.
4192
+ *
4193
+ * @throws KlarnaException
4194
+ * @return void
4195
+ */
4196
+ private function _checkVAT(&$vat)
4197
+ {
4198
+ if (!isset($vat)) {
4199
+ throw new Klarna_ArgumentNotSetException("VAT");
4200
+ }
4201
+ if (is_numeric($vat) && (!is_int($vat) || !is_float($vat))) {
4202
+ $vat = floatval($vat);
4203
+ }
4204
+ if (!is_numeric($vat) || (!is_int($vat) && !is_float($vat))) {
4205
+ throw new Klarna_InvalidTypeException("VAT", "integer or float");
4206
+ }
4207
+ }
4208
+
4209
+ /**
4210
+ * Checks/fixes so the amount input is valid.
4211
+ *
4212
+ * @param int &$amount Amount.
4213
+ *
4214
+ * @throws KlarnaException
4215
+ * @return void
4216
+ */
4217
+ private function _checkAmount(&$amount)
4218
+ {
4219
+ if (!isset($amount)) {
4220
+ throw new Klarna_ArgumentNotSetException("Amount");
4221
+ }
4222
+ if (is_numeric($amount)) {
4223
+ $this->_fixValue($amount);
4224
+ }
4225
+ if (is_numeric($amount) && !is_int($amount)) {
4226
+ $amount = intval($amount);
4227
+ }
4228
+ if (!is_numeric($amount) || !is_int($amount)) {
4229
+ throw new Klarna_InvalidTypeException("amount", "integer");
4230
+ }
4231
+ }
4232
+
4233
+ /**
4234
+ * Checks/fixes so the price input is valid.
4235
+ *
4236
+ * @param int &$price Price.
4237
+ *
4238
+ * @throws KlarnaException
4239
+ * @return void
4240
+ */
4241
+ private function _checkPrice(&$price)
4242
+ {
4243
+ if (!isset($price)) {
4244
+ throw new Klarna_ArgumentNotSetException("Price");
4245
+ }
4246
+ if (is_numeric($price)) {
4247
+ $this->_fixValue($price);
4248
+ }
4249
+ if (is_numeric($price) && !is_int($price)) {
4250
+ $price = intval($price);
4251
+ }
4252
+ if (!is_numeric($price) || !is_int($price)) {
4253
+ throw new Klarna_InvalidTypeException("Price", "integer");
4254
+ }
4255
+ }
4256
+
4257
+ /**
4258
+ * Multiplies value with 100 and rounds it.
4259
+ * This fixes value/price/amount inputs so that KO can handle them.
4260
+ *
4261
+ * @param float &$value value
4262
+ *
4263
+ * @return void
4264
+ */
4265
+ private function _fixValue(&$value)
4266
+ {
4267
+ $value = round($value * 100);
4268
+ }
4269
+
4270
+ /**
4271
+ * Checks/fixes so the discount input is valid.
4272
+ *
4273
+ * @param float &$discount Discount amount.
4274
+ *
4275
+ * @throws KlarnaException
4276
+ * @return void
4277
+ */
4278
+ private function _checkDiscount(&$discount)
4279
+ {
4280
+ if (!isset($discount)) {
4281
+ throw new Klarna_ArgumentNotSetException("Discount");
4282
+ }
4283
+ if (is_numeric($discount)
4284
+ && (!is_int($discount) || !is_float($discount))
4285
+ ) {
4286
+ $discount = floatval($discount);
4287
+ }
4288
+
4289
+ if (!is_numeric($discount)
4290
+ || (!is_int($discount) && !is_float($discount))
4291
+ ) {
4292
+ throw new Klarna_InvalidTypeException("Discount", "integer or float");
4293
+ }
4294
+ }
4295
+
4296
+ /**
4297
+ * Checks/fixes so that the estoreOrderNo input is valid.
4298
+ *
4299
+ * @param string &$estoreOrderNo Estores order number.
4300
+ *
4301
+ * @throws KlarnaException
4302
+ * @return void
4303
+ */
4304
+ private function _checkEstoreOrderNo(&$estoreOrderNo)
4305
+ {
4306
+ if (!isset($estoreOrderNo)) {
4307
+ throw new Klarna_ArgumentNotSetException("Order number");
4308
+ }
4309
+
4310
+ if (!is_string($estoreOrderNo)) {
4311
+ $estoreOrderNo = strval($estoreOrderNo);
4312
+ if (!is_string($estoreOrderNo)) {
4313
+ throw new Klarna_InvalidTypeException("Order number", "string");
4314
+ }
4315
+ }
4316
+ }
4317
+
4318
+ /**
4319
+ * Checks/fixes to the PNO/SSN input is valid.
4320
+ *
4321
+ * @param string &$pno Personal number, social security number, ...
4322
+ * @param int $enc {@link KlarnaEncoding PNO Encoding} constant.
4323
+ *
4324
+ * @throws KlarnaException
4325
+ * @return void
4326
+ */
4327
+ private function _checkPNO(&$pno, $enc)
4328
+ {
4329
+ if (!$pno) {
4330
+ throw new Klarna_ArgumentNotSetException("PNO/SSN");
4331
+ }
4332
+
4333
+ if (!KlarnaEncoding::checkPNO($pno)) {
4334
+ throw new Klarna_InvalidPNOException;
4335
+ }
4336
+ }
4337
+
4338
+ /**
4339
+ * Checks/fixes to the country input is valid.
4340
+ *
4341
+ * @param int &$country {@link KlarnaCountry Country} constant.
4342
+ *
4343
+ * @throws KlarnaException
4344
+ * @return void
4345
+ */
4346
+ private function _checkCountry(&$country)
4347
+ {
4348
+ if (!isset($country)) {
4349
+ throw new Klarna_ArgumentNotSetException("Country");
4350
+ }
4351
+ if (is_numeric($country) && !is_int($country)) {
4352
+ $country = intval($country);
4353
+ }
4354
+ if (!is_numeric($country) || !is_int($country)) {
4355
+ throw new Klarna_InvalidTypeException("Country", "integer");
4356
+ }
4357
+ }
4358
+
4359
+ /**
4360
+ * Checks/fixes to the language input is valid.
4361
+ *
4362
+ * @param int &$language {@link KlarnaLanguage Language} constant.
4363
+ *
4364
+ * @throws KlarnaException
4365
+ * @return void
4366
+ */
4367
+ private function _checkLanguage(&$language)
4368
+ {
4369
+ if (!isset($language)) {
4370
+ throw new Klarna_ArgumentNotSetException("Language");
4371
+ }
4372
+ if (is_numeric($language) && !is_int($language)) {
4373
+ $language = intval($language);
4374
+ }
4375
+ if (!is_numeric($language) || !is_int($language)) {
4376
+ throw new Klarna_InvalidTypeException("Language", "integer");
4377
+ }
4378
+ }
4379
+
4380
+ /**
4381
+ * Checks/fixes to the currency input is valid.
4382
+ *
4383
+ * @param int &$currency {@link KlarnaCurrency Currency} constant.
4384
+ *
4385
+ * @throws KlarnaException
4386
+ * @return void
4387
+ */
4388
+ private function _checkCurrency(&$currency)
4389
+ {
4390
+ if (!isset($currency)) {
4391
+ throw new Klarna_ArgumentNotSetException("Currency");
4392
+ }
4393
+ if (is_numeric($currency) && !is_int($currency)) {
4394
+ $currency = intval($currency);
4395
+ }
4396
+ if (!is_numeric($currency) || !is_int($currency)) {
4397
+ throw new Klarna_InvalidTypeException("Currency", "integer");
4398
+ }
4399
+ }
4400
+
4401
+ /**
4402
+ * Checks/fixes so no/number is a valid input.
4403
+ *
4404
+ * @param int &$no Number.
4405
+ *
4406
+ * @throws KlarnaException
4407
+ * @return void
4408
+ */
4409
+ private function _checkNo(&$no)
4410
+ {
4411
+ if (!isset($no)) {
4412
+ throw new Klarna_ArgumentNotSetException("no");
4413
+ }
4414
+ if (is_numeric($no) && !is_int($no)) {
4415
+ $no = intval($no);
4416
+ }
4417
+ if (!is_numeric($no) || !is_int($no) || $no <= 0) {
4418
+ throw new Klarna_InvalidTypeException('no', 'integer > 0');
4419
+ }
4420
+ }
4421
+
4422
+ /**
4423
+ * Checks/fixes so reservation number is a valid input.
4424
+ *
4425
+ * @param string &$rno Reservation number.
4426
+ *
4427
+ * @throws KlarnaException
4428
+ * @return void
4429
+ */
4430
+ private function _checkRNO(&$rno)
4431
+ {
4432
+ if (!is_string($rno)) {
4433
+ $rno = strval($rno);
4434
+ }
4435
+ if (strlen($rno) == 0) {
4436
+ throw new Klarna_ArgumentNotSetException("RNO");
4437
+ }
4438
+ }
4439
+
4440
+ /**
4441
+ * Checks/fixes so that reference/refCode are valid.
4442
+ *
4443
+ * @param string &$reference Reference string.
4444
+ * @param string &$refCode Reference code.
4445
+ *
4446
+ * @throws KlarnaException
4447
+ * @return void
4448
+ */
4449
+ private function _checkRef(&$reference, &$refCode)
4450
+ {
4451
+ if (!is_string($reference)) {
4452
+ $reference = strval($reference);
4453
+ if (!is_string($reference)) {
4454
+ throw new Klarna_InvalidTypeException("Reference", "string");
4455
+ }
4456
+ }
4457
+
4458
+ if (!is_string($refCode)) {
4459
+ $refCode = strval($refCode);
4460
+ if (!is_string($refCode)) {
4461
+ throw new Klarna_InvalidTypeException("Reference code", "string");
4462
+ }
4463
+ }
4464
+ }
4465
+
4466
+ /**
4467
+ * Checks/fixes so that the OCR input is valid.
4468
+ *
4469
+ * @param string &$ocr OCR number.
4470
+ *
4471
+ * @throws KlarnaException
4472
+ * @return void
4473
+ */
4474
+ private function _checkOCR(&$ocr)
4475
+ {
4476
+ if (!is_string($ocr)) {
4477
+ $ocr = strval($ocr);
4478
+ if (!is_string($ocr)) {
4479
+ throw new Klarna_InvalidTypeException("OCR", "string");
4480
+ }
4481
+ }
4482
+ }
4483
+
4484
+ /**
4485
+ * Check so required argument is supplied.
4486
+ *
4487
+ * @param string $argument argument to check
4488
+ * @param string $name name of argument
4489
+ *
4490
+ * @throws Klarna_ArgumentNotSetException
4491
+ * @return void
4492
+ */
4493
+ private function _checkArgument($argument, $name)
4494
+ {
4495
+ if (!is_string($argument)) {
4496
+ $argument = strval($argument);
4497
+ }
4498
+
4499
+ if (strlen($argument) == 0) {
4500
+ throw new Klarna_ArgumentNotSetException($name);
4501
+ }
4502
+ }
4503
+
4504
+ /**
4505
+ * Check so Locale settings (country, currency, language) are set.
4506
+ *
4507
+ * @throws KlarnaException
4508
+ * @return void
4509
+ */
4510
+ private function _checkLocale()
4511
+ {
4512
+ if (!is_int($this->_country)
4513
+ || !is_int($this->_language)
4514
+ || !is_int($this->_currency)
4515
+ ) {
4516
+ throw new Klarna_InvalidLocaleException;
4517
+ }
4518
+ }
4519
+
4520
+ /**
4521
+ * Checks wether a goodslist is set.
4522
+ *
4523
+ * @throws Klarna_MissingGoodslistException
4524
+ * @return void
4525
+ */
4526
+ private function _checkGoodslist()
4527
+ {
4528
+ if (!is_array($this->goodsList) || empty($this->goodsList)) {
4529
+ throw new Klarna_MissingGoodslistException;
4530
+ }
4531
+ }
4532
+
4533
+ /**
4534
+ * Set the pcStorage method used for this instance
4535
+ *
4536
+ * @param PCStorage $pcStorage PCStorage implementation
4537
+ *
4538
+ * @return void
4539
+ */
4540
+ public function setPCStorage($pcStorage)
4541
+ {
4542
+ if (!($pcStorage instanceof PCStorage)) {
4543
+ throw new Klarna_InvalidTypeException('pcStorage', 'PCStorage');
4544
+ }
4545
+ $this->pcStorage = $pcStorage->getName();
4546
+ $this->pclasses = $pcStorage;
4547
+ }
4548
+
4549
+ /**
4550
+ * Ensure the configuration is of the correct type.
4551
+ *
4552
+ * @param array|ArrayAccess|null $config an optional config to validate
4553
+ *
4554
+ * @return void
4555
+ */
4556
+ private function _checkConfig($config = null)
4557
+ {
4558
+ if ($config === null) {
4559
+ $config = $this->config;
4560
+ }
4561
+ if (!($config instanceof ArrayAccess)
4562
+ && !is_array($config)
4563
+ ) {
4564
+ throw new Klarna_IncompleteConfigurationException;
4565
+ }
4566
+ }
4567
+
4568
+ } //End Klarna
4569
+
4570
+ /**
4571
+ * Include the {@link KlarnaConfig} class.
4572
+ */
4573
+ require_once 'klarnaconfig.php';
4574
+
4575
+ /**
4576
+ * Include the {@link KlarnaPClass} class.
4577
+ */
4578
+ require_once 'klarnapclass.php';
4579
+
4580
+ /**
4581
+ * Include the {@link KlarnaCalc} class.
4582
+ */
4583
+ require_once 'klarnacalc.php';
4584
+
4585
+ /**
4586
+ * Include the {@link KlarnaAddr} class.
4587
+ */
4588
+ require_once 'klarnaaddr.php';
4589
+
4590
+ /**
4591
+ * Include the Exception classes.
4592
+ */
4593
+ require_once 'Exceptions.php';
4594
+
4595
+ /**
4596
+ * Include the KlarnaEncoding class.
4597
+ */
4598
+ require_once 'Encoding.php';
4599
+
4600
+
4601
+ /**
4602
+ * Include the KlarnaFlags class.
4603
+ */
4604
+ require_once 'Flags.php';
4605
+
4606
+ /**
4607
+ * Include KlarnaCountry, KlarnaCurrency, KlarnaLanguage classes
4608
+ */
4609
+ require_once 'Country.php';
4610
+ require_once 'Currency.php';
4611
+ require_once 'Language.php';
lib/Klarna/Language.php ADDED
@@ -0,0 +1,300 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * KlarnaLanguage
5
+ *
6
+ * PHP Version 5.3
7
+ *
8
+ * @category Payment
9
+ * @package KlarnaAPI
10
+ * @author MS Dev <ms.modules@klarna.com>
11
+ * @copyright 2012 Klarna AB (http://klarna.com)
12
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
13
+ * @link http://integration.klarna.com/
14
+ */
15
+
16
+ /**
17
+ * Language Constants class
18
+ *
19
+ * @category Payment
20
+ * @package KlarnaAPI
21
+ * @author MS Dev <ms.modules@klarna.com>
22
+ * @copyright 2012 Klarna AB (http://klarna.com)
23
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
24
+ * @link http://integration.klarna.com/
25
+ */
26
+ class KlarnaLanguage
27
+ {
28
+
29
+ /**
30
+ * Language constant for Danish (DA).<br>
31
+ * ISO639_DA
32
+ *
33
+ * @var int
34
+ */
35
+ const DA = 27;
36
+
37
+ /**
38
+ * Language constant for German (DE).<br>
39
+ * ISO639_DE
40
+ *
41
+ * @var int
42
+ */
43
+ const DE = 28;
44
+
45
+ /**
46
+ * Language constant for English (EN).<br>
47
+ * ISO639_EN
48
+ *
49
+ * @var int
50
+ */
51
+ const EN = 31;
52
+
53
+ /**
54
+ * Language constant for Finnish (FI).<br>
55
+ * ISO639_FI
56
+ *
57
+ * @var int
58
+ */
59
+ const FI = 37;
60
+
61
+ /**
62
+ * Language constant for Norwegian (NB).<br>
63
+ * ISO639_NB
64
+ *
65
+ * @var int
66
+ */
67
+ const NB = 97;
68
+
69
+ /**
70
+ * Language constant for Dutch (NL).<br>
71
+ * ISO639_NL
72
+ *
73
+ * @var int
74
+ */
75
+ const NL = 101;
76
+
77
+ /**
78
+ * Language constant for Swedish (SV).<br>
79
+ * ISO639_SV
80
+ *
81
+ * @var int
82
+ */
83
+ const SV = 138;
84
+
85
+ /**
86
+ * Converts a language code, e.g. 'de' to the KlarnaLanguage constant.
87
+ *
88
+ * @param string $val language code
89
+ *
90
+ * @return int|null
91
+ */
92
+ public static function fromCode($val)
93
+ {
94
+ $val = strtoupper($val);
95
+ if (array_key_exists($val, self::$_languages)) {
96
+ return self::$_languages[$val];
97
+ }
98
+ return null;
99
+ }
100
+
101
+ /**
102
+ * Converts a KlarnaLanguage constant to the respective language code.
103
+ *
104
+ * @param int $val KlarnaLanguage constant
105
+ *
106
+ * @return lowercase string|null
107
+ */
108
+ public static function getCode($val)
109
+ {
110
+ if (self::$_languageFlip === array()) {
111
+ self::$_languageFlip = array_flip(self::$_languages);
112
+ }
113
+ if (array_key_exists($val, self::$_languageFlip)) {
114
+ return strtolower(self::$_languageFlip[$val]);
115
+ }
116
+ return null;
117
+ }
118
+
119
+ /**
120
+ * Cache for the flipped language array
121
+ *
122
+ * @var array
123
+ */
124
+ private static $_languageFlip = array();
125
+
126
+ /**
127
+ * Array containing all languages and their KRED Code
128
+ *
129
+ * @var array
130
+ */
131
+ private static $_languages = array(
132
+ 'AA' => 1, // Afar
133
+ 'AB' => 2, // Abkhazian
134
+ 'AE' => 3, // Avestan
135
+ 'AF' => 4, // Afrikaans
136
+ 'AM' => 5, // Amharic
137
+ 'AR' => 6, // Arabic
138
+ 'AS' => 7, // Assamese
139
+ 'AY' => 8, // Aymara
140
+ 'AZ' => 9, // Azerbaijani
141
+ 'BA' => 10, // Bashkir
142
+ 'BE' => 11, // Byelorussian; Belarusian
143
+ 'BG' => 12, // Bulgarian
144
+ 'BH' => 13, // Bihari
145
+ 'BI' => 14, // Bislama
146
+ 'BN' => 15, // Bengali; Bangla
147
+ 'BO' => 16, // Tibetan
148
+ 'BR' => 17, // Breton
149
+ 'BS' => 18, // Bosnian
150
+ 'CA' => 19, // Catalan
151
+ 'CE' => 20, // Chechen
152
+ 'CH' => 21, // Chamorro
153
+ 'CO' => 22, // Corsican
154
+ 'CS' => 23, // Czech
155
+ 'CU' => 24, // Church Slavic
156
+ 'CV' => 25, // Chuvash
157
+ 'CY' => 26, // Welsh
158
+ 'DA' => 27, // Danish
159
+ 'DE' => 28, // German
160
+ 'DZ' => 29, // Dzongkha; Bhutani
161
+ 'EL' => 30, // Greek
162
+ 'EN' => 31, // English
163
+ 'EO' => 32, // Esperanto
164
+ 'ES' => 33, // Spanish
165
+ 'ET' => 34, // Estonian
166
+ 'EU' => 35, // Basque
167
+ 'FA' => 36, // Persian
168
+ 'FI' => 37, // Finnish
169
+ 'FJ' => 38, // Fijian; Fiji
170
+ 'FO' => 39, // Faroese
171
+ 'FR' => 40, // French
172
+ 'FY' => 41, // Frisian
173
+ 'GA' => 42, // Irish
174
+ 'GD' => 43, // Scots; Gaelic
175
+ 'GL' => 44, // Gallegan; Galician
176
+ 'GN' => 45, // Guarani
177
+ 'GU' => 46, // Gujarati
178
+ 'GV' => 47, // Manx
179
+ 'HA' => 48, // Hausa
180
+ 'HE' => 49, // Hebrew (formerly iw)
181
+ 'HI' => 50, // Hindi
182
+ 'HO' => 51, // Hiri Motu
183
+ 'HR' => 52, // Croatian
184
+ 'HU' => 53, // Hungarian
185
+ 'HY' => 54, // Armenian
186
+ 'HZ' => 55, // Herero
187
+ 'IA' => 56, // Interlingua
188
+ 'ID' => 57, // Indonesian (formerly in)
189
+ 'IE' => 58, // Interlingue
190
+ 'IK' => 59, // Inupiak
191
+ 'IO' => 60, // Ido
192
+ 'IS' => 61, // Icelandic
193
+ 'IT' => 62, // Italian
194
+ 'IU' => 63, // Inuktitut
195
+ 'JA' => 64, // Japanese
196
+ 'JV' => 65, // Javanese
197
+ 'KA' => 66, // Georgian
198
+ 'KI' => 67, // Kikuyu
199
+ 'KJ' => 68, // Kuanyama
200
+ 'KK' => 69, // Kazakh
201
+ 'KL' => 70, // Kalaallisut; Greenlandic
202
+ 'KM' => 71, // Khmer; Cambodian
203
+ 'KN' => 72, // Kannada
204
+ 'KO' => 73, // Korean
205
+ 'KS' => 74, // Kashmiri
206
+ 'KU' => 75, // Kurdish
207
+ 'KV' => 76, // Komi
208
+ 'KW' => 77, // Cornish
209
+ 'KY' => 78, // Kirghiz
210
+ 'LA' => 79, // Latin
211
+ 'LB' => 80, // Letzeburgesch
212
+ 'LN' => 81, // Lingala
213
+ 'LO' => 82, // Lao; Laotian
214
+ 'LT' => 83, // Lithuanian
215
+ 'LV' => 84, // Latvian; Lettish
216
+ 'MG' => 85, // Malagasy
217
+ 'MH' => 86, // Marshall
218
+ 'MI' => 87, // Maori
219
+ 'MK' => 88, // Macedonian
220
+ 'ML' => 89, // Malayalam
221
+ 'MN' => 90, // Mongolian
222
+ 'MO' => 91, // Moldavian
223
+ 'MR' => 92, // Marathi
224
+ 'MS' => 93, // Malay
225
+ 'MT' => 94, // Maltese
226
+ 'MY' => 95, // Burmese
227
+ 'NA' => 96, // Nauru
228
+ 'NB' => 97, // Norwegian Bokmål
229
+ 'ND' => 98, // Ndebele, North
230
+ 'NE' => 99, // Nepali
231
+ 'NG' => 100, // Ndonga
232
+ 'NL' => 101, // Dutch
233
+ 'NN' => 102, // Norwegian Nynorsk
234
+ 'NO' => 103, // Norwegian
235
+ 'NR' => 104, // Ndebele, South
236
+ 'NV' => 105, // Navajo
237
+ 'NY' => 106, // Chichewa; Nyanja
238
+ 'OC' => 107, // Occitan; Provençal
239
+ 'OM' => 108, // (Afan) Oromo
240
+ 'OR' => 109, // Oriya
241
+ 'OS' => 110, // Ossetian; Ossetic
242
+ 'PA' => 111, // Panjabi; Punjabi
243
+ 'PI' => 112, // Pali
244
+ 'PL' => 113, // Polish
245
+ 'PS' => 114, // Pashto, Pushto
246
+ 'PT' => 115, // Portuguese
247
+ 'QU' => 116, // Quechua
248
+ 'RM' => 117, // Rhaeto-Romance
249
+ 'RN' => 118, // Rundi; Kirundi
250
+ 'RO' => 119, // Romanian
251
+ 'RU' => 120, // Russian
252
+ 'RW' => 121, // Kinyarwanda
253
+ 'SA' => 122, // Sanskrit
254
+ 'SC' => 123, // Sardinian
255
+ 'SD' => 124, // Sindhi
256
+ 'SE' => 125, // Northern Sami
257
+ 'SG' => 126, // Sango; Sangro
258
+ 'SI' => 127, // Sinhalese
259
+ 'SK' => 128, // Slovak
260
+ 'SL' => 129, // Slovenian
261
+ 'SM' => 130, // Samoan
262
+ 'SN' => 131, // Shona
263
+ 'SO' => 132, // Somali
264
+ 'SQ' => 133, // Albanian
265
+ 'SR' => 134, // Serbian
266
+ 'SS' => 135, // Swati; Siswati
267
+ 'ST' => 136, // Sesotho; Sotho, Southern
268
+ 'SU' => 137, // Sundanese
269
+ 'SV' => 138, // Swedish
270
+ 'SW' => 139, // Swahili
271
+ 'TA' => 140, // Tamil
272
+ 'TE' => 141, // Telugu
273
+ 'TG' => 142, // Tajik
274
+ 'TH' => 143, // Thai
275
+ 'TI' => 144, // Tigrinya
276
+ 'TK' => 145, // Turkmen
277
+ 'TL' => 146, // Tagalog
278
+ 'TN' => 147, // Tswana; Setswana
279
+ 'TO' => 148, // Tongan
280
+ 'TR' => 149, // Turkish
281
+ 'TS' => 150, // Tsonga
282
+ 'TT' => 151, // Tatar
283
+ 'TW' => 152, // Twi
284
+ 'TY' => 153, // Tahitian
285
+ 'UG' => 154, // Uighur
286
+ 'UK' => 155, // Ukrainian
287
+ 'UR' => 156, // Urdu
288
+ 'UZ' => 157, // Uzbek
289
+ 'VI' => 158, // Vietnamese
290
+ 'VO' => 159, // Volapuk
291
+ 'WA' => 160, // Walloon
292
+ 'WO' => 161, // Wolof
293
+ 'XH' => 162, // Xhosa
294
+ 'YI' => 163, // Yiddish (formerly ji)
295
+ 'YO' => 164, // Yoruba
296
+ 'ZA' => 165, // Zhuang
297
+ 'ZH' => 166, // Chinese
298
+ 'ZU' => 167 // Zulu
299
+ );
300
+ }
lib/Klarna/checkout/checkouthtml.intf.php ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * CheckoutHTML interface for threatmetrix
4
+ *
5
+ * PHP Version 5.3
6
+ *
7
+ * @category Payment
8
+ * @package KlarnaAPI
9
+ * @author MS Dev <ms.modules@klarna.com>
10
+ * @copyright 2012 Klarna AB (http://klarna.com)
11
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
12
+ * @link http://integration.klarna.com/
13
+ */
14
+
15
+ /**
16
+ * This interface provides methods to supply checkout page specific HTML.<br>
17
+ * Can be used to insert device identification, fraud prevention,<br>
18
+ * client side validation code into the checkout page.
19
+ *
20
+ * @category Payment
21
+ * @package KlarnaAPI
22
+ * @author MS Dev <ms.modules@klarna.com>
23
+ * @copyright 2012 Klarna AB (http://klarna.com)
24
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
25
+ * @link http://integration.klarna.com/
26
+ */
27
+ abstract class CheckoutHTML
28
+ {
29
+
30
+ /**
31
+ * Creates a session ID used for e.g. client identification and fraud
32
+ * prevention.
33
+ *
34
+ * This method creates a 40 character long integer.
35
+ * The first 30 numbers is microtime + random numbers.
36
+ * The last 10 numbers is the eid zero-padded.
37
+ *
38
+ * All random functions are automatically seeded as of PHP 4.2.0.
39
+ *
40
+ * E.g. for eid 1004 output could be:
41
+ * 1624100001298454658880354228080000001004
42
+ *
43
+ * @param int $eid merchant id
44
+ *
45
+ * @return string A integer with a string length of 40.
46
+ */
47
+ public static function getSessionID($eid)
48
+ {
49
+ $eid = strval($eid);
50
+ while (strlen($eid) < 10) {
51
+ $eid = "0" . $eid; //Zero-pad the eid.
52
+ }
53
+
54
+ $sid = str_replace(array(' ', ',', '.'), '', microtime());
55
+ $sid[0] = rand(1, 9); //Make sure we always have a non-zero first.
56
+
57
+ //microtime + rand = 30 numbers in length
58
+ while (strlen($sid) < 30) {
59
+ //rand is automatically seeded as of PHP 4.2.0
60
+ $sid .= rand(0, 9999);
61
+ }
62
+ $sid = substr($sid, 0, 30);
63
+ $sid .= $eid;
64
+
65
+ return $sid;
66
+ }
67
+
68
+ /**
69
+ * Initializes this object, this method is always called
70
+ * before {@link CheckoutHTML::toHTML()}.
71
+ * This method is used in {@link Klarna::addTransaction()},
72
+ * {@link Klarna::reserveAmount()} and in {@link Klarna::checkoutHTML()}
73
+ *
74
+ * @param Klarna $klarna The API instance
75
+ * @param int $eid merchant id
76
+ *
77
+ * @return void
78
+ */
79
+ abstract public function init($klarna, $eid);
80
+
81
+ /**
82
+ * This returns the HTML code for this object,
83
+ * which will be used in the checkout page.
84
+ *
85
+ * @return string HTML
86
+ */
87
+ abstract public function toHTML();
88
+
89
+ /**
90
+ * This function is used to clear any stored values
91
+ * (in SESSION, COOKIE or similar)
92
+ * which are required to be unique between purchases.
93
+ *
94
+ * @return void
95
+ */
96
+ abstract public function clear();
97
+ }
lib/Klarna/checkout/threatmetrix.class.php ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * threatmetrix implementation of checckouthtml
4
+ *
5
+ * PHP Version 5.3
6
+ *
7
+ * @category Payment
8
+ * @package KlarnaAPI
9
+ * @author MS Dev <ms.modules@klarna.com>
10
+ * @copyright 2012 Klarna AB (http://klarna.com)
11
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
12
+ * @link http://integration.klarna.com/
13
+ */
14
+
15
+ /**
16
+ * ThreatMetrix is a fraud prevention and device identification software.
17
+ *
18
+ * @category Payment
19
+ * @package KlarnaAPI
20
+ * @author MS Dev <ms.modules@klarna.com>
21
+ * @copyright 2012 Klarna AB (http://klarna.com)
22
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
23
+ * @link http://integration.klarna.com/
24
+ */
25
+ class ThreatMetrix extends CheckoutHTML
26
+ {
27
+
28
+ /**
29
+ * The ID used in conjunction with the Klarna API.
30
+ *
31
+ * @var int
32
+ */
33
+ const ID = 'dev_id_1';
34
+
35
+ /**
36
+ * ThreatMetrix organizational ID.
37
+ *
38
+ * @var string
39
+ */
40
+ protected $orgID = 'qicrzsu4';
41
+
42
+ /**
43
+ * Session ID for the client.
44
+ *
45
+ * @var string
46
+ */
47
+ protected $sessionID;
48
+
49
+ /**
50
+ * Hostname used to access ThreatMetrix.
51
+ *
52
+ * @var string
53
+ */
54
+ protected $host = 'h.online-metrix.net';
55
+
56
+ /**
57
+ * Protocol used to access ThreatMetrix.
58
+ *
59
+ * @var string
60
+ */
61
+ protected $proto = 'https';
62
+
63
+ /**
64
+ * Initializes this object, this method is always called
65
+ * before {@link CheckoutHTML::toHTML()}.
66
+ * This method is used in {@link Klarna::addTransaction()},
67
+ * {@link Klarna::reserveAmount()} and in {@link Klarna::checkoutHTML()}
68
+ *
69
+ * @param Klarna $klarna The API instance
70
+ * @param int $eid Merchant ID
71
+ *
72
+ * @return void
73
+ */
74
+ public function init($klarna, $eid)
75
+ {
76
+ if (!is_int($eid)) {
77
+ throw new Klarna_ConfigFieldMissingException('eid');
78
+ }
79
+ if (isset($_SESSION)) {
80
+ if (!isset($_SESSION[self::ID])
81
+ || (strlen($_SESSION[self::ID]) < 40)
82
+ ) {
83
+ $_SESSION[self::ID] = parent::getSessionID($eid);
84
+ $this->sessionID = $_SESSION[self::ID];
85
+ } else {
86
+ $this->sessionID = $_SESSION[self::ID];
87
+ }
88
+ } else {
89
+ $this->sessionID = parent::getSessionID($eid);
90
+ }
91
+
92
+ $klarna->setSessionID(self::ID, $this->sessionID);
93
+ }
94
+
95
+ /**
96
+ * This function is used to clear any stored values
97
+ * (in SESSION, COOKIE or similar)
98
+ * which are required to be unique between purchases.
99
+ *
100
+ * @return void
101
+ */
102
+ public function clear()
103
+ {
104
+ if (isset($_SESSION) && isset($_SESSION[self::ID])) {
105
+ $_SESSION[self::ID] = null;
106
+ unset($_SESSION[self::ID]);
107
+ }
108
+ }
109
+
110
+ /**
111
+ * This returns the HTML code for this object,
112
+ * which will be used in the checkout page.
113
+ *
114
+ * @return string
115
+ */
116
+ public function toHTML()
117
+ {
118
+ $html
119
+ = "<p style='display: none; ".
120
+ "background:url($this->proto://$this->host/fp/clear.png?org_id=".
121
+ "$this->orgID&session_id=$this->sessionID&m=1)'></p>".
122
+ "<script src='$this->proto://$this->host/fp/check.js?org_id=".
123
+ "$this->orgID&session_id=$this->sessionID' ".
124
+ "type='text/javascript'></script>".
125
+ "<img src='$this->proto://$this->host/fp/clear.png?org_id=".
126
+ "$this->orgID&session_id=$this->sessionID&m=2' alt='' >".
127
+ "<object type='application/x-shockwave-flash' style='display: none' ".
128
+ "data='$this->proto://$this->host/fp/fp.swf?org_id=$this->orgID&".
129
+ "session_id=$this->sessionID' width='1' height='1' id='obj_id'>".
130
+ "<param name='movie' value='$this->proto://$this->host/fp/fp.swf?".
131
+ "org_id=$this->orgID&session_id=$this->sessionID' />".
132
+ "<div></div>".
133
+ "</object>";
134
+ return $html;
135
+ }
136
+ }
lib/Klarna/examples/activate.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ /**
31
+ * 2. Activate the reservation
32
+ */
33
+ $rno = '123456';
34
+
35
+ // Optional fields should be set using
36
+ // [[setActivateInfo]]
37
+ $k->setActivateInfo('key', 'value');
38
+ // [[setActivateInfo]]
39
+
40
+ // [[setActivateInfo:response]]
41
+ null;
42
+ // [[setActivateInfo:response]]
43
+
44
+ try {
45
+ // [[activate]]
46
+ $result = $k->activate($rno);
47
+ // [[activate]]
48
+
49
+ // [[activate:response]]
50
+ array(
51
+ "ok",
52
+ "1234567890"
53
+ );
54
+ // [[activate:response]]
55
+
56
+
57
+ $risk = $result[0]; // ok or no_risk
58
+ $invno = $result[1];
59
+
60
+
61
+ echo "risk: {$risk}\ninvno: {$invno}\n";
62
+ // Reservation is activated, proceed accordingly.
63
+ } catch(Exception $e) {
64
+ // Something went wrong, print the message:
65
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
66
+ }
lib/Klarna/examples/activateInvoice.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+
34
+ /**
35
+ * 2. Activate the invoice
36
+ */
37
+
38
+ // Here you enter the invoice number you got from addTransaction():
39
+ $invNo = '123456';
40
+
41
+ try {
42
+ // You can specify a new pclass ID if the customer wanted to change it
43
+ // before you activate.
44
+ $url = $k->activateInvoice($invNo, $pclass = KlarnaPClass::INVOICE);
45
+
46
+ echo "{$url}\n";
47
+
48
+ // The url points to a PDF file for the invoice.
49
+ // Invoice activated, proceed accordingly.
50
+ } catch(Exception $e) {
51
+ // Something went wrong or the invoice doesn't exist.
52
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
53
+ }
lib/Klarna/examples/activatePart.php ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Partially activate the invoice
35
+ */
36
+
37
+ // Here you specify the quantity of an article you wish to partially activate.
38
+ // artNo must be the same as the one you used in addArticle() when you made the
39
+ // addTransaction() call.
40
+ $k->addArtNo(
41
+ 1, // Quantity
42
+ 'MG200MMS' // Article number
43
+ );
44
+
45
+ // Here you enter the invoice number you got from addTransaction():
46
+ $invNo = '123456';
47
+
48
+ try {
49
+ $result = $k->activatePart(
50
+ $invNo, // Invoice number
51
+ KlarnaPClass::INVOICE // Or the PClass ID used to make the order.
52
+ );
53
+ $url = $result['url'];
54
+ echo "url: ${url}\n";
55
+ if (isset($result['invno'])) {
56
+ $invno = $result['invno'];
57
+ echo "invno: ${invno}\n";
58
+ }
59
+ // The url points to a PDF file for the invoice.
60
+ // The invno field is only present if the invoice was not entirely activated,
61
+ // and in that case it contains the new invoice number.
62
+
63
+ // Invoice activated, proceed accordingly.
64
+ } catch(Exception $e) {
65
+ // Something went wrong or the invoice doesn't exist.
66
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
67
+ }
lib/Klarna/examples/activateReservation.php ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+ // [[init]]
15
+ $k = new Klarna();
16
+ // [[init]]
17
+
18
+ // [[init:response]]
19
+ new Klarna();
20
+ // [[init:response]]
21
+
22
+ // [[config]]
23
+ $k->config(
24
+ 123456, // Merchant ID
25
+ 'sharedSecret', // Shared Secret
26
+ KlarnaCountry::SE, // Country
27
+ KlarnaLanguage::SV, // Language
28
+ KlarnaCurrency::SEK, // Currency
29
+ Klarna::BETA, // Server
30
+ 'json', // PClass Storage
31
+ '/srv/pclasses.json', // PClass Storage URI path
32
+ true, // SSL
33
+ true // Remote logging of response times of xmlrpc calls
34
+ );
35
+ // [[config]]
36
+
37
+ // [[config:response]]
38
+ null;
39
+ // [[config:response]]
40
+
41
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
42
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
43
+
44
+ /**
45
+ * 2. Add the article(s), shipping and/or handling fee.
46
+ */
47
+
48
+ // Here we add a normal product to our goods list.
49
+ $k->addArticle(
50
+ 4, // Quantity
51
+ "MG200MMS", // Article number
52
+ "Matrox G200 MMS", // Article name/title
53
+ 299.99, // Price
54
+ 25, // 25% VAT
55
+ 0, // Discount
56
+ KlarnaFlags::INC_VAT // Price is including VAT.
57
+ );
58
+
59
+ // Next we might want to add a shipment fee for the product
60
+ // [[addArticle]]
61
+ $k->addArticle(
62
+ 1,
63
+ "",
64
+ "Shipping fee",
65
+ 14.5,
66
+ 25,
67
+ 0,
68
+ // Price is including VAT and is shipment fee
69
+ KlarnaFlags::INC_VAT | KlarnaFlags::IS_SHIPMENT
70
+ );
71
+ // [[addArticle]]
72
+
73
+ // [[addArticle:response]]
74
+ null;
75
+ // [[addArticle:response]]
76
+
77
+ // Lastly, we want to use an invoice/handling fee as well
78
+ $k->addArticle(
79
+ 1,
80
+ "",
81
+ "Handling fee",
82
+ 11.5,
83
+ 25,
84
+ 0,
85
+ // Price is including VAT and is handling/invoice fee
86
+ KlarnaFlags::INC_VAT | KlarnaFlags::IS_HANDLING
87
+ );
88
+
89
+
90
+ /**
91
+ * 3. Create and set the address(es).
92
+ */
93
+
94
+ // Create the address object and specify the values.
95
+ // [[setAddress]]
96
+ $addr = new KlarnaAddr(
97
+ 'always_approved@klarna.com', // email
98
+ '', // Telno, only one phone number is needed.
99
+ '0762560000', // Cellno
100
+ 'Testperson-se', // Firstname
101
+ 'Approved', // Lastname
102
+ '', // No care of, C/O.
103
+ 'Stårgatan 1', // Street
104
+ '12345', // Zip Code
105
+ 'Ankeborg', // City
106
+ KlarnaCountry::SE, // Country
107
+ null, // HouseNo for German and Dutch customers.
108
+ null // House Extension. Dutch customers only.
109
+ );
110
+
111
+ $k->setAddress(KlarnaFlags::IS_BILLING, $addr);
112
+ // [[setAddress]]
113
+
114
+ // [[setAddress:response]]
115
+ null;
116
+ // [[setAddress:response]]
117
+
118
+ $k->setAddress(KlarnaFlags::IS_SHIPPING, $addr); // Shipping / delivery address
119
+
120
+ /**
121
+ * 4. Specify relevant information from your store. (OPTIONAL)
122
+ */
123
+
124
+ // Set store specific information so you can e.g. search and associate invoices
125
+ // with order numbers.
126
+ // [[setEstoreInfo]]
127
+ $k->setEstoreInfo(
128
+ 'order id #1',
129
+ 'order id #2'
130
+ );
131
+ // [[setEstoreInfo]]
132
+
133
+ // [[setEstoreInfo:response]]
134
+ null;
135
+ // [[setEstoreInfo:response]]
136
+
137
+ // If you don't have the order id available at this stage, you can later use the
138
+ // method updateOrderNo().
139
+
140
+ /**
141
+ * 5. Set additional information. (OPTIONAL)
142
+ */
143
+
144
+ /** Comment **/
145
+
146
+ // [[setComment]]
147
+ $k->setComment('A text string stored in the invoice commentary area.');
148
+ // [[setComment]]
149
+
150
+ // [[setComment:response]]
151
+ null;
152
+ // [[setComment:response]]
153
+
154
+ /** Extra info **/
155
+
156
+ // Normal shipment is defaulted, delays the start of invoice
157
+ // expiration/due-date.
158
+
159
+ // [[setShipmentInfo]]
160
+ $k->setShipmentInfo('key', 'value');
161
+ // [[setShipmentInfo]]
162
+
163
+ // [[setShipmentInfo:response]]
164
+ null;
165
+ // [[setShipmentInfo:response]]
166
+
167
+ // [[setTravelInfo]]
168
+ $k->setTravelInfo('key', 'value');
169
+ // [[setTravelInfo]]
170
+
171
+ // [[setTravelInfo:response]]
172
+ null;
173
+ // [[setTravelInfo:response]]
174
+
175
+ // [[setBankInfo]]
176
+ $k->setBankInfo('key', 'value');
177
+ // [[setBankInfo]]
178
+
179
+ // [[setBankInfo:response]]
180
+ null;
181
+ // [[setBankInfo:response]]
182
+
183
+ // [[setIncomeInfo]]
184
+ $k->setIncomeInfo('key', 'value');
185
+ // [[setIncomeInfo]]
186
+
187
+ // [[setIncomeInfo:response]]
188
+ null;
189
+ // [[setIncomeInfo:response]]
190
+
191
+ // [[setExtraInfo]]
192
+ $k->setExtraInfo('key', 'value');
193
+ // [[setExtraInfo]]
194
+
195
+ // [[setExtraInfo:response]]
196
+ null;
197
+ // [[setExtraInfo:response]]
198
+
199
+ /**
200
+ * 6. Invoke activateReservation and transmit the data.
201
+ */
202
+
203
+ /* Make sure the order status is ACCEPTED, before activation.
204
+ You can do this by using checkOrderStatus(). */
205
+
206
+ // Here you enter the reservation number you got from reserveAmount():
207
+ $rno = '123456';
208
+
209
+ try {
210
+ // Transmit all the specified data, from the steps above, to Klarna.
211
+ // [[activateReservation]]
212
+ $result = $k->activateReservation(
213
+ '4103219202', // PNO (Date of birth for DE and NL).
214
+ $rno, // Reservation to activate
215
+ null, // Gender.
216
+ '', // OCR number to use if you have reserved one.
217
+ KlarnaFlags::NO_FLAG, // Flags to affect behavior.
218
+ // -1, notes that this is an invoice purchase, for part payment purchase
219
+ // you will have a pclass object which you use getId() from.
220
+ KlarnaPClass::INVOICE
221
+ );
222
+ // [[activateReservation]]
223
+
224
+ // [[activateReservation:response]]
225
+ array(
226
+ "ok",
227
+ "1234567890"
228
+ );
229
+ // [[activateReservation:response]]
230
+
231
+ $risk = $result[0]; // ok or no_risk
232
+ $invno = $result[1];
233
+
234
+
235
+ echo "risk: {$risk}\ninvno: {$invno}\n";
236
+ // Reservation is activated, proceed accordingly.
237
+ } catch(Exception $e) {
238
+ // Something went wrong, print the message:
239
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
240
+ }
lib/Klarna/examples/addTransaction.php ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Add the article(s), shipping and/or handling fee.
35
+ */
36
+
37
+ // Here we add a normal product to our goods list.
38
+ $k->addArticle(
39
+ 4, // Quantity
40
+ "MG200MMS", // Article number
41
+ "Matrox G200 MMS", // Article name/title
42
+ 299.99, // Price
43
+ 25, // 25% VAT
44
+ 0, // Discount
45
+ KlarnaFlags::INC_VAT // Price is including VAT.
46
+ );
47
+
48
+ // Next we might want to add a shipment fee for the product
49
+ $k->addArticle(
50
+ 1,
51
+ "",
52
+ "Shipping fee",
53
+ 14.5,
54
+ 25,
55
+ 0,
56
+ // Price is including VAT and is shipment fee
57
+ KlarnaFlags::INC_VAT | KlarnaFlags::IS_SHIPMENT
58
+ );
59
+
60
+ // Lastly, we want to use an invoice/handling fee as well
61
+ $k->addArticle(
62
+ 1,
63
+ "",
64
+ "Handling fee",
65
+ 11.5,
66
+ 25,
67
+ 0,
68
+ // Price is including VAT and is handling/invoice fee
69
+ KlarnaFlags::INC_VAT | KlarnaFlags::IS_HANDLING
70
+ );
71
+
72
+ /**
73
+ * 3. Create and set the address(es).
74
+ */
75
+
76
+ // Create the address object and specify the values.
77
+ $addr = new KlarnaAddr(
78
+ 'always_approved@klarna.com', // email
79
+ '', // Telno, only one phone number is needed.
80
+ '0762560000', // Cellno
81
+ 'Testperson-se', // Firstname
82
+ 'Approved', // Lastname
83
+ '', // No care of, C/O.
84
+ 'St�rgatan 1', // Street
85
+ '12345', // Zip Code
86
+ 'Ankeborg', // City
87
+ KlarnaCountry::SE, // Country
88
+ null, // HouseNo for German and Dutch customers.
89
+ null // House Extension. Dutch customers only.
90
+ );
91
+
92
+ // Next we tell the Klarna instance to use the address in the next order.
93
+ $k->setAddress(KlarnaFlags::IS_BILLING, $addr); // Billing / invoice address
94
+ $k->setAddress(KlarnaFlags::IS_SHIPPING, $addr); // Shipping / delivery address
95
+
96
+ /**
97
+ * 4. Specify relevant information from your store. (OPTIONAL)
98
+ */
99
+
100
+ // Set store specific information so you can e.g. search and associate invoices
101
+ // with order numbers.
102
+ $k->setEstoreInfo(
103
+ '175012', // Order ID 1
104
+ '1999110234', // Order ID 2
105
+ '' // Optional username, email or identifier
106
+ );
107
+
108
+ // If you don't have the order id available at this stage, you can later use the
109
+ // method updateOrderNo().
110
+
111
+ /**
112
+ * 5. Set additional information. (OPTIONAL)
113
+ */
114
+
115
+ /** Comment **/
116
+
117
+ $k->setComment('A text string stored in the invoice commentary area.');
118
+
119
+ /** Shipment type **/
120
+
121
+ // Normal shipment is defaulted, delays the start of invoice expiration/due-date.
122
+ $k->setShipmentInfo('delay_adjust', KlarnaFlags::EXPRESS_SHIPMENT);
123
+
124
+
125
+ /**
126
+ * 6. Invoke addTransaction and transmit the data.
127
+ */
128
+
129
+ try {
130
+ // Transmit all the specified data, from the steps above, to Klarna.
131
+ $result = $k->addTransaction(
132
+ '4103219202', // PNO (Date of birth for DE and NL).
133
+ null, // Gender.
134
+ KlarnaFlags::NO_FLAG, // Flags to affect behavior.
135
+ // -1, notes that this is an invoice purchase, for part payment purchase
136
+ // you will have a pclass object on which you use getId().
137
+ KlarnaPClass::INVOICE
138
+ );
139
+
140
+ // Check the order status
141
+ if ($result[1] == KlarnaFlags::PENDING) {
142
+ /* The order is under manual review and will be accepted or denied at a
143
+ later stage. Use cronjob with checkOrderStatus() or visit Klarna
144
+ Online to check to see if the status has changed. You should still
145
+ show it to the customer as it was accepted, to avoid further attempts
146
+ to fraud.
147
+ */
148
+ }
149
+
150
+ // Here we get the invoice number
151
+ $invno = $result[0];
152
+
153
+ // Order is complete, store it in a database.
154
+ echo "Status: {$result[1]}\nInvno: {$result[0]}\n";
155
+ } catch(Exception $e) {
156
+ // The purchase was denied or something went wrong, print the message:
157
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
158
+ echo $e->getTraceAsString();
159
+ }
lib/Klarna/examples/calc_monthly_cost.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Calculate the monthly cost for the product page.
35
+ */
36
+
37
+ $pclass = $k->getCheapestPClass($sum, $flag);
38
+
39
+ // Did we get a PClass? (it is false if we didn't)
40
+ if ($pclass) {
41
+ // Here we reuse the same values as above:
42
+ // [[calc_monthly_cost]]
43
+ $value = KlarnaCalc::calc_monthly_cost(
44
+ 149.99,
45
+ $pclass,
46
+ KlarnaFlags::PRODUCT_PAGE // or KlarnaFlags::CHECKOUT_PAGE
47
+ );
48
+ // [[calc_monthly_cost]]
49
+
50
+ // [[calc_monthly_cost_response]]
51
+ 45.50;
52
+ // [[calc_monthly_cost_response]]
53
+
54
+ echo "Value: {$value}\n";
55
+ /*
56
+ $value is now a rounded monthly cost amount to be displayed to the
57
+ customer.
58
+ */
59
+ }
lib/Klarna/examples/cancelReservation.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Cancelling a reservation
35
+ */
36
+
37
+ // Here you enter the reservation number you got from reserveAmount():
38
+ $rno = '123456';
39
+
40
+ try {
41
+ // [[cancelReservation]]
42
+ $result = $k->cancelReservation($rno);
43
+ // [[cancelReservation]]
44
+
45
+
46
+ // [[response]]
47
+ true;
48
+ // [[response]]
49
+
50
+ echo "Result: {$result}\n";
51
+ // Reservation cancelled, proceed accordingly.
52
+ } catch(Exception $e) {
53
+ // Something went wrong or the reservation doesn't exist.
54
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
55
+ }
lib/Klarna/examples/changeReservation.php ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Change the reservation.
35
+ */
36
+
37
+ // Here you enter the reservation number you got from reserveAmount():
38
+ $rno = '123456';
39
+
40
+ try {
41
+ // [[changeReservation]]
42
+ $result = $k->changeReservation(
43
+ $rno, // Reservation number
44
+ 49.99, // Amount
45
+ KlarnaFlags::NEW_AMOUNT // Flag deciding if the amount is the new amount
46
+ // to reserve, or if it is to be added to the
47
+ // existing amount. (KlarnaFlags::ADD_AMOUNT)
48
+ );
49
+ // [[changeReservation]]
50
+
51
+ // [[changeReservation:response]]
52
+ true;
53
+ // [[changeReservation:response]]
54
+
55
+ // Reservation changed, proceed accordingly.
56
+ echo "Result: {$result}\n";
57
+ } catch (Exception $e) {
58
+ // Something went wrong or the reservation doesn't exist.
59
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
60
+ }
lib/Klarna/examples/checkOrderStatus.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Check the status on your order (invoice or reservation)
35
+ */
36
+
37
+ $id = '123456'; // Your reservation or invoice number.
38
+
39
+ try {
40
+ // [[checkOrderStatus]]
41
+ $result = $k->checkOrderStatus(
42
+ $id, // Reservation, invoice number or order id.
43
+ 0 // Flag specifying number type. 0 = rno or invno. 1 = order id.
44
+ );
45
+ // [[checkOrderStatus]]
46
+
47
+ // [[checkOrderStatus:response]]
48
+ "1";
49
+ // [[checkOrderStatus:response]]
50
+
51
+ if ($result == KlarnaFlags::ACCEPTED) {
52
+ // Status changed, you can now activate your invoice/reservation.
53
+ echo "Accepted\n";
54
+ } else if ($result == KlarnaFlags::DENIED) {
55
+ echo "Denied\n";
56
+ // Status changed, it is now denied, proceed accordingly.
57
+ } else {
58
+ echo "Pending\n";
59
+ //Order is still pending, try again later.
60
+ }
61
+ } catch (Exception $e) {
62
+ //Something went wrong, print the message:
63
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
64
+ }
lib/Klarna/examples/creditInvoice.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Completely refund a invoice.
35
+ */
36
+
37
+ // Here you enter the invoice number:
38
+ $invNo = '123456';
39
+
40
+ try {
41
+ // [[creditInvoice]]
42
+ $result = $k->creditInvoice(
43
+ $invNo, // Invoice Number
44
+ '' // Credit number. (Optional).
45
+ );
46
+ // [[creditInvoice]]
47
+
48
+ // [[creditInvoice:response]]
49
+ "123456";
50
+ // [[creditInvoice:response]]
51
+
52
+ echo "Result: {$result}\n";
53
+
54
+ /* Invoice fully refunded, proceed accordingly.
55
+ $result contains the invoice number of the refunded invoice. */
56
+ } catch(Exception $e) {
57
+ // Something went wrong, print the message:
58
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
59
+ }
lib/Klarna/examples/creditPart.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Partially refund a invoice.
35
+ */
36
+
37
+ // Here you enter the invoice number:
38
+ $invNo = '123456';
39
+
40
+ // Specify for which article(s) you want to refund.
41
+ $k->addArtNo(
42
+ 1, // Quantity
43
+ 'MG200MMS' // Article Number. Must be the same as the one you used
44
+ ); // in addArticle() when you made the addTransaction() call.
45
+
46
+ // Adding a return fee is possible. If you are interested in this
47
+ // functionality, make sure to always be in contact with Klarna before
48
+ // integrating return fees.
49
+
50
+ // $k->addArticle(
51
+ // 1,
52
+ // "",
53
+ // "Restocking fee",
54
+ // 11.5,
55
+ // 25,
56
+ // 0,
57
+ // KlarnaFlags::NO_FLAG
58
+ // );
59
+
60
+ try {
61
+ // [[creditPart]]
62
+ $result = $k->creditPart(
63
+ $invNo, // Invoice Number
64
+ '' // Credit Number. (Optional).
65
+ );
66
+ // [[creditPart]]
67
+
68
+ // [[creditPart:response]]
69
+ "123456";
70
+ // [[creditPart:response]]
71
+
72
+ echo "Result: {$result}\n";
73
+
74
+ /* Invoice partially refunded, proceed accordingly.
75
+ $result contains the invoice number of the refunded invoice.
76
+ */
77
+ } catch(Exception $e) {
78
+ // Something went wrong, print the message:
79
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
80
+ }
lib/Klarna/examples/deleteInvoice.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Remove the invoice
35
+ */
36
+
37
+ //Here you enter the invoice number you got from addTransaction():
38
+ $invNo = '123456';
39
+
40
+ try {
41
+ $result = $k->deleteInvoice($invNo);
42
+
43
+ echo "Result: {$result}\n";
44
+
45
+ //Invoice removed, proceed accordingly.
46
+ } catch(Exception $e) {
47
+ //Something went wrong or the invoice doesn't exist.
48
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
49
+ }
lib/Klarna/examples/emailInvoice.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Send an (activated) invoice to the customer via email.
35
+ */
36
+
37
+ // Here you enter the invoice number:
38
+ $invNo = '123456';
39
+
40
+ try {
41
+ // [[emailInvoice]]
42
+ $result = $k->emailInvoice($invNo);
43
+ // [[emailInvoice]]
44
+
45
+ // [[emailInvoice:response]]
46
+ "123456";
47
+ // [[emailInvoice:response]]
48
+
49
+ echo "Result: {$result}\n";
50
+ /* Invoice sent to customer via email, proceed accordingly.
51
+ $result contains the invoice number of the emailed invoice.
52
+ */
53
+ } catch(Exception $e) {
54
+ // Something went wrong, print the message:
55
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
56
+ }
lib/Klarna/examples/fetchPClasses.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Retrieve the PClasses from Klarna.
35
+ */
36
+
37
+ try {
38
+ // [[fetchPClasses]]
39
+ $k->fetchPClasses();
40
+ // [[fetchPClasses]]
41
+
42
+ // [[fetchPClassesResult]]
43
+ null;
44
+ // [[fetchPClassesResult]]
45
+
46
+ /* PClasses successfully fetched, now you can use getPClasses() to load them
47
+ locally or getPClass to load a specific PClass locally.
48
+ */
49
+ echo "Fetched " . count($k->getAllPClasses()) . " pclasses.\n";
50
+
51
+ } catch(Exception $e) {
52
+ // Something went wrong, print the message:
53
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
54
+ }
lib/Klarna/examples/getAddresses.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Get the address(es) from Klarna. (Sweden only!)
35
+ */
36
+ $k->setCountry('se');
37
+ try {
38
+ //Attempt to get the address(es) associated with the SSN/PNO.
39
+ // [[getAddresses]]
40
+ $addrs = $k->getAddresses('410321-9202');
41
+ // [[getAddresses]]
42
+
43
+ // [[getAddresses:response]]
44
+ array(
45
+ new KlarnaAddr(
46
+ '',
47
+ '',
48
+ '',
49
+ 'Testperson-se',
50
+ 'Approved',
51
+ '',
52
+ 'Stårgatan 1',
53
+ '12345',
54
+ 'Ankeborg',
55
+ KlarnaCountry::SE,
56
+ null,
57
+ null
58
+ )
59
+ );
60
+ // [[getAddresses:response]]
61
+ /* If there exists several addresses you would want to output a list in
62
+ which the customer could choose the address which suits him/her.
63
+ */
64
+
65
+ // Print them if available:
66
+ foreach ($addrs as $key => $addr) {
67
+ echo "<table>\n";
68
+
69
+ // This only works if the right getAddresses type is used.
70
+ if ($addr->isCompany) {
71
+ echo "\t<tr><td>Company</td><td>{$addr->getCompanyName()}</td></tr>\n";
72
+ } else {
73
+ echo "\t<tr><td>First name</td><td>{$addr->getFirstName()}</td></tr>\n";
74
+ echo "\t<tr><td>Last name</td><td>{$addr->getLastName()}</td></tr>\n";
75
+ }
76
+
77
+ echo "\t<tr><td>Street</td><td>{$addr->getStreet()}</td></tr>\n";
78
+ echo "\t<tr><td>Zip code</td><td>{$addr->getZipCode()}</td></tr>\n";
79
+ echo "\t<tr><td>City</td><td>{$addr->getCity()}</td></tr>\n";
80
+ echo "\t<tr><td>Country</td><td>{$addr->getCountryCode()}</td></tr>\n";
81
+ echo "</table>\n";
82
+ }
83
+ } catch(Exception $e) {
84
+ //Something went wrong
85
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
86
+ }
lib/Klarna/examples/getPClasses.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Load the PClasses from the local file or MySQL table.
35
+ */
36
+
37
+ /*
38
+ PClasses are loaded from the local storage, as defined by "pcStorage"
39
+ and "pcURI".
40
+ */
41
+
42
+ // Load all PClasses available.
43
+ $pclasses = $k->getPClasses();
44
+ // Here we can define a specific type of PClass we want to load
45
+ // (KlarnaPClass::CAMPAIGN, for example), or leave it empty to get all that
46
+ // are usable.
47
+
48
+ // Next we might want to display the description in a drop down menu:
49
+ echo "<select name='pclass'>\n";
50
+ foreach ($pclasses as $pclass) {
51
+ echo "\t<option value='{$pclass->getId()}'>{$pclass->getDescription()}</option>\n";
52
+ }
53
+ echo "</select>\n";
54
+
55
+ // When the customer has confirmed the purchase and chosen a pclass, you can
56
+ // easily grab just that one by doing:
57
+ $pclassId = $pclasses[0]->getId(); // Let's say the customer picked the first one
58
+ $pclass = $k->getPClass($pclassId);
59
+
60
+ var_dump($pclass);
61
+
62
+ // Next we can use $pclassId in the addTransaction call or in the reserveAmount
63
+ // call.
lib/Klarna/examples/invoiceAddress.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Get the address associated with the purchase/invoice.
35
+ */
36
+
37
+ // Here you enter the invoice number:
38
+ $invNo = '123456';
39
+
40
+ try {
41
+ // Attempt to get the address
42
+ // [[invoiceAddress]]
43
+ $addr = $k->invoiceAddress($invNo);
44
+ // [[invoiceAddress]]
45
+
46
+ // [[invoiceAddress:response]]
47
+ new KlarnaAddr(
48
+ '',
49
+ '',
50
+ '',
51
+ 'Testperson-se',
52
+ 'Approved',
53
+ '',
54
+ 'Stårgatan 1',
55
+ '12345',
56
+ 'Ankeborg',
57
+ KlarnaCountry::SE,
58
+ null,
59
+ null
60
+ );
61
+ // [[invoiceAddress:response]]
62
+
63
+ // Display the retrieved address:
64
+ echo "<table>\n";
65
+ if ($addr->isCompany) {
66
+ echo "\t<tr><td>Company</td><td>{$addr->getCompanyName()}</td></tr>\n";
67
+ } else {
68
+ echo "\t<tr><td>First name</td><td>{$addr->getFirstName()}</td></tr>\n";
69
+ echo "\t<tr><td>Last name</td><td>{$addr->getLastName()}</td></tr>\n";
70
+ }
71
+
72
+ echo "\t<tr><td>Street</td><td>{$addr->getStreet()}</td></tr>\n";
73
+ echo "\t<tr><td>Zip code</td><td>{$addr->getZipCode()}</td></tr>\n";
74
+ echo "\t<tr><td>City</td><td>{$addr->getCity()}</td></tr>\n";
75
+ echo "\t<tr><td>Country</td><td>{$addr->getCountryCode()}</td></tr>\n";
76
+ echo "</table>\n";
77
+ } catch(Exception $e) {
78
+ //Something went wrong
79
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
80
+ }
lib/Klarna/examples/invoiceAmount.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Retrieve the total amount of a invoice.
35
+ */
36
+
37
+ // Here you enter the invoice number:
38
+ $invNo = '123456';
39
+
40
+ try {
41
+ // [[InvoiceAmount]]
42
+ $result = $k->invoiceAmount($invNo);
43
+ // [[InvoiceAmount]]
44
+
45
+ // [[InvoiceAmount:response]]
46
+ 123.45;
47
+ // [[InvoiceAmount:response]]
48
+
49
+ echo "Result: {$result}\n";
50
+ /* Invoice amount successfully retrieved, proceed accordingly.
51
+ $result contains the total sum of the invoice.
52
+ */
53
+ } catch(Exception $e) {
54
+ //Something went wrong, print the message:
55
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
56
+ }
lib/Klarna/examples/invoicePartAmount.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Retrieve the amount for specific article(s) from an invoice.
35
+ */
36
+
37
+ // Here you enter the invoice number:
38
+ $invNo = '123456';
39
+
40
+ // Specify for which article(s) you want the amount.
41
+ // artNo must be the same as the one you used in addArticle() when you made the
42
+ // addTransaction() call.
43
+ // [[addArtNo]]
44
+ $k->addArtNo(
45
+ 1, // Quantity
46
+ 'MG200MMS' // Article number
47
+ );
48
+ // [[addArtNo]]
49
+
50
+ // [[addArtNo:response]]
51
+ null;
52
+ // [[addArtNo:response]]
53
+
54
+ try {
55
+ // [[invoicePartAmount]]
56
+ $result = $k->invoicePartAmount($invNo);
57
+ // [[invoicePartAmount]]
58
+
59
+ // [[invoicePartAmount:response]]
60
+ 45.50;
61
+ // [[invoicePartAmount:response]]
62
+
63
+ echo "Result: {$result}\n";
64
+ /* Partial invoice amount successfully retrieved, proceed accordingly.
65
+ $result contains the sum of specified article(s).
66
+ */
67
+ } catch(Exception $e) {
68
+ // Something went wrong, print the message:
69
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
70
+ }
lib/Klarna/examples/reserveAmount.php ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Add the article(s), shipping and/or handling fee.
35
+ */
36
+
37
+ // Here we add a normal product to our goods list.
38
+ $k->addArticle(
39
+ 4, // Quantity
40
+ "MG200MMS", // Article number
41
+ "Matrox G200 MMS", // Article name/title
42
+ 299.99, // Price
43
+ 25, // 25% VAT
44
+ 0, // Discount
45
+ KlarnaFlags::INC_VAT // Price is including VAT.
46
+ );
47
+
48
+ // Next we might want to add a shipment fee for the product
49
+ $k->addArticle(
50
+ 1,
51
+ "",
52
+ "Shipping fee",
53
+ 14.5,
54
+ 25,
55
+ 0,
56
+ // Price is including VAT and is shipment fee
57
+ KlarnaFlags::INC_VAT | KlarnaFlags::IS_SHIPMENT
58
+ );
59
+
60
+ // Lastly, we want to use an invoice/handling fee as well
61
+ $k->addArticle(
62
+ 1,
63
+ "",
64
+ "Handling fee",
65
+ 11.5,
66
+ 25,
67
+ 0,
68
+ // Price is including VAT and is handling/invoice fee
69
+ KlarnaFlags::INC_VAT | KlarnaFlags::IS_HANDLING
70
+ );
71
+
72
+ /**
73
+ * 3. Create and set the address(es).
74
+ */
75
+
76
+ // Create the address object and specify the values.
77
+ $addr = new KlarnaAddr(
78
+ 'always_approved@klarna.com', // email
79
+ '', // Telno, only one phone number is needed.
80
+ '0762560000', // Cellno
81
+ 'Testperson-se', // Firstname
82
+ 'Approved', // Lastname
83
+ '', // No care of, C/O.
84
+ 'St?rgatan 1', // Street
85
+ '12345', // Zip Code
86
+ 'Ankeborg', // City
87
+ KlarnaCountry::SE, // Country
88
+ null, // HouseNo for German and Dutch customers.
89
+ null // House Extension. Dutch customers only.
90
+ );
91
+
92
+ // Next we tell the Klarna instance to use the address in the next order.
93
+ $k->setAddress(KlarnaFlags::IS_BILLING, $addr); // Billing / invoice address
94
+ $k->setAddress(KlarnaFlags::IS_SHIPPING, $addr); // Shipping / delivery address
95
+
96
+ /**
97
+ * 4. Specify relevant information from your store. (OPTIONAL)
98
+ */
99
+
100
+ // Set store specific information so you can e.g. search and associate invoices
101
+ // with order numbers.
102
+ $k->setEstoreInfo(
103
+ '175012', // Order ID 1
104
+ '1999110234', // Order ID 2
105
+ '' // Optional username, email or identifier
106
+ );
107
+
108
+ // If you don't have the order id available at this stage, you can later use the
109
+ // method updateOrderNo().
110
+
111
+ /**
112
+ * 5. Set additional information. (OPTIONAL)
113
+ */
114
+
115
+ /** Comment **/
116
+
117
+ $k->setComment('A text string stored in the invoice commentary area.');
118
+
119
+ /** Shipment type **/
120
+
121
+ // Normal shipment is defaulted, delays the start of invoice expiration/due-date.
122
+ $k->setShipmentInfo('delay_adjust', KlarnaFlags::EXPRESS_SHIPMENT);
123
+
124
+ /**
125
+ * 6. Invoke reserveAmount and transmit the data.
126
+ */
127
+
128
+ try {
129
+ // Transmit all the specified data, from the steps above, to Klarna.
130
+ // [[reserveAmount]]
131
+ $result = $k->reserveAmount(
132
+ '4103219202', // PNO (Date of birth for DE and NL).
133
+ null, // Gender.
134
+ // Amount. -1 specifies that calculation should calculate the amount
135
+ // using the goods list
136
+ -1,
137
+ KlarnaFlags::NO_FLAG, // Flags to affect behavior.
138
+ // -1 notes that this is an invoice purchase, for part payment purchase
139
+ // you will have a pclass object on which you use getId().
140
+ KlarnaPClass::INVOICE
141
+ );
142
+ // [[reserveAmount]]
143
+
144
+ // [[reserveAmount:response]]
145
+ array(
146
+ "123456",
147
+ 1
148
+ );
149
+ // [[reserveAmount:response]]
150
+
151
+
152
+ //Check the order status
153
+ if ($result[1] == KlarnaFlags::PENDING) {
154
+ /* The order is under manual review and will be accepted or denied at a
155
+ later stage. Use cronjob with checkOrderStatus() or visit Klarna
156
+ Online to check to see if the status has changed. You should still
157
+ show it to the customer as it was accepted, to avoid further attempts
158
+ to fraud.
159
+ */
160
+ }
161
+
162
+ // Here we get the reservation number
163
+ $rno = $result[0];
164
+
165
+ echo "status: {$result[1]}\nrno: {$result[0]}\n";
166
+ // Order is complete, store it in a database.
167
+ } catch(Exception $e) {
168
+ // The purchase was denied or something went wrong, print the message:
169
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
170
+ }
lib/Klarna/examples/reserveOCR.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Reserve the OCR numbers.
35
+ */
36
+
37
+ try {
38
+ // Reserve the OCR number(s):
39
+ // [[reserveOCR]]
40
+ $result = $k->reserveOCR(
41
+ 1 // Number of OCR numbers you wish to reserve.
42
+ );
43
+ // [[reserveOCR]]
44
+
45
+ // [[reserveOCR:response]]
46
+ array(
47
+ "41789461815156"
48
+ );
49
+ // [[reserveOCR:response]]
50
+
51
+ echo "Result: \n";
52
+ foreach ($result as $r) {
53
+ echo "{$r}\n";
54
+ }
55
+ /* $result now contains an array of OCR numbers, proceed accordingly. */
56
+ } catch(Exception $e) {
57
+ // Something went wrong, print the message:
58
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
59
+ }
lib/Klarna/examples/returnAmount.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Give a discount on the invoice.
35
+ */
36
+
37
+ // Here you enter the invoice number:
38
+ $invNo = '123456';
39
+
40
+ try {
41
+ // [[returnAmount]]
42
+ $result = $k->returnAmount(
43
+ $invNo, // Invoice number
44
+ 19.99, // Amount given as a discount.
45
+ 25, // 25% VAT
46
+ KlarnaFlags::INC_VAT, // Amount including VAT.
47
+ "Family discount" // Description
48
+ );
49
+ // [[returnAmount]]
50
+
51
+ // [[returnAmountResult]]
52
+ "123456";
53
+ // [[returnAmountResult]]
54
+
55
+ echo "Result: {$result}\n";
56
+
57
+ /* Discount given, proceed accordingly.
58
+ $result contains the invoice number of the discounted invoice.
59
+ */
60
+ } catch(Exception $e) {
61
+ // Something went wrong, print the message:
62
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
63
+ }
lib/Klarna/examples/sendInvoice.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Send an (activated) invoice to the customer.
35
+ * (Postal service / normal mail)
36
+ */
37
+
38
+ // Here you enter the invoice number:
39
+ $invNo = '123456';
40
+
41
+ try {
42
+ // [[sendInvoice]]
43
+ $result = $k->sendInvoice("123456");
44
+ // [[sendInvoice]]
45
+
46
+ // [[sendInvoice:response]]
47
+ "123456";
48
+ // [[sendInvoice:response]]
49
+
50
+ echo "Result: {$result}\n";
51
+
52
+ /* Invoice sent to customer, proceed accordingly.
53
+ $result contains the invoice number of the sent invoice.
54
+ */
55
+ } catch(Exception $e) {
56
+ // Something went wrong, print the message:
57
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
58
+ }
lib/Klarna/examples/splitReservation.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Split the reservation.
35
+ */
36
+
37
+ // Here you enter the reservation number you got from reserveAmount():
38
+ $rno = '123456';
39
+
40
+ try {
41
+ // Transmit all the specified data, from the steps above, to Klarna.
42
+ // [[splitReservation]]
43
+ $result = $k->splitReservation(
44
+ $rno, // Reservation number
45
+ 99.5, // Amount to be subtracted from the reservation.
46
+ KlarnaFlags::NO_FLAG // No specific behaviour.
47
+ );
48
+ // [[splitReservation]]
49
+
50
+ // [[splitReservation:response]]
51
+ array(
52
+ '12345',
53
+ 1
54
+ );
55
+ // [[splitReservation:response]]
56
+
57
+
58
+ // Split successful, proceed accordingly.
59
+ $newRno = $result[0]; // New reservation number
60
+ $status = $result[1]; // Status of the new reservation (1 or 2)
61
+
62
+ echo "New RNO: {$result[0]}\nStatus: {$result[1]}\n";
63
+ } catch(Exception $e) {
64
+ // Something went wrong, print the message:
65
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
66
+ }
lib/Klarna/examples/update.php ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Add the article(s), shipping and/or handling fee. (OPTIONAL)
35
+ */
36
+
37
+ // Here we add a normal product to our goods list.
38
+ $k->addArticle(
39
+ 4, // Quantity
40
+ "MG200MMS", // Article number
41
+ "Matrox G200 MMS", // Article name/title
42
+ 299.99, // Price
43
+ 25, // 25% VAT
44
+ 0, // Discount
45
+ KlarnaFlags::INC_VAT // Price is including VAT.
46
+ );
47
+
48
+ // Next we might want to add a shipment fee for the product
49
+ $k->addArticle(
50
+ 1,
51
+ "",
52
+ "Shipping fee",
53
+ 14.5,
54
+ 25,
55
+ 0,
56
+ // Price is including VAT and is shipment fee
57
+ KlarnaFlags::INC_VAT | KlarnaFlags::IS_SHIPMENT
58
+ );
59
+
60
+ // Lastly, we want to use an invoice/handling fee as well
61
+ $k->addArticle(
62
+ 1,
63
+ "",
64
+ "Handling fee",
65
+ 11.5,
66
+ 25,
67
+ 0,
68
+ // Price is including VAT and is handling/invoice fee
69
+ KlarnaFlags::INC_VAT | KlarnaFlags::IS_HANDLING
70
+ );
71
+
72
+ /**
73
+ * 3. Create and set the address(es). (OPTIONAL)
74
+ */
75
+
76
+ // Create the address object and specify the values.
77
+ $addr = new KlarnaAddr(
78
+ 'always_approved@klarna.com', // email
79
+ '', // Telno, only one phone number is needed.
80
+ '0762560000', // Cellno
81
+ 'Testperson-se', // Firstname
82
+ 'Approved', // Lastname
83
+ '', // No care of, C/O.
84
+ 'St�rgatan 1', // Street
85
+ '12345', // Zip Code
86
+ 'Ankeborg', // City
87
+ KlarnaCountry::SE, // Country
88
+ null, // HouseNo for German and Dutch customers.
89
+ null // House Extension. Dutch customers only.
90
+ );
91
+
92
+ // Next we tell the Klarna instance to use the address in the next order.
93
+ $k->setAddress(KlarnaFlags::IS_BILLING, $addr); // Billing / invoice address
94
+ $k->setAddress(KlarnaFlags::IS_SHIPPING, $addr); // Shipping / delivery address
95
+
96
+ /**
97
+ * 4. Specify relevant information from your store. (OPTIONAL)
98
+ */
99
+
100
+ // Set store specific information so you can e.g. search and associate invoices
101
+ // with order numbers.
102
+ $k->setEstoreInfo(
103
+ '175012', // Order ID 1
104
+ '1999110234', // Order ID 2
105
+ '' // Optional username, email or identifier
106
+ );
107
+
108
+ /**
109
+ * 5. Make the call to Klarna
110
+ */
111
+
112
+ // Reservation number
113
+ $rno = '123456';
114
+
115
+ try {
116
+ // [[update]]
117
+ $result = $k->update($rno);
118
+ // [[update]]
119
+
120
+ // [[update:response]]
121
+ true;
122
+ // [[update:response]]
123
+
124
+ if ($result) {
125
+ echo "Update successful\n";
126
+ }
127
+ } catch(KlarnaException $e) {
128
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
129
+ }
lib/Klarna/examples/updateChargeAmount.php ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Update charge amount (shipping fee or handling fee).
35
+ */
36
+
37
+ // Here you enter the invoice number you got from addTransaction():
38
+ $invNo = '123456';
39
+
40
+ try {
41
+ // [[updateChargeAmount]]
42
+ $result = $k->updateChargeAmount(
43
+ $invNo, // Invoice number
44
+ KlarnaFlags::IS_SHIPMENT, // IS_SHIPMENT or IS_HANDLING
45
+ 16.7 // Set the shipping fee to 16.7
46
+ );
47
+ // [[updateChargeAmount]]
48
+
49
+ // [[updateChargeAmount:response]]
50
+ "123456";
51
+ // [[updateChargeAmount:response]]
52
+
53
+ echo "Result: {$result}\n";
54
+ /* Charge type updated successfully, proceed accordingly.
55
+ $result contains the same invoice number.
56
+ */
57
+ } catch(Exception $e) {
58
+ // Something went wrong, print the message:
59
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
60
+ }
lib/Klarna/examples/updateGoodsQty.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Update goods quantity.
35
+ */
36
+
37
+ // Here you enter the invoice number you got from addTransaction():
38
+ $invNo = '123456';
39
+
40
+ try {
41
+ // [[updateGoodsQty]]
42
+ $result = $k->updateGoodsQty(
43
+ $invNo, // Invoice number
44
+ 'MG200MMS', // ArtNo must be the same as the one you used in
45
+ // addArticle() when you made the addTransaction() call.
46
+ 2 // New Quantity
47
+ );
48
+ // [[updateGoodsQty]]
49
+
50
+ // [[updateGoodsQty:response]]
51
+ "123456";
52
+ // [[updateGoodsQty:response]]
53
+
54
+ echo "Result: {$result}\n";
55
+ /* Article quantity updated successfully, proceed accordingly.
56
+ $result contains the same invoice number.
57
+ */
58
+ } catch(Exception $e) {
59
+ // Something went wrong, print the message:
60
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
61
+ }
lib/Klarna/examples/updateOrderNo.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once dirname(__DIR__) . '/Klarna.php';
4
+
5
+ // Dependencies from http://phpxmlrpc.sourceforge.net/
6
+ require_once dirname(dirname(__FILE__)) .
7
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
8
+ require_once dirname(dirname(__FILE__)) .
9
+ '/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc';
10
+
11
+ /**
12
+ * 1. Initialize and setup the Klarna instance.
13
+ */
14
+
15
+ $k = new Klarna();
16
+
17
+ $k->config(
18
+ 123456, // Merchant ID
19
+ 'sharedSecret', // Shared Secret
20
+ KlarnaCountry::SE, // Country
21
+ KlarnaLanguage::SV, // Language
22
+ KlarnaCurrency::SEK, // Currency
23
+ Klarna::BETA, // Server
24
+ 'json', // PClass Storage
25
+ '/srv/pclasses.json', // PClass Storage URI path
26
+ true, // SSL
27
+ true // Remote logging of response times of xmlrpc calls
28
+ );
29
+
30
+ // OR you can set the config to loads from a file, for example /srv/klarna.json:
31
+ // $k->setConfig(new KlarnaConfig('/srv/klarna.json'));
32
+
33
+ /**
34
+ * 2. Update order number / id.
35
+ */
36
+
37
+ // Here you enter the invoice number you got from addTransaction():
38
+ $invNo = '123456';
39
+
40
+ try {
41
+ // [[updateOrderNo]]
42
+ $result = $k->updateOrderNo(
43
+ $invNo, // Invoice Number
44
+ '1234' // The order id/number you wish to associated.
45
+ );
46
+ // [[updateOrderNo]]
47
+
48
+ // [[updateOrderNo:response]]
49
+ "123456";
50
+ // [[updateOrderNo:response]]
51
+
52
+ echo "Result: {$result}\n";
53
+ /* Order id is now assicated with the invoice, proceed accordingly.
54
+ $result contains the same invoice number.
55
+ */
56
+ } catch(Exception $e) {
57
+ // Something went wrong, print the message:
58
+ echo "{$e->getMessage()} (#{$e->getCode()})\n";
59
+ }
lib/Klarna/klarnaaddr.php ADDED
@@ -0,0 +1,609 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * KlarnaAddr
4
+ *
5
+ * PHP Version 5.3
6
+ *
7
+ * @category Payment
8
+ * @package KlarnaAPI
9
+ * @author MS Dev <ms.modules@klarna.com>
10
+ * @copyright 2012 Klarna AB (http://klarna.com)
11
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
12
+ * @link http://integration.klarna.com/
13
+ */
14
+
15
+ /**
16
+ * KlarnaAddr is an object of convenience, to parse and create addresses.
17
+ *
18
+ * @category Payment
19
+ * @package KlarnaAPI
20
+ * @author MS Dev <ms.modules@klarna.com>
21
+ * @copyright 2012 Klarna AB (http://klarna.com)
22
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
23
+ * @link http://integration.klarna.com/
24
+ */
25
+ class KlarnaAddr
26
+ {
27
+
28
+ /**
29
+ * Email address.
30
+ *
31
+ * @var string
32
+ */
33
+ protected $email;
34
+
35
+ /**
36
+ * Phone number.
37
+ *
38
+ * @var string
39
+ */
40
+ protected $telno;
41
+
42
+ /**
43
+ * Cellphone number.
44
+ *
45
+ * @var string
46
+ */
47
+ protected $cellno;
48
+
49
+ /**
50
+ * First name.
51
+ *
52
+ * @var string
53
+ */
54
+ protected $fname;
55
+
56
+ /**
57
+ * Last name.
58
+ *
59
+ * @var string
60
+ */
61
+ protected $lname;
62
+
63
+ /**
64
+ * Company name.
65
+ *
66
+ * @var string
67
+ */
68
+ protected $company;
69
+
70
+ /**
71
+ * Care of, C/O.
72
+ *
73
+ * @var string
74
+ */
75
+ protected $careof;
76
+
77
+ /**
78
+ * Street address.
79
+ *
80
+ * @var string
81
+ */
82
+ protected $street;
83
+
84
+ /**
85
+ * Zip code.
86
+ *
87
+ * @var string
88
+ */
89
+ protected $zip;
90
+
91
+ /**
92
+ * City.
93
+ *
94
+ * @var string
95
+ */
96
+ protected $city;
97
+
98
+ /**
99
+ * KlarnaCountry constant
100
+ *
101
+ * @var int
102
+ */
103
+ protected $country;
104
+
105
+ /**
106
+ * House number.
107
+ * Only for NL and DE!
108
+ *
109
+ * @var string
110
+ */
111
+ protected $houseNo;
112
+
113
+ /**
114
+ * House extension.
115
+ * Only for NL!
116
+ *
117
+ * @var string
118
+ */
119
+ protected $houseExt;
120
+
121
+ /**
122
+ * When using {@link Klarna::getAddresses()} this might be guessed
123
+ * depending on type used.
124
+ *
125
+ * Signifies if address is for a company or a private person.
126
+ * If isCompany is null, then it is unknown and will be assumed to
127
+ * be a private person.
128
+ *
129
+ * <b>Note</b>:<br>
130
+ * This has no effect on transmitted data.
131
+ *
132
+ * @var bool|null
133
+ */
134
+ public $isCompany = null;
135
+
136
+ /**
137
+ * Class constructor.
138
+ *
139
+ * Calls the set methods for all arguments.
140
+ *
141
+ * @param string $email Email address.
142
+ * @param string $telno Phone number.
143
+ * @param string $cellno Cellphone number.
144
+ * @param string $fname First name.
145
+ * @param string $lname Last name.
146
+ * @param string $careof Care of, C/O.
147
+ * @param string $street Street address.
148
+ * @param string $zip Zip code.
149
+ * @param string $city City.
150
+ * @param string|int $country KlarnaCountry constant or two letter code.
151
+ * @param string $houseNo House number, only used in DE and NL.
152
+ * @param string $houseExt House extension, only used in NL.
153
+ *
154
+ * @throws KlarnaException
155
+ */
156
+ public function __construct(
157
+ $email = null, $telno = null, $cellno = null, $fname = null,
158
+ $lname = null, $careof = "", $street = null, $zip = null,
159
+ $city = null, $country = null, $houseNo = "", $houseExt = ""
160
+ ) {
161
+ //Set all string values to ""
162
+ $this->company = "";
163
+ $this->telno = "";
164
+ $this->careof = "";
165
+ $this->cellno = "";
166
+ $this->city = "";
167
+ $this->email = "";
168
+ $this->fname = "";
169
+ $this->lname = "";
170
+ $this->zip = "";
171
+
172
+ if ($email !== null) {
173
+ $this->setEmail($email);
174
+ }
175
+
176
+ if ($telno !== null) {
177
+ $this->setTelno($telno);
178
+ }
179
+
180
+ if ($cellno !== null) {
181
+ $this->setCellno($cellno);
182
+ }
183
+
184
+ if ($fname !== null) {
185
+ $this->setFirstName($fname);
186
+ }
187
+
188
+ if ($lname !== null) {
189
+ $this->setLastName($lname);
190
+ }
191
+
192
+ $this->setCareof($careof);
193
+
194
+ if ($street !== null) {
195
+ $this->setStreet($street);
196
+ }
197
+
198
+ if ($zip !== null) {
199
+ $this->setZipCode($zip);
200
+ }
201
+
202
+ if ($city !== null) {
203
+ $this->setCity($city);
204
+ }
205
+
206
+ if ($country !== null) {
207
+ $this->setCountry($country);
208
+ }
209
+
210
+ $this->setHouseNumber($houseNo);
211
+ $this->setHouseExt($houseExt);
212
+ }
213
+
214
+ /**
215
+ * Returns the email address.
216
+ *
217
+ * @return string
218
+ */
219
+ public function getEmail()
220
+ {
221
+ return $this->email;
222
+ }
223
+
224
+ /**
225
+ * Sets the email address.
226
+ *
227
+ * @param string $email email address
228
+ *
229
+ * @return void
230
+ */
231
+ public function setEmail($email)
232
+ {
233
+ if (!is_string($email)) {
234
+ $email = strval($email);
235
+ }
236
+
237
+ $this->email = $email;
238
+ }
239
+
240
+ /**
241
+ * Returns the phone number.
242
+ *
243
+ * @return string
244
+ */
245
+ public function getTelno()
246
+ {
247
+ return $this->telno;
248
+ }
249
+
250
+ /**
251
+ * Sets the phone number.
252
+ *
253
+ * @param string $telno telno
254
+ *
255
+ * @return void
256
+ */
257
+ public function setTelno($telno)
258
+ {
259
+ if (!is_string($telno)) {
260
+ $telno = strval($telno);
261
+ }
262
+ $this->telno = $telno;
263
+ }
264
+
265
+ /**
266
+ * Returns the cellphone number.
267
+ *
268
+ * @return string
269
+ */
270
+ public function getCellno()
271
+ {
272
+ return $this->cellno;
273
+ }
274
+
275
+ /**
276
+ * Sets the cellphone number.
277
+ *
278
+ * @param string $cellno mobile number
279
+ *
280
+ * @return void
281
+ */
282
+ public function setCellno($cellno)
283
+ {
284
+ if (!is_string($cellno)) {
285
+ $cellno = strval($cellno);
286
+ }
287
+
288
+ $this->cellno = $cellno;
289
+ }
290
+
291
+ /**
292
+ * Returns the first name.
293
+ *
294
+ * @return string
295
+ */
296
+ public function getFirstName()
297
+ {
298
+ return $this->fname;
299
+ }
300
+
301
+ /**
302
+ * Sets the first name.
303
+ *
304
+ * @param string $fname firstname
305
+ *
306
+ * @return void
307
+ */
308
+ public function setFirstName($fname)
309
+ {
310
+ if (!is_string($fname)) {
311
+ $fname = strval($fname);
312
+ }
313
+
314
+ $this->fname = $fname;
315
+ }
316
+
317
+ /**
318
+ * Returns the last name.
319
+ *
320
+ * @return string
321
+ */
322
+ public function getLastName()
323
+ {
324
+ return $this->lname;
325
+ }
326
+
327
+ /**
328
+ * Sets the last name.
329
+ *
330
+ * @param string $lname lastname
331
+ *
332
+ * @return void
333
+ */
334
+ public function setLastName($lname)
335
+ {
336
+ if (!is_string($lname)) {
337
+ $lname = strval($lname);
338
+ }
339
+
340
+ $this->lname = $lname;
341
+ }
342
+
343
+ /**
344
+ * Returns the company name.
345
+ *
346
+ * @return string
347
+ */
348
+ public function getCompanyName()
349
+ {
350
+ return $this->company;
351
+ }
352
+
353
+ /**
354
+ * Sets the company name.
355
+ * If the purchase results in a company purchase,
356
+ * reference person will be used from first and last name,
357
+ * or the value set with {@link Klarna::setReference()}.
358
+ *
359
+ * @param string $company company name
360
+ *
361
+ * @see Klarna::setReference
362
+ * @return void
363
+ */
364
+ public function setCompanyName($company)
365
+ {
366
+ if (!is_string($company)) {
367
+ $company = strval($company);
368
+ }
369
+
370
+ $this->company = $company;
371
+ }
372
+
373
+ /**
374
+ * Returns the care of, C/O.
375
+ *
376
+ * @return string
377
+ */
378
+ public function getCareof()
379
+ {
380
+ return $this->careof;
381
+ }
382
+
383
+ /**
384
+ * Sets the care of, C/O.
385
+ *
386
+ * @param string $careof care of address
387
+ *
388
+ * @return void
389
+ */
390
+ public function setCareof($careof)
391
+ {
392
+ if (!is_string($careof)) {
393
+ $careof = strval($careof);
394
+ }
395
+
396
+ $this->careof = $careof;
397
+ }
398
+
399
+ /**
400
+ * Returns the street address.
401
+ *
402
+ * @return string
403
+ */
404
+ public function getStreet()
405
+ {
406
+ return $this->street;
407
+ }
408
+
409
+ /**
410
+ * Sets the street address.
411
+ *
412
+ * @param string $street street address
413
+ *
414
+ * @return void
415
+ */
416
+ public function setStreet($street)
417
+ {
418
+ if (!is_string($street)) {
419
+ $street = strval($street);
420
+ }
421
+
422
+ $this->street = $street;
423
+ }
424
+
425
+ /**
426
+ * Returns the zip code.
427
+ *
428
+ * @return string
429
+ */
430
+ public function getZipCode()
431
+ {
432
+ return $this->zip;
433
+ }
434
+
435
+ /**
436
+ * Sets the zip code.
437
+ *
438
+ * @param string $zip zip code
439
+ *
440
+ * @return void
441
+ */
442
+ public function setZipCode($zip)
443
+ {
444
+
445
+ if (!is_string($zip)) {
446
+ $zip = strval($zip);
447
+ }
448
+
449
+ $zip = str_replace(' ', '', $zip); //remove spaces
450
+
451
+ $this->zip = $zip;
452
+ }
453
+
454
+ /**
455
+ * Returns the city.
456
+ *
457
+ * @return string
458
+ */
459
+ public function getCity()
460
+ {
461
+ return $this->city;
462
+ }
463
+
464
+ /**
465
+ * Sets the city.
466
+ *
467
+ * @param string $city city
468
+ *
469
+ * @return void
470
+ */
471
+ public function setCity($city)
472
+ {
473
+ if (!is_string($city)) {
474
+ $city = strval($city);
475
+ }
476
+
477
+ $this->city = $city;
478
+ }
479
+
480
+ /**
481
+ * Returns the country as a integer constant.
482
+ *
483
+ * @return int {@link KlarnaCountry}
484
+ */
485
+ public function getCountry()
486
+ {
487
+ return $this->country;
488
+ }
489
+
490
+ /**
491
+ * Returns the country as a two letter representation.
492
+ *
493
+ * @throws KlarnaException
494
+ * @return string E.g. 'de', 'dk', ...
495
+ */
496
+ public function getCountryCode()
497
+ {
498
+ return KlarnaCountry::getCode($this->country);
499
+ }
500
+
501
+ /**
502
+ * Sets the country, use either a two letter representation or the integer
503
+ * constant.
504
+ *
505
+ * @param int $country {@link KlarnaCountry}
506
+ *
507
+ * @throws KlarnaException
508
+ * @return void
509
+ */
510
+ public function setCountry($country)
511
+ {
512
+ if ($country === null) {
513
+ throw new Klarna_ArgumentNotSetException('Country');
514
+ }
515
+ if (is_numeric($country)) {
516
+ if (!is_int($country)) {
517
+ $country = intval($country);
518
+ }
519
+ $this->country = $country;
520
+ return;
521
+ }
522
+ if (strlen($country) == 2 || strlen($country) == 3) {
523
+ $this->setCountry(KlarnaCountry::fromCode($country));
524
+ return;
525
+ }
526
+ throw new KlarnaException("Failed to set country! ($country)");
527
+ }
528
+
529
+ /**
530
+ * Returns the house number.<br>
531
+ * Only used in Germany and Netherlands.<br>
532
+ *
533
+ * @return string
534
+ */
535
+ public function getHouseNumber()
536
+ {
537
+ return $this->houseNo;
538
+ }
539
+
540
+ /**
541
+ * Sets the house number.<br>
542
+ * Only used in Germany and Netherlands.<br>
543
+ *
544
+ * @param string $houseNo house number
545
+ *
546
+ * @return void
547
+ */
548
+ public function setHouseNumber($houseNo)
549
+ {
550
+ if (!is_string($houseNo)) {
551
+ $houseNo = strval($houseNo);
552
+ }
553
+
554
+ $this->houseNo = $houseNo;
555
+ }
556
+
557
+ /**
558
+ * Returns the house extension.<br>
559
+ * Only used in Netherlands.<br>
560
+ *
561
+ * @return string
562
+ */
563
+ public function getHouseExt()
564
+ {
565
+ return $this->houseExt;
566
+ }
567
+
568
+ /**
569
+ * Sets the house extension.<br>
570
+ * Only used in Netherlands.<br>
571
+ *
572
+ * @param string $houseExt house extension
573
+ *
574
+ * @return void
575
+ */
576
+ public function setHouseExt($houseExt)
577
+ {
578
+ if (!is_string($houseExt)) {
579
+ $houseExt = strval($houseExt);
580
+ }
581
+
582
+ $this->houseExt = $houseExt;
583
+ }
584
+
585
+ /**
586
+ * Returns an associative array representing this object.
587
+ *
588
+ * @return array
589
+ */
590
+ public function toArray()
591
+ {
592
+ return array(
593
+ 'email' => $this->getEmail(),
594
+ 'telno' => $this->getTelno(),
595
+ 'cellno' => $this->getCellno(),
596
+ 'fname' => $this->getFirstName(),
597
+ 'lname' => $this->getLastName(),
598
+ 'company' => $this->getCompanyName(),
599
+ 'careof' => $this->getCareof(),
600
+ 'street' => $this->getStreet(),
601
+ 'house_number' => $this->getHouseNumber(),
602
+ 'house_extension' => $this->getHouseExt(),
603
+ 'zip' => $this->getZipCode(),
604
+ 'city' => $this->getCity(),
605
+ 'country' => $this->getCountry(),
606
+ );
607
+ }
608
+
609
+ }
lib/Klarna/klarnacalc.php ADDED
@@ -0,0 +1,655 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * KlarnaCalc
4
+ *
5
+ * PHP Version 5.3
6
+ *
7
+ * @category Payment
8
+ * @package KlarnaAPI
9
+ * @author MS Dev <ms.modules@klarna.com>
10
+ * @copyright 2012 Klarna AB (http://klarna.com)
11
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
12
+ * @link http://integration.klarna.com/
13
+ */
14
+
15
+ /**
16
+ * KlarnaCalc provides methods to calculate part payment functions.
17
+ *
18
+ * All rates are yearly rates, but they are calculated monthly. So
19
+ * a rate of 9 % is used 0.75% monthly. The first is the one we specify
20
+ * to the customers, and the second one is the one added each month to
21
+ * the account. The IRR uses the same notation.
22
+ *
23
+ * The APR is however calculated by taking the monthly rate and raising
24
+ * it to the 12 power. This is according to the EU law, and will give
25
+ * very large numbers if the $pval is small compared to the $fee and
26
+ * the amount of months you repay is small as well.
27
+ *
28
+ * All functions work in discrete mode, and the time interval is the
29
+ * mythical evenly divided month. There is no way to calculate APR in
30
+ * days without using integrals and other hairy math. So don't try.
31
+ * The amount of days between actual purchase and the first bill can
32
+ * of course vary between 28 and 61 days, but all calculations in this
33
+ * class assume this time is exactly and that is ok since this will only
34
+ * overestimate the APR and all examples in EU law uses whole months as well.
35
+ *
36
+ * @category Payment
37
+ * @package KlarnaAPI
38
+ * @author MS Dev <ms.modules@klarna.com>
39
+ * @copyright 2012 Klarna AB (http://klarna.com)
40
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
41
+ * @link http://integration.klarna.com/
42
+ */
43
+ class KlarnaCalc
44
+ {
45
+
46
+ /**
47
+ * This constant tells the irr function when to stop.
48
+ * If the calculation error is lower than this the calculation is done.
49
+ *
50
+ * @var float
51
+ */
52
+ protected static $accuracy = 0.01;
53
+
54
+ /**
55
+ * Calculates the midpoint between two points. Used by divide and conquer.
56
+ *
57
+ * @param float $a point a
58
+ * @param float $b point b
59
+ *
60
+ * @return float
61
+ */
62
+ private static function _midpoint($a, $b)
63
+ {
64
+ return (($a+$b)/2);
65
+ }
66
+
67
+ /**
68
+ * npv - Net Present Value
69
+ * Calculates the difference between the initial loan to the customer
70
+ * and the individual payments adjusted for the inverse of the interest
71
+ * rate. The variable we are searching for is $rate and if $pval,
72
+ * $payarray and $rate is perfectly balanced this function returns 0.0.
73
+ *
74
+ * @param float $pval initial loan to customer (in any currency)
75
+ * @param array $payarray array of monthly payments from the customer
76
+ * @param float $rate interest rate per year in %
77
+ * @param int $fromdayone count interest from the first day? yes(1)/no(0)
78
+ *
79
+ * @return float
80
+ */
81
+ private static function _npv($pval, $payarray, $rate, $fromdayone)
82
+ {
83
+ $month = $fromdayone;
84
+ foreach ($payarray as $payment) {
85
+ $pval -= $payment / pow(1 + $rate/(12*100.0), $month++);
86
+ }
87
+
88
+ return ($pval);
89
+ }
90
+
91
+ /**
92
+ * This function uses divide and conquer to numerically find the IRR,
93
+ * Internal Rate of Return. It starts of by trying a low of 0% and a
94
+ * high of 100%. If this isn't enough it will double the interval up
95
+ * to 1000000%. Note that this is insanely high, and if you try to convert
96
+ * an IRR that high to an APR you will get even more insane values,
97
+ * so feed this function good data.
98
+ *
99
+ * Return values: float irr if it was possible to find a rate that gets
100
+ * npv closer to 0 than $accuracy.
101
+ * int -1 The sum of the payarray is less than the lent
102
+ * amount, $pval. Hellooooooo. Impossible.
103
+ * int -2 the IRR is way to high, giving up.
104
+ *
105
+ * This algorithm works in logarithmic time no matter what inputs you give
106
+ * and it will come to a good answer within ~30 steps.
107
+ *
108
+ * @param float $pval initial loan to customer (in any currency)
109
+ * @param array $payarray array of monthly payments from the customer
110
+ * @param int $fromdayone count interest from the first day? yes(1)/no(0)
111
+ *
112
+ * @return float
113
+ */
114
+ private static function _irr($pval, $payarray, $fromdayone)
115
+ {
116
+ $low = 0.0;
117
+ $high = 100.0;
118
+ $lowval = self::_npv($pval, $payarray, $low, $fromdayone);
119
+ $highval = self::_npv($pval, $payarray, $high, $fromdayone);
120
+
121
+ // The sum of $payarray is smaller than $pval, impossible!
122
+ if ($lowval > 0.0) {
123
+ return (-1);
124
+ }
125
+
126
+ // Standard divide and conquer.
127
+ do {
128
+ $mid = self::_midpoint($low, $high);
129
+ $midval = self::_npv($pval, $payarray, $mid, $fromdayone);
130
+ if (abs($midval) < self::$accuracy) {
131
+ //we are close enough
132
+ return ($mid);
133
+ }
134
+
135
+ if ($highval < 0.0) {
136
+ // we are not in range, so double it
137
+ $low = $high;
138
+ $lowval = $highval;
139
+ $high *= 2;
140
+ $highval = self::_npv($pval, $payarray, $high, $fromdayone);
141
+ } else if ($midval >= 0.0) {
142
+ // irr is between low and mid
143
+ $high = $mid;
144
+ $highval = $midval;
145
+ } else {
146
+ // irr is between mid and high
147
+ $low = $mid;
148
+ $lowval = $midval;
149
+ }
150
+ } while ($high < 1000000);
151
+ // bad input, insanely high interest. APR will be INSANER!
152
+ return (-2);
153
+ }
154
+
155
+ /**
156
+ * IRR is not the same thing as APR, Annual Percentage Rate. The
157
+ * IRR is per time period, i.e. 1 month, and the APR is per year,
158
+ * and note that that you need to raise to the power of 12, not
159
+ * mutliply by 12.
160
+ *
161
+ * This function turns an IRR into an APR.
162
+ *
163
+ * If you feed it a value of 100%, yes the APR will be millions!
164
+ * If you feed it a value of 9%, it will be 9.3806%.
165
+ * That is the nature of this math and you can check the wiki
166
+ * page for APR for more info.
167
+ *
168
+ * @param float $irr Internal Rate of Return, expressed yearly, in %
169
+ *
170
+ * @return float Annual Percentage Rate, in %
171
+ */
172
+ private static function _irr2apr($irr)
173
+ {
174
+ return (100 * (pow(1 + $irr / (12 * 100.0), 12) - 1));
175
+ }
176
+
177
+ /**
178
+ * This is a simplified model of how our paccengine works if
179
+ * a client always pays their bills. It adds interest and fees
180
+ * and checks minimum payments. It will run until the value
181
+ * of the account reaches 0, and return an array of all the
182
+ * individual payments. Months is the amount of months to run
183
+ * the simulation. Important! Don't feed it too few months or
184
+ * the whole loan won't be paid off, but the other functions
185
+ * should handle this correctly.
186
+ *
187
+ * Giving it too many months has no bad effects, or negative
188
+ * amount of months which means run forever, but it will stop
189
+ * as soon as the account is paid in full.
190
+ *
191
+ * Depending if the account is a base account or not, the
192
+ * payment has to be 1/24 of the capital amount.
193
+ *
194
+ * The payment has to be at least $minpay, unless the capital
195
+ * amount + interest + fee is less than $minpay; in that case
196
+ * that amount is paid and the function returns since the client
197
+ * no longer owes any money.
198
+ *
199
+ * @param float $pval initial loan to customer (in any currency)
200
+ * @param float $rate interest rate per year in %
201
+ * @param float $fee monthly invoice fee
202
+ * @param float $minpay minimum monthly payment allowed for this country.
203
+ * @param float $payment payment the client to pay each month
204
+ * @param int $months amount of months to run (-1 => infinity)
205
+ * @param boolean $base is it a base account?
206
+ *
207
+ * @return array An array of monthly payments for the customer.
208
+ */
209
+ private static function _fulpacc(
210
+ $pval, $rate, $fee, $minpay, $payment, $months, $base
211
+ ) {
212
+ $bal = $pval;
213
+ $payarray = array();
214
+ while (($months != 0) && ($bal > self::$accuracy)) {
215
+ $interest = $bal * $rate / (100.0 * 12);
216
+ $newbal = $bal + $interest + $fee;
217
+
218
+ if ($minpay >= $newbal || $payment >= $newbal) {
219
+ $payarray[] = $newbal;
220
+ return $payarray;
221
+ }
222
+
223
+ $newpay = max($payment, $minpay);
224
+ if ($base) {
225
+ $newpay = max($newpay, $bal/24.0 + $fee + $interest);
226
+ }
227
+
228
+ $bal = $newbal - $newpay;
229
+ $payarray[] = $newpay;
230
+ $months -= 1;
231
+ }
232
+
233
+ return $payarray;
234
+ }
235
+
236
+ /**
237
+ * Calculates how much you have to pay each month if you want to
238
+ * pay exactly the same amount each month. The interesting input
239
+ * is the amount of $months.
240
+ *
241
+ * It does not include the fee so add that later.
242
+ *
243
+ * Return value: monthly payment.
244
+ *
245
+ * @param float $pval principal value
246
+ * @param int $months months to pay of in
247
+ * @param float $rate interest rate in % as before
248
+ *
249
+ * @return float monthly payment
250
+ */
251
+ private static function _annuity($pval, $months, $rate)
252
+ {
253
+ if ($months == 0) {
254
+ return $pval;
255
+ }
256
+
257
+ if ($rate == 0) {
258
+ return $pval/$months;
259
+ }
260
+
261
+ $p = $rate / (100.0*12);
262
+ return $pval * $p / (1 - pow((1+$p), -$months));
263
+ }
264
+
265
+ /**
266
+ * Calculate the APR for an annuity given the following inputs.
267
+ *
268
+ * If you give it bad inputs, it will return negative values.
269
+ *
270
+ * @param float $pval principal value
271
+ * @param int $months months to pay off in
272
+ * @param float $rate interest rate in % as before
273
+ * @param float $fee monthly fee
274
+ * @param float $minpay minimum payment per month
275
+ *
276
+ * @return float APR in %
277
+ */
278
+ private static function _aprAnnuity($pval, $months, $rate, $fee, $minpay)
279
+ {
280
+ $payment = self::_annuity($pval, $months, $rate) + $fee;
281
+ if ($payment < 0) {
282
+ return $payment;
283
+ }
284
+ $payarray = self::_fulpacc(
285
+ $pval, $rate, $fee, $minpay, $payment, $months, false
286
+ );
287
+ $apr = self::_irr2apr(self::_irr($pval, $payarray, 1));
288
+
289
+ return $apr;
290
+ }
291
+
292
+ /**
293
+ * Grabs the array of all monthly payments for specified PClass.
294
+ *
295
+ * <b>Flags can be either</b>:<br>
296
+ * {@link KlarnaFlags::CHECKOUT_PAGE}<br>
297
+ * {@link KlarnaFlags::PRODUCT_PAGE}<br>
298
+ *
299
+ * @param float $sum The sum for the order/product.
300
+ * @param KlarnaPClass $pclass KlarnaPClass used to calculate the APR.
301
+ * @param int $flags Checkout or Product page.
302
+ *
303
+ * @throws KlarnaException
304
+ * @return array An array of monthly payments.
305
+ */
306
+ private static function _getPayArray($sum, $pclass, $flags)
307
+ {
308
+ $monthsfee = 0;
309
+ if ($flags === KlarnaFlags::CHECKOUT_PAGE) {
310
+ $monthsfee = $pclass->getInvoiceFee();
311
+ }
312
+ $startfee = 0;
313
+ if ($flags === KlarnaFlags::CHECKOUT_PAGE) {
314
+ $startfee = $pclass->getStartFee();
315
+ }
316
+
317
+ //Include start fee in sum
318
+ $sum += $startfee;
319
+
320
+ $base = ($pclass->getType() === KlarnaPClass::ACCOUNT);
321
+ $lowest = self::get_lowest_payment_for_account($pclass->getCountry());
322
+
323
+ if ($flags == KlarnaFlags::CHECKOUT_PAGE) {
324
+ $minpay = ($pclass->getType() === KlarnaPClass::ACCOUNT) ? $lowest : 0;
325
+ } else {
326
+ $minpay = 0;
327
+ }
328
+
329
+ $payment = self::_annuity(
330
+ $sum,
331
+ $pclass->getMonths(),
332
+ $pclass->getInterestRate()
333
+ );
334
+
335
+ //Add monthly fee
336
+ $payment += $monthsfee;
337
+
338
+ return self::_fulpacc(
339
+ $sum,
340
+ $pclass->getInterestRate(),
341
+ $monthsfee,
342
+ $minpay,
343
+ $payment,
344
+ $pclass->getMonths(),
345
+ $base
346
+ );
347
+ }
348
+
349
+ /**
350
+ * Calculates APR for the specified values.<br>
351
+ * Result is rounded with two decimals.<br>
352
+ *
353
+ * <b>Flags can be either</b>:<br>
354
+ * {@link KlarnaFlags::CHECKOUT_PAGE}<br>
355
+ * {@link KlarnaFlags::PRODUCT_PAGE}<br>
356
+ *
357
+ * @param float $sum The sum for the order/product.
358
+ * @param KlarnaPClass $pclass KlarnaPClass used to calculate the APR.
359
+ * @param int $flags Checkout or Product page.
360
+ * @param int $free Number of free months.
361
+ *
362
+ * @throws KlarnaException
363
+ * @return float APR in %
364
+ */
365
+ public static function calc_apr($sum, $pclass, $flags, $free = 0)
366
+ {
367
+ if (!is_numeric($sum)) {
368
+ throw new Klarna_InvalidTypeException('sum', 'numeric');
369
+ }
370
+ if (is_numeric($sum) && (!is_int($sum) || !is_float($sum))) {
371
+ $sum = floatval($sum);
372
+ }
373
+
374
+ if (!($pclass instanceof KlarnaPClass)) {
375
+ throw new Klarna_InvalidTypeException('pclass', 'KlarnaPClass');
376
+ }
377
+
378
+ if (!is_numeric($free)) {
379
+ throw new Klarna_InvalidTypeException('free', 'integer');
380
+ }
381
+
382
+ if (is_numeric($free) && !is_int($free)) {
383
+ $free = intval($free);
384
+ }
385
+
386
+ if ($free < 0) {
387
+ throw new KlarnaException(
388
+ 'Error in ' . __METHOD__ .
389
+ ': Number of free months must be positive or zero!'
390
+ );
391
+ }
392
+
393
+ if (is_numeric($flags) && !is_int($flags)) {
394
+ $flags = intval($flags);
395
+ }
396
+
397
+ if (!is_numeric($flags)
398
+ || !in_array(
399
+ $flags, array(
400
+ KlarnaFlags::CHECKOUT_PAGE, KlarnaFlags::PRODUCT_PAGE
401
+ )
402
+ )
403
+ ) {
404
+ throw new Klarna_InvalidTypeException(
405
+ 'flags',
406
+ KlarnaFlags::CHECKOUT_PAGE . ' or ' . KlarnaFlags::PRODUCT_PAGE
407
+ );
408
+ }
409
+
410
+ $monthsfee = 0;
411
+ if ($flags === KlarnaFlags::CHECKOUT_PAGE) {
412
+ $monthsfee = $pclass->getInvoiceFee();
413
+ }
414
+ $startfee = 0;
415
+ if ($flags === KlarnaFlags::CHECKOUT_PAGE) {
416
+ $startfee = $pclass->getStartFee();
417
+ }
418
+
419
+ //Include start fee in sum
420
+ $sum += $startfee;
421
+
422
+ $lowest = self::get_lowest_payment_for_account($pclass->getCountry());
423
+
424
+ if ($flags == KlarnaFlags::CHECKOUT_PAGE) {
425
+ $minpay = ($pclass->getType() === KlarnaPClass::ACCOUNT) ? $lowest : 0;
426
+ } else {
427
+ $minpay = 0;
428
+ }
429
+
430
+ //add monthly fee
431
+ $payment = self::_annuity(
432
+ $sum,
433
+ $pclass->getMonths(),
434
+ $pclass->getInterestRate()
435
+ ) + $monthsfee;
436
+
437
+ $type = $pclass->getType();
438
+ switch($type) {
439
+ case KlarnaPClass::CAMPAIGN:
440
+ case KlarnaPClass::ACCOUNT:
441
+ return round(
442
+ self::_aprAnnuity(
443
+ $sum, $pclass->getMonths(),
444
+ $pclass->getInterestRate(),
445
+ $pclass->getInvoiceFee(),
446
+ $minpay
447
+ ),
448
+ 2
449
+ );
450
+ case KlarnaPClass::SPECIAL:
451
+ throw new Klarna_PClassException(
452
+ 'Method is not available for SPECIAL pclasses'
453
+ );
454
+ case KlarnaPClass::FIXED:
455
+ throw new Klarna_PClassException(
456
+ 'Method is not available for FIXED pclasses'
457
+ );
458
+ default:
459
+ throw new Klarna_PClassException(
460
+ 'Unknown PClass type! ('.$type.')'
461
+ );
462
+ }
463
+ }
464
+
465
+ /**
466
+ * Calculates the total credit purchase cost.<br>
467
+ * The result is rounded up, depending on the pclass country.<br>
468
+ *
469
+ * <b>Flags can be either</b>:<br>
470
+ * {@link KlarnaFlags::CHECKOUT_PAGE}<br>
471
+ * {@link KlarnaFlags::PRODUCT_PAGE}<br>
472
+ *
473
+ * @param float $sum The sum for the order/product.
474
+ * @param KlarnaPClass $pclass PClass used to calculate total credit cost.
475
+ * @param int $flags Checkout or Product page.
476
+ *
477
+ * @throws KlarnaException
478
+ * @return float Total credit purchase cost.
479
+ */
480
+ public static function total_credit_purchase_cost($sum, $pclass, $flags)
481
+ {
482
+ if (!is_numeric($sum)) {
483
+ throw new Klarna_InvalidTypeException('sum', 'numeric');
484
+ }
485
+
486
+ if (is_numeric($sum) && (!is_int($sum) || !is_float($sum))) {
487
+ $sum = floatval($sum);
488
+ }
489
+
490
+ if (!($pclass instanceof KlarnaPClass)) {
491
+ throw new Klarna_InvalidTypeException('pclass', 'KlarnaPClass');
492
+ }
493
+
494
+ if (is_numeric($flags) && !is_int($flags)) {
495
+ $flags = intval($flags);
496
+ }
497
+
498
+ if (!is_numeric($flags)
499
+ || !in_array(
500
+ $flags,
501
+ array(
502
+ KlarnaFlags::CHECKOUT_PAGE, KlarnaFlags::PRODUCT_PAGE
503
+ )
504
+ )
505
+ ) {
506
+ throw new Klarna_InvalidTypeException(
507
+ 'flags',
508
+ KlarnaFlags::CHECKOUT_PAGE . ' or ' . KlarnaFlags::PRODUCT_PAGE
509
+ );
510
+ }
511
+
512
+ $payarr = self::_getPayArray($sum, $pclass, $flags);
513
+
514
+ $credit_cost = 0;
515
+ foreach ($payarr as $pay) {
516
+ $credit_cost += $pay;
517
+ }
518
+
519
+ return self::pRound($credit_cost, $pclass->getCountry());
520
+ }
521
+
522
+ /**
523
+ * Calculates the monthly cost for the specified pclass.
524
+ * The result is rounded up to the correct value depending on the
525
+ * pclass country.<br>
526
+ *
527
+ * Example:<br>
528
+ * <ul>
529
+ * <li>In product view, round monthly cost with max 0.5 or 0.1
530
+ * depending on currency.<br>
531
+ * <ul>
532
+ * <li>10.50 SEK rounds to 11 SEK</li>
533
+ * <li>10.49 SEK rounds to 10 SEK</li>
534
+ * <li> 8.55 EUR rounds to 8.6 EUR</li>
535
+ * <li> 8.54 EUR rounds to 8.5 EUR</li>
536
+ * </ul></li>
537
+ * <li>
538
+ * In checkout, round the monthly cost to have 2 decimals.<br>
539
+ * For example 10.57 SEK/per månad
540
+ * </li>
541
+ * </ul>
542
+ *
543
+ * <b>Flags can be either</b>:<br>
544
+ * {@link KlarnaFlags::CHECKOUT_PAGE}<br>
545
+ * {@link KlarnaFlags::PRODUCT_PAGE}<br>
546
+ *
547
+ * @param int $sum The sum for the order/product.
548
+ * @param KlarnaPClass $pclass PClass used to calculate monthly cost.
549
+ * @param int $flags Checkout or product page.
550
+ *
551
+ * @throws KlarnaException
552
+ * @return float The monthly cost.
553
+ */
554
+ public static function calc_monthly_cost($sum, $pclass, $flags)
555
+ {
556
+ if (!is_numeric($sum)) {
557
+ throw new Klarna_InvalidTypeException('sum', 'numeric');
558
+ }
559
+
560
+ if (is_numeric($sum) && (!is_int($sum) || !is_float($sum))) {
561
+ $sum = floatval($sum);
562
+ }
563
+
564
+ if (!($pclass instanceof KlarnaPClass)) {
565
+ throw new Klarna_InvalidTypeException('pclass', 'KlarnaPClass');
566
+ }
567
+
568
+ if (is_numeric($flags) && !is_int($flags)) {
569
+ $flags = intval($flags);
570
+ }
571
+
572
+ if (!is_numeric($flags)
573
+ || !in_array(
574
+ $flags,
575
+ array(
576
+ KlarnaFlags::CHECKOUT_PAGE, KlarnaFlags::PRODUCT_PAGE
577
+ )
578
+ )
579
+ ) {
580
+ throw new Klarna_InvalidTypeException(
581
+ 'flags',
582
+ KlarnaFlags::CHECKOUT_PAGE . ' or ' . KlarnaFlags::PRODUCT_PAGE
583
+ );
584
+ }
585
+
586
+ $payarr = self::_getPayArray($sum, $pclass, $flags);
587
+ $value = 0;
588
+ if (isset($payarr[0])) {
589
+ $value = $payarr[0];
590
+ }
591
+
592
+ if (KlarnaFlags::CHECKOUT_PAGE == $flags) {
593
+ return round($value, 2);
594
+ }
595
+
596
+ return self::pRound($value, $pclass->getCountry());
597
+ }
598
+
599
+ /**
600
+ * Returns the lowest monthly payment for Klarna Account.
601
+ *
602
+ * @param int $country KlarnaCountry constant.
603
+ *
604
+ * @throws KlarnaException
605
+ * @return int|float Lowest monthly payment.
606
+ */
607
+ public static function get_lowest_payment_for_account($country)
608
+ {
609
+ $country = KlarnaCountry::getCode($country);
610
+
611
+ switch (strtoupper($country)) {
612
+ case "SE":
613
+ return 50.0;
614
+ case "NO":
615
+ return 95.0;
616
+ case "FI":
617
+ return 8.95;
618
+ case "DK":
619
+ return 89.0;
620
+ case "DE":
621
+ case "AT":
622
+ return 6.95;
623
+ case "NL":
624
+ return 5.0;
625
+ default:
626
+ throw new KlarnaException("Invalid country {$country}");
627
+ }
628
+ }
629
+
630
+ /**
631
+ * Rounds a value depending on the specified country.
632
+ *
633
+ * @param int|float $value The value to be rounded.
634
+ * @param int $country KlarnaCountry constant.
635
+ *
636
+ * @return float|int
637
+ */
638
+ public static function pRound($value, $country)
639
+ {
640
+ $multiply = 1; //Round to closest integer
641
+ $country = KlarnaCountry::getCode($country);
642
+ switch($country) {
643
+ case "FI":
644
+ case "DE":
645
+ case "NL":
646
+ case "AT":
647
+ $multiply = 10; //Round to closest decimal
648
+ break;
649
+ }
650
+
651
+ return floor(($value*$multiply)+0.5)/$multiply;
652
+ }
653
+
654
+ }
655
+
lib/Klarna/klarnaconfig.php ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * KlarnaConfig
4
+ *
5
+ * PHP Version 5.3
6
+ *
7
+ * @category Payment
8
+ * @package KlarnaAPI
9
+ * @author MS Dev <ms.modules@klarna.com>
10
+ * @copyright 2012 Klarna AB (http://klarna.com)
11
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
12
+ * @link http://integration.klarna.com/
13
+ */
14
+
15
+ /**
16
+ * Configuration class for the Klarna instance.
17
+ *
18
+ * KlarnaConfig stores added fields in JSON, it also prepends.<br>
19
+ * Loads/saves specified file, or default file, if {@link KlarnaConfig::$store}
20
+ * is set to true.<br>
21
+ *
22
+ * You add settings using the ArrayAccess:<br>
23
+ * $arr['field'] = $val or $arr->offsetSet('field', $val);<br>
24
+ *
25
+ * Available settings are:<br>
26
+ * eid - Merchant ID (int)
27
+ * secret - Shared secret (string)
28
+ * country - Country constant or code (int|string)
29
+ * language - Language constant or code (int|string)
30
+ * currency - Currency constant or code (int|string)
31
+ * mode - Klarna::BETA or Klarna::LIVE
32
+ * ssl - Use HTTPS or HTTP. (bool)
33
+ * candice - Status reporting to Klarna, to detect erroneous
34
+ * integrations, etc. (bool)
35
+ * pcStorage - Storage module, e.g. 'json'
36
+ * pcURI - URI to where the PClasses are stored, e.g.
37
+ * '/srv/shop/pclasses.json'
38
+ * xmlrpcDebug - XMLRPC debugging (bool)
39
+ * debug - Normal debugging (bool)
40
+ *
41
+ * @category Payment
42
+ * @package KlarnaAPI
43
+ * @author MS Dev <ms.modules@klarna.com>
44
+ * @copyright 2012 Klarna AB (http://klarna.com)
45
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
46
+ * @link http://integration.klarna.com/
47
+ */
48
+ class KlarnaConfig implements ArrayAccess
49
+ {
50
+
51
+ /**
52
+ * An array containing all the options for this config.
53
+ *
54
+ * @ignore Do not show in PHPDoc.
55
+ * @var array
56
+ */
57
+ protected $options;
58
+
59
+ /**
60
+ * If set to true, saves the config.
61
+ *
62
+ * @var bool
63
+ */
64
+ public static $store = true;
65
+
66
+ /**
67
+ * URI to the config file.
68
+ *
69
+ * @ignore Do not show in PHPDoc.
70
+ * @var string
71
+ */
72
+ protected $file;
73
+
74
+ /**
75
+ * Class constructor
76
+ *
77
+ * Loads specified file, or default file,
78
+ * if {@link KlarnaConfig::$store} is set to true.
79
+ *
80
+ * @param string $file URI to config file, e.g. ./config.json
81
+ */
82
+ public function __construct($file = null)
83
+ {
84
+ $this->options = array();
85
+ if ($file) {
86
+ $this->file = $file;
87
+ if (is_readable($this->file)) {
88
+ $this->options = json_decode(
89
+ file_get_contents(
90
+ $this->file
91
+ ), true
92
+ );
93
+ }
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Clears the config.
99
+ *
100
+ * @return void
101
+ */
102
+ public function clear()
103
+ {
104
+ $this->options = array();
105
+ }
106
+
107
+ /**
108
+ * Class destructor
109
+ *
110
+ * Saves specified file, or default file,
111
+ * if {@link KlarnaConfig::$store} is set to true.
112
+ */
113
+ public function __destruct()
114
+ {
115
+ if (self::$store && $this->file) {
116
+ if ((!file_exists($this->file)
117
+ && is_writable(dirname($this->file)))
118
+ || is_writable($this->file)
119
+ ) {
120
+ file_put_contents($this->file, json_encode($this->options));
121
+ }
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Returns true whether the field exists.
127
+ *
128
+ * @param mixed $offset field
129
+ *
130
+ * @return bool
131
+ */
132
+ public function offsetExists($offset)
133
+ {
134
+ return isset($this->options[$offset]);
135
+ }
136
+
137
+ /**
138
+ * Used to get the value of a field.
139
+ *
140
+ * @param mixed $offset field
141
+ *
142
+ * @return mixed
143
+ */
144
+ public function offsetGet($offset)
145
+ {
146
+ if (!$this->offsetExists($offset)) {
147
+ return null;
148
+ }
149
+ return $this->options[$offset];
150
+ }
151
+
152
+ /**
153
+ * Used to set a value to a field.
154
+ *
155
+ * @param mixed $offset field
156
+ * @param mixed $value value
157
+ *
158
+ * @return void
159
+ */
160
+ public function offsetSet($offset, $value)
161
+ {
162
+ $this->options[$offset] = $value;
163
+ }
164
+
165
+ /**
166
+ * Removes the specified field.
167
+ *
168
+ * @param mixed $offset field
169
+ *
170
+ * @return void
171
+ */
172
+ public function offsetUnset($offset)
173
+ {
174
+ unset($this->options[$offset]);
175
+ }
176
+ }
lib/Klarna/klarnapclass.php ADDED
@@ -0,0 +1,573 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * KlarnaPClass
4
+ *
5
+ * PHP Version 5.3
6
+ *
7
+ * @ignore Do not show in PHPDoc.
8
+ * @category Payment
9
+ * @package KlarnaAPI
10
+ * @author MS Dev <ms.modules@klarna.com>
11
+ * @copyright 2012 Klarna AB (http://klarna.com)
12
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
13
+ * @link http://integration.klarna.com/
14
+ */
15
+
16
+ /**
17
+ * PClass object used for part payment.
18
+ *
19
+ * PClasses are used in conjunction with KlarnaCalc to determine part payment costs.
20
+ *
21
+ * @ignore Do not show in PHPDoc.
22
+ * @category Payment
23
+ * @package KlarnaAPI
24
+ * @author MS Dev <ms.modules@klarna.com>
25
+ * @copyright 2012 Klarna AB (http://klarna.com)
26
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
27
+ * @link http://integration.klarna.com/
28
+ */
29
+ class KlarnaPClass
30
+ {
31
+
32
+ /**
33
+ * Invoice type/identifier, used for invoice purchases.
34
+ *
35
+ * @var int
36
+ */
37
+ const INVOICE = -1;
38
+
39
+ /**
40
+ * Campaign type pclass.
41
+ *
42
+ * @var int
43
+ */
44
+ const CAMPAIGN = 0;
45
+
46
+ /**
47
+ * Account type pclass.
48
+ *
49
+ * @var int
50
+ */
51
+ const ACCOUNT = 1;
52
+
53
+ /**
54
+ * Special campaign type pclass.<br>
55
+ * "Buy now, pay in x month"<br>
56
+ *
57
+ * @var int
58
+ */
59
+ const SPECIAL = 2;
60
+
61
+ /**
62
+ * Fixed campaign type pclass
63
+ *
64
+ * @var int
65
+ */
66
+ const FIXED = 3;
67
+
68
+ /**
69
+ * Delayed campaign type pclass.<br>
70
+ * "Pay in X months"<br>
71
+ *
72
+ * @var int
73
+ */
74
+ const DELAY = 4;
75
+
76
+ /**
77
+ * Klarna Mobile type pclass
78
+ *
79
+ * @var int
80
+ */
81
+ const MOBILE = 5;
82
+
83
+ /**
84
+ * The description for this PClass.
85
+ * HTML entities for special characters.
86
+ *
87
+ * @ignore Do not show in PHPDoc.
88
+ * @var string
89
+ */
90
+ protected $description;
91
+
92
+ /**
93
+ * Number of months for this PClass.
94
+ *
95
+ * @ignore Do not show in PHPDoc.
96
+ * @var int
97
+ */
98
+ protected $months;
99
+
100
+ /**
101
+ * PClass starting fee.
102
+ *
103
+ * @ignore Do not show in PHPDoc.
104
+ * @var float
105
+ */
106
+ protected $startFee;
107
+
108
+ /**
109
+ * PClass invoice/handling fee.
110
+ *
111
+ * @ignore Do not show in PHPDoc.
112
+ * @var float
113
+ */
114
+ protected $invoiceFee;
115
+
116
+ /**
117
+ * PClass interest rate.
118
+ *
119
+ * @ignore Do not show in PHPDoc.
120
+ * @var float
121
+ */
122
+ protected $interestRate;
123
+
124
+ /**
125
+ * PClass minimum amount for purchase/product.
126
+ *
127
+ * @ignore Do not show in PHPDoc.
128
+ * @var float
129
+ */
130
+ protected $minAmount;
131
+
132
+ /**
133
+ * PClass country.
134
+ *
135
+ * @ignore Do not show in PHPDoc.
136
+ * @see KlarnaCountry
137
+ * @var int
138
+ */
139
+ protected $country;
140
+
141
+ /**
142
+ * PClass ID.
143
+ *
144
+ * @ignore Do not show in PHPDoc.
145
+ * @var int
146
+ */
147
+ protected $id;
148
+
149
+ /**
150
+ * PClass type.
151
+ *
152
+ * @see self::CAMPAIGN
153
+ * @see self::ACCOUNT
154
+ * @see self::SPECIAL
155
+ * @see self::FIXED
156
+ * @see self::DELAY
157
+ * @see self::MOBILE
158
+ *
159
+ * @ignore Do not show in PHPDoc.
160
+ * @var int
161
+ */
162
+ protected $type;
163
+
164
+ /**
165
+ * Expire date / valid until date as unix timestamp.<br>
166
+ * Compare it with e.g. $_SERVER['REQUEST_TIME'].<br>
167
+ *
168
+ * @ignore Do not show in PHPDoc.
169
+ * @var int
170
+ */
171
+ protected $expire;
172
+
173
+ /**
174
+ * Merchant ID / Estore ID.
175
+ *
176
+ * @ignore Do not show in PHPDoc.
177
+ * @var int
178
+ */
179
+ protected $eid;
180
+
181
+ /**
182
+ * Class constructor
183
+ *
184
+ * The optional array argument can be:
185
+ * array (
186
+ * 0 = eid (this is created in the API)
187
+ * 1 = id number
188
+ * 2 = description
189
+ * 3 = amount of months for part payment
190
+ * 4 = start fee
191
+ * 5 = invoice fee
192
+ * 6 = interest rate
193
+ * 7 = minimum purchase amount for pclass
194
+ * 8 = country
195
+ * 9 = type
196
+ * (This is used to determine which pclass-id is an account and
197
+ * a campaign, 0 = campaign, 1 = account, 2 = special campaign
198
+ * i.e. x-mas campaign)
199
+ * 10 = expire date
200
+ *
201
+ * @param null|array $arr Associative or numeric array of PClass data.
202
+ */
203
+ public function __construct($arr = null)
204
+ {
205
+ if (!is_array($arr) || count($arr) < 11) {
206
+ return;
207
+ }
208
+
209
+ foreach ($arr as $key => $val) {
210
+ switch($key) {
211
+ case "0":
212
+ case "eid":
213
+ $this->setEid($val);
214
+ break;
215
+ case "1":
216
+ case "id":
217
+ $this->setId($val);
218
+ break;
219
+ case "2":
220
+ case "desc":
221
+ case "description":
222
+ $this->setDescription($val);
223
+ break;
224
+ case "3":
225
+ case "months":
226
+ $this->setMonths($val);
227
+ break;
228
+ case "4":
229
+ case "startfee":
230
+ $this->setStartFee($val);
231
+ break;
232
+ case "5":
233
+ case "invoicefee":
234
+ $this->setInvoiceFee($val);
235
+ break;
236
+ case "6":
237
+ case "interestrate":
238
+ $this->setInterestRate($val);
239
+ break;
240
+ case "7":
241
+ case "minamount":
242
+ $this->setMinAmount($val);
243
+ break;
244
+ case "8":
245
+ case "country":
246
+ $this->setCountry($val);
247
+ break;
248
+ case "9":
249
+ case "type":
250
+ $this->setType($val);
251
+ break;
252
+ case "10":
253
+ case "expire":
254
+ $this->setExpire($val);
255
+ break;
256
+ default:
257
+ //Array index not supported.
258
+ break;
259
+ }
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Returns an associative array mirroring this PClass.
265
+ *
266
+ * @return array
267
+ */
268
+ public function toArray()
269
+ {
270
+ return array(
271
+ 'eid' => $this->eid,
272
+ 'id' => $this->id,
273
+ 'description' => $this->description,
274
+ 'months' => $this->months,
275
+ 'startfee' => $this->startFee,
276
+ 'invoicefee' => $this->invoiceFee,
277
+ 'interestrate' => $this->interestRate,
278
+ 'minamount' => $this->minAmount,
279
+ 'country' => $this->country,
280
+ 'type' => $this->type,
281
+ 'expire' => $this->expire
282
+ );
283
+ }
284
+
285
+ /**
286
+ * Sets the descriptiton, converts to HTML entities.
287
+ *
288
+ * @param string $description PClass description.
289
+ *
290
+ * @return void
291
+ */
292
+ public function setDescription($description)
293
+ {
294
+ $this->description = $description;
295
+ }
296
+
297
+ /**
298
+ * Sets the number of months.
299
+ *
300
+ * @param int $months Number of months.
301
+ *
302
+ * @return void
303
+ */
304
+ public function setMonths($months)
305
+ {
306
+ $this->months = intval($months);
307
+ }
308
+
309
+ /**
310
+ * Sets the starting fee.
311
+ *
312
+ * @param float $startFee Starting fee.
313
+ *
314
+ * @return void
315
+ */
316
+ public function setStartFee($startFee)
317
+ {
318
+ $this->startFee = floatval($startFee);
319
+ }
320
+
321
+ /**
322
+ * Sets the invoicing/handling fee.
323
+ *
324
+ * @param float $invoiceFee Invoicing fee.
325
+ *
326
+ * @return void
327
+ */
328
+ public function setInvoiceFee($invoiceFee)
329
+ {
330
+ $this->invoiceFee = floatval($invoiceFee);
331
+ }
332
+
333
+ /**
334
+ * Sets the interest rate.
335
+ *
336
+ * @param float $interestRate Interest rate.
337
+ *
338
+ * @return void
339
+ */
340
+ public function setInterestRate($interestRate)
341
+ {
342
+ $this->interestRate = floatval($interestRate);
343
+ }
344
+
345
+ /**
346
+ * Sets the Minimum amount to use this PClass.
347
+ *
348
+ * @param float $minAmount Minimum amount.
349
+ *
350
+ * @return void
351
+ */
352
+ public function setMinAmount($minAmount)
353
+ {
354
+ $this->minAmount = floatval($minAmount);
355
+ }
356
+
357
+ /**
358
+ * Sets the country for this PClass.
359
+ *
360
+ * @param int $country {@link KlarnaCountry} constant.
361
+ *
362
+ * @see KlarnaCountry
363
+ *
364
+ * @return void
365
+ */
366
+ public function setCountry($country)
367
+ {
368
+ $this->country = intval($country);
369
+ }
370
+
371
+ /**
372
+ * Sets the ID for this pclass.
373
+ *
374
+ * @param int $id PClass identifier.
375
+ *
376
+ * @return void
377
+ */
378
+ public function setId($id)
379
+ {
380
+ $this->id = intval($id);
381
+ }
382
+
383
+ /**
384
+ * Sets the type for this pclass.
385
+ *
386
+ * @param int $type PClass type identifier.
387
+ *
388
+ * @see self::CAMPAIGN
389
+ * @see self::ACCOUNT
390
+ * @see self::SPECIAL
391
+ * @see self::FIXED
392
+ * @see self::DELAY
393
+ * @see self::MOBILE
394
+ *
395
+ * @return void
396
+ */
397
+ public function setType($type)
398
+ {
399
+ $this->type = intval($type);
400
+ }
401
+
402
+
403
+ /**
404
+ * Returns the ID for this PClass.
405
+ *
406
+ * @return int PClass identifier.
407
+ */
408
+ public function getId()
409
+ {
410
+ return $this->id;
411
+ }
412
+
413
+ /**
414
+ * Returns this PClass's type.
415
+ *
416
+ * @see self::CAMPAIGN
417
+ * @see self::ACCOUNT
418
+ * @see self::SPECIAL
419
+ * @see self::FIXED
420
+ * @see self::DELAY
421
+ * @see self::MOBILE
422
+ *
423
+ * @return int PClass type identifier.
424
+ */
425
+ public function getType()
426
+ {
427
+ return $this->type;
428
+ }
429
+
430
+ /**
431
+ * Returns the Merchant ID or Estore ID connected to this PClass.
432
+ *
433
+ * @return int
434
+ */
435
+ public function getEid()
436
+ {
437
+ return $this->eid;
438
+ }
439
+
440
+ /**
441
+ * Merchant ID or Estore ID connected to this PClass.
442
+ *
443
+ * @param int $eid Merchant ID.
444
+ *
445
+ * @return void
446
+ */
447
+ public function setEid($eid)
448
+ {
449
+ $this->eid = intval($eid);
450
+ }
451
+
452
+ /**
453
+ * Checks whether this PClass is valid.
454
+ *
455
+ * @param int $now Unix timestamp
456
+ *
457
+ * @return bool
458
+ */
459
+ public function isValid($now = null)
460
+ {
461
+ if ($this->expire == null
462
+ || $this->expire == '-'
463
+ || $this->expire <= 0
464
+ ) {
465
+ //No expire, or unset? assume valid.
466
+ return true;
467
+ }
468
+
469
+ if ($now === null || !is_numeric($now)) {
470
+ $now = time();
471
+ }
472
+
473
+ //If now is before expire, it is still valid.
474
+ return ($now > $this->expire) ? false : true;
475
+ }
476
+
477
+ /**
478
+ * Returns the valid until/expire date unix timestamp.
479
+ *
480
+ * @return int
481
+ */
482
+ public function getExpire()
483
+ {
484
+ return $this->expire;
485
+ }
486
+
487
+ /**
488
+ * Sets the valid until/expire date unix timestamp.
489
+ *
490
+ * @param int $expire unix timestamp for expire
491
+ *
492
+ * @return void
493
+ */
494
+ public function setExpire($expire)
495
+ {
496
+ $this->expire = $expire;
497
+ }
498
+
499
+ /**
500
+ * Returns the description for this PClass.
501
+ *
502
+ * <b>Note</b>:<br>
503
+ * Encoded with HTML entities.
504
+ *
505
+ * @return string PClass description.
506
+ */
507
+ public function getDescription()
508
+ {
509
+ return $this->description;
510
+ }
511
+
512
+ /**
513
+ * Returns the number of months for this PClass.
514
+ *
515
+ * @return int Number of months.
516
+ */
517
+ public function getMonths()
518
+ {
519
+ return $this->months;
520
+ }
521
+
522
+ /**
523
+ * Returns the starting fee for this PClass.
524
+ *
525
+ * @return float Starting fee.
526
+ */
527
+ public function getStartFee()
528
+ {
529
+ return $this->startFee;
530
+ }
531
+
532
+ /**
533
+ * Returns the invoicing/handling fee for this PClass.
534
+ *
535
+ * @return float Invoicing fee.
536
+ */
537
+ public function getInvoiceFee()
538
+ {
539
+ return $this->invoiceFee;
540
+ }
541
+
542
+ /**
543
+ * Returns the interest rate for this PClass.
544
+ *
545
+ * @return float Interest rate.
546
+ */
547
+ public function getInterestRate()
548
+ {
549
+ return $this->interestRate;
550
+ }
551
+
552
+ /**
553
+ * Returns the minimum order/product amount for which this PClass is allowed.
554
+ *
555
+ * @return float Minimum amount to use this PClass.
556
+ */
557
+ public function getMinAmount()
558
+ {
559
+ return $this->minAmount;
560
+ }
561
+
562
+ /**
563
+ * Returns the country related to this PClass.
564
+ *
565
+ * @see KlarnaCountry
566
+ * @return int {@link KlarnaCountry} constant.
567
+ */
568
+ public function getCountry()
569
+ {
570
+ return $this->country;
571
+ }
572
+
573
+ }
lib/Klarna/pclasses/jsonstorage.class.php ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * JsonStorage
4
+ *
5
+ * PHP Version 5.3
6
+ *
7
+ * @category Payment
8
+ * @package KlarnaAPI
9
+ * @author MS Dev <ms.modules@klarna.com>
10
+ * @copyright 2012 Klarna AB (http://klarna.com)
11
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
12
+ * @link http://integration.klarna.com/
13
+ */
14
+
15
+ /**
16
+ * Include the {@link PCStorage} interface.
17
+ */
18
+ require_once 'storage.intf.php';
19
+
20
+ /**
21
+ * JSON storage class for KlarnaPClass
22
+ *
23
+ * This class is an JSON implementation of the PCStorage interface.
24
+ *
25
+ * @category Payment
26
+ * @package KlarnaAPI
27
+ * @author MS Dev <ms.modules@klarna.com>
28
+ * @copyright 2012 Klarna AB (http://klarna.com)
29
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
30
+ * @link http://integration.klarna.com/
31
+ */
32
+
33
+ class JSONStorage extends PCStorage
34
+ {
35
+
36
+
37
+ /**
38
+ * return the name of the storage type
39
+ *
40
+ * @return string
41
+ */
42
+ public function getName()
43
+ {
44
+ return "json";
45
+ }
46
+
47
+ /**
48
+ * Checks if the file is writeable, readable or if the directory is.
49
+ *
50
+ * @param string $jsonFile json file that holds the pclasses
51
+ *
52
+ * @throws error
53
+ * @return void
54
+ */
55
+ protected function checkURI($jsonFile)
56
+ {
57
+ //If file doesn't exist, check the directory.
58
+ if (!file_exists($jsonFile)) {
59
+ $jsonFile = dirname($jsonFile);
60
+ }
61
+
62
+ if (!is_writable($jsonFile)) {
63
+ throw new Klarna_FileNotWritableException($jsonFile);
64
+ }
65
+
66
+ if (!is_readable($jsonFile)) {
67
+ throw new Klarna_FileNotReadableException($jsonFile);
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Clear the pclasses
73
+ *
74
+ * @param string $uri uri to file to clear
75
+ *
76
+ * @throws KlarnaException
77
+ * @return void
78
+ */
79
+ public function clear($uri)
80
+ {
81
+ $this->checkURI($uri);
82
+ unset($this->pclasses);
83
+ if (file_exists($uri)) {
84
+ unlink($uri);
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Load pclasses from file
90
+ *
91
+ * @param string $uri uri to file to load
92
+ *
93
+ * @throws KlarnaException
94
+ * @return void
95
+ */
96
+ public function load($uri)
97
+ {
98
+ $this->checkURI($uri);
99
+ if (!file_exists($uri)) {
100
+ //Do not fail, if file doesn't exist.
101
+ return;
102
+ }
103
+ $arr = json_decode(file_get_contents($uri), true);
104
+
105
+ if (count($arr) == 0) {
106
+ return;
107
+ }
108
+
109
+ foreach ($arr as $pclasses) {
110
+ if (count($pclasses) == 0) {
111
+ continue;
112
+ }
113
+ foreach ($pclasses as $pclass) {
114
+ $this->addPClass(new KlarnaPClass($pclass));
115
+ }
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Save pclasses to file
121
+ *
122
+ * @param string $uri uri to file to save
123
+ *
124
+ * @throws KlarnaException
125
+ * @return void
126
+ */
127
+ public function save($uri)
128
+ {
129
+ try {
130
+ $this->checkURI($uri);
131
+
132
+ $output = array();
133
+ foreach ($this->pclasses as $eid => $pclasses) {
134
+ foreach ($pclasses as $pclass) {
135
+ if (!isset($output[$eid])) {
136
+ $output[$eid] = array();
137
+ }
138
+ $output[$eid][] = $pclass->toArray();
139
+ }
140
+ }
141
+ if (count($this->pclasses) > 0) {
142
+ file_put_contents($uri, json_encode($output));
143
+ } else {
144
+ file_put_contents($uri, "");
145
+ }
146
+ } catch(Exception $e) {
147
+ throw new KlarnaException($e->getMessage());
148
+ }
149
+ }
150
+ }
lib/Klarna/pclasses/mysqlstorage.class.php ADDED
@@ -0,0 +1,335 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * MySQL Storage
4
+ *
5
+ * PHP Version 5.3
6
+ *
7
+ * @category Payment
8
+ * @package KlarnaAPI
9
+ * @author MS Dev <ms.modules@klarna.com>
10
+ * @copyright 2012 Klarna AB (http://klarna.com)
11
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
12
+ * @link http://integration.klarna.com/
13
+ */
14
+
15
+ /**
16
+ * Include the {@link PCStorage} interface.
17
+ */
18
+ require_once 'storage.intf.php';
19
+
20
+ /**
21
+ * MySQL storage class for KlarnaPClass
22
+ *
23
+ * This class is an MySQL implementation of the PCStorage interface.<br>
24
+ * Config field pcURI needs to match format:
25
+ * user:passwd@addr:port/dbName.dbTable<br>
26
+ * Port can be omitted.<br>
27
+ *
28
+ * <b>Acceptable characters</b>:<br>
29
+ * Username: [A-Za-z0-9_]<br>
30
+ * Password: [A-Za-z0-9_]<br>
31
+ * Address: [A-Za-z0-9_.]<br>
32
+ * Port: [0-9]<br>
33
+ * DB name: [A-Za-z0-9_]<br>
34
+ * DB table: [A-Za-z0-9_]<br>
35
+ *
36
+ * To allow for more special characters, and to avoid having<br>
37
+ * a regular expression that is too hard to understand, you can<br>
38
+ * use an associative array:<br>
39
+ * <code>
40
+ * array(
41
+ * "user" => "myuser",
42
+ * "passwd" => "mypass",
43
+ * "dsn" => "localhost",
44
+ * "db" => "mydatabase",
45
+ * "table" => "mytable"
46
+ * );
47
+ * </code>
48
+ *
49
+ * @category Payment
50
+ * @package KlarnaAPI
51
+ * @author MS Dev <ms.modules@klarna.com>
52
+ * @copyright 2012 Klarna AB (http://klarna.com)
53
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
54
+ * @link http://integration.klarna.com/
55
+ */
56
+ class MySQLStorage extends PCStorage
57
+ {
58
+
59
+ /**
60
+ * Database name.
61
+ *
62
+ * @var string
63
+ */
64
+ protected $dbName;
65
+
66
+ /**
67
+ * Database table.
68
+ *
69
+ * @var string
70
+ */
71
+ protected $dbTable;
72
+
73
+ /**
74
+ * Database address.
75
+ *
76
+ * @var string
77
+ */
78
+ protected $addr;
79
+
80
+ /**
81
+ * Database username.
82
+ *
83
+ * @var string
84
+ */
85
+ protected $user;
86
+
87
+ /**
88
+ * Database password.
89
+ *
90
+ * @var string
91
+ */
92
+ protected $passwd;
93
+
94
+ /**
95
+ * MySQL DB link resource.
96
+ *
97
+ * @var resource
98
+ */
99
+ protected $link;
100
+
101
+ /**
102
+ * return the name of the storage type
103
+ *
104
+ * @return string
105
+ */
106
+ public function getName()
107
+ {
108
+ return "mysql";
109
+ }
110
+
111
+ /**
112
+ * Connects to the DB and checks if DB and table exists.
113
+ *
114
+ * @throws KlarnaException
115
+ * @return void
116
+ */
117
+ protected function connect()
118
+ {
119
+ $this->link = mysql_connect($this->addr, $this->user, $this->passwd);
120
+ if ($this->link === false) {
121
+ throw new Klarna_DatabaseException(
122
+ 'Failed to connect to database! ('.mysql_error().')'
123
+ );
124
+ }
125
+
126
+ if (!mysql_query(
127
+ "CREATE DATABASE IF NOT EXISTS `{$this->dbName}`",
128
+ $this->link
129
+ )
130
+ ) {
131
+ throw new Klarna_DatabaseException(
132
+ 'Failed to create! ('.mysql_error().')'
133
+ );
134
+ }
135
+
136
+ $create = mysql_query(
137
+ "CREATE TABLE IF NOT EXISTS `{$this->dbName}`.`{$this->dbTable}` (
138
+ `eid` int(10) unsigned NOT NULL,
139
+ `id` int(10) unsigned NOT NULL,
140
+ `type` tinyint(4) NOT NULL,
141
+ `description` varchar(255) NOT NULL,
142
+ `months` int(11) NOT NULL,
143
+ `interestrate` decimal(11,2) NOT NULL,
144
+ `invoicefee` decimal(11,2) NOT NULL,
145
+ `startfee` decimal(11,2) NOT NULL,
146
+ `minamount` decimal(11,2) NOT NULL,
147
+ `country` int(11) NOT NULL,
148
+ `expire` int(11) NOT NULL,
149
+ KEY `id` (`id`)
150
+ )", $this->link
151
+ );
152
+
153
+ if (!$create) {
154
+ throw new Klarna_DatabaseException(
155
+ 'Table not existing, failed to create! ('.mysql_error().')'
156
+ );
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Splits the URI in format: user:passwd@addr/dbName.dbTable<br>
162
+ *
163
+ * To allow for more special characters, and to avoid having<br>
164
+ * a regular expression that is too hard to understand, you can<br>
165
+ * use an associative array:<br>
166
+ * <code>
167
+ * array(
168
+ * "user" => "myuser",
169
+ * "passwd" => "mypass",
170
+ * "dsn" => "localhost",
171
+ * "db" => "mydatabase",
172
+ * "table" => "mytable"
173
+ * );
174
+ * </code>
175
+ *
176
+ * @param string|array $uri Specified URI to database and table.
177
+ *
178
+ * @throws KlarnaException
179
+ * @return void
180
+ */
181
+ protected function splitURI($uri)
182
+ {
183
+ if (is_array($uri)) {
184
+ $this->user = $uri['user'];
185
+ $this->passwd = $uri['passwd'];
186
+ $this->addr = $uri['dsn'];
187
+ $this->dbName = $uri['db'];
188
+ $this->dbTable = $uri['table'];
189
+ } else if (preg_match(
190
+ '/^([\w-]+):([\w-]+)@([\w\.-]+|[\w\.-]+:[\d]+)\/([\w-]+).([\w-]+)$/',
191
+ $uri,
192
+ $arr
193
+ ) === 1
194
+ ) {
195
+ /*
196
+ [0] => user:passwd@addr/dbName.dbTable
197
+ [1] => user
198
+ [2] => passwd
199
+ [3] => addr
200
+ [4] => dbName
201
+ [5] => dbTable
202
+ */
203
+ if (count($arr) != 6) {
204
+ throw new Klarna_DatabaseException(
205
+ 'URI is invalid! Missing field or invalid characters used!'
206
+ );
207
+ }
208
+
209
+ $this->user = $arr[1];
210
+ $this->passwd = $arr[2];
211
+ $this->addr = $arr[3];
212
+ $this->dbName = $arr[4];
213
+ $this->dbTable = $arr[5];
214
+ } else {
215
+ throw new Klarna_DatabaseException(
216
+ 'URI to MySQL is not valid! ( user:passwd@addr/dbName.dbTable )'
217
+ );
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Load pclasses
223
+ *
224
+ * @param string $uri pclass uri
225
+ *
226
+ * @throws KlarnaException
227
+ * @return void
228
+ */
229
+ public function load($uri)
230
+ {
231
+ $this->splitURI($uri);
232
+ $this->connect();
233
+ $result = mysql_query(
234
+ "SELECT * FROM `{$this->dbName}`.`{$this->dbTable}`",
235
+ $this->link
236
+ );
237
+ if ($result === false) {
238
+ throw new Klarna_DatabaseException(
239
+ 'SELECT query failed! ('.mysql_error().')'
240
+ );
241
+ }
242
+ while ($row = mysql_fetch_assoc($result)) {
243
+ $this->addPClass(new KlarnaPClass($row));
244
+ }
245
+ }
246
+
247
+ /**
248
+ * Save pclasses to database
249
+ *
250
+ * @param string $uri pclass uri
251
+ *
252
+ * @throws KlarnaException
253
+ * @return void
254
+ */
255
+ public function save($uri)
256
+ {
257
+ $this->splitURI($uri);
258
+
259
+ $this->connect();
260
+ if (!is_array($this->pclasses) || count($this->pclasses) == 0) {
261
+ return;
262
+ }
263
+
264
+ foreach ($this->pclasses as $pclasses) {
265
+ foreach ($pclasses as $pclass) {
266
+ //Remove the pclass if it exists.
267
+ mysql_query(
268
+ "DELETE FROM `{$this->dbName}`.`{$this->dbTable}`
269
+ WHERE `id` = '{$pclass->getId()}'
270
+ AND `eid` = '{$pclass->getEid()}'"
271
+ );
272
+
273
+ //Insert it again.
274
+ $result = mysql_query(
275
+ "INSERT INTO `{$this->dbName}`.`{$this->dbTable}`
276
+ (`eid`,
277
+ `id`,
278
+ `type`,
279
+ `description`,
280
+ `months`,
281
+ `interestrate`,
282
+ `invoicefee`,
283
+ `startfee`,
284
+ `minamount`,
285
+ `country`,
286
+ `expire`
287
+ )
288
+ VALUES
289
+ ('{$pclass->getEid()}',
290
+ '{$pclass->getId()}',
291
+ '{$pclass->getType()}',
292
+ '{$pclass->getDescription()}',
293
+ '{$pclass->getMonths()}',
294
+ '{$pclass->getInterestRate()}',
295
+ '{$pclass->getInvoiceFee()}',
296
+ '{$pclass->getStartFee()}',
297
+ '{$pclass->getMinAmount()}',
298
+ '{$pclass->getCountry()}',
299
+ '{$pclass->getExpire()}')", $this->link
300
+ );
301
+ if ($result === false) {
302
+ throw new Klarna_DatabaseException(
303
+ 'INSERT INTO query failed! ('.mysql_error().')'
304
+ );
305
+ }
306
+ }
307
+ }
308
+ }
309
+
310
+ /**
311
+ * Clear the pclasses
312
+ *
313
+ * @param string $uri pclass uri
314
+ *
315
+ * @throws KlarnaException
316
+ * @return void
317
+ */
318
+ public function clear($uri)
319
+ {
320
+ try {
321
+ $this->splitURI($uri);
322
+ unset($this->pclasses);
323
+ $this->connect();
324
+
325
+ mysql_query(
326
+ "DELETE FROM `{$this->dbName}`.`{$this->dbTable}`",
327
+ $this->link
328
+ );
329
+ } catch(Exception $e) {
330
+ throw new Klarna_DatabaseException(
331
+ $e->getMessage(), $e->getCode()
332
+ );
333
+ }
334
+ }
335
+ }
lib/Klarna/pclasses/sqlstorage.class.php ADDED
@@ -0,0 +1,473 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * SQL Storage
4
+ *
5
+ * PHP Version 5.3
6
+ *
7
+ * @category Payment
8
+ * @package KlarnaAPI
9
+ * @author MS Dev <ms.modules@klarna.com>
10
+ * @copyright 2012 Klarna AB (http://klarna.com)
11
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
12
+ * @link http://integration.klarna.com/
13
+ */
14
+
15
+ /**
16
+ * Include the {@link PCStorage} interface.
17
+ */
18
+ require_once 'storage.intf.php';
19
+
20
+ /**
21
+ * SQL storage class for KlarnaPClass
22
+ *
23
+ * This class is an MySQL implementation of the PCStorage interface.<br>
24
+ * Config field pcURI needs to match format:
25
+ * user:passwd@addr:port/dbName.dbTable<br>
26
+ * Port can be omitted.<br>
27
+ *
28
+ * <b>Acceptable characters</b>:<br>
29
+ * Username: [A-Za-z0-9_]<br>
30
+ * Password: [A-Za-z0-9_]<br>
31
+ * Address: [A-Za-z0-9_.]<br>
32
+ * Port: [0-9]<br>
33
+ * DB name: [A-Za-z0-9_]<br>
34
+ * DB table: [A-Za-z0-9_]<br>
35
+ *
36
+ * To allow for more special characters, and to avoid having<br>
37
+ * a regular expression that is too hard to understand, you can<br>
38
+ * use an associative array:<br>
39
+ * <code>
40
+ * array(
41
+ * "user" => "myuser",
42
+ * "passwd" => "mypass",
43
+ * "dsn" => "localhost",
44
+ * "db" => "mydatabase",
45
+ * "table" => "mytable"
46
+ * );
47
+ * </code>
48
+ *
49
+ * @category Payment
50
+ * @package KlarnaAPI
51
+ * @author MS Dev <ms.modules@klarna.com>
52
+ * @copyright 2012 Klarna AB (http://klarna.com)
53
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
54
+ * @link http://integration.klarna.com/
55
+ */
56
+ class SQLStorage extends PCStorage
57
+ {
58
+
59
+ /**
60
+ * Database name.
61
+ *
62
+ * @var string
63
+ */
64
+ protected $dbName;
65
+
66
+ /**
67
+ * Database table.
68
+ *
69
+ * @var string
70
+ */
71
+ protected $dbTable;
72
+
73
+ /**
74
+ * Database address.
75
+ *
76
+ * @var string
77
+ */
78
+ protected $addr;
79
+
80
+ /**
81
+ * PDO DSN notation.
82
+ *
83
+ * @var string
84
+ */
85
+ protected $dsn;
86
+
87
+ /**
88
+ * Database username.
89
+ *
90
+ * @var string
91
+ */
92
+ protected $user;
93
+
94
+ /**
95
+ * Database password.
96
+ *
97
+ * @var string
98
+ */
99
+ protected $passwd;
100
+
101
+ /**
102
+ * PDO DB link resource.
103
+ *
104
+ * @var PDO
105
+ */
106
+ protected $pdo;
107
+
108
+ /**
109
+ * return the name of the storage type
110
+ *
111
+ * @return string
112
+ */
113
+ public function getName()
114
+ {
115
+ return "sql";
116
+ }
117
+
118
+ /**
119
+ * Splits the URI for the following formats:<br>
120
+ * user:passwd@addr/dbName.dbTable (assumes MySQL)<br>
121
+ * user:password@pdo:dsn/dbName.dbTable<br>
122
+ *
123
+ * To allow for more special characters, and to avoid having<br>
124
+ * a regular expression that is too hard to understand, you can<br>
125
+ * use an associative array:<br>
126
+ * <code>
127
+ * array(
128
+ * "user" => "myuser",
129
+ * "passwd" => "mypass",
130
+ * "dsn" => "localhost",
131
+ * "db" => "mydatabase",
132
+ * "table" => "mytable"
133
+ * );
134
+ * </code>
135
+ *
136
+ * @param string|array $uri Specified URI to database and table.
137
+ *
138
+ * @throws KlarnaException
139
+ * @return void
140
+ */
141
+ protected function splitURI($uri)
142
+ {
143
+ /* If you want to have some characters that would make the
144
+ regexp too complex, you can use an array as input instead. */
145
+ if (is_array($uri)) {
146
+ $this->user = $uri['user'];
147
+ $this->passwd = $uri['passwd'];
148
+ $this->dsn = $uri['dsn'];
149
+ $this->dbName = $uri['db'];
150
+ $this->dbTable = $uri['table'];
151
+
152
+ return array(
153
+ $uri,
154
+ $this->user,
155
+ $this->passwd,
156
+ $this->dsn,
157
+ $this->dbName,
158
+ $this->dbTable
159
+ );
160
+ }
161
+ $pdo_rex
162
+ = '/^([\w-]+):([\w-]+)@pdo:([\w.,:;\/ \\\t=\(\){}\*-]+)\/([\w-]+)'.
163
+ '.([\w-]+)$/';
164
+ $pcuri_rex
165
+ = '/^([\w-]+):([\w-]+)@([\w\.-]+|[\w\.-]+:[\d]+|[\w\.-]+:'.
166
+ '[\w\.\/-]+|:[\w\.\/-]+)\/([\w-]+).([\w-]+)$/';
167
+ $arr = null;
168
+ if (preg_match($pdo_rex, $uri, $arr) === 1) {
169
+ /*
170
+ * [0] => user:password@pdo:dsn/dbName.dbTable
171
+ * [1] => user
172
+ * [2] => passwd
173
+ * [3] => dsn
174
+ * [4] => dbName
175
+ * [5] => dbTable
176
+ */
177
+ if (count($arr) != 6) {
178
+ throw new Klarna_DatabaseException(
179
+ 'URI is invalid! Missing field or invalid characters used!'
180
+ );
181
+ }
182
+
183
+ $this->user = $arr[1];
184
+ $this->passwd = $arr[2];
185
+ $this->dsn = $arr[3];
186
+ $this->dbName = $arr[4];
187
+ $this->dbTable = $arr[5];
188
+ } else if (preg_match($pcuri_rex, $uri, $arr) === 1) {
189
+ //user:pass@127.0.0.1:3306/dbName.dbTable
190
+ //user:pass@localhost:/tmp/mysql.sock/dbName.dbTable
191
+ /*
192
+ * [0] => user:passwd@addr/dbName.dbTable
193
+ * [1] => user
194
+ * [2] => passwd
195
+ * [3] => addr
196
+ * [4] => dbName
197
+ * [5] => dbTable
198
+ */
199
+ if (count($arr) != 6) {
200
+ throw new Klarna_DatabaseException(
201
+ 'URI is invalid! Missing field or invalid characters used!'
202
+ );
203
+ }
204
+
205
+ $this->user = $arr[1];
206
+ $this->passwd = $arr[2];
207
+ $this->addr = $arr[3];
208
+ $this->port = 3306;
209
+ if (preg_match(
210
+ '/^([0-9.]+(:([0-9]+))?)$/', $this->addr, $tmp
211
+ ) === 1
212
+ ) {
213
+ if (isset($tmp[3])) {
214
+ $this->port = $tmp[3];
215
+ }
216
+ }
217
+ $this->dbName = $arr[4];
218
+ $this->dbTable = $arr[5];
219
+ $this->dsn = "mysql:host={$this->addr};port={$this->port};";
220
+ } else {
221
+ throw new Klarna_DatabaseException(
222
+ 'URI to SQL is not valid! ( user:passwd@addr/dbName.dbTable )'
223
+ );
224
+ }
225
+
226
+ return $arr;
227
+ }
228
+
229
+ /**
230
+ * Grabs the PDO connection to the database, specified by the URI.
231
+ *
232
+ * @param string $uri pclass uri
233
+ *
234
+ * @return void
235
+ * @throws KlarnaException
236
+ */
237
+ protected function getConnection($uri)
238
+ {
239
+ if ($this->pdo) {
240
+ return; //Already have a connection
241
+ }
242
+
243
+ $this->splitURI($uri);
244
+
245
+ try {
246
+ $this->pdo = new PDO($this->dsn, $this->user, $this->passwd);
247
+ $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
248
+ } catch (PDOException $e) {
249
+ throw new Klarna_DatabaseException('Failed to connect to database!');
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Initializes the DB, if the database or table is missing.
255
+ *
256
+ * @return void
257
+ * @throws KlarnaException
258
+ */
259
+ protected function initDB()
260
+ {
261
+ try {
262
+ $this->pdo->exec("CREATE DATABASE `{$this->dbName}`");
263
+ } catch (PDOException $e) {
264
+ //SQLite does not support this...
265
+ //throw new KlarnaException(
266
+ // 'Database non-existant, failed to create it!'
267
+ //);
268
+ }
269
+
270
+ $sql = <<<SQL
271
+ CREATE TABLE IF NOT EXISTS `{$this->dbName}`.`{$this->dbTable}` (
272
+ `eid` int(10) NOT NULL,
273
+ `id` int(10) NOT NULL,
274
+ `type` int(4) NOT NULL,
275
+ `description` varchar(255) NOT NULL,
276
+ `months` int(11) NOT NULL,
277
+ `interestrate` decimal(11,2) NOT NULL,
278
+ `invoicefee` decimal(11,2) NOT NULL,
279
+ `startfee` decimal(11,2) NOT NULL,
280
+ `minamount` decimal(11,2) NOT NULL,
281
+ `country` int(11) NOT NULL,
282
+ `expire` int(11) NOT NULL
283
+ );
284
+ SQL;
285
+ try {
286
+ $this->pdo->exec($sql);
287
+ } catch (PDOException $e) {
288
+ throw new Klarna_DatabaseException(
289
+ 'Table non-existant, failed to create it!'
290
+ );
291
+ }
292
+ }
293
+
294
+ /**
295
+ * Connects to the DB and checks if DB and table exists.
296
+ *
297
+ * @param string|array $uri pclass uri
298
+ *
299
+ * @throws KlarnaException
300
+ * @return void
301
+ */
302
+ protected function connect($uri)
303
+ {
304
+ $this->getConnection($uri);
305
+ $this->initDB();
306
+ }
307
+
308
+ /**
309
+ * Loads the PClasses.
310
+ *
311
+ * @param string|array $uri pclass uri
312
+ *
313
+ * @return void
314
+ * @throws KlarnaException
315
+ */
316
+ public function load($uri)
317
+ {
318
+ $this->connect($uri);
319
+ $this->loadPClasses();
320
+ }
321
+
322
+ /**
323
+ * Loads the PClasses.
324
+ *
325
+ * @return void
326
+ * @throws KlarnaException
327
+ */
328
+ protected function loadPClasses()
329
+ {
330
+ try {
331
+ $sth = $this->pdo->prepare(
332
+ "SELECT * FROM `{$this->dbName}`.`{$this->dbTable}`",
333
+ array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY)
334
+ );
335
+ $sth->execute();
336
+
337
+ while ($row = $sth->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_NEXT)) {
338
+ $this->addPClass(new KlarnaPClass($row));
339
+ }
340
+
341
+ $sth->closeCursor();
342
+ $sth = null;
343
+ } catch (PDOException $e) {
344
+ throw new Klarna_DatabaseException(
345
+ 'Could not fetch PClasses from database!'
346
+ );
347
+ }
348
+ }
349
+
350
+ /**
351
+ * Saves the PClasses.
352
+ *
353
+ * @param string|array $uri pclass uri
354
+ *
355
+ * @return void
356
+ * @throws KlarnaException
357
+ */
358
+ public function save($uri)
359
+ {
360
+ $this->connect($uri);
361
+ //Only attempt to savePClasses if there are any.
362
+ if (!is_array($this->pclasses)) {
363
+ return;
364
+ }
365
+ if (count($this->pclasses) == 0) {
366
+ return;
367
+ }
368
+ $this->savePClasses();
369
+ }
370
+
371
+ /**
372
+ * Saves the PClasses.
373
+ *
374
+ * @return void
375
+ * @throws KlarnaException
376
+ */
377
+ protected function savePClasses()
378
+ {
379
+ //Insert PClass SQL statement.
380
+ $sql = <<<SQL
381
+ INSERT INTO `{$this->dbName}`.`{$this->dbTable}`
382
+ (`eid`, `id`, `type`, `description`, `months`, `interestrate`,
383
+ `invoicefee`, `startfee`, `minamount`, `country`, `expire`)
384
+ VALUES
385
+ (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
386
+ SQL;
387
+
388
+ foreach ($this->pclasses as $pclasses) {
389
+ foreach ($pclasses as $pclass) {
390
+ try {
391
+ //Remove the pclass if it exists.
392
+ $sth = $this->pdo->prepare(
393
+ "DELETE FROM `{$this->dbName}`.`{$this->dbTable}`
394
+ WHERE `id` = ? AND `eid` = ?"
395
+ );
396
+ $sth->execute(
397
+ array(
398
+ $pclass->getId(), $pclass->getEid()
399
+ )
400
+ );
401
+
402
+ $sth->closeCursor();
403
+ $sth = null;
404
+ } catch(PDOException $e) {
405
+ //Fail silently, we don't care if the removal failed.
406
+ }
407
+
408
+ try {
409
+ //Attempt to insert the PClass into the DB.
410
+ $sth = $this->pdo->prepare($sql);
411
+ $sth->execute(
412
+ array(
413
+ $pclass->getEid(),
414
+ $pclass->getId(),
415
+ $pclass->getType(),
416
+ $pclass->getDescription(),
417
+ $pclass->getMonths(),
418
+ $pclass->getInterestRate(),
419
+ $pclass->getInvoiceFee(),
420
+ $pclass->getStartFee(),
421
+ $pclass->getMinAmount(),
422
+ $pclass->getCountry(),
423
+ $pclass->getExpire()
424
+ )
425
+ );
426
+
427
+ $sth->closeCursor();
428
+ $sth = null;
429
+ } catch(PDOException $e) {
430
+ throw new Klarna_DatabaseException(
431
+ 'Failed to insert PClass into database!'
432
+ );
433
+ }
434
+ }
435
+ }
436
+ }
437
+
438
+ /**
439
+ * Drops the database table, to clear the PClasses.
440
+ *
441
+ * @param string|array $uri pclass uri
442
+ *
443
+ * @return void
444
+ * @throws KlarnaException
445
+ */
446
+ public function clear($uri)
447
+ {
448
+ try {
449
+ $this->connect($uri);
450
+ unset($this->pclasses);
451
+ $this->clearTable();
452
+ } catch(Exception $e) {
453
+ throw new Klarna_DatabaseException(
454
+ $e->getMessage(), $e->getCode()
455
+ );
456
+ }
457
+ }
458
+
459
+ /**
460
+ * Drops the database table, to clear the PClasses.
461
+ *
462
+ * @return void
463
+ * @throws KlarnaException
464
+ */
465
+ protected function clearTable()
466
+ {
467
+ try {
468
+ $this->pdo->exec("DELETE FROM `{$this->dbName}`.`{$this->dbTable}`");
469
+ } catch (PDOException $e) {
470
+ throw new Klarna_DatabaseException('Could not clear the database!');
471
+ }
472
+ }
473
+ }
lib/Klarna/pclasses/storage.intf.php ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * PClass Storage Interface
4
+ *
5
+ * PHP Version 5.3
6
+ *
7
+ * @category Payment
8
+ * @package KlarnaAPI
9
+ * @author MS Dev <ms.modules@klarna.com>
10
+ * @copyright 2012 Klarna AB (http://klarna.com)
11
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
12
+ * @link http://integration.klarna.com/
13
+ */
14
+
15
+ /**
16
+ * KlarnaPClass Storage interface
17
+ *
18
+ * This class provides an interface with which to save the PClasses easily.
19
+ *
20
+ * @category Payment
21
+ * @package KlarnaAPI
22
+ * @author MS Dev <ms.modules@klarna.com>
23
+ * @copyright 2012 Klarna AB (http://klarna.com)
24
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
25
+ * @link http://integration.klarna.com/
26
+ */
27
+ abstract class PCStorage
28
+ {
29
+
30
+ /**
31
+ * An array of KlarnaPClasses.
32
+ *
33
+ * @var array
34
+ */
35
+ protected $pclasses;
36
+
37
+ /**
38
+ * Thhe name of the implementation.
39
+ * The file should be <name>storage.class.php
40
+ *
41
+ * @return string
42
+ */
43
+ abstract public function getName();
44
+
45
+ /**
46
+ * Adds a PClass to the storage.
47
+ *
48
+ * @param KlarnaPClass $pclass PClass object.
49
+ *
50
+ * @throws KlarnaException
51
+ * @return void
52
+ */
53
+ public function addPClass($pclass)
54
+ {
55
+ if (! $pclass instanceof KlarnaPClass) {
56
+ throw new Klarna_InvalidTypeException('pclass', 'KlarnaPClass');
57
+ }
58
+
59
+ if (!isset($this->pclasses) || !is_array($this->pclasses)) {
60
+ $this->pclasses = array();
61
+ }
62
+
63
+ if ($pclass->getDescription() === null || $pclass->getType() === null) {
64
+ //Something went wrong, do not save these!
65
+ return;
66
+ }
67
+
68
+ if (!isset($this->pclasses[$pclass->getEid()])) {
69
+ $this->pclasses[$pclass->getEid()] = array();
70
+ }
71
+ $this->pclasses[$pclass->getEid()][$pclass->getId()] = $pclass;
72
+ }
73
+
74
+ /**
75
+ * Gets the PClass by ID.
76
+ *
77
+ * @param int $id PClass ID.
78
+ * @param int $eid Merchant ID.
79
+ * @param int $country {@link KlarnaCountry Country} constant.
80
+ *
81
+ * @throws KlarnaException
82
+ * @return KlarnaPClass
83
+ */
84
+ public function getPClass($id, $eid, $country)
85
+ {
86
+ if (!is_int($id)) {
87
+ throw new InvalidArgumentException('Supplied ID is not an integer!');
88
+ }
89
+
90
+ if (!is_array($this->pclasses)) {
91
+ throw new Klarna_PClassException('No match for that eid!');
92
+ }
93
+
94
+ if (!isset($this->pclasses[$eid]) || !is_array($this->pclasses[$eid])) {
95
+ throw new Klarna_PClassException('No match for that eid!');
96
+ }
97
+
98
+ if (!isset($this->pclasses[$eid][$id])
99
+ || !$this->pclasses[$eid][$id]->isValid()
100
+ ) {
101
+ throw new Klarna_PClassException('No such pclass available!');
102
+ }
103
+
104
+ if ($this->pclasses[$eid][$id]->getCountry() !== $country) {
105
+ throw new Klarna_PClassException(
106
+ 'You cannot use this pclass with set country!'
107
+ );
108
+ }
109
+
110
+ return $this->pclasses[$eid][$id];
111
+ }
112
+
113
+ /**
114
+ * Returns an array of KlarnaPClasses, keyed with pclass ID.
115
+ * If type is specified, only that type will be returned.
116
+ *
117
+ * <b>Types available</b>:<br>
118
+ * {@link KlarnaPClass::ACCOUNT}<br>
119
+ * {@link KlarnaPClass::CAMPAIGN}<br>
120
+ * {@link KlarnaPClass::SPECIAL}<br>
121
+ * {@link KlarnaPClass::DELAY}<br>
122
+ * {@link KlarnaPClass::MOBILE}<br>
123
+ *
124
+ * @param int $eid Merchant ID.
125
+ * @param int $country {@link KlarnaCountry Country} constant.
126
+ * @param int $type PClass type identifier.
127
+ *
128
+ * @throws KlarnaException
129
+ * @return array An array of {@link KlarnaPClass PClasses}.
130
+ */
131
+ public function getPClasses($eid, $country, $type = null)
132
+ {
133
+ if (!is_int($country)) {
134
+ throw new Klarna_ArgumentNotSetException('country');
135
+ }
136
+
137
+ $tmp = false;
138
+ if (!is_array($this->pclasses)) {
139
+ return;
140
+ }
141
+
142
+ $tmp = array();
143
+ foreach ($this->pclasses as $eid => $pclasses) {
144
+ $tmp[$eid] = array();
145
+
146
+ foreach ($pclasses as $pclass) {
147
+ if (!$pclass->isValid()) {
148
+ continue; //Pclass invalid, skip it.
149
+ }
150
+ if ($pclass->getEid() === $eid
151
+ && $pclass->getCountry() === $country
152
+ && ($pclass->getType() === $type || $type === null)
153
+ ) {
154
+ $tmp[$eid][$pclass->getId()] = $pclass;
155
+ }
156
+ }
157
+ }
158
+
159
+ return $tmp;
160
+ }
161
+
162
+ /**
163
+ * Returns a flattened array of all pclasses
164
+ *
165
+ * @return array
166
+ */
167
+ public function getAllPClasses()
168
+ {
169
+ if (!is_array($this->pclasses)) {
170
+ return array();
171
+ }
172
+ return $this->_flatten(array_values($this->pclasses));
173
+ }
174
+
175
+ /**
176
+ * Flatten an array
177
+ *
178
+ * @param array $array array to flatten
179
+ *
180
+ * @return array
181
+ */
182
+ private function _flatten($array)
183
+ {
184
+ if (!is_array($array)) {
185
+ // nothing to do if it's not an array
186
+ return array($array);
187
+ }
188
+ $result = array();
189
+ foreach ($array as $value) {
190
+ // explode the sub-array, and add the parts
191
+ $result = array_merge($result, $this->_flatten($value));
192
+ }
193
+ return $result;
194
+ }
195
+
196
+ /**
197
+ * Loads the PClasses and calls {@link self::addPClass()} to store them
198
+ * in runtime.
199
+ * URI can be location to a file, or a db prefixed table.
200
+ *
201
+ * @param string $uri URI to stored PClasses.
202
+ *
203
+ * @throws KlarnaException|Exception
204
+ * @return void
205
+ */
206
+ abstract public function load($uri);
207
+
208
+ /**
209
+ * Takes the internal PClass array and stores it.
210
+ * URI can be location to a file, or a db prefixed table.
211
+ *
212
+ * @param string $uri URI to stored PClasses.
213
+ *
214
+ * @throws KlarnaException|Exception
215
+ * @return void
216
+ */
217
+ abstract public function save($uri);
218
+
219
+ /**
220
+ * Removes the internally stored pclasses.
221
+ *
222
+ * @param string $uri URI to stored PClasses.
223
+ *
224
+ * @throws KlarnaException|Exception
225
+ * @return void
226
+ */
227
+ abstract public function clear($uri);
228
+
229
+ }
lib/Klarna/pclasses/xmlstorage.class.php ADDED
@@ -0,0 +1,280 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * XMLStorage
4
+ *
5
+ * PHP Version 5.3
6
+ *
7
+ * @category Payment
8
+ * @package KlarnaAPI
9
+ * @author MS Dev <ms.modules@klarna.com>
10
+ * @copyright 2012 Klarna AB (http://klarna.com)
11
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
12
+ * @link http://integration.klarna.com/
13
+ */
14
+
15
+ /**
16
+ * Include the {@link PCStorage} interface.
17
+ */
18
+ require_once 'storage.intf.php';
19
+
20
+ /**
21
+ * XML storage class for KlarnaPClass
22
+ *
23
+ * This class is an XML implementation of the PCStorage interface.
24
+ *
25
+ * @category Payment
26
+ * @package KlarnaAPI
27
+ * @author MS Dev <ms.modules@klarna.com>
28
+ * @copyright 2012 Klarna AB (http://klarna.com)
29
+ * @license http://opensource.org/licenses/BSD-2-Clause BSD-2
30
+ * @link http://integration.klarna.com/
31
+ */
32
+ class XMLStorage extends PCStorage
33
+ {
34
+
35
+ /**
36
+ * The internal XML document.
37
+ *
38
+ * @var DOMDocument
39
+ */
40
+ protected $dom;
41
+
42
+ /**
43
+ * XML version for the DOM document.
44
+ *
45
+ * @var string
46
+ */
47
+ protected $version = '1.0';
48
+
49
+ /**
50
+ * Encoding for the DOM document.
51
+ *
52
+ * @var string
53
+ */
54
+ protected $encoding = 'ISO-8859-1';
55
+
56
+ /**
57
+ * Class constructor
58
+ */
59
+ public function __construct()
60
+ {
61
+ $this->dom = new DOMDocument($this->version, $this->encoding);
62
+ $this->dom->formatOutput = true;
63
+ $this->dom->preserveWhiteSpace = false;
64
+ }
65
+
66
+ /**
67
+ * return the name of the storage type
68
+ *
69
+ * @return string
70
+ */
71
+ public function getName()
72
+ {
73
+ return "xml";
74
+ }
75
+
76
+ /**
77
+ * Checks if the file is writeable, readable or if the directory is.
78
+ *
79
+ * @param string $xmlFile URI to XML file.
80
+ *
81
+ * @throws KlarnaException
82
+ * @return void
83
+ */
84
+ protected function checkURI($xmlFile)
85
+ {
86
+ //If file doesn't exist, check the directory.
87
+ if (!file_exists($xmlFile)) {
88
+ $xmlFile = dirname($xmlFile);
89
+ }
90
+
91
+ if (!is_writable($xmlFile)) {
92
+ throw new Klarna_FileNotWritableException($xmlFile);
93
+ }
94
+
95
+ if (!is_readable($xmlFile)) {
96
+ throw new Klarna_FileNotReadableException($xmlFile);
97
+ }
98
+ }
99
+
100
+
101
+ /**
102
+ * Load pclasses from file
103
+ *
104
+ * @param string $uri uri to file to load
105
+ *
106
+ * @throws KlarnaException
107
+ * @return void
108
+ */
109
+ public function load($uri)
110
+ {
111
+ $this->checkURI($uri);
112
+ if (!file_exists($uri)) {
113
+ //Do not fail, if file doesn't exist.
114
+ return;
115
+ }
116
+ if (!@$this->dom->load($uri)) {
117
+ throw new Klarna_XMLParseException($uri);
118
+ }
119
+
120
+ $xpath = new DOMXpath($this->dom);
121
+ foreach ($xpath->query('/klarna/estore') as $estore) {
122
+ $eid = $estore->getAttribute('id');
123
+
124
+ foreach ($xpath->query('pclass', $estore) as $node) {
125
+ $pclass = new KlarnaPClass();
126
+ $pclass->setId(
127
+ $node->getAttribute('pid')
128
+ );
129
+ $pclass->setType(
130
+ $node->getAttribute('type')
131
+ );
132
+ $pclass->setEid($eid);
133
+ $pclass->setDescription(
134
+ $xpath->query('description', $node)->item(0)->textContent
135
+ );
136
+ $pclass->setMonths(
137
+ $xpath->query('months', $node)->item(0)->textContent
138
+ );
139
+ $pclass->setStartFee(
140
+ $xpath->query('startfee', $node)->item(0)->textContent
141
+ );
142
+ $pclass->setInvoiceFee(
143
+ $xpath->query('invoicefee', $node)->item(0)->textContent
144
+ );
145
+ $pclass->setInterestRate(
146
+ $xpath->query('interestrate', $node)->item(0)->textContent
147
+ );
148
+ $pclass->setMinAmount(
149
+ $xpath->query('minamount', $node)->item(0)->textContent
150
+ );
151
+ $pclass->setCountry(
152
+ $xpath->query('country', $node)->item(0)->textContent
153
+ );
154
+ $pclass->setExpire(
155
+ $xpath->query('expire', $node)->item(0)->textContent
156
+ );
157
+
158
+ $this->addPClass($pclass);
159
+ }
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Creates DOMElement for all fields for specified PClass.
165
+ *
166
+ * @param KlarnaPClass $pclass pclass object
167
+ *
168
+ * @return array Array of DOMElements.
169
+ */
170
+ protected function createFields($pclass)
171
+ {
172
+ $fields = array();
173
+
174
+ //This is to prevent HTMLEntities to be converted to the real character.
175
+ $fields[] = $this->dom->createElement('description');
176
+ end($fields)->appendChild(
177
+ $this->dom->createTextNode($pclass->getDescription())
178
+ );
179
+ $fields[] = $this->dom->createElement(
180
+ 'months', $pclass->getMonths()
181
+ );
182
+ $fields[] = $this->dom->createElement(
183
+ 'startfee', $pclass->getStartFee()
184
+ );
185
+ $fields[] = $this->dom->createElement(
186
+ 'invoicefee', $pclass->getInvoiceFee()
187
+ );
188
+ $fields[] = $this->dom->createElement(
189
+ 'interestrate', $pclass->getInterestRate()
190
+ );
191
+ $fields[] = $this->dom->createElement(
192
+ 'minamount', $pclass->getMinAmount()
193
+ );
194
+ $fields[] = $this->dom->createElement(
195
+ 'country', $pclass->getCountry()
196
+ );
197
+ $fields[] = $this->dom->createElement(
198
+ 'expire', $pclass->getExpire()
199
+ );
200
+
201
+ return $fields;
202
+ }
203
+
204
+ /**
205
+ * Save pclasses to file
206
+ *
207
+ * @param string $uri uri to file to save
208
+ *
209
+ * @throws KlarnaException
210
+ * @return void
211
+ */
212
+ public function save($uri)
213
+ {
214
+ $this->checkURI($uri);
215
+
216
+ //Reset DOMDocument.
217
+ if (!$this->dom->loadXML(
218
+ "<?xml version='$this->version' encoding='$this->encoding'?"
219
+ ."><klarna/>"
220
+ )
221
+ ) {
222
+ throw new Klarna_XMLParseException($uri);
223
+ }
224
+
225
+ ksort($this->pclasses, SORT_NUMERIC);
226
+ $xpath = new DOMXpath($this->dom);
227
+
228
+ foreach ($this->pclasses as $eid => $pclasses) {
229
+ $estore = $xpath->query('/klarna/estore[@id="'.$eid.'"]');
230
+
231
+ if ($estore === false || $estore->length === 0) {
232
+ //No estore with matching eid, create it.
233
+ $estore = $this->dom->createElement('estore');
234
+ $estore->setAttribute('id', $eid);
235
+ $this->dom->documentElement->appendChild($estore);
236
+ } else {
237
+ $estore = $estore->item(0);
238
+ }
239
+
240
+ foreach ($pclasses as $pclass) {
241
+ if ($eid != $pclass->getEid()) {
242
+ //This should never occur, failsafe.
243
+ continue;
244
+ }
245
+
246
+ $pnode = $this->dom->createElement('pclass');
247
+
248
+ foreach ($this->createFields($pclass) as $field) {
249
+ $pnode->appendChild($field);
250
+ }
251
+
252
+ $pnode->setAttribute('pid', $pclass->getId());
253
+ $pnode->setAttribute('type', $pclass->getType());
254
+
255
+ $estore->appendChild($pnode);
256
+ }
257
+ }
258
+
259
+ if (!$this->dom->save($uri)) {
260
+ throw new KlarnaException('Failed to save XML document!');
261
+ }
262
+ }
263
+
264
+ /**
265
+ * This uses unlink (delete) to clear the pclasses!
266
+ *
267
+ * @param string $uri uri to file to clear
268
+ *
269
+ * @throws KlarnaException
270
+ * @return void
271
+ */
272
+ public function clear($uri)
273
+ {
274
+ $this->checkURI($uri);
275
+ unset($this->pclasses);
276
+ if (file_exists($uri)) {
277
+ unlink($uri);
278
+ }
279
+ }
280
+ }
lib/Klarna/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc ADDED
@@ -0,0 +1,3776 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // by Edd Dumbill (C) 1999-2002
3
+ // <edd@usefulinc.com>
4
+ // $Id: xmlrpc.inc,v 1.174 2009/03/16 19:36:38 ggiunta Exp $
5
+
6
+ // Copyright (c) 1999,2000,2002 Edd Dumbill.
7
+ // All rights reserved.
8
+ //
9
+ // Redistribution and use in source and binary forms, with or without
10
+ // modification, are permitted provided that the following conditions
11
+ // are met:
12
+ //
13
+ // * Redistributions of source code must retain the above copyright
14
+ // notice, this list of conditions and the following disclaimer.
15
+ //
16
+ // * Redistributions in binary form must reproduce the above
17
+ // copyright notice, this list of conditions and the following
18
+ // disclaimer in the documentation and/or other materials provided
19
+ // with the distribution.
20
+ //
21
+ // * Neither the name of the "XML-RPC for PHP" nor the names of its
22
+ // contributors may be used to endorse or promote products derived
23
+ // from this software without specific prior written permission.
24
+ //
25
+ // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26
+ // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27
+ // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28
+ // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29
+ // REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30
+ // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31
+ // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32
+ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33
+ // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34
+ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35
+ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
36
+ // OF THE POSSIBILITY OF SUCH DAMAGE.
37
+
38
+ if(!function_exists('xml_parser_create'))
39
+ {
40
+ // For PHP 4 onward, XML functionality is always compiled-in on windows:
41
+ // no more need to dl-open it. It might have been compiled out on *nix...
42
+ if(strtoupper(substr(PHP_OS, 0, 3) != 'WIN'))
43
+ {
44
+ dl('xml.so');
45
+ }
46
+ }
47
+
48
+ // G. Giunta 2005/01/29: declare global these variables,
49
+ // so that xmlrpc.inc will work even if included from within a function
50
+ // Milosch: 2005/08/07 - explicitly request these via $GLOBALS where used.
51
+ $GLOBALS['xmlrpcI4']='i4';
52
+ $GLOBALS['xmlrpcInt']='int';
53
+ $GLOBALS['xmlrpcBoolean']='boolean';
54
+ $GLOBALS['xmlrpcDouble']='double';
55
+ $GLOBALS['xmlrpcString']='string';
56
+ $GLOBALS['xmlrpcDateTime']='dateTime.iso8601';
57
+ $GLOBALS['xmlrpcBase64']='base64';
58
+ $GLOBALS['xmlrpcArray']='array';
59
+ $GLOBALS['xmlrpcStruct']='struct';
60
+ $GLOBALS['xmlrpcValue']='undefined';
61
+
62
+ $GLOBALS['xmlrpcTypes']=array(
63
+ $GLOBALS['xmlrpcI4'] => 1,
64
+ $GLOBALS['xmlrpcInt'] => 1,
65
+ $GLOBALS['xmlrpcBoolean'] => 1,
66
+ $GLOBALS['xmlrpcString'] => 1,
67
+ $GLOBALS['xmlrpcDouble'] => 1,
68
+ $GLOBALS['xmlrpcDateTime'] => 1,
69
+ $GLOBALS['xmlrpcBase64'] => 1,
70
+ $GLOBALS['xmlrpcArray'] => 2,
71
+ $GLOBALS['xmlrpcStruct'] => 3
72
+ );
73
+
74
+ $GLOBALS['xmlrpc_valid_parents'] = array(
75
+ 'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'),
76
+ 'BOOLEAN' => array('VALUE'),
77
+ 'I4' => array('VALUE'),
78
+ 'INT' => array('VALUE'),
79
+ 'STRING' => array('VALUE'),
80
+ 'DOUBLE' => array('VALUE'),
81
+ 'DATETIME.ISO8601' => array('VALUE'),
82
+ 'BASE64' => array('VALUE'),
83
+ 'MEMBER' => array('STRUCT'),
84
+ 'NAME' => array('MEMBER'),
85
+ 'DATA' => array('ARRAY'),
86
+ 'ARRAY' => array('VALUE'),
87
+ 'STRUCT' => array('VALUE'),
88
+ 'PARAM' => array('PARAMS'),
89
+ 'METHODNAME' => array('METHODCALL'),
90
+ 'PARAMS' => array('METHODCALL', 'METHODRESPONSE'),
91
+ 'FAULT' => array('METHODRESPONSE'),
92
+ 'NIL' => array('VALUE'), // only used when extension activated
93
+ 'EX:NIL' => array('VALUE') // only used when extension activated
94
+ );
95
+
96
+ // define extra types for supporting NULL (useful for json or <NIL/>)
97
+ $GLOBALS['xmlrpcNull']='null';
98
+ $GLOBALS['xmlrpcTypes']['null']=1;
99
+
100
+ // Not in use anymore since 2.0. Shall we remove it?
101
+ /// @deprecated
102
+ $GLOBALS['xmlEntities']=array(
103
+ 'amp' => '&',
104
+ 'quot' => '"',
105
+ 'lt' => '<',
106
+ 'gt' => '>',
107
+ 'apos' => "'"
108
+ );
109
+
110
+ // tables used for transcoding different charsets into us-ascii xml
111
+
112
+ $GLOBALS['xml_iso88591_Entities']=array();
113
+ $GLOBALS['xml_iso88591_Entities']['in'] = array();
114
+ $GLOBALS['xml_iso88591_Entities']['out'] = array();
115
+ for ($i = 0; $i < 32; $i++)
116
+ {
117
+ $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
118
+ $GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
119
+ }
120
+ for ($i = 160; $i < 256; $i++)
121
+ {
122
+ $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
123
+ $GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
124
+ }
125
+
126
+ /// @todo add to iso table the characters from cp_1252 range, i.e. 128 to 159?
127
+ /// These will NOT be present in true ISO-8859-1, but will save the unwary
128
+ /// windows user from sending junk (though no luck when reciving them...)
129
+ /*
130
+ $GLOBALS['xml_cp1252_Entities']=array();
131
+ for ($i = 128; $i < 160; $i++)
132
+ {
133
+ $GLOBALS['xml_cp1252_Entities']['in'][] = chr($i);
134
+ }
135
+ $GLOBALS['xml_cp1252_Entities']['out'] = array(
136
+ '&#x20AC;', '?', '&#x201A;', '&#x0192;',
137
+ '&#x201E;', '&#x2026;', '&#x2020;', '&#x2021;',
138
+ '&#x02C6;', '&#x2030;', '&#x0160;', '&#x2039;',
139
+ '&#x0152;', '?', '&#x017D;', '?',
140
+ '?', '&#x2018;', '&#x2019;', '&#x201C;',
141
+ '&#x201D;', '&#x2022;', '&#x2013;', '&#x2014;',
142
+ '&#x02DC;', '&#x2122;', '&#x0161;', '&#x203A;',
143
+ '&#x0153;', '?', '&#x017E;', '&#x0178;'
144
+ );
145
+ */
146
+
147
+ $GLOBALS['xmlrpcerr'] = array(
148
+ 'unknown_method'=>1,
149
+ 'invalid_return'=>2,
150
+ 'incorrect_params'=>3,
151
+ 'introspect_unknown'=>4,
152
+ 'http_error'=>5,
153
+ 'no_data'=>6,
154
+ 'no_ssl'=>7,
155
+ 'curl_fail'=>8,
156
+ 'invalid_request'=>15,
157
+ 'no_curl'=>16,
158
+ 'server_error'=>17,
159
+ 'multicall_error'=>18,
160
+ 'multicall_notstruct'=>9,
161
+ 'multicall_nomethod'=>10,
162
+ 'multicall_notstring'=>11,
163
+ 'multicall_recursion'=>12,
164
+ 'multicall_noparams'=>13,
165
+ 'multicall_notarray'=>14,
166
+
167
+ 'cannot_decompress'=>103,
168
+ 'decompress_fail'=>104,
169
+ 'dechunk_fail'=>105,
170
+ 'server_cannot_decompress'=>106,
171
+ 'server_decompress_fail'=>107
172
+ );
173
+
174
+ $GLOBALS['xmlrpcstr'] = array(
175
+ 'unknown_method'=>'Unknown method',
176
+ 'invalid_return'=>'Invalid return payload: enable debugging to examine incoming payload',
177
+ 'incorrect_params'=>'Incorrect parameters passed to method',
178
+ 'introspect_unknown'=>"Can't introspect: method unknown",
179
+ 'http_error'=>"Didn't receive 200 OK from remote server.",
180
+ 'no_data'=>'No data received from server.',
181
+ 'no_ssl'=>'No SSL support compiled in.',
182
+ 'curl_fail'=>'CURL error',
183
+ 'invalid_request'=>'Invalid request payload',
184
+ 'no_curl'=>'No CURL support compiled in.',
185
+ 'server_error'=>'Internal server error',
186
+ 'multicall_error'=>'Received from server invalid multicall response',
187
+ 'multicall_notstruct'=>'system.multicall expected struct',
188
+ 'multicall_nomethod'=>'missing methodName',
189
+ 'multicall_notstring'=>'methodName is not a string',
190
+ 'multicall_recursion'=>'recursive system.multicall forbidden',
191
+ 'multicall_noparams'=>'missing params',
192
+ 'multicall_notarray'=>'params is not an array',
193
+
194
+ 'cannot_decompress'=>'Received from server compressed HTTP and cannot decompress',
195
+ 'decompress_fail'=>'Received from server invalid compressed HTTP',
196
+ 'dechunk_fail'=>'Received from server invalid chunked HTTP',
197
+ 'server_cannot_decompress'=>'Received from client compressed HTTP request and cannot decompress',
198
+ 'server_decompress_fail'=>'Received from client invalid compressed HTTP request'
199
+ );
200
+
201
+ // The charset encoding used by the server for received messages and
202
+ // by the client for received responses when received charset cannot be determined
203
+ // or is not supported
204
+ $GLOBALS['xmlrpc_defencoding']='UTF-8';
205
+
206
+ // The encoding used internally by PHP.
207
+ // String values received as xml will be converted to this, and php strings will be converted to xml
208
+ // as if having been coded with this
209
+ $GLOBALS['xmlrpc_internalencoding']='ISO-8859-1';
210
+
211
+ $GLOBALS['xmlrpcName']='XML-RPC for PHP';
212
+ $GLOBALS['xmlrpcVersion']='3.0.0.beta';
213
+
214
+ // let user errors start at 800
215
+ $GLOBALS['xmlrpcerruser']=800;
216
+ // let XML parse errors start at 100
217
+ $GLOBALS['xmlrpcerrxml']=100;
218
+
219
+ // formulate backslashes for escaping regexp
220
+ // Not in use anymore since 2.0. Shall we remove it?
221
+ /// @deprecated
222
+ $GLOBALS['xmlrpc_backslash']=chr(92).chr(92);
223
+
224
+ // set to TRUE to enable correct decoding of <NIL/> and <EX:NIL/> values
225
+ $GLOBALS['xmlrpc_null_extension']=false;
226
+
227
+ // set to TRUE to enable encoding of php NULL values to <EX:NIL/> instead of <NIL/>
228
+ $GLOBALS['xmlrpc_null_apache_encoding']=false;
229
+
230
+ // used to store state during parsing
231
+ // quick explanation of components:
232
+ // ac - used to accumulate values
233
+ // isf - used to indicate a parsing fault (2) or xmlrpcresp fault (1)
234
+ // isf_reason - used for storing xmlrpcresp fault string
235
+ // lv - used to indicate "looking for a value": implements
236
+ // the logic to allow values with no types to be strings
237
+ // params - used to store parameters in method calls
238
+ // method - used to store method name
239
+ // stack - array with genealogy of xml elements names:
240
+ // used to validate nesting of xmlrpc elements
241
+ $GLOBALS['_xh']=null;
242
+
243
+ /**
244
+ * Convert a string to the correct XML representation in a target charset
245
+ * To help correct communication of non-ascii chars inside strings, regardless
246
+ * of the charset used when sending requests, parsing them, sending responses
247
+ * and parsing responses, an option is to convert all non-ascii chars present in the message
248
+ * into their equivalent 'charset entity'. Charset entities enumerated this way
249
+ * are independent of the charset encoding used to transmit them, and all XML
250
+ * parsers are bound to understand them.
251
+ * Note that in the std case we are not sending a charset encoding mime type
252
+ * along with http headers, so we are bound by RFC 3023 to emit strict us-ascii.
253
+ *
254
+ * @todo do a bit of basic benchmarking (strtr vs. str_replace)
255
+ * @todo make usage of iconv() or recode_string() or mb_string() where available
256
+ */
257
+ function xmlrpc_encode_entitites($data, $src_encoding='', $dest_encoding='')
258
+ {
259
+ if ($src_encoding == '')
260
+ {
261
+ // lame, but we know no better...
262
+ $src_encoding = $GLOBALS['xmlrpc_internalencoding'];
263
+ }
264
+
265
+ switch(strtoupper($src_encoding.'_'.$dest_encoding))
266
+ {
267
+ case 'ISO-8859-1_':
268
+ case 'ISO-8859-1_US-ASCII':
269
+ $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
270
+ $escaped_data = str_replace($GLOBALS['xml_iso88591_Entities']['in'], $GLOBALS['xml_iso88591_Entities']['out'], $escaped_data);
271
+ break;
272
+ case 'ISO-8859-1_UTF-8':
273
+ $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
274
+ $escaped_data = utf8_encode($escaped_data);
275
+ break;
276
+ case 'ISO-8859-1_ISO-8859-1':
277
+ case 'US-ASCII_US-ASCII':
278
+ case 'US-ASCII_UTF-8':
279
+ case 'US-ASCII_':
280
+ case 'US-ASCII_ISO-8859-1':
281
+ case 'UTF-8_UTF-8':
282
+ //case 'CP1252_CP1252':
283
+ $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
284
+ break;
285
+ case 'UTF-8_':
286
+ case 'UTF-8_US-ASCII':
287
+ case 'UTF-8_ISO-8859-1':
288
+ // NB: this will choke on invalid UTF-8, going most likely beyond EOF
289
+ $escaped_data = '';
290
+ // be kind to users creating string xmlrpcvals out of different php types
291
+ $data = (string) $data;
292
+ $ns = strlen ($data);
293
+ for ($nn = 0; $nn < $ns; $nn++)
294
+ {
295
+ $ch = $data[$nn];
296
+ $ii = ord($ch);
297
+ //1 7 0bbbbbbb (127)
298
+ if ($ii < 128)
299
+ {
300
+ /// @todo shall we replace this with a (supposedly) faster str_replace?
301
+ switch($ii){
302
+ case 34:
303
+ $escaped_data .= '&quot;';
304
+ break;
305
+ case 38:
306
+ $escaped_data .= '&amp;';
307
+ break;
308
+ case 39:
309
+ $escaped_data .= '&apos;';
310
+ break;
311
+ case 60:
312
+ $escaped_data .= '&lt;';
313
+ break;
314
+ case 62:
315
+ $escaped_data .= '&gt;';
316
+ break;
317
+ default:
318
+ $escaped_data .= $ch;
319
+ } // switch
320
+ }
321
+ //2 11 110bbbbb 10bbbbbb (2047)
322
+ else if ($ii>>5 == 6)
323
+ {
324
+ $b1 = ($ii & 31);
325
+ $ii = ord($data[$nn+1]);
326
+ $b2 = ($ii & 63);
327
+ $ii = ($b1 * 64) + $b2;
328
+ $ent = sprintf ('&#%d;', $ii);
329
+ $escaped_data .= $ent;
330
+ $nn += 1;
331
+ }
332
+ //3 16 1110bbbb 10bbbbbb 10bbbbbb
333
+ else if ($ii>>4 == 14)
334
+ {
335
+ $b1 = ($ii & 15);
336
+ $ii = ord($data[$nn+1]);
337
+ $b2 = ($ii & 63);
338
+ $ii = ord($data[$nn+2]);
339
+ $b3 = ($ii & 63);
340
+ $ii = ((($b1 * 64) + $b2) * 64) + $b3;
341
+ $ent = sprintf ('&#%d;', $ii);
342
+ $escaped_data .= $ent;
343
+ $nn += 2;
344
+ }
345
+ //4 21 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
346
+ else if ($ii>>3 == 30)
347
+ {
348
+ $b1 = ($ii & 7);
349
+ $ii = ord($data[$nn+1]);
350
+ $b2 = ($ii & 63);
351
+ $ii = ord($data[$nn+2]);
352
+ $b3 = ($ii & 63);
353
+ $ii = ord($data[$nn+3]);
354
+ $b4 = ($ii & 63);
355
+ $ii = ((((($b1 * 64) + $b2) * 64) + $b3) * 64) + $b4;
356
+ $ent = sprintf ('&#%d;', $ii);
357
+ $escaped_data .= $ent;
358
+ $nn += 3;
359
+ }
360
+ }
361
+ break;
362
+ /*
363
+ case 'CP1252_':
364
+ case 'CP1252_US-ASCII':
365
+ $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
366
+ $escaped_data = str_replace($GLOBALS['xml_iso88591_Entities']['in'], $GLOBALS['xml_iso88591_Entities']['out'], $escaped_data);
367
+ $escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
368
+ break;
369
+ case 'CP1252_UTF-8':
370
+ $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
371
+ /// @todo we could use real UTF8 chars here instead of xml entities... (note that utf_8 encode all allone will NOT convert them)
372
+ $escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
373
+ $escaped_data = utf8_encode($escaped_data);
374
+ break;
375
+ case 'CP1252_ISO-8859-1':
376
+ $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
377
+ // we might as well replave all funky chars with a '?' here, but we are kind and leave it to the receiving application layer to decide what to do with these weird entities...
378
+ $escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
379
+ break;
380
+ */
381
+ default:
382
+ $escaped_data = '';
383
+ error_log("Converting from $src_encoding to $dest_encoding: not supported...");
384
+ }
385
+ return $escaped_data;
386
+ }
387
+
388
+ /// xml parser handler function for opening element tags
389
+ function xmlrpc_se($parser, $name, $attrs, $accept_single_vals=false)
390
+ {
391
+ // if invalid xmlrpc already detected, skip all processing
392
+ if ($GLOBALS['_xh']['isf'] < 2)
393
+ {
394
+ // check for correct element nesting
395
+ // top level element can only be of 2 types
396
+ /// @todo optimization creep: save this check into a bool variable, instead of using count() every time:
397
+ /// there is only a single top level element in xml anyway
398
+ if (count($GLOBALS['_xh']['stack']) == 0)
399
+ {
400
+ if ($name != 'METHODRESPONSE' && $name != 'METHODCALL' && (
401
+ $name != 'VALUE' && !$accept_single_vals))
402
+ {
403
+ $GLOBALS['_xh']['isf'] = 2;
404
+ $GLOBALS['_xh']['isf_reason'] = 'missing top level xmlrpc element';
405
+ return;
406
+ }
407
+ else
408
+ {
409
+ $GLOBALS['_xh']['rt'] = strtolower($name);
410
+ $GLOBALS['_xh']['rt'] = strtolower($name);
411
+ }
412
+ }
413
+ else
414
+ {
415
+ // not top level element: see if parent is OK
416
+ $parent = end($GLOBALS['_xh']['stack']);
417
+ if (!array_key_exists($name, $GLOBALS['xmlrpc_valid_parents']) || !in_array($parent, $GLOBALS['xmlrpc_valid_parents'][$name]))
418
+ {
419
+ $GLOBALS['_xh']['isf'] = 2;
420
+ $GLOBALS['_xh']['isf_reason'] = "xmlrpc element $name cannot be child of $parent";
421
+ return;
422
+ }
423
+ }
424
+
425
+ switch($name)
426
+ {
427
+ // optimize for speed switch cases: most common cases first
428
+ case 'VALUE':
429
+ /// @todo we could check for 2 VALUE elements inside a MEMBER or PARAM element
430
+ $GLOBALS['_xh']['vt']='value'; // indicator: no value found yet
431
+ $GLOBALS['_xh']['ac']='';
432
+ $GLOBALS['_xh']['lv']=1;
433
+ $GLOBALS['_xh']['php_class']=null;
434
+ break;
435
+ case 'I4':
436
+ case 'INT':
437
+ case 'STRING':
438
+ case 'BOOLEAN':
439
+ case 'DOUBLE':
440
+ case 'DATETIME.ISO8601':
441
+ case 'BASE64':
442
+ if ($GLOBALS['_xh']['vt']!='value')
443
+ {
444
+ //two data elements inside a value: an error occurred!
445
+ $GLOBALS['_xh']['isf'] = 2;
446
+ $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
447
+ return;
448
+ }
449
+ $GLOBALS['_xh']['ac']=''; // reset the accumulator
450
+ break;
451
+ case 'STRUCT':
452
+ case 'ARRAY':
453
+ if ($GLOBALS['_xh']['vt']!='value')
454
+ {
455
+ //two data elements inside a value: an error occurred!
456
+ $GLOBALS['_xh']['isf'] = 2;
457
+ $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
458
+ return;
459
+ }
460
+ // create an empty array to hold child values, and push it onto appropriate stack
461
+ $cur_val = array();
462
+ $cur_val['values'] = array();
463
+ $cur_val['type'] = $name;
464
+ // check for out-of-band information to rebuild php objs
465
+ // and in case it is found, save it
466
+ if (@isset($attrs['PHP_CLASS']))
467
+ {
468
+ $cur_val['php_class'] = $attrs['PHP_CLASS'];
469
+ }
470
+ $GLOBALS['_xh']['valuestack'][] = $cur_val;
471
+ $GLOBALS['_xh']['vt']='data'; // be prepared for a data element next
472
+ break;
473
+ case 'DATA':
474
+ if ($GLOBALS['_xh']['vt']!='data')
475
+ {
476
+ //two data elements inside a value: an error occurred!
477
+ $GLOBALS['_xh']['isf'] = 2;
478
+ $GLOBALS['_xh']['isf_reason'] = "found two data elements inside an array element";
479
+ return;
480
+ }
481
+ case 'METHODCALL':
482
+ case 'METHODRESPONSE':
483
+ case 'PARAMS':
484
+ // valid elements that add little to processing
485
+ break;
486
+ case 'METHODNAME':
487
+ case 'NAME':
488
+ /// @todo we could check for 2 NAME elements inside a MEMBER element
489
+ $GLOBALS['_xh']['ac']='';
490
+ break;
491
+ case 'FAULT':
492
+ $GLOBALS['_xh']['isf']=1;
493
+ break;
494
+ case 'MEMBER':
495
+ $GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name']=''; // set member name to null, in case we do not find in the xml later on
496
+ //$GLOBALS['_xh']['ac']='';
497
+ // Drop trough intentionally
498
+ case 'PARAM':
499
+ // clear value type, so we can check later if no value has been passed for this param/member
500
+ $GLOBALS['_xh']['vt']=null;
501
+ break;
502
+ case 'NIL':
503
+ case 'EX:NIL':
504
+ if ($GLOBALS['xmlrpc_null_extension'])
505
+ {
506
+ if ($GLOBALS['_xh']['vt']!='value')
507
+ {
508
+ //two data elements inside a value: an error occurred!
509
+ $GLOBALS['_xh']['isf'] = 2;
510
+ $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
511
+ return;
512
+ }
513
+ $GLOBALS['_xh']['ac']=''; // reset the accumulator
514
+ break;
515
+ }
516
+ // we do not support the <NIL/> extension, so
517
+ // drop through intentionally
518
+ default:
519
+ /// INVALID ELEMENT: RAISE ISF so that it is later recognized!!!
520
+ $GLOBALS['_xh']['isf'] = 2;
521
+ $GLOBALS['_xh']['isf_reason'] = "found not-xmlrpc xml element $name";
522
+ break;
523
+ }
524
+
525
+ // Save current element name to stack, to validate nesting
526
+ $GLOBALS['_xh']['stack'][] = $name;
527
+
528
+ /// @todo optimization creep: move this inside the big switch() above
529
+ if($name!='VALUE')
530
+ {
531
+ $GLOBALS['_xh']['lv']=0;
532
+ }
533
+ }
534
+ }
535
+
536
+ /// Used in decoding xml chunks that might represent single xmlrpc values
537
+ function xmlrpc_se_any($parser, $name, $attrs)
538
+ {
539
+ xmlrpc_se($parser, $name, $attrs, true);
540
+ }
541
+
542
+ /// xml parser handler function for close element tags
543
+ function xmlrpc_ee($parser, $name, $rebuild_xmlrpcvals = true)
544
+ {
545
+ if ($GLOBALS['_xh']['isf'] < 2)
546
+ {
547
+ // push this element name from stack
548
+ // NB: if XML validates, correct opening/closing is guaranteed and
549
+ // we do not have to check for $name == $curr_elem.
550
+ // we also checked for proper nesting at start of elements...
551
+ $curr_elem = array_pop($GLOBALS['_xh']['stack']);
552
+
553
+ switch($name)
554
+ {
555
+ case 'VALUE':
556
+ // This if() detects if no scalar was inside <VALUE></VALUE>
557
+ if ($GLOBALS['_xh']['vt']=='value')
558
+ {
559
+ $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
560
+ $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcString'];
561
+ }
562
+
563
+ if ($rebuild_xmlrpcvals)
564
+ {
565
+ // build the xmlrpc val out of the data received, and substitute it
566
+ $temp = new xmlrpcval($GLOBALS['_xh']['value'], $GLOBALS['_xh']['vt']);
567
+ // in case we got info about underlying php class, save it
568
+ // in the object we're rebuilding
569
+ if (isset($GLOBALS['_xh']['php_class']))
570
+ $temp->_php_class = $GLOBALS['_xh']['php_class'];
571
+ // check if we are inside an array or struct:
572
+ // if value just built is inside an array, let's move it into array on the stack
573
+ $vscount = count($GLOBALS['_xh']['valuestack']);
574
+ if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
575
+ {
576
+ $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $temp;
577
+ }
578
+ else
579
+ {
580
+ $GLOBALS['_xh']['value'] = $temp;
581
+ }
582
+ }
583
+ else
584
+ {
585
+ /// @todo this needs to treat correctly php-serialized objects,
586
+ /// since std deserializing is done by php_xmlrpc_decode,
587
+ /// which we will not be calling...
588
+ if (isset($GLOBALS['_xh']['php_class']))
589
+ {
590
+ }
591
+
592
+ // check if we are inside an array or struct:
593
+ // if value just built is inside an array, let's move it into array on the stack
594
+ $vscount = count($GLOBALS['_xh']['valuestack']);
595
+ if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
596
+ {
597
+ $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $GLOBALS['_xh']['value'];
598
+ }
599
+ }
600
+ break;
601
+ case 'BOOLEAN':
602
+ case 'I4':
603
+ case 'INT':
604
+ case 'STRING':
605
+ case 'DOUBLE':
606
+ case 'DATETIME.ISO8601':
607
+ case 'BASE64':
608
+ $GLOBALS['_xh']['vt']=strtolower($name);
609
+ /// @todo: optimization creep - remove the if/elseif cycle below
610
+ /// since the case() in which we are already did that
611
+ if ($name=='STRING')
612
+ {
613
+ $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
614
+ }
615
+ elseif ($name=='DATETIME.ISO8601')
616
+ {
617
+ if (!preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $GLOBALS['_xh']['ac']))
618
+ {
619
+ error_log('XML-RPC: invalid value received in DATETIME: '.$GLOBALS['_xh']['ac']);
620
+ }
621
+ $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcDateTime'];
622
+ $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
623
+ }
624
+ elseif ($name=='BASE64')
625
+ {
626
+ /// @todo check for failure of base64 decoding / catch warnings
627
+ $GLOBALS['_xh']['value']=base64_decode($GLOBALS['_xh']['ac']);
628
+ }
629
+ elseif ($name=='BOOLEAN')
630
+ {
631
+ // special case here: we translate boolean 1 or 0 into PHP
632
+ // constants true or false.
633
+ // Strings 'true' and 'false' are accepted, even though the
634
+ // spec never mentions them (see eg. Blogger api docs)
635
+ // NB: this simple checks helps a lot sanitizing input, ie no
636
+ // security problems around here
637
+ if ($GLOBALS['_xh']['ac']=='1' || strcasecmp($GLOBALS['_xh']['ac'], 'true') == 0)
638
+ {
639
+ $GLOBALS['_xh']['value']=true;
640
+ }
641
+ else
642
+ {
643
+ // log if receiveing something strange, even though we set the value to false anyway
644
+ if ($GLOBALS['_xh']['ac']!='0' && strcasecmp($GLOBALS['_xh']['ac'], 'false') != 0)
645
+ error_log('XML-RPC: invalid value received in BOOLEAN: '.$GLOBALS['_xh']['ac']);
646
+ $GLOBALS['_xh']['value']=false;
647
+ }
648
+ }
649
+ elseif ($name=='DOUBLE')
650
+ {
651
+ // we have a DOUBLE
652
+ // we must check that only 0123456789-.<space> are characters here
653
+ // NOTE: regexp could be much stricter than this...
654
+ if (!preg_match('/^[+-eE0123456789 \t.]+$/', $GLOBALS['_xh']['ac']))
655
+ {
656
+ /// @todo: find a better way of throwing an error than this!
657
+ error_log('XML-RPC: non numeric value received in DOUBLE: '.$GLOBALS['_xh']['ac']);
658
+ $GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND';
659
+ }
660
+ else
661
+ {
662
+ // it's ok, add it on
663
+ $GLOBALS['_xh']['value']=(double)$GLOBALS['_xh']['ac'];
664
+ }
665
+ }
666
+ else
667
+ {
668
+ // we have an I4/INT
669
+ // we must check that only 0123456789-<space> are characters here
670
+ if (!preg_match('/^[+-]?[0123456789 \t]+$/', $GLOBALS['_xh']['ac']))
671
+ {
672
+ /// @todo find a better way of throwing an error than this!
673
+ error_log('XML-RPC: non numeric value received in INT: '.$GLOBALS['_xh']['ac']);
674
+ $GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND';
675
+ }
676
+ else
677
+ {
678
+ // it's ok, add it on
679
+ $GLOBALS['_xh']['value']=(int)$GLOBALS['_xh']['ac'];
680
+ }
681
+ }
682
+ //$GLOBALS['_xh']['ac']=''; // is this necessary?
683
+ $GLOBALS['_xh']['lv']=3; // indicate we've found a value
684
+ break;
685
+ case 'NAME':
686
+ $GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name'] = $GLOBALS['_xh']['ac'];
687
+ break;
688
+ case 'MEMBER':
689
+ //$GLOBALS['_xh']['ac']=''; // is this necessary?
690
+ // add to array in the stack the last element built,
691
+ // unless no VALUE was found
692
+ if ($GLOBALS['_xh']['vt'])
693
+ {
694
+ $vscount = count($GLOBALS['_xh']['valuestack']);
695
+ $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][$GLOBALS['_xh']['valuestack'][$vscount-1]['name']] = $GLOBALS['_xh']['value'];
696
+ } else
697
+ error_log('XML-RPC: missing VALUE inside STRUCT in received xml');
698
+ break;
699
+ case 'DATA':
700
+ //$GLOBALS['_xh']['ac']=''; // is this necessary?
701
+ $GLOBALS['_xh']['vt']=null; // reset this to check for 2 data elements in a row - even if they're empty
702
+ break;
703
+ case 'STRUCT':
704
+ case 'ARRAY':
705
+ // fetch out of stack array of values, and promote it to current value
706
+ $curr_val = array_pop($GLOBALS['_xh']['valuestack']);
707
+ $GLOBALS['_xh']['value'] = $curr_val['values'];
708
+ $GLOBALS['_xh']['vt']=strtolower($name);
709
+ if (isset($curr_val['php_class']))
710
+ {
711
+ $GLOBALS['_xh']['php_class'] = $curr_val['php_class'];
712
+ }
713
+ break;
714
+ case 'PARAM':
715
+ // add to array of params the current value,
716
+ // unless no VALUE was found
717
+ if ($GLOBALS['_xh']['vt'])
718
+ {
719
+ $GLOBALS['_xh']['params'][]=$GLOBALS['_xh']['value'];
720
+ $GLOBALS['_xh']['pt'][]=$GLOBALS['_xh']['vt'];
721
+ }
722
+ else
723
+ error_log('XML-RPC: missing VALUE inside PARAM in received xml');
724
+ break;
725
+ case 'METHODNAME':
726
+ $GLOBALS['_xh']['method']=preg_replace('/^[\n\r\t ]+/', '', $GLOBALS['_xh']['ac']);
727
+ break;
728
+ case 'NIL':
729
+ case 'EX:NIL':
730
+ if ($GLOBALS['xmlrpc_null_extension'])
731
+ {
732
+ $GLOBALS['_xh']['vt']='null';
733
+ $GLOBALS['_xh']['value']=null;
734
+ $GLOBALS['_xh']['lv']=3;
735
+ break;
736
+ }
737
+ // drop through intentionally if nil extension not enabled
738
+ case 'PARAMS':
739
+ case 'FAULT':
740
+ case 'METHODCALL':
741
+ case 'METHORESPONSE':
742
+ break;
743
+ default:
744
+ // End of INVALID ELEMENT!
745
+ // shall we add an assert here for unreachable code???
746
+ break;
747
+ }
748
+ }
749
+ }
750
+
751
+ /// Used in decoding xmlrpc requests/responses without rebuilding xmlrpc values
752
+ function xmlrpc_ee_fast($parser, $name)
753
+ {
754
+ xmlrpc_ee($parser, $name, false);
755
+ }
756
+
757
+ /// xml parser handler function for character data
758
+ function xmlrpc_cd($parser, $data)
759
+ {
760
+ // skip processing if xml fault already detected
761
+ if ($GLOBALS['_xh']['isf'] < 2)
762
+ {
763
+ // "lookforvalue==3" means that we've found an entire value
764
+ // and should discard any further character data
765
+ if($GLOBALS['_xh']['lv']!=3)
766
+ {
767
+ // G. Giunta 2006-08-23: useless change of 'lv' from 1 to 2
768
+ //if($GLOBALS['_xh']['lv']==1)
769
+ //{
770
+ // if we've found text and we're just in a <value> then
771
+ // say we've found a value
772
+ //$GLOBALS['_xh']['lv']=2;
773
+ //}
774
+ // we always initialize the accumulator before starting parsing, anyway...
775
+ //if(!@isset($GLOBALS['_xh']['ac']))
776
+ //{
777
+ // $GLOBALS['_xh']['ac'] = '';
778
+ //}
779
+ $GLOBALS['_xh']['ac'].=$data;
780
+ }
781
+ }
782
+ }
783
+
784
+ /// xml parser handler function for 'other stuff', ie. not char data or
785
+ /// element start/end tag. In fact it only gets called on unknown entities...
786
+ function xmlrpc_dh($parser, $data)
787
+ {
788
+ // skip processing if xml fault already detected
789
+ if ($GLOBALS['_xh']['isf'] < 2)
790
+ {
791
+ if(substr($data, 0, 1) == '&' && substr($data, -1, 1) == ';')
792
+ {
793
+ // G. Giunta 2006-08-25: useless change of 'lv' from 1 to 2
794
+ //if($GLOBALS['_xh']['lv']==1)
795
+ //{
796
+ // $GLOBALS['_xh']['lv']=2;
797
+ //}
798
+ $GLOBALS['_xh']['ac'].=$data;
799
+ }
800
+ }
801
+ return true;
802
+ }
803
+
804
+ class xmlrpc_client
805
+ {
806
+ var $path;
807
+ var $server;
808
+ var $port=0;
809
+ var $method='http';
810
+ var $errno;
811
+ var $errstr;
812
+ var $debug=0;
813
+ var $username='';
814
+ var $password='';
815
+ var $authtype=1;
816
+ var $cert='';
817
+ var $certpass='';
818
+ var $cacert='';
819
+ var $cacertdir='';
820
+ var $key='';
821
+ var $keypass='';
822
+ var $verifypeer=true;
823
+ var $verifyhost=1;
824
+ var $no_multicall=false;
825
+ var $proxy='';
826
+ var $proxyport=0;
827
+ var $proxy_user='';
828
+ var $proxy_pass='';
829
+ var $proxy_authtype=1;
830
+ var $cookies=array();
831
+ var $extracurlopts=array();
832
+
833
+ /**
834
+ * List of http compression methods accepted by the client for responses.
835
+ * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
836
+ *
837
+ * NNB: you can set it to any non-empty array for HTTP11 and HTTPS, since
838
+ * in those cases it will be up to CURL to decide the compression methods
839
+ * it supports. You might check for the presence of 'zlib' in the output of
840
+ * curl_version() to determine wheter compression is supported or not
841
+ */
842
+ var $accepted_compression = array();
843
+ /**
844
+ * Name of compression scheme to be used for sending requests.
845
+ * Either null, gzip or deflate
846
+ */
847
+ var $request_compression = '';
848
+ /**
849
+ * CURL handle: used for keep-alive connections (PHP 4.3.8 up, see:
850
+ * http://curl.haxx.se/docs/faq.html#7.3)
851
+ */
852
+ var $xmlrpc_curl_handle = null;
853
+ /// Wheter to use persistent connections for http 1.1 and https
854
+ var $keepalive = false;
855
+ /// Charset encodings that can be decoded without problems by the client
856
+ var $accepted_charset_encodings = array();
857
+ /// Charset encoding to be used in serializing request. NULL = use ASCII
858
+ var $request_charset_encoding = '';
859
+ /**
860
+ * Decides the content of xmlrpcresp objects returned by calls to send()
861
+ * valid strings are 'xmlrpcvals', 'phpvals' or 'xml'
862
+ */
863
+ var $return_type = 'xmlrpcvals';
864
+ /**
865
+ * Sent to servers in http headers
866
+ */
867
+ var $user_agent;
868
+
869
+ /**
870
+ * @param string $path either the complete server URL or the PATH part of the xmlrc server URL, e.g. /xmlrpc/server.php
871
+ * @param string $server the server name / ip address
872
+ * @param integer $port the port the server is listening on, defaults to 80 or 443 depending on protocol used
873
+ * @param string $method the http protocol variant: defaults to 'http', 'https' and 'http11' can be used if CURL is installed
874
+ */
875
+ function xmlrpc_client($path, $server='', $port='', $method='')
876
+ {
877
+ // allow user to specify all params in $path
878
+ if($server == '' and $port == '' and $method == '')
879
+ {
880
+ $parts = parse_url($path);
881
+ $server = $parts['host'];
882
+ $path = isset($parts['path']) ? $parts['path'] : '';
883
+ if(isset($parts['query']))
884
+ {
885
+ $path .= '?'.$parts['query'];
886
+ }
887
+ if(isset($parts['fragment']))
888
+ {
889
+ $path .= '#'.$parts['fragment'];
890
+ }
891
+ if(isset($parts['port']))
892
+ {
893
+ $port = $parts['port'];
894
+ }
895
+ if(isset($parts['scheme']))
896
+ {
897
+ $method = $parts['scheme'];
898
+ }
899
+ if(isset($parts['user']))
900
+ {
901
+ $this->username = $parts['user'];
902
+ }
903
+ if(isset($parts['pass']))
904
+ {
905
+ $this->password = $parts['pass'];
906
+ }
907
+ }
908
+ if($path == '' || $path[0] != '/')
909
+ {
910
+ $this->path='/'.$path;
911
+ }
912
+ else
913
+ {
914
+ $this->path=$path;
915
+ }
916
+ $this->server=$server;
917
+ if($port != '')
918
+ {
919
+ $this->port=$port;
920
+ }
921
+ if($method != '')
922
+ {
923
+ $this->method=$method;
924
+ }
925
+
926
+ // if ZLIB is enabled, let the client by default accept compressed responses
927
+ if(function_exists('gzinflate') || (
928
+ function_exists('curl_init') && (($info = curl_version()) &&
929
+ ((is_string($info) && strpos($info, 'zlib') !== null) || isset($info['libz_version'])))
930
+ ))
931
+ {
932
+ $this->accepted_compression = array('gzip', 'deflate');
933
+ }
934
+
935
+ // keepalives: enabled by default
936
+ $this->keepalive = true;
937
+
938
+ // by default the xml parser can support these 3 charset encodings
939
+ $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
940
+
941
+ // initialize user_agent string
942
+ $this->user_agent = $GLOBALS['xmlrpcName'] . ' ' . $GLOBALS['xmlrpcVersion'];
943
+ }
944
+
945
+ /**
946
+ * Enables/disables the echoing to screen of the xmlrpc responses received
947
+ * @param integer $debug values 0, 1 and 2 are supported (2 = echo sent msg too, before received response)
948
+ * @access public
949
+ */
950
+ function setDebug($in)
951
+ {
952
+ $this->debug=$in;
953
+ }
954
+
955
+ /**
956
+ * Add some http BASIC AUTH credentials, used by the client to authenticate
957
+ * @param string $u username
958
+ * @param string $p password
959
+ * @param integer $t auth type. See curl_setopt man page for supported auth types. Defaults to CURLAUTH_BASIC (basic auth)
960
+ * @access public
961
+ */
962
+ function setCredentials($u, $p, $t=1)
963
+ {
964
+ $this->username=$u;
965
+ $this->password=$p;
966
+ $this->authtype=$t;
967
+ }
968
+
969
+ /**
970
+ * Add a client-side https certificate
971
+ * @param string $cert
972
+ * @param string $certpass
973
+ * @access public
974
+ */
975
+ function setCertificate($cert, $certpass)
976
+ {
977
+ $this->cert = $cert;
978
+ $this->certpass = $certpass;
979
+ }
980
+
981
+ /**
982
+ * Add a CA certificate to verify server with (see man page about
983
+ * CURLOPT_CAINFO for more details
984
+ * @param string $cacert certificate file name (or dir holding certificates)
985
+ * @param bool $is_dir set to true to indicate cacert is a dir. defaults to false
986
+ * @access public
987
+ */
988
+ function setCaCertificate($cacert, $is_dir=false)
989
+ {
990
+ if ($is_dir)
991
+ {
992
+ $this->cacertdir = $cacert;
993
+ }
994
+ else
995
+ {
996
+ $this->cacert = $cacert;
997
+ }
998
+ }
999
+
1000
+ /**
1001
+ * Set attributes for SSL communication: private SSL key
1002
+ * NB: does not work in older php/curl installs
1003
+ * Thanks to Daniel Convissor
1004
+ * @param string $key The name of a file containing a private SSL key
1005
+ * @param string $keypass The secret password needed to use the private SSL key
1006
+ * @access public
1007
+ */
1008
+ function setKey($key, $keypass)
1009
+ {
1010
+ $this->key = $key;
1011
+ $this->keypass = $keypass;
1012
+ }
1013
+
1014
+ /**
1015
+ * Set attributes for SSL communication: verify server certificate
1016
+ * @param bool $i enable/disable verification of peer certificate
1017
+ * @access public
1018
+ */
1019
+ function setSSLVerifyPeer($i)
1020
+ {
1021
+ $this->verifypeer = $i;
1022
+ }
1023
+
1024
+ /**
1025
+ * Set attributes for SSL communication: verify match of server cert w. hostname
1026
+ * @param int $i
1027
+ * @access public
1028
+ */
1029
+ function setSSLVerifyHost($i)
1030
+ {
1031
+ $this->verifyhost = $i;
1032
+ }
1033
+
1034
+ /**
1035
+ * Set proxy info
1036
+ * @param string $proxyhost
1037
+ * @param string $proxyport Defaults to 8080 for HTTP and 443 for HTTPS
1038
+ * @param string $proxyusername Leave blank if proxy has public access
1039
+ * @param string $proxypassword Leave blank if proxy has public access
1040
+ * @param int $proxyauthtype set to constant CURLAUTH_NTLM to use NTLM auth with proxy
1041
+ * @access public
1042
+ */
1043
+ function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 1)
1044
+ {
1045
+ $this->proxy = $proxyhost;
1046
+ $this->proxyport = $proxyport;
1047
+ $this->proxy_user = $proxyusername;
1048
+ $this->proxy_pass = $proxypassword;
1049
+ $this->proxy_authtype = $proxyauthtype;
1050
+ }
1051
+
1052
+ /**
1053
+ * Enables/disables reception of compressed xmlrpc responses.
1054
+ * Note that enabling reception of compressed responses merely adds some standard
1055
+ * http headers to xmlrpc requests. It is up to the xmlrpc server to return
1056
+ * compressed responses when receiving such requests.
1057
+ * @param string $compmethod either 'gzip', 'deflate', 'any' or ''
1058
+ * @access public
1059
+ */
1060
+ function setAcceptedCompression($compmethod)
1061
+ {
1062
+ if ($compmethod == 'any')
1063
+ $this->accepted_compression = array('gzip', 'deflate');
1064
+ else
1065
+ $this->accepted_compression = array($compmethod);
1066
+ }
1067
+
1068
+ /**
1069
+ * Enables/disables http compression of xmlrpc request.
1070
+ * Take care when sending compressed requests: servers might not support them
1071
+ * (and automatic fallback to uncompressed requests is not yet implemented)
1072
+ * @param string $compmethod either 'gzip', 'deflate' or ''
1073
+ * @access public
1074
+ */
1075
+ function setRequestCompression($compmethod)
1076
+ {
1077
+ $this->request_compression = $compmethod;
1078
+ }
1079
+
1080
+ /**
1081
+ * Adds a cookie to list of cookies that will be sent to server.
1082
+ * NB: setting any param but name and value will turn the cookie into a 'version 1' cookie:
1083
+ * do not do it unless you know what you are doing
1084
+ * @param string $name
1085
+ * @param string $value
1086
+ * @param string $path
1087
+ * @param string $domain
1088
+ * @param int $port
1089
+ * @access public
1090
+ *
1091
+ * @todo check correctness of urlencoding cookie value (copied from php way of doing it...)
1092
+ */
1093
+ function setCookie($name, $value='', $path='', $domain='', $port=null)
1094
+ {
1095
+ $this->cookies[$name]['value'] = urlencode($value);
1096
+ if ($path || $domain || $port)
1097
+ {
1098
+ $this->cookies[$name]['path'] = $path;
1099
+ $this->cookies[$name]['domain'] = $domain;
1100
+ $this->cookies[$name]['port'] = $port;
1101
+ $this->cookies[$name]['version'] = 1;
1102
+ }
1103
+ else
1104
+ {
1105
+ $this->cookies[$name]['version'] = 0;
1106
+ }
1107
+ }
1108
+
1109
+ /**
1110
+ * Directly set cURL options, for extra flexibility
1111
+ * It allows eg. to bind client to a specific IP interface / address
1112
+ * @param $options array
1113
+ */
1114
+ function SetCurlOptions( $options )
1115
+ {
1116
+ $this->extracurlopts = $options;
1117
+ }
1118
+
1119
+ /**
1120
+ * Set user-agent string that will be used by this client instance
1121
+ * in http headers sent to the server
1122
+ */
1123
+ function SetUserAgent( $agentstring )
1124
+ {
1125
+ $this->user_agent = $agentstring;
1126
+ }
1127
+
1128
+ /**
1129
+ * Send an xmlrpc request
1130
+ * @param mixed $msg The message object, or an array of messages for using multicall, or the complete xml representation of a request
1131
+ * @param integer $timeout Connection timeout, in seconds, If unspecified, a platform specific timeout will apply
1132
+ * @param string $method if left unspecified, the http protocol chosen during creation of the object will be used
1133
+ * @return xmlrpcresp
1134
+ * @access public
1135
+ */
1136
+ function& send($msg, $timeout=0, $method='')
1137
+ {
1138
+ // if user deos not specify http protocol, use native method of this client
1139
+ // (i.e. method set during call to constructor)
1140
+ if($method == '')
1141
+ {
1142
+ $method = $this->method;
1143
+ }
1144
+
1145
+ if(is_array($msg))
1146
+ {
1147
+ // $msg is an array of xmlrpcmsg's
1148
+ $r = $this->multicall($msg, $timeout, $method);
1149
+ return $r;
1150
+ }
1151
+ elseif(is_string($msg))
1152
+ {
1153
+ $n = new xmlrpcmsg('');
1154
+ $n->payload = $msg;
1155
+ $msg = $n;
1156
+ }
1157
+
1158
+ // where msg is an xmlrpcmsg
1159
+ $msg->debug=$this->debug;
1160
+
1161
+ if($method == 'https')
1162
+ {
1163
+ $r =& $this->sendPayloadHTTPS(
1164
+ $msg,
1165
+ $this->server,
1166
+ $this->port,
1167
+ $timeout,
1168
+ $this->username,
1169
+ $this->password,
1170
+ $this->authtype,
1171
+ $this->cert,
1172
+ $this->certpass,
1173
+ $this->cacert,
1174
+ $this->cacertdir,
1175
+ $this->proxy,
1176
+ $this->proxyport,
1177
+ $this->proxy_user,
1178
+ $this->proxy_pass,
1179
+ $this->proxy_authtype,
1180
+ $this->keepalive,
1181
+ $this->key,
1182
+ $this->keypass
1183
+ );
1184
+ }
1185
+ elseif($method == 'http11')
1186
+ {
1187
+ $r =& $this->sendPayloadCURL(
1188
+ $msg,
1189
+ $this->server,
1190
+ $this->port,
1191
+ $timeout,
1192
+ $this->username,
1193
+ $this->password,
1194
+ $this->authtype,
1195
+ null,
1196
+ null,
1197
+ null,
1198
+ null,
1199
+ $this->proxy,
1200
+ $this->proxyport,
1201
+ $this->proxy_user,
1202
+ $this->proxy_pass,
1203
+ $this->proxy_authtype,
1204
+ 'http',
1205
+ $this->keepalive
1206
+ );
1207
+ }
1208
+ else
1209
+ {
1210
+ $r =& $this->sendPayloadHTTP10(
1211
+ $msg,
1212
+ $this->server,
1213
+ $this->port,
1214
+ $timeout,
1215
+ $this->username,
1216
+ $this->password,
1217
+ $this->authtype,
1218
+ $this->proxy,
1219
+ $this->proxyport,
1220
+ $this->proxy_user,
1221
+ $this->proxy_pass,
1222
+ $this->proxy_authtype
1223
+ );
1224
+ }
1225
+
1226
+ return $r;
1227
+ }
1228
+
1229
+ /**
1230
+ * @access private
1231
+ */
1232
+ function &sendPayloadHTTP10($msg, $server, $port, $timeout=0,
1233
+ $username='', $password='', $authtype=1, $proxyhost='',
1234
+ $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1)
1235
+ {
1236
+ if($port==0)
1237
+ {
1238
+ $port=80;
1239
+ }
1240
+
1241
+ // Only create the payload if it was not created previously
1242
+ if(empty($msg->payload))
1243
+ {
1244
+ $msg->createPayload($this->request_charset_encoding);
1245
+ }
1246
+
1247
+ $payload = $msg->payload;
1248
+ // Deflate request body and set appropriate request headers
1249
+ if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
1250
+ {
1251
+ if($this->request_compression == 'gzip')
1252
+ {
1253
+ $a = @gzencode($payload);
1254
+ if($a)
1255
+ {
1256
+ $payload = $a;
1257
+ $encoding_hdr = "Content-Encoding: gzip\r\n";
1258
+ }
1259
+ }
1260
+ else
1261
+ {
1262
+ $a = @gzcompress($payload);
1263
+ if($a)
1264
+ {
1265
+ $payload = $a;
1266
+ $encoding_hdr = "Content-Encoding: deflate\r\n";
1267
+ }
1268
+ }
1269
+ }
1270
+ else
1271
+ {
1272
+ $encoding_hdr = '';
1273
+ }
1274
+
1275
+ // thanks to Grant Rauscher <grant7@firstworld.net> for this
1276
+ $credentials='';
1277
+ if($username!='')
1278
+ {
1279
+ $credentials='Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n";
1280
+ if ($authtype != 1)
1281
+ {
1282
+ error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth is supported with HTTP 1.0');
1283
+ }
1284
+ }
1285
+
1286
+ $accepted_encoding = '';
1287
+ if(is_array($this->accepted_compression) && count($this->accepted_compression))
1288
+ {
1289
+ $accepted_encoding = 'Accept-Encoding: ' . implode(', ', $this->accepted_compression) . "\r\n";
1290
+ }
1291
+
1292
+ $proxy_credentials = '';
1293
+ if($proxyhost)
1294
+ {
1295
+ if($proxyport == 0)
1296
+ {
1297
+ $proxyport = 8080;
1298
+ }
1299
+ $connectserver = $proxyhost;
1300
+ $connectport = $proxyport;
1301
+ $uri = 'http://'.$server.':'.$port.$this->path;
1302
+ if($proxyusername != '')
1303
+ {
1304
+ if ($proxyauthtype != 1)
1305
+ {
1306
+ error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth to proxy is supported with HTTP 1.0');
1307
+ }
1308
+ $proxy_credentials = 'Proxy-Authorization: Basic ' . base64_encode($proxyusername.':'.$proxypassword) . "\r\n";
1309
+ }
1310
+ }
1311
+ else
1312
+ {
1313
+ $connectserver = $server;
1314
+ $connectport = $port;
1315
+ $uri = $this->path;
1316
+ }
1317
+
1318
+ // Cookie generation, as per rfc2965 (version 1 cookies) or
1319
+ // netscape's rules (version 0 cookies)
1320
+ $cookieheader='';
1321
+ if (count($this->cookies))
1322
+ {
1323
+ $version = '';
1324
+ foreach ($this->cookies as $name => $cookie)
1325
+ {
1326
+ if ($cookie['version'])
1327
+ {
1328
+ $version = ' $Version="' . $cookie['version'] . '";';
1329
+ $cookieheader .= ' ' . $name . '="' . $cookie['value'] . '";';
1330
+ if ($cookie['path'])
1331
+ $cookieheader .= ' $Path="' . $cookie['path'] . '";';
1332
+ if ($cookie['domain'])
1333
+ $cookieheader .= ' $Domain="' . $cookie['domain'] . '";';
1334
+ if ($cookie['port'])
1335
+ $cookieheader .= ' $Port="' . $cookie['port'] . '";';
1336
+ }
1337
+ else
1338
+ {
1339
+ $cookieheader .= ' ' . $name . '=' . $cookie['value'] . ";";
1340
+ }
1341
+ }
1342
+ $cookieheader = 'Cookie:' . $version . substr($cookieheader, 0, -1) . "\r\n";
1343
+ }
1344
+
1345
+ $op= 'POST ' . $uri. " HTTP/1.0\r\n" .
1346
+ 'User-Agent: ' . $this->user_agent . "\r\n" .
1347
+ 'Host: '. $server . ':' . $port . "\r\n" .
1348
+ $credentials .
1349
+ $proxy_credentials .
1350
+ $accepted_encoding .
1351
+ $encoding_hdr .
1352
+ 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings) . "\r\n" .
1353
+ $cookieheader .
1354
+ 'Content-Type: ' . $msg->content_type . "\r\nContent-Length: " .
1355
+ strlen($payload) . "\r\n\r\n" .
1356
+ $payload;
1357
+
1358
+ if($this->debug > 1)
1359
+ {
1360
+ print "<PRE>\n---SENDING---\n" . htmlentities($op) . "\n---END---\n</PRE>";
1361
+ // let the client see this now in case http times out...
1362
+ flush();
1363
+ }
1364
+
1365
+ if($timeout>0)
1366
+ {
1367
+ $fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr, $timeout);
1368
+ }
1369
+ else
1370
+ {
1371
+ $fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr);
1372
+ }
1373
+ if($fp)
1374
+ {
1375
+ if($timeout>0 && function_exists('stream_set_timeout'))
1376
+ {
1377
+ stream_set_timeout($fp, $timeout);
1378
+ }
1379
+ }
1380
+ else
1381
+ {
1382
+ $this->errstr='Connect error: '.$this->errstr;
1383
+ $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr . ' (' . $this->errno . ')');
1384
+ return $r;
1385
+ }
1386
+
1387
+ if(!fputs($fp, $op, strlen($op)))
1388
+ {
1389
+ fclose($fp);
1390
+ $this->errstr='Write error';
1391
+ $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr);
1392
+ return $r;
1393
+ }
1394
+ else
1395
+ {
1396
+ // reset errno and errstr on succesful socket connection
1397
+ $this->errstr = '';
1398
+ }
1399
+ // G. Giunta 2005/10/24: close socket before parsing.
1400
+ // should yeld slightly better execution times, and make easier recursive calls (e.g. to follow http redirects)
1401
+ $ipd='';
1402
+ do
1403
+ {
1404
+ // shall we check for $data === FALSE?
1405
+ // as per the manual, it signals an error
1406
+ $ipd.=fread($fp, 32768);
1407
+ } while(!feof($fp));
1408
+ fclose($fp);
1409
+ $r =& $msg->parseResponse($ipd, false, $this->return_type);
1410
+ return $r;
1411
+
1412
+ }
1413
+
1414
+ /**
1415
+ * @access private
1416
+ */
1417
+ function &sendPayloadHTTPS($msg, $server, $port, $timeout=0, $username='',
1418
+ $password='', $authtype=1, $cert='',$certpass='', $cacert='', $cacertdir='',
1419
+ $proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1,
1420
+ $keepalive=false, $key='', $keypass='')
1421
+ {
1422
+ $r =& $this->sendPayloadCURL($msg, $server, $port, $timeout, $username,
1423
+ $password, $authtype, $cert, $certpass, $cacert, $cacertdir, $proxyhost, $proxyport,
1424
+ $proxyusername, $proxypassword, $proxyauthtype, 'https', $keepalive, $key, $keypass);
1425
+ return $r;
1426
+ }
1427
+
1428
+ /**
1429
+ * Contributed by Justin Miller <justin@voxel.net>
1430
+ * Requires curl to be built into PHP
1431
+ * NB: CURL versions before 7.11.10 cannot use proxy to talk to https servers!
1432
+ * @access private
1433
+ */
1434
+ function &sendPayloadCURL($msg, $server, $port, $timeout=0, $username='',
1435
+ $password='', $authtype=1, $cert='', $certpass='', $cacert='', $cacertdir='',
1436
+ $proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1, $method='https',
1437
+ $keepalive=false, $key='', $keypass='')
1438
+ {
1439
+ if(!function_exists('curl_init'))
1440
+ {
1441
+ $this->errstr='CURL unavailable on this install';
1442
+ $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_curl'], $GLOBALS['xmlrpcstr']['no_curl']);
1443
+ return $r;
1444
+ }
1445
+ if($method == 'https')
1446
+ {
1447
+ if(($info = curl_version()) &&
1448
+ ((is_string($info) && strpos($info, 'OpenSSL') === null) || (is_array($info) && !isset($info['ssl_version']))))
1449
+ {
1450
+ $this->errstr='SSL unavailable on this install';
1451
+ $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_ssl'], $GLOBALS['xmlrpcstr']['no_ssl']);
1452
+ return $r;
1453
+ }
1454
+ }
1455
+
1456
+ if($port == 0)
1457
+ {
1458
+ if($method == 'http')
1459
+ {
1460
+ $port = 80;
1461
+ }
1462
+ else
1463
+ {
1464
+ $port = 443;
1465
+ }
1466
+ }
1467
+
1468
+ // Only create the payload if it was not created previously
1469
+ if(empty($msg->payload))
1470
+ {
1471
+ $msg->createPayload($this->request_charset_encoding);
1472
+ }
1473
+
1474
+ // Deflate request body and set appropriate request headers
1475
+ $payload = $msg->payload;
1476
+ if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
1477
+ {
1478
+ if($this->request_compression == 'gzip')
1479
+ {
1480
+ $a = @gzencode($payload);
1481
+ if($a)
1482
+ {
1483
+ $payload = $a;
1484
+ $encoding_hdr = 'Content-Encoding: gzip';
1485
+ }
1486
+ }
1487
+ else
1488
+ {
1489
+ $a = @gzcompress($payload);
1490
+ if($a)
1491
+ {
1492
+ $payload = $a;
1493
+ $encoding_hdr = 'Content-Encoding: deflate';
1494
+ }
1495
+ }
1496
+ }
1497
+ else
1498
+ {
1499
+ $encoding_hdr = '';
1500
+ }
1501
+
1502
+ if($this->debug > 1)
1503
+ {
1504
+ print "<PRE>\n---SENDING---\n" . htmlentities($payload) . "\n---END---\n</PRE>";
1505
+ // let the client see this now in case http times out...
1506
+ flush();
1507
+ }
1508
+
1509
+ if(!$keepalive || !$this->xmlrpc_curl_handle)
1510
+ {
1511
+ $curl = curl_init($method . '://' . $server . ':' . $port . $this->path);
1512
+ if($keepalive)
1513
+ {
1514
+ $this->xmlrpc_curl_handle = $curl;
1515
+ }
1516
+ }
1517
+ else
1518
+ {
1519
+ $curl = $this->xmlrpc_curl_handle;
1520
+ }
1521
+
1522
+ // results into variable
1523
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
1524
+
1525
+ if($this->debug)
1526
+ {
1527
+ curl_setopt($curl, CURLOPT_VERBOSE, 1);
1528
+ }
1529
+ curl_setopt($curl, CURLOPT_USERAGENT, $this->user_agent);
1530
+ // required for XMLRPC: post the data
1531
+ curl_setopt($curl, CURLOPT_POST, 1);
1532
+ // the data
1533
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
1534
+
1535
+ // return the header too
1536
+ curl_setopt($curl, CURLOPT_HEADER, 1);
1537
+
1538
+ // will only work with PHP >= 5.0
1539
+ // NB: if we set an empty string, CURL will add http header indicating
1540
+ // ALL methods it is supporting. This is possibly a better option than
1541
+ // letting the user tell what curl can / cannot do...
1542
+ if(is_array($this->accepted_compression) && count($this->accepted_compression))
1543
+ {
1544
+ //curl_setopt($curl, CURLOPT_ENCODING, implode(',', $this->accepted_compression));
1545
+ // empty string means 'any supported by CURL' (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1546
+ if (count($this->accepted_compression) == 1)
1547
+ {
1548
+ curl_setopt($curl, CURLOPT_ENCODING, $this->accepted_compression[0]);
1549
+ }
1550
+ else
1551
+ curl_setopt($curl, CURLOPT_ENCODING, '');
1552
+ }
1553
+ // extra headers
1554
+ $headers = array('Content-Type: ' . $msg->content_type , 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings));
1555
+ // if no keepalive is wanted, let the server know it in advance
1556
+ if(!$keepalive)
1557
+ {
1558
+ $headers[] = 'Connection: close';
1559
+ }
1560
+ // request compression header
1561
+ if($encoding_hdr)
1562
+ {
1563
+ $headers[] = $encoding_hdr;
1564
+ }
1565
+
1566
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
1567
+ // timeout is borked
1568
+ if($timeout)
1569
+ {
1570
+ curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1);
1571
+ }
1572
+
1573
+ if($username && $password)
1574
+ {
1575
+ curl_setopt($curl, CURLOPT_USERPWD, $username.':'.$password);
1576
+ if (defined('CURLOPT_HTTPAUTH'))
1577
+ {
1578
+ curl_setopt($curl, CURLOPT_HTTPAUTH, $authtype);
1579
+ }
1580
+ else if ($authtype != 1)
1581
+ {
1582
+ error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth is supported by the current PHP/curl install');
1583
+ }
1584
+ }
1585
+
1586
+ if($method == 'https')
1587
+ {
1588
+ // set cert file
1589
+ if($cert)
1590
+ {
1591
+ curl_setopt($curl, CURLOPT_SSLCERT, $cert);
1592
+ }
1593
+ // set cert password
1594
+ if($certpass)
1595
+ {
1596
+ curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $certpass);
1597
+ }
1598
+ // whether to verify remote host's cert
1599
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verifypeer);
1600
+ // set ca certificates file/dir
1601
+ if($cacert)
1602
+ {
1603
+ curl_setopt($curl, CURLOPT_CAINFO, $cacert);
1604
+ }
1605
+ if($cacertdir)
1606
+ {
1607
+ curl_setopt($curl, CURLOPT_CAPATH, $cacertdir);
1608
+ }
1609
+ // set key file (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1610
+ if($key)
1611
+ {
1612
+ curl_setopt($curl, CURLOPT_SSLKEY, $key);
1613
+ }
1614
+ // set key password (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1615
+ if($keypass)
1616
+ {
1617
+ curl_setopt($curl, CURLOPT_SSLKEYPASSWD, $keypass);
1618
+ }
1619
+ // whether to verify cert's common name (CN); 0 for no, 1 to verify that it exists, and 2 to verify that it matches the hostname used
1620
+ curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->verifyhost);
1621
+ }
1622
+
1623
+ // proxy info
1624
+ if($proxyhost)
1625
+ {
1626
+ if($proxyport == 0)
1627
+ {
1628
+ $proxyport = 8080; // NB: even for HTTPS, local connection is on port 8080
1629
+ }
1630
+ curl_setopt($curl, CURLOPT_PROXY, $proxyhost.':'.$proxyport);
1631
+ //curl_setopt($curl, CURLOPT_PROXYPORT,$proxyport);
1632
+ if($proxyusername)
1633
+ {
1634
+ curl_setopt($curl, CURLOPT_PROXYUSERPWD, $proxyusername.':'.$proxypassword);
1635
+ if (defined('CURLOPT_PROXYAUTH'))
1636
+ {
1637
+ curl_setopt($curl, CURLOPT_PROXYAUTH, $proxyauthtype);
1638
+ }
1639
+ else if ($proxyauthtype != 1)
1640
+ {
1641
+ error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth to proxy is supported by the current PHP/curl install');
1642
+ }
1643
+ }
1644
+ }
1645
+
1646
+ // NB: should we build cookie http headers by hand rather than let CURL do it?
1647
+ // the following code does not honour 'expires', 'path' and 'domain' cookie attributes
1648
+ // set to client obj the the user...
1649
+ if (count($this->cookies))
1650
+ {
1651
+ $cookieheader = '';
1652
+ foreach ($this->cookies as $name => $cookie)
1653
+ {
1654
+ $cookieheader .= $name . '=' . $cookie['value'] . '; ';
1655
+ }
1656
+ curl_setopt($curl, CURLOPT_COOKIE, substr($cookieheader, 0, -2));
1657
+ }
1658
+
1659
+ foreach ($this->extracurlopts as $opt => $val)
1660
+ {
1661
+ curl_setopt($curl, $opt, $val);
1662
+ }
1663
+
1664
+ $result = curl_exec($curl);
1665
+
1666
+ if ($this->debug > 1)
1667
+ {
1668
+ print "<PRE>\n---CURL INFO---\n";
1669
+ foreach(curl_getinfo($curl) as $name => $val)
1670
+ print $name . ': ' . htmlentities($val). "\n";
1671
+ print "---END---\n</PRE>";
1672
+ }
1673
+
1674
+ if(!$result) /// @todo we should use a better check here - what if we get back '' or '0'?
1675
+ {
1676
+ $this->errstr='no response';
1677
+ $resp=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['curl_fail'], $GLOBALS['xmlrpcstr']['curl_fail']. ': '. curl_error($curl));
1678
+ curl_close($curl);
1679
+ if($keepalive)
1680
+ {
1681
+ $this->xmlrpc_curl_handle = null;
1682
+ }
1683
+ }
1684
+ else
1685
+ {
1686
+ if(!$keepalive)
1687
+ {
1688
+ curl_close($curl);
1689
+ }
1690
+ $resp =& $msg->parseResponse($result, true, $this->return_type);
1691
+ }
1692
+ return $resp;
1693
+ }
1694
+
1695
+ /**
1696
+ * Send an array of request messages and return an array of responses.
1697
+ * Unless $this->no_multicall has been set to true, it will try first
1698
+ * to use one single xmlrpc call to server method system.multicall, and
1699
+ * revert to sending many successive calls in case of failure.
1700
+ * This failure is also stored in $this->no_multicall for subsequent calls.
1701
+ * Unfortunately, there is no server error code universally used to denote
1702
+ * the fact that multicall is unsupported, so there is no way to reliably
1703
+ * distinguish between that and a temporary failure.
1704
+ * If you are sure that server supports multicall and do not want to
1705
+ * fallback to using many single calls, set the fourth parameter to FALSE.
1706
+ *
1707
+ * NB: trying to shoehorn extra functionality into existing syntax has resulted
1708
+ * in pretty much convoluted code...
1709
+ *
1710
+ * @param array $msgs an array of xmlrpcmsg objects
1711
+ * @param integer $timeout connection timeout (in seconds)
1712
+ * @param string $method the http protocol variant to be used
1713
+ * @param boolean fallback When true, upon receiveing an error during multicall, multiple single calls will be attempted
1714
+ * @return array
1715
+ * @access public
1716
+ */
1717
+ function multicall($msgs, $timeout=0, $method='', $fallback=true)
1718
+ {
1719
+ if ($method == '')
1720
+ {
1721
+ $method = $this->method;
1722
+ }
1723
+ if(!$this->no_multicall)
1724
+ {
1725
+ $results = $this->_try_multicall($msgs, $timeout, $method);
1726
+ if(is_array($results))
1727
+ {
1728
+ // System.multicall succeeded
1729
+ return $results;
1730
+ }
1731
+ else
1732
+ {
1733
+ // either system.multicall is unsupported by server,
1734
+ // or call failed for some other reason.
1735
+ if ($fallback)
1736
+ {
1737
+ // Don't try it next time...
1738
+ $this->no_multicall = true;
1739
+ }
1740
+ else
1741
+ {
1742
+ if (is_a($results, 'xmlrpcresp'))
1743
+ {
1744
+ $result = $results;
1745
+ }
1746
+ else
1747
+ {
1748
+ $result = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['multicall_error'], $GLOBALS['xmlrpcstr']['multicall_error']);
1749
+ }
1750
+ }
1751
+ }
1752
+ }
1753
+ else
1754
+ {
1755
+ // override fallback, in case careless user tries to do two
1756
+ // opposite things at the same time
1757
+ $fallback = true;
1758
+ }
1759
+
1760
+ $results = array();
1761
+ if ($fallback)
1762
+ {
1763
+ // system.multicall is (probably) unsupported by server:
1764
+ // emulate multicall via multiple requests
1765
+ foreach($msgs as $msg)
1766
+ {
1767
+ $results[] =& $this->send($msg, $timeout, $method);
1768
+ }
1769
+ }
1770
+ else
1771
+ {
1772
+ // user does NOT want to fallback on many single calls:
1773
+ // since we should always return an array of responses,
1774
+ // return an array with the same error repeated n times
1775
+ foreach($msgs as $msg)
1776
+ {
1777
+ $results[] = $result;
1778
+ }
1779
+ }
1780
+ return $results;
1781
+ }
1782
+
1783
+ /**
1784
+ * Attempt to boxcar $msgs via system.multicall.
1785
+ * Returns either an array of xmlrpcreponses, an xmlrpc error response
1786
+ * or false (when received response does not respect valid multicall syntax)
1787
+ * @access private
1788
+ */
1789
+ function _try_multicall($msgs, $timeout, $method)
1790
+ {
1791
+ // Construct multicall message
1792
+ $calls = array();
1793
+ foreach($msgs as $msg)
1794
+ {
1795
+ $call['methodName'] = new xmlrpcval($msg->method(),'string');
1796
+ $numParams = $msg->getNumParams();
1797
+ $params = array();
1798
+ for($i = 0; $i < $numParams; $i++)
1799
+ {
1800
+ $params[$i] = $msg->getParam($i);
1801
+ }
1802
+ $call['params'] = new xmlrpcval($params, 'array');
1803
+ $calls[] = new xmlrpcval($call, 'struct');
1804
+ }
1805
+ $multicall = new xmlrpcmsg('system.multicall');
1806
+ $multicall->addParam(new xmlrpcval($calls, 'array'));
1807
+
1808
+ // Attempt RPC call
1809
+ $result =& $this->send($multicall, $timeout, $method);
1810
+
1811
+ if($result->faultCode() != 0)
1812
+ {
1813
+ // call to system.multicall failed
1814
+ return $result;
1815
+ }
1816
+
1817
+ // Unpack responses.
1818
+ $rets = $result->value();
1819
+
1820
+ if ($this->return_type == 'xml')
1821
+ {
1822
+ return $rets;
1823
+ }
1824
+ else if ($this->return_type == 'phpvals')
1825
+ {
1826
+ ///@todo test this code branch...
1827
+ $rets = $result->value();
1828
+ if(!is_array($rets))
1829
+ {
1830
+ return false; // bad return type from system.multicall
1831
+ }
1832
+ $numRets = count($rets);
1833
+ if($numRets != count($msgs))
1834
+ {
1835
+ return false; // wrong number of return values.
1836
+ }
1837
+
1838
+ $response = array();
1839
+ for($i = 0; $i < $numRets; $i++)
1840
+ {
1841
+ $val = $rets[$i];
1842
+ if (!is_array($val)) {
1843
+ return false;
1844
+ }
1845
+ switch(count($val))
1846
+ {
1847
+ case 1:
1848
+ if(!isset($val[0]))
1849
+ {
1850
+ return false; // Bad value
1851
+ }
1852
+ // Normal return value
1853
+ $response[$i] = new xmlrpcresp($val[0], 0, '', 'phpvals');
1854
+ break;
1855
+ case 2:
1856
+ /// @todo remove usage of @: it is apparently quite slow
1857
+ $code = @$val['faultCode'];
1858
+ if(!is_int($code))
1859
+ {
1860
+ return false;
1861
+ }
1862
+ $str = @$val['faultString'];
1863
+ if(!is_string($str))
1864
+ {
1865
+ return false;
1866
+ }
1867
+ $response[$i] = new xmlrpcresp(0, $code, $str);
1868
+ break;
1869
+ default:
1870
+ return false;
1871
+ }
1872
+ }
1873
+ return $response;
1874
+ }
1875
+ else // return type == 'xmlrpcvals'
1876
+ {
1877
+ $rets = $result->value();
1878
+ if($rets->kindOf() != 'array')
1879
+ {
1880
+ return false; // bad return type from system.multicall
1881
+ }
1882
+ $numRets = $rets->arraysize();
1883
+ if($numRets != count($msgs))
1884
+ {
1885
+ return false; // wrong number of return values.
1886
+ }
1887
+
1888
+ $response = array();
1889
+ for($i = 0; $i < $numRets; $i++)
1890
+ {
1891
+ $val = $rets->arraymem($i);
1892
+ switch($val->kindOf())
1893
+ {
1894
+ case 'array':
1895
+ if($val->arraysize() != 1)
1896
+ {
1897
+ return false; // Bad value
1898
+ }
1899
+ // Normal return value
1900
+ $response[$i] = new xmlrpcresp($val->arraymem(0));
1901
+ break;
1902
+ case 'struct':
1903
+ $code = $val->structmem('faultCode');
1904
+ if($code->kindOf() != 'scalar' || $code->scalartyp() != 'int')
1905
+ {
1906
+ return false;
1907
+ }
1908
+ $str = $val->structmem('faultString');
1909
+ if($str->kindOf() != 'scalar' || $str->scalartyp() != 'string')
1910
+ {
1911
+ return false;
1912
+ }
1913
+ $response[$i] = new xmlrpcresp(0, $code->scalarval(), $str->scalarval());
1914
+ break;
1915
+ default:
1916
+ return false;
1917
+ }
1918
+ }
1919
+ return $response;
1920
+ }
1921
+ }
1922
+ } // end class xmlrpc_client
1923
+
1924
+ class xmlrpcresp
1925
+ {
1926
+ var $val = 0;
1927
+ var $valtyp;
1928
+ var $errno = 0;
1929
+ var $errstr = '';
1930
+ var $payload;
1931
+ var $hdrs = array();
1932
+ var $_cookies = array();
1933
+ var $content_type = 'text/xml';
1934
+ var $raw_data = '';
1935
+
1936
+ /**
1937
+ * @param mixed $val either an xmlrpcval obj, a php value or the xml serialization of an xmlrpcval (a string)
1938
+ * @param integer $fcode set it to anything but 0 to create an error response
1939
+ * @param string $fstr the error string, in case of an error response
1940
+ * @param string $valtyp either 'xmlrpcvals', 'phpvals' or 'xml'
1941
+ *
1942
+ * @todo add check that $val / $fcode / $fstr is of correct type???
1943
+ * NB: as of now we do not do it, since it might be either an xmlrpcval or a plain
1944
+ * php val, or a complete xml chunk, depending on usage of xmlrpc_client::send() inside which creator is called...
1945
+ */
1946
+ function xmlrpcresp($val, $fcode = 0, $fstr = '', $valtyp='')
1947
+ {
1948
+ if($fcode != 0)
1949
+ {
1950
+ // error response
1951
+ $this->errno = $fcode;
1952
+ $this->errstr = $fstr;
1953
+ //$this->errstr = htmlspecialchars($fstr); // XXX: encoding probably shouldn't be done here; fix later.
1954
+ }
1955
+ else
1956
+ {
1957
+ // successful response
1958
+ $this->val = $val;
1959
+ if ($valtyp == '')
1960
+ {
1961
+ // user did not declare type of response value: try to guess it
1962
+ if (is_object($this->val) && is_a($this->val, 'xmlrpcval'))
1963
+ {
1964
+ $this->valtyp = 'xmlrpcvals';
1965
+ }
1966
+ else if (is_string($this->val))
1967
+ {
1968
+ $this->valtyp = 'xml';
1969
+
1970
+ }
1971
+ else
1972
+ {
1973
+ $this->valtyp = 'phpvals';
1974
+ }
1975
+ }
1976
+ else
1977
+ {
1978
+ // user declares type of resp value: believe him
1979
+ $this->valtyp = $valtyp;
1980
+ }
1981
+ }
1982
+ }
1983
+
1984
+ /**
1985
+ * Returns the error code of the response.
1986
+ * @return integer the error code of this response (0 for not-error responses)
1987
+ * @access public
1988
+ */
1989
+ function faultCode()
1990
+ {
1991
+ return $this->errno;
1992
+ }
1993
+
1994
+ /**
1995
+ * Returns the error code of the response.
1996
+ * @return string the error string of this response ('' for not-error responses)
1997
+ * @access public
1998
+ */
1999
+ function faultString()
2000
+ {
2001
+ return $this->errstr;
2002
+ }
2003
+
2004
+ /**
2005
+ * Returns the value received by the server.
2006
+ * @return mixed the xmlrpcval object returned by the server. Might be an xml string or php value if the response has been created by specially configured xmlrpc_client objects
2007
+ * @access public
2008
+ */
2009
+ function value()
2010
+ {
2011
+ return $this->val;
2012
+ }
2013
+
2014
+ /**
2015
+ * Returns an array with the cookies received from the server.
2016
+ * Array has the form: $cookiename => array ('value' => $val, $attr1 => $val1, $attr2 = $val2, ...)
2017
+ * with attributes being e.g. 'expires', 'path', domain'.
2018
+ * NB: cookies sent as 'expired' by the server (i.e. with an expiry date in the past)
2019
+ * are still present in the array. It is up to the user-defined code to decide
2020
+ * how to use the received cookies, and wheter they have to be sent back with the next
2021
+ * request to the server (using xmlrpc_client::setCookie) or not
2022
+ * @return array array of cookies received from the server
2023
+ * @access public
2024
+ */
2025
+ function cookies()
2026
+ {
2027
+ return $this->_cookies;
2028
+ }
2029
+
2030
+ /**
2031
+ * Returns xml representation of the response. XML prologue not included
2032
+ * @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
2033
+ * @return string the xml representation of the response
2034
+ * @access public
2035
+ */
2036
+ function serialize($charset_encoding='')
2037
+ {
2038
+ if ($charset_encoding != '')
2039
+ $this->content_type = 'text/xml; charset=' . $charset_encoding;
2040
+ else
2041
+ $this->content_type = 'text/xml';
2042
+ $result = "<methodResponse>\n";
2043
+ if($this->errno)
2044
+ {
2045
+ // G. Giunta 2005/2/13: let non-ASCII response messages be tolerated by clients
2046
+ // by xml-encoding non ascii chars
2047
+ $result .= "<fault>\n" .
2048
+ "<value>\n<struct><member><name>faultCode</name>\n<value><int>" . $this->errno .
2049
+ "</int></value>\n</member>\n<member>\n<name>faultString</name>\n<value><string>" .
2050
+ xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "</string></value>\n</member>\n" .
2051
+ "</struct>\n</value>\n</fault>";
2052
+ }
2053
+ else
2054
+ {
2055
+ if(!is_object($this->val) || !is_a($this->val, 'xmlrpcval'))
2056
+ {
2057
+ if (is_string($this->val) && $this->valtyp == 'xml')
2058
+ {
2059
+ $result .= "<params>\n<param>\n" .
2060
+ $this->val .
2061
+ "</param>\n</params>";
2062
+ }
2063
+ else
2064
+ {
2065
+ /// @todo try to build something serializable?
2066
+ die('cannot serialize xmlrpcresp objects whose content is native php values');
2067
+ }
2068
+ }
2069
+ else
2070
+ {
2071
+ $result .= "<params>\n<param>\n" .
2072
+ $this->val->serialize($charset_encoding) .
2073
+ "</param>\n</params>";
2074
+ }
2075
+ }
2076
+ $result .= "\n</methodResponse>";
2077
+ $this->payload = $result;
2078
+ return $result;
2079
+ }
2080
+ }
2081
+
2082
+ class xmlrpcmsg
2083
+ {
2084
+ var $payload;
2085
+ var $methodname;
2086
+ var $params=array();
2087
+ var $debug=0;
2088
+ var $content_type = 'text/xml';
2089
+
2090
+ /**
2091
+ * @param string $meth the name of the method to invoke
2092
+ * @param array $pars array of parameters to be paased to the method (xmlrpcval objects)
2093
+ */
2094
+ function xmlrpcmsg($meth, $pars=0)
2095
+ {
2096
+ $this->methodname=$meth;
2097
+ if(is_array($pars) && count($pars)>0)
2098
+ {
2099
+ for($i=0; $i<count($pars); $i++)
2100
+ {
2101
+ $this->addParam($pars[$i]);
2102
+ }
2103
+ }
2104
+ }
2105
+
2106
+ /**
2107
+ * @access private
2108
+ */
2109
+ function xml_header($charset_encoding='')
2110
+ {
2111
+ if ($charset_encoding != '')
2112
+ {
2113
+ return "<?xml version=\"1.0\" encoding=\"$charset_encoding\" ?" . ">\n<methodCall>\n";
2114
+ }
2115
+ else
2116
+ {
2117
+ return "<?xml version=\"1.0\"?" . ">\n<methodCall>\n";
2118
+ }
2119
+ }
2120
+
2121
+ /**
2122
+ * @access private
2123
+ */
2124
+ function xml_footer()
2125
+ {
2126
+ return '</methodCall>';
2127
+ }
2128
+
2129
+ /**
2130
+ * @access private
2131
+ */
2132
+ function kindOf()
2133
+ {
2134
+ return 'msg';
2135
+ }
2136
+
2137
+ /**
2138
+ * @access private
2139
+ */
2140
+ function createPayload($charset_encoding='')
2141
+ {
2142
+ if ($charset_encoding != '')
2143
+ $this->content_type = 'text/xml; charset=' . $charset_encoding;
2144
+ else
2145
+ $this->content_type = 'text/xml';
2146
+ $this->payload=$this->xml_header($charset_encoding);
2147
+ $this->payload.='<methodName>' . $this->methodname . "</methodName>\n";
2148
+ $this->payload.="<params>\n";
2149
+ for($i=0; $i<count($this->params); $i++)
2150
+ {
2151
+ $p=$this->params[$i];
2152
+ $this->payload.="<param>\n" . $p->serialize($charset_encoding) .
2153
+ "</param>\n";
2154
+ }
2155
+ $this->payload.="</params>\n";
2156
+ $this->payload.=$this->xml_footer();
2157
+ }
2158
+
2159
+ /**
2160
+ * Gets/sets the xmlrpc method to be invoked
2161
+ * @param string $meth the method to be set (leave empty not to set it)
2162
+ * @return string the method that will be invoked
2163
+ * @access public
2164
+ */
2165
+ function method($meth='')
2166
+ {
2167
+ if($meth!='')
2168
+ {
2169
+ $this->methodname=$meth;
2170
+ }
2171
+ return $this->methodname;
2172
+ }
2173
+
2174
+ /**
2175
+ * Returns xml representation of the message. XML prologue included
2176
+ * @return string the xml representation of the message, xml prologue included
2177
+ * @access public
2178
+ */
2179
+ function serialize($charset_encoding='')
2180
+ {
2181
+ $this->createPayload($charset_encoding);
2182
+ return $this->payload;
2183
+ }
2184
+
2185
+ /**
2186
+ * Add a parameter to the list of parameters to be used upon method invocation
2187
+ * @param xmlrpcval $par
2188
+ * @return boolean false on failure
2189
+ * @access public
2190
+ */
2191
+ function addParam($par)
2192
+ {
2193
+ // add check: do not add to self params which are not xmlrpcvals
2194
+ if(is_object($par) && is_a($par, 'xmlrpcval'))
2195
+ {
2196
+ $this->params[]=$par;
2197
+ return true;
2198
+ }
2199
+ else
2200
+ {
2201
+ return false;
2202
+ }
2203
+ }
2204
+
2205
+ /**
2206
+ * Returns the nth parameter in the message. The index zero-based.
2207
+ * @param integer $i the index of the parameter to fetch (zero based)
2208
+ * @return xmlrpcval the i-th parameter
2209
+ * @access public
2210
+ */
2211
+ function getParam($i) { return $this->params[$i]; }
2212
+
2213
+ /**
2214
+ * Returns the number of parameters in the messge.
2215
+ * @return integer the number of parameters currently set
2216
+ * @access public
2217
+ */
2218
+ function getNumParams() { return count($this->params); }
2219
+
2220
+ /**
2221
+ * Given an open file handle, read all data available and parse it as axmlrpc response.
2222
+ * NB: the file handle is not closed by this function.
2223
+ * NNB: might have trouble in rare cases to work on network streams, as we
2224
+ * check for a read of 0 bytes instead of feof($fp).
2225
+ * But since checking for feof(null) returns false, we would risk an
2226
+ * infinite loop in that case, because we cannot trust the caller
2227
+ * to give us a valid pointer to an open file...
2228
+ * @access public
2229
+ * @return xmlrpcresp
2230
+ * @todo add 2nd & 3rd param to be passed to ParseResponse() ???
2231
+ */
2232
+ function &parseResponseFile($fp)
2233
+ {
2234
+ $ipd='';
2235
+ while($data=fread($fp, 32768))
2236
+ {
2237
+ $ipd.=$data;
2238
+ }
2239
+ //fclose($fp);
2240
+ $r =& $this->parseResponse($ipd);
2241
+ return $r;
2242
+ }
2243
+
2244
+ /**
2245
+ * Parses HTTP headers and separates them from data.
2246
+ * @access private
2247
+ */
2248
+ function &parseResponseHeaders(&$data, $headers_processed=false)
2249
+ {
2250
+ // Support "web-proxy-tunelling" connections for https through proxies
2251
+ if(preg_match('/^HTTP\/1\.[0-1] 200 Connection established/', $data))
2252
+ {
2253
+ // Look for CR/LF or simple LF as line separator,
2254
+ // (even though it is not valid http)
2255
+ $pos = strpos($data,"\r\n\r\n");
2256
+ if($pos || is_int($pos))
2257
+ {
2258
+ $bd = $pos+4;
2259
+ }
2260
+ else
2261
+ {
2262
+ $pos = strpos($data,"\n\n");
2263
+ if($pos || is_int($pos))
2264
+ {
2265
+ $bd = $pos+2;
2266
+ }
2267
+ else
2268
+ {
2269
+ // No separation between response headers and body: fault?
2270
+ $bd = 0;
2271
+ }
2272
+ }
2273
+ if ($bd)
2274
+ {
2275
+ // this filters out all http headers from proxy.
2276
+ // maybe we could take them into account, too?
2277
+ $data = substr($data, $bd);
2278
+ }
2279
+ else
2280
+ {
2281
+ error_log('XML-RPC: '.__METHOD__.': HTTPS via proxy error, tunnel connection possibly failed');
2282
+ $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (HTTPS via proxy error, tunnel connection possibly failed)');
2283
+ return $r;
2284
+ }
2285
+ }
2286
+
2287
+ // Strip HTTP 1.1 100 Continue header if present
2288
+ while(preg_match('/^HTTP\/1\.1 1[0-9]{2} /', $data))
2289
+ {
2290
+ $pos = strpos($data, 'HTTP', 12);
2291
+ // server sent a Continue header without any (valid) content following...
2292
+ // give the client a chance to know it
2293
+ if(!$pos && !is_int($pos)) // works fine in php 3, 4 and 5
2294
+ {
2295
+ break;
2296
+ }
2297
+ $data = substr($data, $pos);
2298
+ }
2299
+ if(!preg_match('/^HTTP\/[0-9.]+ 200 /', $data))
2300
+ {
2301
+ $errstr= substr($data, 0, strpos($data, "\n")-1);
2302
+ error_log('XML-RPC: '.__METHOD__.': HTTP error, got response: ' .$errstr);
2303
+ $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (' . $errstr . ')');
2304
+ return $r;
2305
+ }
2306
+
2307
+ $GLOBALS['_xh']['headers'] = array();
2308
+ $GLOBALS['_xh']['cookies'] = array();
2309
+
2310
+ // be tolerant to usage of \n instead of \r\n to separate headers and data
2311
+ // (even though it is not valid http)
2312
+ $pos = strpos($data,"\r\n\r\n");
2313
+ if($pos || is_int($pos))
2314
+ {
2315
+ $bd = $pos+4;
2316
+ }
2317
+ else
2318
+ {
2319
+ $pos = strpos($data,"\n\n");
2320
+ if($pos || is_int($pos))
2321
+ {
2322
+ $bd = $pos+2;
2323
+ }
2324
+ else
2325
+ {
2326
+ // No separation between response headers and body: fault?
2327
+ // we could take some action here instead of going on...
2328
+ $bd = 0;
2329
+ }
2330
+ }
2331
+ // be tolerant to line endings, and extra empty lines
2332
+ $ar = preg_split("/\r?\n/", trim(substr($data, 0, $pos)));
2333
+ while(list(,$line) = @each($ar))
2334
+ {
2335
+ // take care of multi-line headers and cookies
2336
+ $arr = explode(':',$line,2);
2337
+ if(count($arr) > 1)
2338
+ {
2339
+ $header_name = strtolower(trim($arr[0]));
2340
+ /// @todo some other headers (the ones that allow a CSV list of values)
2341
+ /// do allow many values to be passed using multiple header lines.
2342
+ /// We should add content to $GLOBALS['_xh']['headers'][$header_name]
2343
+ /// instead of replacing it for those...
2344
+ if ($header_name == 'set-cookie' || $header_name == 'set-cookie2')
2345
+ {
2346
+ if ($header_name == 'set-cookie2')
2347
+ {
2348
+ // version 2 cookies:
2349
+ // there could be many cookies on one line, comma separated
2350
+ $cookies = explode(',', $arr[1]);
2351
+ }
2352
+ else
2353
+ {
2354
+ $cookies = array($arr[1]);
2355
+ }
2356
+ foreach ($cookies as $cookie)
2357
+ {
2358
+ // glue together all received cookies, using a comma to separate them
2359
+ // (same as php does with getallheaders())
2360
+ if (isset($GLOBALS['_xh']['headers'][$header_name]))
2361
+ $GLOBALS['_xh']['headers'][$header_name] .= ', ' . trim($cookie);
2362
+ else
2363
+ $GLOBALS['_xh']['headers'][$header_name] = trim($cookie);
2364
+ // parse cookie attributes, in case user wants to correctly honour them
2365
+ // feature creep: only allow rfc-compliant cookie attributes?
2366
+ // @todo support for server sending multiple time cookie with same name, but using different PATHs
2367
+ $cookie = explode(';', $cookie);
2368
+ foreach ($cookie as $pos => $val)
2369
+ {
2370
+ $val = explode('=', $val, 2);
2371
+ $tag = trim($val[0]);
2372
+ $val = trim(@$val[1]);
2373
+ /// @todo with version 1 cookies, we should strip leading and trailing " chars
2374
+ if ($pos == 0)
2375
+ {
2376
+ $cookiename = $tag;
2377
+ $GLOBALS['_xh']['cookies'][$tag] = array();
2378
+ $GLOBALS['_xh']['cookies'][$cookiename]['value'] = urldecode($val);
2379
+ }
2380
+ else
2381
+ {
2382
+ if ($tag != 'value')
2383
+ {
2384
+ $GLOBALS['_xh']['cookies'][$cookiename][$tag] = $val;
2385
+ }
2386
+ }
2387
+ }
2388
+ }
2389
+ }
2390
+ else
2391
+ {
2392
+ $GLOBALS['_xh']['headers'][$header_name] = trim($arr[1]);
2393
+ }
2394
+ }
2395
+ elseif(isset($header_name))
2396
+ {
2397
+ /// @todo version1 cookies might span multiple lines, thus breaking the parsing above
2398
+ $GLOBALS['_xh']['headers'][$header_name] .= ' ' . trim($line);
2399
+ }
2400
+ }
2401
+
2402
+ $data = substr($data, $bd);
2403
+
2404
+ if($this->debug && count($GLOBALS['_xh']['headers']))
2405
+ {
2406
+ print '<PRE>';
2407
+ foreach($GLOBALS['_xh']['headers'] as $header => $value)
2408
+ {
2409
+ print htmlentities("HEADER: $header: $value\n");
2410
+ }
2411
+ foreach($GLOBALS['_xh']['cookies'] as $header => $value)
2412
+ {
2413
+ print htmlentities("COOKIE: $header={$value['value']}\n");
2414
+ }
2415
+ print "</PRE>\n";
2416
+ }
2417
+
2418
+ // if CURL was used for the call, http headers have been processed,
2419
+ // and dechunking + reinflating have been carried out
2420
+ if(!$headers_processed)
2421
+ {
2422
+ // Decode chunked encoding sent by http 1.1 servers
2423
+ if(isset($GLOBALS['_xh']['headers']['transfer-encoding']) && $GLOBALS['_xh']['headers']['transfer-encoding'] == 'chunked')
2424
+ {
2425
+ if(!$data = decode_chunked($data))
2426
+ {
2427
+ error_log('XML-RPC: '.__METHOD__.': errors occurred when trying to rebuild the chunked data received from server');
2428
+ $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['dechunk_fail'], $GLOBALS['xmlrpcstr']['dechunk_fail']);
2429
+ return $r;
2430
+ }
2431
+ }
2432
+
2433
+ // Decode gzip-compressed stuff
2434
+ // code shamelessly inspired from nusoap library by Dietrich Ayala
2435
+ if(isset($GLOBALS['_xh']['headers']['content-encoding']))
2436
+ {
2437
+ $GLOBALS['_xh']['headers']['content-encoding'] = str_replace('x-', '', $GLOBALS['_xh']['headers']['content-encoding']);
2438
+ if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' || $GLOBALS['_xh']['headers']['content-encoding'] == 'gzip')
2439
+ {
2440
+ // if decoding works, use it. else assume data wasn't gzencoded
2441
+ if(function_exists('gzinflate'))
2442
+ {
2443
+ if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data))
2444
+ {
2445
+ $data = $degzdata;
2446
+ if($this->debug)
2447
+ print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
2448
+ }
2449
+ elseif($GLOBALS['_xh']['headers']['content-encoding'] == 'gzip' && $degzdata = @gzinflate(substr($data, 10)))
2450
+ {
2451
+ $data = $degzdata;
2452
+ if($this->debug)
2453
+ print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
2454
+ }
2455
+ else
2456
+ {
2457
+ error_log('XML-RPC: '.__METHOD__.': errors occurred when trying to decode the deflated data received from server');
2458
+ $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['decompress_fail'], $GLOBALS['xmlrpcstr']['decompress_fail']);
2459
+ return $r;
2460
+ }
2461
+ }
2462
+ else
2463
+ {
2464
+ error_log('XML-RPC: '.__METHOD__.': the server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
2465
+ $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['cannot_decompress'], $GLOBALS['xmlrpcstr']['cannot_decompress']);
2466
+ return $r;
2467
+ }
2468
+ }
2469
+ }
2470
+ } // end of 'if needed, de-chunk, re-inflate response'
2471
+
2472
+ // real stupid hack to avoid PHP complaining about returning NULL by ref
2473
+ $r = null;
2474
+ $r =& $r;
2475
+ return $r;
2476
+ }
2477
+
2478
+ /**
2479
+ * Parse the xmlrpc response contained in the string $data and return an xmlrpcresp object.
2480
+ * @param string $data the xmlrpc response, eventually including http headers
2481
+ * @param bool $headers_processed when true prevents parsing HTTP headers for interpretation of content-encoding and consequent decoding
2482
+ * @param string $return_type decides return type, i.e. content of response->value(). Either 'xmlrpcvals', 'xml' or 'phpvals'
2483
+ * @return xmlrpcresp
2484
+ * @access public
2485
+ */
2486
+ function &parseResponse($data='', $headers_processed=false, $return_type='xmlrpcvals')
2487
+ {
2488
+ if($this->debug)
2489
+ {
2490
+ //by maHo, replaced htmlspecialchars with htmlentities
2491
+ print "<PRE>---GOT---\n" . htmlentities($data) . "\n---END---\n</PRE>";
2492
+ }
2493
+
2494
+ if($data == '')
2495
+ {
2496
+ error_log('XML-RPC: '.__METHOD__.': no response received from server.');
2497
+ $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_data'], $GLOBALS['xmlrpcstr']['no_data']);
2498
+ return $r;
2499
+ }
2500
+
2501
+ $GLOBALS['_xh']=array();
2502
+
2503
+ $raw_data = $data;
2504
+ // parse the HTTP headers of the response, if present, and separate them from data
2505
+ if(substr($data, 0, 4) == 'HTTP')
2506
+ {
2507
+ $r =& $this->parseResponseHeaders($data, $headers_processed);
2508
+ if ($r)
2509
+ {
2510
+ // failed processing of HTTP response headers
2511
+ // save into response obj the full payload received, for debugging
2512
+ $r->raw_data = $data;
2513
+ return $r;
2514
+ }
2515
+ }
2516
+ else
2517
+ {
2518
+ $GLOBALS['_xh']['headers'] = array();
2519
+ $GLOBALS['_xh']['cookies'] = array();
2520
+ }
2521
+
2522
+ if($this->debug)
2523
+ {
2524
+ $start = strpos($data, '<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
2525
+ if ($start)
2526
+ {
2527
+ $start += strlen('<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
2528
+ $end = strpos($data, '-->', $start);
2529
+ $comments = substr($data, $start, $end-$start);
2530
+ print "<PRE>---SERVER DEBUG INFO (DECODED) ---\n\t".htmlentities(str_replace("\n", "\n\t", base64_decode($comments)))."\n---END---\n</PRE>";
2531
+ }
2532
+ }
2533
+
2534
+ // be tolerant of extra whitespace in response body
2535
+ $data = trim($data);
2536
+
2537
+ /// @todo return an error msg if $data=='' ?
2538
+
2539
+ // be tolerant of junk after methodResponse (e.g. javascript ads automatically inserted by free hosts)
2540
+ // idea from Luca Mariano <luca.mariano@email.it> originally in PEARified version of the lib
2541
+ $pos = strrpos($data, '</methodResponse>');
2542
+ if($pos !== false)
2543
+ {
2544
+ $data = substr($data, 0, $pos+17);
2545
+ }
2546
+
2547
+ // if user wants back raw xml, give it to him
2548
+ if ($return_type == 'xml')
2549
+ {
2550
+ $r = new xmlrpcresp($data, 0, '', 'xml');
2551
+ $r->hdrs = $GLOBALS['_xh']['headers'];
2552
+ $r->_cookies = $GLOBALS['_xh']['cookies'];
2553
+ $r->raw_data = $raw_data;
2554
+ return $r;
2555
+ }
2556
+
2557
+ // try to 'guestimate' the character encoding of the received response
2558
+ $resp_encoding = guess_encoding(@$GLOBALS['_xh']['headers']['content-type'], $data);
2559
+
2560
+ $GLOBALS['_xh']['ac']='';
2561
+ //$GLOBALS['_xh']['qt']=''; //unused...
2562
+ $GLOBALS['_xh']['stack'] = array();
2563
+ $GLOBALS['_xh']['valuestack'] = array();
2564
+ $GLOBALS['_xh']['isf']=0; // 0 = OK, 1 for xmlrpc fault responses, 2 = invalid xmlrpc
2565
+ $GLOBALS['_xh']['isf_reason']='';
2566
+ $GLOBALS['_xh']['rt']=''; // 'methodcall or 'methodresponse'
2567
+
2568
+ // if response charset encoding is not known / supported, try to use
2569
+ // the default encoding and parse the xml anyway, but log a warning...
2570
+ if (!in_array($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
2571
+ // the following code might be better for mb_string enabled installs, but
2572
+ // makes the lib about 200% slower...
2573
+ //if (!is_valid_charset($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
2574
+ {
2575
+ error_log('XML-RPC: '.__METHOD__.': invalid charset encoding of received response: '.$resp_encoding);
2576
+ $resp_encoding = $GLOBALS['xmlrpc_defencoding'];
2577
+ }
2578
+ $parser = xml_parser_create($resp_encoding);
2579
+ xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
2580
+ // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
2581
+ // the xml parser to give us back data in the expected charset.
2582
+ // What if internal encoding is not in one of the 3 allowed?
2583
+ // we use the broadest one, ie. utf8
2584
+ // This allows to send data which is native in various charset,
2585
+ // by extending xmlrpc_encode_entitites() and setting xmlrpc_internalencoding
2586
+ if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
2587
+ {
2588
+ xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
2589
+ }
2590
+ else
2591
+ {
2592
+ xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
2593
+ }
2594
+
2595
+ if ($return_type == 'phpvals')
2596
+ {
2597
+ xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
2598
+ }
2599
+ else
2600
+ {
2601
+ xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
2602
+ }
2603
+
2604
+ xml_set_character_data_handler($parser, 'xmlrpc_cd');
2605
+ xml_set_default_handler($parser, 'xmlrpc_dh');
2606
+
2607
+ // first error check: xml not well formed
2608
+ if(!xml_parse($parser, $data, count($data)))
2609
+ {
2610
+ // thanks to Peter Kocks <peter.kocks@baygate.com>
2611
+ if((xml_get_current_line_number($parser)) == 1)
2612
+ {
2613
+ $errstr = 'XML error at line 1, check URL';
2614
+ }
2615
+ else
2616
+ {
2617
+ $errstr = sprintf('XML error: %s at line %d, column %d',
2618
+ xml_error_string(xml_get_error_code($parser)),
2619
+ xml_get_current_line_number($parser), xml_get_current_column_number($parser));
2620
+ }
2621
+ error_log($errstr);
2622
+ $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'], $GLOBALS['xmlrpcstr']['invalid_return'].' ('.$errstr.')');
2623
+ xml_parser_free($parser);
2624
+ if($this->debug)
2625
+ {
2626
+ print $errstr;
2627
+ }
2628
+ $r->hdrs = $GLOBALS['_xh']['headers'];
2629
+ $r->_cookies = $GLOBALS['_xh']['cookies'];
2630
+ $r->raw_data = $raw_data;
2631
+ return $r;
2632
+ }
2633
+ xml_parser_free($parser);
2634
+ // second error check: xml well formed but not xml-rpc compliant
2635
+ if ($GLOBALS['_xh']['isf'] > 1)
2636
+ {
2637
+ if ($this->debug)
2638
+ {
2639
+ /// @todo echo something for user?
2640
+ }
2641
+
2642
+ $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
2643
+ $GLOBALS['xmlrpcstr']['invalid_return'] . ' ' . $GLOBALS['_xh']['isf_reason']);
2644
+ }
2645
+ // third error check: parsing of the response has somehow gone boink.
2646
+ // NB: shall we omit this check, since we trust the parsing code?
2647
+ elseif ($return_type == 'xmlrpcvals' && !is_object($GLOBALS['_xh']['value']))
2648
+ {
2649
+ // something odd has happened
2650
+ // and it's time to generate a client side error
2651
+ // indicating something odd went on
2652
+ $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
2653
+ $GLOBALS['xmlrpcstr']['invalid_return']);
2654
+ }
2655
+ else
2656
+ {
2657
+ if ($this->debug)
2658
+ {
2659
+ print "<PRE>---PARSED---\n";
2660
+ // somehow htmlentities chokes on var_export, and some full html string...
2661
+ //print htmlentitites(var_export($GLOBALS['_xh']['value'], true));
2662
+ print htmlspecialchars(var_export($GLOBALS['_xh']['value'], true));
2663
+ print "\n---END---</PRE>";
2664
+ }
2665
+
2666
+ // note that using =& will raise an error if $GLOBALS['_xh']['st'] does not generate an object.
2667
+ $v =& $GLOBALS['_xh']['value'];
2668
+
2669
+ if($GLOBALS['_xh']['isf'])
2670
+ {
2671
+ /// @todo we should test here if server sent an int and a string,
2672
+ /// and/or coerce them into such...
2673
+ if ($return_type == 'xmlrpcvals')
2674
+ {
2675
+ $errno_v = $v->structmem('faultCode');
2676
+ $errstr_v = $v->structmem('faultString');
2677
+ $errno = $errno_v->scalarval();
2678
+ $errstr = $errstr_v->scalarval();
2679
+ }
2680
+ else
2681
+ {
2682
+ $errno = $v['faultCode'];
2683
+ $errstr = $v['faultString'];
2684
+ }
2685
+
2686
+ if($errno == 0)
2687
+ {
2688
+ // FAULT returned, errno needs to reflect that
2689
+ $errno = -1;
2690
+ }
2691
+
2692
+ $r = new xmlrpcresp(0, $errno, $errstr);
2693
+ }
2694
+ else
2695
+ {
2696
+ $r=new xmlrpcresp($v, 0, '', $return_type);
2697
+ }
2698
+ }
2699
+
2700
+ $r->hdrs = $GLOBALS['_xh']['headers'];
2701
+ $r->_cookies = $GLOBALS['_xh']['cookies'];
2702
+ $r->raw_data = $raw_data;
2703
+ return $r;
2704
+ }
2705
+ }
2706
+
2707
+ class xmlrpcval
2708
+ {
2709
+ var $me=array();
2710
+ var $mytype=0;
2711
+ var $_php_class=null;
2712
+
2713
+ /**
2714
+ * @param mixed $val
2715
+ * @param string $type any valid xmlrpc type name (lowercase). If null, 'string' is assumed
2716
+ */
2717
+ function xmlrpcval($val=-1, $type='')
2718
+ {
2719
+ /// @todo: optimization creep - do not call addXX, do it all inline.
2720
+ /// downside: booleans will not be coerced anymore
2721
+ if($val!==-1 || $type!='')
2722
+ {
2723
+ // optimization creep: inlined all work done by constructor
2724
+ switch($type)
2725
+ {
2726
+ case '':
2727
+ $this->mytype=1;
2728
+ $this->me['string']=$val;
2729
+ break;
2730
+ case 'i4':
2731
+ case 'int':
2732
+ case 'double':
2733
+ case 'string':
2734
+ case 'boolean':
2735
+ case 'dateTime.iso8601':
2736
+ case 'base64':
2737
+ case 'null':
2738
+ $this->mytype=1;
2739
+ $this->me[$type]=$val;
2740
+ break;
2741
+ case 'array':
2742
+ $this->mytype=2;
2743
+ $this->me['array']=$val;
2744
+ break;
2745
+ case 'struct':
2746
+ $this->mytype=3;
2747
+ $this->me['struct']=$val;
2748
+ break;
2749
+ default:
2750
+ error_log("XML-RPC: ".__METHOD__.": not a known type ($type)");
2751
+ }
2752
+ /*if($type=='')
2753
+ {
2754
+ $type='string';
2755
+ }
2756
+ if($GLOBALS['xmlrpcTypes'][$type]==1)
2757
+ {
2758
+ $this->addScalar($val,$type);
2759
+ }
2760
+ elseif($GLOBALS['xmlrpcTypes'][$type]==2)
2761
+ {
2762
+ $this->addArray($val);
2763
+ }
2764
+ elseif($GLOBALS['xmlrpcTypes'][$type]==3)
2765
+ {
2766
+ $this->addStruct($val);
2767
+ }*/
2768
+ }
2769
+ }
2770
+
2771
+ /**
2772
+ * Add a single php value to an (unitialized) xmlrpcval
2773
+ * @param mixed $val
2774
+ * @param string $type
2775
+ * @return int 1 or 0 on failure
2776
+ */
2777
+ function addScalar($val, $type='string')
2778
+ {
2779
+ $typeof=@$GLOBALS['xmlrpcTypes'][$type];
2780
+ if($typeof!=1)
2781
+ {
2782
+ error_log("XML-RPC: ".__METHOD__.": not a scalar type ($type)");
2783
+ return 0;
2784
+ }
2785
+
2786
+ // coerce booleans into correct values
2787
+ // NB: we should either do it for datetimes, integers and doubles, too,
2788
+ // or just plain remove this check, implemented on booleans only...
2789
+ if($type==$GLOBALS['xmlrpcBoolean'])
2790
+ {
2791
+ if(strcasecmp($val,'true')==0 || $val==1 || ($val==true && strcasecmp($val,'false')))
2792
+ {
2793
+ $val=true;
2794
+ }
2795
+ else
2796
+ {
2797
+ $val=false;
2798
+ }
2799
+ }
2800
+
2801
+ switch($this->mytype)
2802
+ {
2803
+ case 1:
2804
+ error_log('XML-RPC: '.__METHOD__.': scalar xmlrpcval can have only one value');
2805
+ return 0;
2806
+ case 3:
2807
+ error_log('XML-RPC: '.__METHOD__.': cannot add anonymous scalar to struct xmlrpcval');
2808
+ return 0;
2809
+ case 2:
2810
+ // we're adding a scalar value to an array here
2811
+ //$ar=$this->me['array'];
2812
+ //$ar[]=new xmlrpcval($val, $type);
2813
+ //$this->me['array']=$ar;
2814
+ // Faster (?) avoid all the costly array-copy-by-val done here...
2815
+ $this->me['array'][]=new xmlrpcval($val, $type);
2816
+ return 1;
2817
+ default:
2818
+ // a scalar, so set the value and remember we're scalar
2819
+ $this->me[$type]=$val;
2820
+ $this->mytype=$typeof;
2821
+ return 1;
2822
+ }
2823
+ }
2824
+
2825
+ /**
2826
+ * Add an array of xmlrpcval objects to an xmlrpcval
2827
+ * @param array $vals
2828
+ * @return int 1 or 0 on failure
2829
+ * @access public
2830
+ *
2831
+ * @todo add some checking for $vals to be an array of xmlrpcvals?
2832
+ */
2833
+ function addArray($vals)
2834
+ {
2835
+ if($this->mytype==0)
2836
+ {
2837
+ $this->mytype=$GLOBALS['xmlrpcTypes']['array'];
2838
+ $this->me['array']=$vals;
2839
+ return 1;
2840
+ }
2841
+ elseif($this->mytype==2)
2842
+ {
2843
+ // we're adding to an array here
2844
+ $this->me['array'] = array_merge($this->me['array'], $vals);
2845
+ return 1;
2846
+ }
2847
+ else
2848
+ {
2849
+ error_log('XML-RPC: '.__METHOD__.': already initialized as a [' . $this->kindOf() . ']');
2850
+ return 0;
2851
+ }
2852
+ }
2853
+
2854
+ /**
2855
+ * Add an array of named xmlrpcval objects to an xmlrpcval
2856
+ * @param array $vals
2857
+ * @return int 1 or 0 on failure
2858
+ * @access public
2859
+ *
2860
+ * @todo add some checking for $vals to be an array?
2861
+ */
2862
+ function addStruct($vals)
2863
+ {
2864
+ if($this->mytype==0)
2865
+ {
2866
+ $this->mytype=$GLOBALS['xmlrpcTypes']['struct'];
2867
+ $this->me['struct']=$vals;
2868
+ return 1;
2869
+ }
2870
+ elseif($this->mytype==3)
2871
+ {
2872
+ // we're adding to a struct here
2873
+ $this->me['struct'] = array_merge($this->me['struct'], $vals);
2874
+ return 1;
2875
+ }
2876
+ else
2877
+ {
2878
+ error_log('XML-RPC: '.__METHOD__.': already initialized as a [' . $this->kindOf() . ']');
2879
+ return 0;
2880
+ }
2881
+ }
2882
+
2883
+ // poor man's version of print_r ???
2884
+ // DEPRECATED!
2885
+ function dump($ar)
2886
+ {
2887
+ foreach($ar as $key => $val)
2888
+ {
2889
+ echo "$key => $val<br />";
2890
+ if($key == 'array')
2891
+ {
2892
+ while(list($key2, $val2) = each($val))
2893
+ {
2894
+ echo "-- $key2 => $val2<br />";
2895
+ }
2896
+ }
2897
+ }
2898
+ }
2899
+
2900
+ /**
2901
+ * Returns a string containing "struct", "array" or "scalar" describing the base type of the value
2902
+ * @return string
2903
+ * @access public
2904
+ */
2905
+ function kindOf()
2906
+ {
2907
+ switch($this->mytype)
2908
+ {
2909
+ case 3:
2910
+ return 'struct';
2911
+ break;
2912
+ case 2:
2913
+ return 'array';
2914
+ break;
2915
+ case 1:
2916
+ return 'scalar';
2917
+ break;
2918
+ default:
2919
+ return 'undef';
2920
+ }
2921
+ }
2922
+
2923
+ /**
2924
+ * @access private
2925
+ */
2926
+ function serializedata($typ, $val, $charset_encoding='')
2927
+ {
2928
+ $rs='';
2929
+ switch(@$GLOBALS['xmlrpcTypes'][$typ])
2930
+ {
2931
+ case 1:
2932
+ switch($typ)
2933
+ {
2934
+ case $GLOBALS['xmlrpcBase64']:
2935
+ $rs.="<${typ}>" . base64_encode($val) . "</${typ}>";
2936
+ break;
2937
+ case $GLOBALS['xmlrpcBoolean']:
2938
+ $rs.="<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
2939
+ break;
2940
+ case $GLOBALS['xmlrpcString']:
2941
+ // G. Giunta 2005/2/13: do NOT use htmlentities, since
2942
+ // it will produce named html entities, which are invalid xml
2943
+ $rs.="<${typ}>" . xmlrpc_encode_entitites($val, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding). "</${typ}>";
2944
+ break;
2945
+ case $GLOBALS['xmlrpcInt']:
2946
+ case $GLOBALS['xmlrpcI4']:
2947
+ $rs.="<${typ}>".(int)$val."</${typ}>";
2948
+ break;
2949
+ case $GLOBALS['xmlrpcDouble']:
2950
+ // avoid using standard conversion of float to string because it is locale-dependent,
2951
+ // and also because the xmlrpc spec forbids exponential notation.
2952
+ // sprintf('%F') could be most likely ok but it fails eg. on 2e-14.
2953
+ // The code below tries its best at keeping max precision while avoiding exp notation,
2954
+ // but there is of course no limit in the number of decimal places to be used...
2955
+ $rs.="<${typ}>".preg_replace('/\\.?0+$/','',number_format((double)$val, 128, '.', ''))."</${typ}>";
2956
+ break;
2957
+ case $GLOBALS['xmlrpcDateTime']:
2958
+ if (is_string($val))
2959
+ {
2960
+ $rs.="<${typ}>${val}</${typ}>";
2961
+ }
2962
+ else if(is_a($val, 'DateTime'))
2963
+ {
2964
+ $rs.="<${typ}>".$val->format('Ymd\TH:i:s')."</${typ}>";
2965
+ }
2966
+ else if(is_int($val))
2967
+ {
2968
+ $rs.="<${typ}>".strftime("%Y%m%dT%H:%M:%S", $val)."</${typ}>";
2969
+ }
2970
+ else
2971
+ {
2972
+ // not really a good idea here: but what shall we output anyway? left for backward compat...
2973
+ $rs.="<${typ}>${val}</${typ}>";
2974
+ }
2975
+ break;
2976
+ case $GLOBALS['xmlrpcNull']:
2977
+ if ($GLOBALS['xmlrpc_null_apache_encoding'])
2978
+ {
2979
+ $rs.="<ex:nil/>";
2980
+ }
2981
+ else
2982
+ {
2983
+ $rs.="<nil/>";
2984
+ }
2985
+ break;
2986
+ default:
2987
+ // no standard type value should arrive here, but provide a possibility
2988
+ // for xmlrpcvals of unknown type...
2989
+ $rs.="<${typ}>${val}</${typ}>";
2990
+ }
2991
+ break;
2992
+ case 3:
2993
+ // struct
2994
+ if ($this->_php_class)
2995
+ {
2996
+ $rs.='<struct php_class="' . $this->_php_class . "\">\n";
2997
+ }
2998
+ else
2999
+ {
3000
+ $rs.="<struct>\n";
3001
+ }
3002
+ foreach($val as $key2 => $val2)
3003
+ {
3004
+ $rs.='<member><name>'.xmlrpc_encode_entitites($key2, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding)."</name>\n";
3005
+ //$rs.=$this->serializeval($val2);
3006
+ $rs.=$val2->serialize($charset_encoding);
3007
+ $rs.="</member>\n";
3008
+ }
3009
+ $rs.='</struct>';
3010
+ break;
3011
+ case 2:
3012
+ // array
3013
+ $rs.="<array>\n<data>\n";
3014
+ for($i=0; $i<count($val); $i++)
3015
+ {
3016
+ //$rs.=$this->serializeval($val[$i]);
3017
+ $rs.=$val[$i]->serialize($charset_encoding);
3018
+ }
3019
+ $rs.="</data>\n</array>";
3020
+ break;
3021
+ default:
3022
+ break;
3023
+ }
3024
+ return $rs;
3025
+ }
3026
+
3027
+ /**
3028
+ * Returns xml representation of the value. XML prologue not included
3029
+ * @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
3030
+ * @return string
3031
+ * @access public
3032
+ */
3033
+ function serialize($charset_encoding='')
3034
+ {
3035
+ // add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
3036
+ //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
3037
+ //{
3038
+ reset($this->me);
3039
+ list($typ, $val) = each($this->me);
3040
+ return '<value>' . $this->serializedata($typ, $val, $charset_encoding) . "</value>\n";
3041
+ //}
3042
+ }
3043
+
3044
+ // DEPRECATED
3045
+ function serializeval($o)
3046
+ {
3047
+ // add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
3048
+ //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
3049
+ //{
3050
+ $ar=$o->me;
3051
+ reset($ar);
3052
+ list($typ, $val) = each($ar);
3053
+ return '<value>' . $this->serializedata($typ, $val) . "</value>\n";
3054
+ //}
3055
+ }
3056
+
3057
+ /**
3058
+ * Checks wheter a struct member with a given name is present.
3059
+ * Works only on xmlrpcvals of type struct.
3060
+ * @param string $m the name of the struct member to be looked up
3061
+ * @return boolean
3062
+ * @access public
3063
+ */
3064
+ function structmemexists($m)
3065
+ {
3066
+ return array_key_exists($m, $this->me['struct']);
3067
+ }
3068
+
3069
+ /**
3070
+ * Returns the value of a given struct member (an xmlrpcval object in itself).
3071
+ * Will raise a php warning if struct member of given name does not exist
3072
+ * @param string $m the name of the struct member to be looked up
3073
+ * @return xmlrpcval
3074
+ * @access public
3075
+ */
3076
+ function structmem($m)
3077
+ {
3078
+ return $this->me['struct'][$m];
3079
+ }
3080
+
3081
+ /**
3082
+ * Reset internal pointer for xmlrpcvals of type struct.
3083
+ * @access public
3084
+ */
3085
+ function structreset()
3086
+ {
3087
+ reset($this->me['struct']);
3088
+ }
3089
+
3090
+ /**
3091
+ * Return next member element for xmlrpcvals of type struct.
3092
+ * @return xmlrpcval
3093
+ * @access public
3094
+ */
3095
+ function structeach()
3096
+ {
3097
+ return each($this->me['struct']);
3098
+ }
3099
+
3100
+ // DEPRECATED! this code looks like it is very fragile and has not been fixed
3101
+ // for a long long time. Shall we remove it for 2.0?
3102
+ function getval()
3103
+ {
3104
+ // UNSTABLE
3105
+ reset($this->me);
3106
+ list($a,$b)=each($this->me);
3107
+ // contributed by I Sofer, 2001-03-24
3108
+ // add support for nested arrays to scalarval
3109
+ // i've created a new method here, so as to
3110
+ // preserve back compatibility
3111
+
3112
+ if(is_array($b))
3113
+ {
3114
+ @reset($b);
3115
+ while(list($id,$cont) = @each($b))
3116
+ {
3117
+ $b[$id] = $cont->scalarval();
3118
+ }
3119
+ }
3120
+
3121
+ // add support for structures directly encoding php objects
3122
+ if(is_object($b))
3123
+ {
3124
+ $t = get_object_vars($b);
3125
+ @reset($t);
3126
+ while(list($id,$cont) = @each($t))
3127
+ {
3128
+ $t[$id] = $cont->scalarval();
3129
+ }
3130
+ @reset($t);
3131
+ while(list($id,$cont) = @each($t))
3132
+ {
3133
+ @$b->$id = $cont;
3134
+ }
3135
+ }
3136
+ // end contrib
3137
+ return $b;
3138
+ }
3139
+
3140
+ /**
3141
+ * Returns the value of a scalar xmlrpcval
3142
+ * @return mixed
3143
+ * @access public
3144
+ */
3145
+ function scalarval()
3146
+ {
3147
+ reset($this->me);
3148
+ list(,$b)=each($this->me);
3149
+ return $b;
3150
+ }
3151
+
3152
+ /**
3153
+ * Returns the type of the xmlrpcval.
3154
+ * For integers, 'int' is always returned in place of 'i4'
3155
+ * @return string
3156
+ * @access public
3157
+ */
3158
+ function scalartyp()
3159
+ {
3160
+ reset($this->me);
3161
+ list($a,)=each($this->me);
3162
+ if($a==$GLOBALS['xmlrpcI4'])
3163
+ {
3164
+ $a=$GLOBALS['xmlrpcInt'];
3165
+ }
3166
+ return $a;
3167
+ }
3168
+
3169
+ /**
3170
+ * Returns the m-th member of an xmlrpcval of struct type
3171
+ * @param integer $m the index of the value to be retrieved (zero based)
3172
+ * @return xmlrpcval
3173
+ * @access public
3174
+ */
3175
+ function arraymem($m)
3176
+ {
3177
+ return $this->me['array'][$m];
3178
+ }
3179
+
3180
+ /**
3181
+ * Returns the number of members in an xmlrpcval of array type
3182
+ * @return integer
3183
+ * @access public
3184
+ */
3185
+ function arraysize()
3186
+ {
3187
+ return count($this->me['array']);
3188
+ }
3189
+
3190
+ /**
3191
+ * Returns the number of members in an xmlrpcval of struct type
3192
+ * @return integer
3193
+ * @access public
3194
+ */
3195
+ function structsize()
3196
+ {
3197
+ return count($this->me['struct']);
3198
+ }
3199
+ }
3200
+
3201
+
3202
+ // date helpers
3203
+
3204
+ /**
3205
+ * Given a timestamp, return the corresponding ISO8601 encoded string.
3206
+ *
3207
+ * Really, timezones ought to be supported
3208
+ * but the XML-RPC spec says:
3209
+ *
3210
+ * "Don't assume a timezone. It should be specified by the server in its
3211
+ * documentation what assumptions it makes about timezones."
3212
+ *
3213
+ * These routines always assume localtime unless
3214
+ * $utc is set to 1, in which case UTC is assumed
3215
+ * and an adjustment for locale is made when encoding
3216
+ *
3217
+ * @param int $timet (timestamp)
3218
+ * @param int $utc (0 or 1)
3219
+ * @return string
3220
+ */
3221
+ function iso8601_encode($timet, $utc=0)
3222
+ {
3223
+ if(!$utc)
3224
+ {
3225
+ $t=strftime("%Y%m%dT%H:%M:%S", $timet);
3226
+ }
3227
+ else
3228
+ {
3229
+ if(function_exists('gmstrftime'))
3230
+ {
3231
+ // gmstrftime doesn't exist in some versions
3232
+ // of PHP
3233
+ $t=gmstrftime("%Y%m%dT%H:%M:%S", $timet);
3234
+ }
3235
+ else
3236
+ {
3237
+ $t=strftime("%Y%m%dT%H:%M:%S", $timet-date('Z'));
3238
+ }
3239
+ }
3240
+ return $t;
3241
+ }
3242
+
3243
+ /**
3244
+ * Given an ISO8601 date string, return a timet in the localtime, or UTC
3245
+ * @param string $idate
3246
+ * @param int $utc either 0 or 1
3247
+ * @return int (datetime)
3248
+ */
3249
+ function iso8601_decode($idate, $utc=0)
3250
+ {
3251
+ $t=0;
3252
+ if(preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $idate, $regs))
3253
+ {
3254
+ if($utc)
3255
+ {
3256
+ $t=gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
3257
+ }
3258
+ else
3259
+ {
3260
+ $t=mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
3261
+ }
3262
+ }
3263
+ return $t;
3264
+ }
3265
+
3266
+ /**
3267
+ * Takes an xmlrpc value in PHP xmlrpcval object format and translates it into native PHP types.
3268
+ *
3269
+ * Works with xmlrpc message objects as input, too.
3270
+ *
3271
+ * Given proper options parameter, can rebuild generic php object instances
3272
+ * (provided those have been encoded to xmlrpc format using a corresponding
3273
+ * option in php_xmlrpc_encode())
3274
+ * PLEASE NOTE that rebuilding php objects involves calling their constructor function.
3275
+ * This means that the remote communication end can decide which php code will
3276
+ * get executed on your server, leaving the door possibly open to 'php-injection'
3277
+ * style of attacks (provided you have some classes defined on your server that
3278
+ * might wreak havoc if instances are built outside an appropriate context).
3279
+ * Make sure you trust the remote server/client before eanbling this!
3280
+ *
3281
+ * @author Dan Libby (dan@libby.com)
3282
+ *
3283
+ * @param xmlrpcval $xmlrpc_val
3284
+ * @param array $options if 'decode_php_objs' is set in the options array, xmlrpc structs can be decoded into php objects; if 'dates_as_objects' is set xmlrpc datetimes are decoded as php DateTime objects (standard is
3285
+ * @return mixed
3286
+ */
3287
+ function php_xmlrpc_decode($xmlrpc_val, $options=array())
3288
+ {
3289
+ switch($xmlrpc_val->kindOf())
3290
+ {
3291
+ case 'scalar':
3292
+ if (in_array('extension_api', $options))
3293
+ {
3294
+ reset($xmlrpc_val->me);
3295
+ list($typ,$val) = each($xmlrpc_val->me);
3296
+ switch ($typ)
3297
+ {
3298
+ case 'dateTime.iso8601':
3299
+ $xmlrpc_val->scalar = $val;
3300
+ $xmlrpc_val->xmlrpc_type = 'datetime';
3301
+ $xmlrpc_val->timestamp = iso8601_decode($val);
3302
+ return $xmlrpc_val;
3303
+ case 'base64':
3304
+ $xmlrpc_val->scalar = $val;
3305
+ $xmlrpc_val->type = $typ;
3306
+ return $xmlrpc_val;
3307
+ default:
3308
+ return $xmlrpc_val->scalarval();
3309
+ }
3310
+ }
3311
+ if (in_array('dates_as_objects', $options) && $xmlrpc_val->scalartyp() == 'dateTime.iso8601')
3312
+ {
3313
+ // we return a Datetime object instead of a string
3314
+ // since now the constructor of xmlrpcval accepts safely strings, ints and datetimes,
3315
+ // we cater to all 3 cases here
3316
+ $out = $xmlrpc_val->scalarval();
3317
+ if (is_string($out))
3318
+ {
3319
+ $out = strtotime($out);
3320
+ }
3321
+ if (is_int($out))
3322
+ {
3323
+ $result = new Datetime();
3324
+ $result->setTimestamp($out);
3325
+ return $result;
3326
+ }
3327
+ elseif (is_a($out, 'Datetime'))
3328
+ {
3329
+ return $out;
3330
+ }
3331
+ }
3332
+ return $xmlrpc_val->scalarval();
3333
+ case 'array':
3334
+ $size = $xmlrpc_val->arraysize();
3335
+ $arr = array();
3336
+ for($i = 0; $i < $size; $i++)
3337
+ {
3338
+ $arr[] = php_xmlrpc_decode($xmlrpc_val->arraymem($i), $options);
3339
+ }
3340
+ return $arr;
3341
+ case 'struct':
3342
+ $xmlrpc_val->structreset();
3343
+ // If user said so, try to rebuild php objects for specific struct vals.
3344
+ /// @todo should we raise a warning for class not found?
3345
+ // shall we check for proper subclass of xmlrpcval instead of
3346
+ // presence of _php_class to detect what we can do?
3347
+ if (in_array('decode_php_objs', $options) && $xmlrpc_val->_php_class != ''
3348
+ && class_exists($xmlrpc_val->_php_class))
3349
+ {
3350
+ $obj = @new $xmlrpc_val->_php_class;
3351
+ while(list($key,$value)=$xmlrpc_val->structeach())
3352
+ {
3353
+ $obj->$key = php_xmlrpc_decode($value, $options);
3354
+ }
3355
+ return $obj;
3356
+ }
3357
+ else
3358
+ {
3359
+ $arr = array();
3360
+ while(list($key,$value)=$xmlrpc_val->structeach())
3361
+ {
3362
+ $arr[$key] = php_xmlrpc_decode($value, $options);
3363
+ }
3364
+ return $arr;
3365
+ }
3366
+ case 'msg':
3367
+ $paramcount = $xmlrpc_val->getNumParams();
3368
+ $arr = array();
3369
+ for($i = 0; $i < $paramcount; $i++)
3370
+ {
3371
+ $arr[] = php_xmlrpc_decode($xmlrpc_val->getParam($i));
3372
+ }
3373
+ return $arr;
3374
+ }
3375
+ }
3376
+
3377
+ // This constant left here only for historical reasons...
3378
+ // it was used to decide if we have to define xmlrpc_encode on our own, but
3379
+ // we do not do it anymore
3380
+ if(function_exists('xmlrpc_decode'))
3381
+ {
3382
+ define('XMLRPC_EPI_ENABLED','1');
3383
+ }
3384
+ else
3385
+ {
3386
+ define('XMLRPC_EPI_ENABLED','0');
3387
+ }
3388
+
3389
+ /**
3390
+ * Takes native php types and encodes them into xmlrpc PHP object format.
3391
+ * It will not re-encode xmlrpcval objects.
3392
+ *
3393
+ * Feature creep -- could support more types via optional type argument
3394
+ * (string => datetime support has been added, ??? => base64 not yet)
3395
+ *
3396
+ * If given a proper options parameter, php object instances will be encoded
3397
+ * into 'special' xmlrpc values, that can later be decoded into php objects
3398
+ * by calling php_xmlrpc_decode() with a corresponding option
3399
+ *
3400
+ * @author Dan Libby (dan@libby.com)
3401
+ *
3402
+ * @param mixed $php_val the value to be converted into an xmlrpcval object
3403
+ * @param array $options can include 'encode_php_objs', 'auto_dates', 'null_extension' or 'extension_api'
3404
+ * @return xmlrpcval
3405
+ */
3406
+ function php_xmlrpc_encode($php_val, $options=array())
3407
+ {
3408
+ $type = gettype($php_val);
3409
+ switch($type)
3410
+ {
3411
+ case 'string':
3412
+ if (in_array('auto_dates', $options) && preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $php_val))
3413
+ $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcDateTime']);
3414
+ else
3415
+ $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcString']);
3416
+ break;
3417
+ case 'integer':
3418
+ $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcInt']);
3419
+ break;
3420
+ case 'double':
3421
+ $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcDouble']);
3422
+ break;
3423
+ // <G_Giunta_2001-02-29>
3424
+ // Add support for encoding/decoding of booleans, since they are supported in PHP
3425
+ case 'boolean':
3426
+ $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcBoolean']);
3427
+ break;
3428
+ // </G_Giunta_2001-02-29>
3429
+ case 'array':
3430
+ // PHP arrays can be encoded to either xmlrpc structs or arrays,
3431
+ // depending on wheter they are hashes or plain 0..n integer indexed
3432
+ // A shorter one-liner would be
3433
+ // $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1));
3434
+ // but execution time skyrockets!
3435
+ $j = 0;
3436
+ $arr = array();
3437
+ $ko = false;
3438
+ foreach($php_val as $key => $val)
3439
+ {
3440
+ $arr[$key] = php_xmlrpc_encode($val, $options);
3441
+ if(!$ko && $key !== $j)
3442
+ {
3443
+ $ko = true;
3444
+ }
3445
+ $j++;
3446
+ }
3447
+ if($ko)
3448
+ {
3449
+ $xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']);
3450
+ }
3451
+ else
3452
+ {
3453
+ $xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcArray']);
3454
+ }
3455
+ break;
3456
+ case 'object':
3457
+ if(is_a($php_val, 'xmlrpcval'))
3458
+ {
3459
+ $xmlrpc_val = $php_val;
3460
+ }
3461
+ else if(is_a($php_val, 'DateTime'))
3462
+ {
3463
+ $xmlrpc_val = new xmlrpcval($php_val->format('Ymd\TH:i:s'), $GLOBALS['xmlrpcStruct']);
3464
+ }
3465
+ else
3466
+ {
3467
+ $arr = array();
3468
+ reset($php_val);
3469
+ while(list($k,$v) = each($php_val))
3470
+ {
3471
+ $arr[$k] = php_xmlrpc_encode($v, $options);
3472
+ }
3473
+ $xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']);
3474
+ if (in_array('encode_php_objs', $options))
3475
+ {
3476
+ // let's save original class name into xmlrpcval:
3477
+ // might be useful later on...
3478
+ $xmlrpc_val->_php_class = get_class($php_val);
3479
+ }
3480
+ }
3481
+ break;
3482
+ case 'NULL':
3483
+ if (in_array('extension_api', $options))
3484
+ {
3485
+ $xmlrpc_val = new xmlrpcval('', $GLOBALS['xmlrpcString']);
3486
+ }
3487
+ else if (in_array('null_extension', $options))
3488
+ {
3489
+ $xmlrpc_val = new xmlrpcval('', $GLOBALS['xmlrpcNull']);
3490
+ }
3491
+ else
3492
+ {
3493
+ $xmlrpc_val = new xmlrpcval();
3494
+ }
3495
+ break;
3496
+ case 'resource':
3497
+ if (in_array('extension_api', $options))
3498
+ {
3499
+ $xmlrpc_val = new xmlrpcval((int)$php_val, $GLOBALS['xmlrpcInt']);
3500
+ }
3501
+ else
3502
+ {
3503
+ $xmlrpc_val = new xmlrpcval();
3504
+ }
3505
+ // catch "user function", "unknown type"
3506
+ default:
3507
+ // giancarlo pinerolo <ping@alt.it>
3508
+ // it has to return
3509
+ // an empty object in case, not a boolean.
3510
+ $xmlrpc_val = new xmlrpcval();
3511
+ break;
3512
+ }
3513
+ return $xmlrpc_val;
3514
+ }
3515
+
3516
+ /**
3517
+ * Convert the xml representation of a method response, method request or single
3518
+ * xmlrpc value into the appropriate object (a.k.a. deserialize)
3519
+ * @param string $xml_val
3520
+ * @param array $options
3521
+ * @return mixed false on error, or an instance of either xmlrpcval, xmlrpcmsg or xmlrpcresp
3522
+ */
3523
+ function php_xmlrpc_decode_xml($xml_val, $options=array())
3524
+ {
3525
+ $GLOBALS['_xh'] = array();
3526
+ $GLOBALS['_xh']['ac'] = '';
3527
+ $GLOBALS['_xh']['stack'] = array();
3528
+ $GLOBALS['_xh']['valuestack'] = array();
3529
+ $GLOBALS['_xh']['params'] = array();
3530
+ $GLOBALS['_xh']['pt'] = array();
3531
+ $GLOBALS['_xh']['isf'] = 0;
3532
+ $GLOBALS['_xh']['isf_reason'] = '';
3533
+ $GLOBALS['_xh']['method'] = false;
3534
+ $GLOBALS['_xh']['rt'] = '';
3535
+ /// @todo 'guestimate' encoding
3536
+ $parser = xml_parser_create();
3537
+ xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
3538
+ // What if internal encoding is not in one of the 3 allowed?
3539
+ // we use the broadest one, ie. utf8!
3540
+ if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
3541
+ {
3542
+ xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
3543
+ }
3544
+ else
3545
+ {
3546
+ xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
3547
+ }
3548
+ xml_set_element_handler($parser, 'xmlrpc_se_any', 'xmlrpc_ee');
3549
+ xml_set_character_data_handler($parser, 'xmlrpc_cd');
3550
+ xml_set_default_handler($parser, 'xmlrpc_dh');
3551
+ if(!xml_parse($parser, $xml_val, 1))
3552
+ {
3553
+ $errstr = sprintf('XML error: %s at line %d, column %d',
3554
+ xml_error_string(xml_get_error_code($parser)),
3555
+ xml_get_current_line_number($parser), xml_get_current_column_number($parser));
3556
+ error_log($errstr);
3557
+ xml_parser_free($parser);
3558
+ return false;
3559
+ }
3560
+ xml_parser_free($parser);
3561
+ if ($GLOBALS['_xh']['isf'] > 1) // test that $GLOBALS['_xh']['value'] is an obj, too???
3562
+ {
3563
+ error_log($GLOBALS['_xh']['isf_reason']);
3564
+ return false;
3565
+ }
3566
+ switch ($GLOBALS['_xh']['rt'])
3567
+ {
3568
+ case 'methodresponse':
3569
+ $v =& $GLOBALS['_xh']['value'];
3570
+ if ($GLOBALS['_xh']['isf'] == 1)
3571
+ {
3572
+ $vc = $v->structmem('faultCode');
3573
+ $vs = $v->structmem('faultString');
3574
+ $r = new xmlrpcresp(0, $vc->scalarval(), $vs->scalarval());
3575
+ }
3576
+ else
3577
+ {
3578
+ $r = new xmlrpcresp($v);
3579
+ }
3580
+ return $r;
3581
+ case 'methodcall':
3582
+ $m = new xmlrpcmsg($GLOBALS['_xh']['method']);
3583
+ for($i=0; $i < count($GLOBALS['_xh']['params']); $i++)
3584
+ {
3585
+ $m->addParam($GLOBALS['_xh']['params'][$i]);
3586
+ }
3587
+ return $m;
3588
+ case 'value':
3589
+ return $GLOBALS['_xh']['value'];
3590
+ default:
3591
+ return false;
3592
+ }
3593
+ }
3594
+
3595
+ /**
3596
+ * decode a string that is encoded w/ "chunked" transfer encoding
3597
+ * as defined in rfc2068 par. 19.4.6
3598
+ * code shamelessly stolen from nusoap library by Dietrich Ayala
3599
+ *
3600
+ * @param string $buffer the string to be decoded
3601
+ * @return string
3602
+ */
3603
+ function decode_chunked($buffer)
3604
+ {
3605
+ // length := 0
3606
+ $length = 0;
3607
+ $new = '';
3608
+
3609
+ // read chunk-size, chunk-extension (if any) and crlf
3610
+ // get the position of the linebreak
3611
+ $chunkend = strpos($buffer,"\r\n") + 2;
3612
+ $temp = substr($buffer,0,$chunkend);
3613
+ $chunk_size = hexdec( trim($temp) );
3614
+ $chunkstart = $chunkend;
3615
+ while($chunk_size > 0)
3616
+ {
3617
+ $chunkend = strpos($buffer, "\r\n", $chunkstart + $chunk_size);
3618
+
3619
+ // just in case we got a broken connection
3620
+ if($chunkend == false)
3621
+ {
3622
+ $chunk = substr($buffer,$chunkstart);
3623
+ // append chunk-data to entity-body
3624
+ $new .= $chunk;
3625
+ $length += strlen($chunk);
3626
+ break;
3627
+ }
3628
+
3629
+ // read chunk-data and crlf
3630
+ $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
3631
+ // append chunk-data to entity-body
3632
+ $new .= $chunk;
3633
+ // length := length + chunk-size
3634
+ $length += strlen($chunk);
3635
+ // read chunk-size and crlf
3636
+ $chunkstart = $chunkend + 2;
3637
+
3638
+ $chunkend = strpos($buffer,"\r\n",$chunkstart)+2;
3639
+ if($chunkend == false)
3640
+ {
3641
+ break; //just in case we got a broken connection
3642
+ }
3643
+ $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
3644
+ $chunk_size = hexdec( trim($temp) );
3645
+ $chunkstart = $chunkend;
3646
+ }
3647
+ return $new;
3648
+ }
3649
+
3650
+ /**
3651
+ * xml charset encoding guessing helper function.
3652
+ * Tries to determine the charset encoding of an XML chunk received over HTTP.
3653
+ * NB: according to the spec (RFC 3023), if text/xml content-type is received over HTTP without a content-type,
3654
+ * we SHOULD assume it is strictly US-ASCII. But we try to be more tolerant of unconforming (legacy?) clients/servers,
3655
+ * which will be most probably using UTF-8 anyway...
3656
+ *
3657
+ * @param string $httpheaders the http Content-type header
3658
+ * @param string $xmlchunk xml content buffer
3659
+ * @param string $encoding_prefs comma separated list of character encodings to be used as default (when mb extension is enabled)
3660
+ *
3661
+ * @todo explore usage of mb_http_input(): does it detect http headers + post data? if so, use it instead of hand-detection!!!
3662
+ */
3663
+ function guess_encoding($httpheader='', $xmlchunk='', $encoding_prefs=null)
3664
+ {
3665
+ // discussion: see http://www.yale.edu/pclt/encoding/
3666
+ // 1 - test if encoding is specified in HTTP HEADERS
3667
+
3668
+ //Details:
3669
+ // LWS: (\13\10)?( |\t)+
3670
+ // token: (any char but excluded stuff)+
3671
+ // quoted string: " (any char but double quotes and cointrol chars)* "
3672
+ // header: Content-type = ...; charset=value(; ...)*
3673
+ // where value is of type token, no LWS allowed between 'charset' and value
3674
+ // Note: we do not check for invalid chars in VALUE:
3675
+ // this had better be done using pure ereg as below
3676
+ // Note 2: we might be removing whitespace/tabs that ought to be left in if
3677
+ // the received charset is a quoted string. But nobody uses such charset names...
3678
+
3679
+ /// @todo this test will pass if ANY header has charset specification, not only Content-Type. Fix it?
3680
+ $matches = array();
3681
+ if(preg_match('/;\s*charset\s*=([^;]+)/i', $httpheader, $matches))
3682
+ {
3683
+ return strtoupper(trim($matches[1], " \t\""));
3684
+ }
3685
+
3686
+ // 2 - scan the first bytes of the data for a UTF-16 (or other) BOM pattern
3687
+ // (source: http://www.w3.org/TR/2000/REC-xml-20001006)
3688
+ // NOTE: actually, according to the spec, even if we find the BOM and determine
3689
+ // an encoding, we should check if there is an encoding specified
3690
+ // in the xml declaration, and verify if they match.
3691
+ /// @todo implement check as described above?
3692
+ /// @todo implement check for first bytes of string even without a BOM? (It sure looks harder than for cases WITH a BOM)
3693
+ if(preg_match('/^(\x00\x00\xFE\xFF|\xFF\xFE\x00\x00|\x00\x00\xFF\xFE|\xFE\xFF\x00\x00)/', $xmlchunk))
3694
+ {
3695
+ return 'UCS-4';
3696
+ }
3697
+ elseif(preg_match('/^(\xFE\xFF|\xFF\xFE)/', $xmlchunk))
3698
+ {
3699
+ return 'UTF-16';
3700
+ }
3701
+ elseif(preg_match('/^(\xEF\xBB\xBF)/', $xmlchunk))
3702
+ {
3703
+ return 'UTF-8';
3704
+ }
3705
+
3706
+ // 3 - test if encoding is specified in the xml declaration
3707
+ // Details:
3708
+ // SPACE: (#x20 | #x9 | #xD | #xA)+ === [ \x9\xD\xA]+
3709
+ // EQ: SPACE?=SPACE? === [ \x9\xD\xA]*=[ \x9\xD\xA]*
3710
+ if (preg_match('/^<\?xml\s+version\s*=\s*'. "((?:\"[a-zA-Z0-9_.:-]+\")|(?:'[a-zA-Z0-9_.:-]+'))".
3711
+ '\s+encoding\s*=\s*' . "((?:\"[A-Za-z][A-Za-z0-9._-]*\")|(?:'[A-Za-z][A-Za-z0-9._-]*'))/",
3712
+ $xmlchunk, $matches))
3713
+ {
3714
+ return strtoupper(substr($matches[2], 1, -1));
3715
+ }
3716
+
3717
+ // 4 - if mbstring is available, let it do the guesswork
3718
+ // NB: we favour finding an encoding that is compatible with what we can process
3719
+ if(extension_loaded('mbstring'))
3720
+ {
3721
+ if($encoding_prefs)
3722
+ {
3723
+ $enc = mb_detect_encoding($xmlchunk, $encoding_prefs);
3724
+ }
3725
+ else
3726
+ {
3727
+ $enc = mb_detect_encoding($xmlchunk);
3728
+ }
3729
+ // NB: mb_detect likes to call it ascii, xml parser likes to call it US_ASCII...
3730
+ // IANA also likes better US-ASCII, so go with it
3731
+ if($enc == 'ASCII')
3732
+ {
3733
+ $enc = 'US-'.$enc;
3734
+ }
3735
+ return $enc;
3736
+ }
3737
+ else
3738
+ {
3739
+ // no encoding specified: as per HTTP1.1 assume it is iso-8859-1?
3740
+ // Both RFC 2616 (HTTP 1.1) and 1945 (HTTP 1.0) clearly state that for text/xxx content types
3741
+ // this should be the standard. And we should be getting text/xml as request and response.
3742
+ // BUT we have to be backward compatible with the lib, which always used UTF-8 as default...
3743
+ return $GLOBALS['xmlrpc_defencoding'];
3744
+ }
3745
+ }
3746
+
3747
+ /**
3748
+ * Checks if a given charset encoding is present in a list of encodings or
3749
+ * if it is a valid subset of any encoding in the list
3750
+ * @param string $encoding charset to be tested
3751
+ * @param mixed $validlist comma separated list of valid charsets (or array of charsets)
3752
+ */
3753
+ function is_valid_charset($encoding, $validlist)
3754
+ {
3755
+ $charset_supersets = array(
3756
+ 'US-ASCII' => array ('ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4',
3757
+ 'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8',
3758
+ 'ISO-8859-9', 'ISO-8859-10', 'ISO-8859-11', 'ISO-8859-12',
3759
+ 'ISO-8859-13', 'ISO-8859-14', 'ISO-8859-15', 'UTF-8',
3760
+ 'EUC-JP', 'EUC-', 'EUC-KR', 'EUC-CN')
3761
+ );
3762
+ if (is_string($validlist))
3763
+ $validlist = explode(',', $validlist);
3764
+ if (@in_array(strtoupper($encoding), $validlist))
3765
+ return true;
3766
+ else
3767
+ {
3768
+ if (array_key_exists($encoding, $charset_supersets))
3769
+ foreach ($validlist as $allowed)
3770
+ if (in_array($allowed, $charset_supersets[$encoding]))
3771
+ return true;
3772
+ return false;
3773
+ }
3774
+ }
3775
+
3776
+ ?>
lib/Klarna/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc ADDED
@@ -0,0 +1,955 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * PHP-XMLRPC "wrapper" functions
4
+ * Generate stubs to transparently access xmlrpc methods as php functions and viceversa
5
+ *
6
+ * @version $Id: xmlrpc_wrappers.inc,v 1.13 2008/09/20 01:23:47 ggiunta Exp $
7
+ * @author Gaetano Giunta
8
+ * @copyright (C) 2006-2009 G. Giunta
9
+ * @license code licensed under the BSD License: http://phpxmlrpc.sourceforge.net/license.txt
10
+ *
11
+ * @todo separate introspection from code generation for func-2-method wrapping
12
+ * @todo use some better templating system for code generation?
13
+ * @todo implement method wrapping with preservation of php objs in calls
14
+ * @todo when wrapping methods without obj rebuilding, use return_type = 'phpvals' (faster)
15
+ * @todo implement self-parsing of php code for PHP <= 4
16
+ */
17
+
18
+ // requires: xmlrpc.inc
19
+
20
+ /**
21
+ * Given a string defining a php type or phpxmlrpc type (loosely defined: strings
22
+ * accepted come from javadoc blocks), return corresponding phpxmlrpc type.
23
+ * NB: for php 'resource' types returns empty string, since resources cannot be serialized;
24
+ * for php class names returns 'struct', since php objects can be serialized as xmlrpc structs
25
+ * for php arrays always return array, even though arrays sometiles serialize as json structs
26
+ * @param string $phptype
27
+ * @return string
28
+ */
29
+ function php_2_xmlrpc_type($phptype)
30
+ {
31
+ switch(strtolower($phptype))
32
+ {
33
+ case 'string':
34
+ return $GLOBALS['xmlrpcString'];
35
+ case 'integer':
36
+ case $GLOBALS['xmlrpcInt']: // 'int'
37
+ case $GLOBALS['xmlrpcI4']:
38
+ return $GLOBALS['xmlrpcInt'];
39
+ case 'double':
40
+ return $GLOBALS['xmlrpcDouble'];
41
+ case 'boolean':
42
+ return $GLOBALS['xmlrpcBoolean'];
43
+ case 'array':
44
+ return $GLOBALS['xmlrpcArray'];
45
+ case 'object':
46
+ return $GLOBALS['xmlrpcStruct'];
47
+ case $GLOBALS['xmlrpcBase64']:
48
+ case $GLOBALS['xmlrpcStruct']:
49
+ return strtolower($phptype);
50
+ case 'resource':
51
+ return '';
52
+ default:
53
+ if(class_exists($phptype))
54
+ {
55
+ return $GLOBALS['xmlrpcStruct'];
56
+ }
57
+ else
58
+ {
59
+ // unknown: might be any 'extended' xmlrpc type
60
+ return $GLOBALS['xmlrpcValue'];
61
+ }
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Given a string defining a phpxmlrpc type return corresponding php type.
67
+ * @param string $xmlrpctype
68
+ * @return string
69
+ */
70
+ function xmlrpc_2_php_type($xmlrpctype)
71
+ {
72
+ switch(strtolower($xmlrpctype))
73
+ {
74
+ case 'base64':
75
+ case 'datetime.iso8601':
76
+ case 'string':
77
+ return $GLOBALS['xmlrpcString'];
78
+ case 'int':
79
+ case 'i4':
80
+ return 'integer';
81
+ case 'struct':
82
+ case 'array':
83
+ return 'array';
84
+ case 'double':
85
+ return 'float';
86
+ case 'undefined':
87
+ return 'mixed';
88
+ case 'boolean':
89
+ case 'null':
90
+ default:
91
+ // unknown: might be any xmlrpc type
92
+ return strtolower($xmlrpctype);
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Given a user-defined PHP function, create a PHP 'wrapper' function that can
98
+ * be exposed as xmlrpc method from an xmlrpc_server object and called from remote
99
+ * clients (as well as its corresponding signature info).
100
+ *
101
+ * Since php is a typeless language, to infer types of input and output parameters,
102
+ * it relies on parsing the javadoc-style comment block associated with the given
103
+ * function. Usage of xmlrpc native types (such as datetime.dateTime.iso8601 and base64)
104
+ * in the @param tag is also allowed, if you need the php function to receive/send
105
+ * data in that particular format (note that base64 encoding/decoding is transparently
106
+ * carried out by the lib, while datetime vals are passed around as strings)
107
+ *
108
+ * Known limitations:
109
+ * - requires PHP 5.0.3 +
110
+ * - only works for user-defined functions, not for PHP internal functions
111
+ * (reflection does not support retrieving number/type of params for those)
112
+ * - functions returning php objects will generate special xmlrpc responses:
113
+ * when the xmlrpc decoding of those responses is carried out by this same lib, using
114
+ * the appropriate param in php_xmlrpc_decode, the php objects will be rebuilt.
115
+ * In short: php objects can be serialized, too (except for their resource members),
116
+ * using this function.
117
+ * Other libs might choke on the very same xml that will be generated in this case
118
+ * (i.e. it has a nonstandard attribute on struct element tags)
119
+ * - usage of javadoc @param tags using param names in a different order from the
120
+ * function prototype is not considered valid (to be fixed?)
121
+ *
122
+ * Note that since rel. 2.0RC3 the preferred method to have the server call 'standard'
123
+ * php functions (ie. functions not expecting a single xmlrpcmsg obj as parameter)
124
+ * is by making use of the functions_parameters_type class member.
125
+ *
126
+ * @param string $funcname the name of the PHP user function to be exposed as xmlrpc method; array($obj, 'methodname') and array('class', 'methodname') are ok too
127
+ * @param string $newfuncname (optional) name for function to be created
128
+ * @param array $extra_options (optional) array of options for conversion. valid values include:
129
+ * bool return_source when true, php code w. function definition will be returned, not evaluated
130
+ * bool encode_php_objs let php objects be sent to server using the 'improved' xmlrpc notation, so server can deserialize them as php objects
131
+ * bool decode_php_objs --- WARNING !!! possible security hazard. only use it with trusted servers ---
132
+ * bool suppress_warnings remove from produced xml any runtime warnings due to the php function being invoked
133
+ * @return false on error, or an array containing the name of the new php function,
134
+ * its signature and docs, to be used in the server dispatch map
135
+ *
136
+ * @todo decide how to deal with params passed by ref: bomb out or allow?
137
+ * @todo finish using javadoc info to build method sig if all params are named but out of order
138
+ * @todo add a check for params of 'resource' type
139
+ * @todo add some trigger_errors / error_log when returning false?
140
+ * @todo what to do when the PHP function returns NULL? we are currently returning an empty string value...
141
+ * @todo add an option to suppress php warnings in invocation of user function, similar to server debug level 3?
142
+ * @todo if $newfuncname is empty, we could use create_user_func instead of eval, as it is possibly faster
143
+ * @todo add a verbatim_object_copy parameter to allow avoiding the same obj instance?
144
+ */
145
+ function wrap_php_function($funcname, $newfuncname='', $extra_options=array())
146
+ {
147
+ $buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
148
+ $prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
149
+ $encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
150
+ $decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
151
+ $catch_warnings = isset($extra_options['suppress_warnings']) && $extra_options['suppress_warnings'] ? '@' : '';
152
+
153
+ if(version_compare(phpversion(), '5.0.3') == -1)
154
+ {
155
+ // up to php 5.0.3 some useful reflection methods were missing
156
+ error_log('XML-RPC: cannot not wrap php functions unless running php version bigger than 5.0.3');
157
+ return false;
158
+ }
159
+
160
+ $exists = false;
161
+ if (is_string($funcname) && strpos($funcname, '::') !== false)
162
+ {
163
+ $funcname = explode('::', $funcname);
164
+ }
165
+ if(is_array($funcname))
166
+ {
167
+ if(count($funcname) < 2 || (!is_string($funcname[0]) && !is_object($funcname[0])))
168
+ {
169
+ error_log('XML-RPC: syntax for function to be wrapped is wrong');
170
+ return false;
171
+ }
172
+ if(is_string($funcname[0]))
173
+ {
174
+ $plainfuncname = implode('::', $funcname);
175
+ }
176
+ elseif(is_object($funcname[0]))
177
+ {
178
+ $plainfuncname = get_class($funcname[0]) . '->' . $funcname[1];
179
+ }
180
+ $exists = method_exists($funcname[0], $funcname[1]);
181
+ if (!$exists && version_compare(phpversion(), '5.1') < 0)
182
+ {
183
+ // workaround for php 5.0: static class methods are not seen by method_exists
184
+ $exists = is_callable( $funcname );
185
+ }
186
+ }
187
+ else
188
+ {
189
+ $plainfuncname = $funcname;
190
+ $exists = function_exists($funcname);
191
+ }
192
+
193
+ if(!$exists)
194
+ {
195
+ error_log('XML-RPC: function to be wrapped is not defined: '.$plainfuncname);
196
+ return false;
197
+ }
198
+ else
199
+ {
200
+ // determine name of new php function
201
+ if($newfuncname == '')
202
+ {
203
+ if(is_array($funcname))
204
+ {
205
+ if(is_string($funcname[0]))
206
+ $xmlrpcfuncname = "{$prefix}_".implode('_', $funcname);
207
+ else
208
+ $xmlrpcfuncname = "{$prefix}_".get_class($funcname[0]) . '_' . $funcname[1];
209
+ }
210
+ else
211
+ {
212
+ $xmlrpcfuncname = "{$prefix}_$funcname";
213
+ }
214
+ }
215
+ else
216
+ {
217
+ $xmlrpcfuncname = $newfuncname;
218
+ }
219
+ while($buildit && function_exists($xmlrpcfuncname))
220
+ {
221
+ $xmlrpcfuncname .= 'x';
222
+ }
223
+
224
+ // start to introspect PHP code
225
+ if(is_array($funcname))
226
+ {
227
+ $func = new ReflectionMethod($funcname[0], $funcname[1]);
228
+ if($func->isPrivate())
229
+ {
230
+ error_log('XML-RPC: method to be wrapped is private: '.$plainfuncname);
231
+ return false;
232
+ }
233
+ if($func->isProtected())
234
+ {
235
+ error_log('XML-RPC: method to be wrapped is protected: '.$plainfuncname);
236
+ return false;
237
+ }
238
+ if($func->isConstructor())
239
+ {
240
+ error_log('XML-RPC: method to be wrapped is the constructor: '.$plainfuncname);
241
+ return false;
242
+ }
243
+ // php 503 always says isdestructor = true...
244
+ if( version_compare(phpversion(), '5.0.3') != 0 && $func->isDestructor())
245
+ {
246
+ error_log('XML-RPC: method to be wrapped is the destructor: '.$plainfuncname);
247
+ return false;
248
+ }
249
+ if($func->isAbstract())
250
+ {
251
+ error_log('XML-RPC: method to be wrapped is abstract: '.$plainfuncname);
252
+ return false;
253
+ }
254
+ /// @todo add more checks for static vs. nonstatic?
255
+ }
256
+ else
257
+ {
258
+ $func = new ReflectionFunction($funcname);
259
+ }
260
+ if($func->isInternal())
261
+ {
262
+ // Note: from PHP 5.1.0 onward, we will possibly be able to use invokeargs
263
+ // instead of getparameters to fully reflect internal php functions ?
264
+ error_log('XML-RPC: function to be wrapped is internal: '.$plainfuncname);
265
+ return false;
266
+ }
267
+
268
+ // retrieve parameter names, types and description from javadoc comments
269
+
270
+ // function description
271
+ $desc = '';
272
+ // type of return val: by default 'any'
273
+ $returns = $GLOBALS['xmlrpcValue'];
274
+ // desc of return val
275
+ $returnsDocs = '';
276
+ // type + name of function parameters
277
+ $paramDocs = array();
278
+
279
+ $docs = $func->getDocComment();
280
+ if($docs != '')
281
+ {
282
+ $docs = explode("\n", $docs);
283
+ $i = 0;
284
+ foreach($docs as $doc)
285
+ {
286
+ $doc = trim($doc, " \r\t/*");
287
+ if(strlen($doc) && strpos($doc, '@') !== 0 && !$i)
288
+ {
289
+ if($desc)
290
+ {
291
+ $desc .= "\n";
292
+ }
293
+ $desc .= $doc;
294
+ }
295
+ elseif(strpos($doc, '@param') === 0)
296
+ {
297
+ // syntax: @param type [$name] desc
298
+ if(preg_match('/@param\s+(\S+)(\s+\$\S+)?\s+(.+)/', $doc, $matches))
299
+ {
300
+ if(strpos($matches[1], '|'))
301
+ {
302
+ //$paramDocs[$i]['type'] = explode('|', $matches[1]);
303
+ $paramDocs[$i]['type'] = 'mixed';
304
+ }
305
+ else
306
+ {
307
+ $paramDocs[$i]['type'] = $matches[1];
308
+ }
309
+ $paramDocs[$i]['name'] = trim($matches[2]);
310
+ $paramDocs[$i]['doc'] = $matches[3];
311
+ }
312
+ $i++;
313
+ }
314
+ elseif(strpos($doc, '@return') === 0)
315
+ {
316
+ // syntax: @return type desc
317
+ //$returns = preg_split('/\s+/', $doc);
318
+ if(preg_match('/@return\s+(\S+)\s+(.+)/', $doc, $matches))
319
+ {
320
+ $returns = php_2_xmlrpc_type($matches[1]);
321
+ if(isset($matches[2]))
322
+ {
323
+ $returnsDocs = $matches[2];
324
+ }
325
+ }
326
+ }
327
+ }
328
+ }
329
+
330
+ // execute introspection of actual function prototype
331
+ $params = array();
332
+ $i = 0;
333
+ foreach($func->getParameters() as $paramobj)
334
+ {
335
+ $params[$i] = array();
336
+ $params[$i]['name'] = '$'.$paramobj->getName();
337
+ $params[$i]['isoptional'] = $paramobj->isOptional();
338
+ $i++;
339
+ }
340
+
341
+
342
+ // start building of PHP code to be eval'd
343
+ $innercode = '';
344
+ $i = 0;
345
+ $parsvariations = array();
346
+ $pars = array();
347
+ $pnum = count($params);
348
+ foreach($params as $param)
349
+ {
350
+ if (isset($paramDocs[$i]['name']) && $paramDocs[$i]['name'] && strtolower($paramDocs[$i]['name']) != strtolower($param['name']))
351
+ {
352
+ // param name from phpdoc info does not match param definition!
353
+ $paramDocs[$i]['type'] = 'mixed';
354
+ }
355
+
356
+ if($param['isoptional'])
357
+ {
358
+ // this particular parameter is optional. save as valid previous list of parameters
359
+ $innercode .= "if (\$paramcount > $i) {\n";
360
+ $parsvariations[] = $pars;
361
+ }
362
+ $innercode .= "\$p$i = \$msg->getParam($i);\n";
363
+ if ($decode_php_objects)
364
+ {
365
+ $innercode .= "if (\$p{$i}->kindOf() == 'scalar') \$p$i = \$p{$i}->scalarval(); else \$p$i = php_{$prefix}_decode(\$p$i, array('decode_php_objs'));\n";
366
+ }
367
+ else
368
+ {
369
+ $innercode .= "if (\$p{$i}->kindOf() == 'scalar') \$p$i = \$p{$i}->scalarval(); else \$p$i = php_{$prefix}_decode(\$p$i);\n";
370
+ }
371
+
372
+ $pars[] = "\$p$i";
373
+ $i++;
374
+ if($param['isoptional'])
375
+ {
376
+ $innercode .= "}\n";
377
+ }
378
+ if($i == $pnum)
379
+ {
380
+ // last allowed parameters combination
381
+ $parsvariations[] = $pars;
382
+ }
383
+ }
384
+
385
+ $sigs = array();
386
+ $psigs = array();
387
+ if(count($parsvariations) == 0)
388
+ {
389
+ // only known good synopsis = no parameters
390
+ $parsvariations[] = array();
391
+ $minpars = 0;
392
+ }
393
+ else
394
+ {
395
+ $minpars = count($parsvariations[0]);
396
+ }
397
+
398
+ if($minpars)
399
+ {
400
+ // add to code the check for min params number
401
+ // NB: this check needs to be done BEFORE decoding param values
402
+ $innercode = "\$paramcount = \$msg->getNumParams();\n" .
403
+ "if (\$paramcount < $minpars) return new {$prefix}resp(0, {$GLOBALS['xmlrpcerr']['incorrect_params']}, '{$GLOBALS['xmlrpcstr']['incorrect_params']}');\n" . $innercode;
404
+ }
405
+ else
406
+ {
407
+ $innercode = "\$paramcount = \$msg->getNumParams();\n" . $innercode;
408
+ }
409
+
410
+ $innercode .= "\$np = false;\n";
411
+ // since there are no closures in php, if we are given an object instance,
412
+ // we store a pointer to it in a global var...
413
+ if ( is_array($funcname) && is_object($funcname[0]) )
414
+ {
415
+ $GLOBALS['xmlrpcWPFObjHolder'][$xmlrpcfuncname] =& $funcname[0];
416
+ $innercode .= "\$obj =& \$GLOBALS['xmlrpcWPFObjHolder']['$xmlrpcfuncname'];\n";
417
+ $realfuncname = '$obj->'.$funcname[1];
418
+ }
419
+ else
420
+ {
421
+ $realfuncname = $plainfuncname;
422
+ }
423
+ foreach($parsvariations as $pars)
424
+ {
425
+ $innercode .= "if (\$paramcount == " . count($pars) . ") \$retval = {$catch_warnings}$realfuncname(" . implode(',', $pars) . "); else\n";
426
+ // build a 'generic' signature (only use an appropriate return type)
427
+ $sig = array($returns);
428
+ $psig = array($returnsDocs);
429
+ for($i=0; $i < count($pars); $i++)
430
+ {
431
+ if (isset($paramDocs[$i]['type']))
432
+ {
433
+ $sig[] = php_2_xmlrpc_type($paramDocs[$i]['type']);
434
+ }
435
+ else
436
+ {
437
+ $sig[] = $GLOBALS['xmlrpcValue'];
438
+ }
439
+ $psig[] = isset($paramDocs[$i]['doc']) ? $paramDocs[$i]['doc'] : '';
440
+ }
441
+ $sigs[] = $sig;
442
+ $psigs[] = $psig;
443
+ }
444
+ $innercode .= "\$np = true;\n";
445
+ $innercode .= "if (\$np) return new {$prefix}resp(0, {$GLOBALS['xmlrpcerr']['incorrect_params']}, '{$GLOBALS['xmlrpcstr']['incorrect_params']}'); else {\n";
446
+ //$innercode .= "if (\$_xmlrpcs_error_occurred) return new xmlrpcresp(0, $GLOBALS['xmlrpcerr']user, \$_xmlrpcs_error_occurred); else\n";
447
+ $innercode .= "if (is_a(\$retval, '{$prefix}resp')) return \$retval; else\n";
448
+ if($returns == $GLOBALS['xmlrpcDateTime'] || $returns == $GLOBALS['xmlrpcBase64'])
449
+ {
450
+ $innercode .= "return new {$prefix}resp(new {$prefix}val(\$retval, '$returns'));";
451
+ }
452
+ else
453
+ {
454
+ if ($encode_php_objects)
455
+ $innercode .= "return new {$prefix}resp(php_{$prefix}_encode(\$retval, array('encode_php_objs')));\n";
456
+ else
457
+ $innercode .= "return new {$prefix}resp(php_{$prefix}_encode(\$retval));\n";
458
+ }
459
+ // shall we exclude functions returning by ref?
460
+ // if($func->returnsReference())
461
+ // return false;
462
+ $code = "function $xmlrpcfuncname(\$msg) {\n" . $innercode . "}\n}";
463
+ //print_r($code);
464
+ if ($buildit)
465
+ {
466
+ $allOK = 0;
467
+ eval($code.'$allOK=1;');
468
+ // alternative
469
+ //$xmlrpcfuncname = create_function('$m', $innercode);
470
+
471
+ if(!$allOK)
472
+ {
473
+ error_log('XML-RPC: could not create function '.$xmlrpcfuncname.' to wrap php function '.$plainfuncname);
474
+ return false;
475
+ }
476
+ }
477
+
478
+ /// @todo examine if $paramDocs matches $parsvariations and build array for
479
+ /// usage as method signature, plus put together a nice string for docs
480
+
481
+ $ret = array('function' => $xmlrpcfuncname, 'signature' => $sigs, 'docstring' => $desc, 'signature_docs' => $psigs, 'source' => $code);
482
+ return $ret;
483
+ }
484
+ }
485
+
486
+ /**
487
+ * Given a user-defined PHP class or php object, map its methods onto a list of
488
+ * PHP 'wrapper' functions that can be exposed as xmlrpc methods from an xmlrpc_server
489
+ * object and called from remote clients (as well as their corresponding signature info).
490
+ *
491
+ * @param mixed $classname the name of the class whose methods are to be exposed as xmlrpc methods, or an object instance of that class
492
+ * @param array $extra_options see the docs for wrap_php_method for more options
493
+ * string method_type 'static', 'nonstatic', 'all' and 'auto' (default); the latter will switch between static and non-static depending on wheter $classname is a class name or object instance
494
+ * @return array or false on failure
495
+ *
496
+ * @todo get_class_methods will return both static and non-static methods.
497
+ * we have to differentiate the action, depending on wheter we recived a class name or object
498
+ */
499
+ function wrap_php_class($classname, $extra_options=array())
500
+ {
501
+ $methodfilter = isset($extra_options['method_filter']) ? $extra_options['method_filter'] : '';
502
+ $methodtype = isset($extra_options['method_type']) ? $extra_options['method_type'] : 'auto';
503
+
504
+ if(version_compare(phpversion(), '5.0.3') == -1)
505
+ {
506
+ // up to php 5.0.3 some useful reflection methods were missing
507
+ error_log('XML-RPC: cannot not wrap php functions unless running php version bigger than 5.0.3');
508
+ return false;
509
+ }
510
+
511
+ $result = array();
512
+ $mlist = get_class_methods($classname);
513
+ foreach($mlist as $mname)
514
+ {
515
+ if ($methodfilter == '' || preg_match($methodfilter, $mname))
516
+ {
517
+ // echo $mlist."\n";
518
+ $func = new ReflectionMethod($classname, $mname);
519
+ if(!$func->isPrivate() && !$func->isProtected() && !$func->isConstructor() && !$func->isDestructor() && !$func->isAbstract())
520
+ {
521
+ if(($func->isStatic && ($methodtype == 'all' || $methodtype == 'static' || ($methodtype == 'auto' && is_string($classname)))) ||
522
+ (!$func->isStatic && ($methodtype == 'all' || $methodtype == 'nonstatic' || ($methodtype == 'auto' && is_object($classname)))))
523
+ {
524
+ $methodwrap = wrap_php_function(array($classname, $mname), '', $extra_options);
525
+ if ( $methodwrap )
526
+ {
527
+ $result[$methodwrap['function']] = $methodwrap['function'];
528
+ }
529
+ }
530
+ }
531
+ }
532
+ }
533
+ return $result;
534
+ }
535
+
536
+ /**
537
+ * Given an xmlrpc client and a method name, register a php wrapper function
538
+ * that will call it and return results using native php types for both
539
+ * params and results. The generated php function will return an xmlrpcresp
540
+ * oject for failed xmlrpc calls
541
+ *
542
+ * Known limitations:
543
+ * - server must support system.methodsignature for the wanted xmlrpc method
544
+ * - for methods that expose many signatures, only one can be picked (we
545
+ * could in priciple check if signatures differ only by number of params
546
+ * and not by type, but it would be more complication than we can spare time)
547
+ * - nested xmlrpc params: the caller of the generated php function has to
548
+ * encode on its own the params passed to the php function if these are structs
549
+ * or arrays whose (sub)members include values of type datetime or base64
550
+ *
551
+ * Notes: the connection properties of the given client will be copied
552
+ * and reused for the connection used during the call to the generated
553
+ * php function.
554
+ * Calling the generated php function 'might' be slow: a new xmlrpc client
555
+ * is created on every invocation and an xmlrpc-connection opened+closed.
556
+ * An extra 'debug' param is appended to param list of xmlrpc method, useful
557
+ * for debugging purposes.
558
+ *
559
+ * @param xmlrpc_client $client an xmlrpc client set up correctly to communicate with target server
560
+ * @param string $methodname the xmlrpc method to be mapped to a php function
561
+ * @param array $extra_options array of options that specify conversion details. valid ptions include
562
+ * integer signum the index of the method signature to use in mapping (if method exposes many sigs)
563
+ * integer timeout timeout (in secs) to be used when executing function/calling remote method
564
+ * string protocol 'http' (default), 'http11' or 'https'
565
+ * string new_function_name the name of php function to create. If unsepcified, lib will pick an appropriate name
566
+ * string return_source if true return php code w. function definition instead fo function name
567
+ * bool encode_php_objs let php objects be sent to server using the 'improved' xmlrpc notation, so server can deserialize them as php objects
568
+ * bool decode_php_objs --- WARNING !!! possible security hazard. only use it with trusted servers ---
569
+ * mixed return_on_fault a php value to be returned when the xmlrpc call fails/returns a fault response (by default the xmlrpcresp object is returned in this case). If a string is used, '%faultCode%' and '%faultString%' tokens will be substituted with actual error values
570
+ * bool debug set it to 1 or 2 to see debug results of querying server for method synopsis
571
+ * @return string the name of the generated php function (or false) - OR AN ARRAY...
572
+ */
573
+ function wrap_xmlrpc_method($client, $methodname, $extra_options=0, $timeout=0, $protocol='', $newfuncname='')
574
+ {
575
+ // mind numbing: let caller use sane calling convention (as per javadoc, 3 params),
576
+ // OR the 2.0 calling convention (no options) - we really love backward compat, don't we?
577
+ if (!is_array($extra_options))
578
+ {
579
+ $signum = $extra_options;
580
+ $extra_options = array();
581
+ }
582
+ else
583
+ {
584
+ $signum = isset($extra_options['signum']) ? (int)$extra_options['signum'] : 0;
585
+ $timeout = isset($extra_options['timeout']) ? (int)$extra_options['timeout'] : 0;
586
+ $protocol = isset($extra_options['protocol']) ? $extra_options['protocol'] : '';
587
+ $newfuncname = isset($extra_options['new_function_name']) ? $extra_options['new_function_name'] : '';
588
+ }
589
+ //$encode_php_objects = in_array('encode_php_objects', $extra_options);
590
+ //$verbatim_client_copy = in_array('simple_client_copy', $extra_options) ? 1 :
591
+ // in_array('build_class_code', $extra_options) ? 2 : 0;
592
+
593
+ $encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
594
+ $decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
595
+ $simple_client_copy = isset($extra_options['simple_client_copy']) ? (int)($extra_options['simple_client_copy']) : 0;
596
+ $buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
597
+ $prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
598
+ if (isset($extra_options['return_on_fault']))
599
+ {
600
+ $decode_fault = true;
601
+ $fault_response = $extra_options['return_on_fault'];
602
+ }
603
+ else
604
+ {
605
+ $decode_fault = false;
606
+ $fault_response = '';
607
+ }
608
+ $debug = isset($extra_options['debug']) ? ($extra_options['debug']) : 0;
609
+
610
+ $msgclass = $prefix.'msg';
611
+ $valclass = $prefix.'val';
612
+ $decodefunc = 'php_'.$prefix.'_decode';
613
+
614
+ $msg = new $msgclass('system.methodSignature');
615
+ $msg->addparam(new $valclass($methodname));
616
+ $client->setDebug($debug);
617
+ $response =& $client->send($msg, $timeout, $protocol);
618
+ if($response->faultCode())
619
+ {
620
+ error_log('XML-RPC: could not retrieve method signature from remote server for method '.$methodname);
621
+ return false;
622
+ }
623
+ else
624
+ {
625
+ $msig = $response->value();
626
+ if ($client->return_type != 'phpvals')
627
+ {
628
+ $msig = $decodefunc($msig);
629
+ }
630
+ if(!is_array($msig) || count($msig) <= $signum)
631
+ {
632
+ error_log('XML-RPC: could not retrieve method signature nr.'.$signum.' from remote server for method '.$methodname);
633
+ return false;
634
+ }
635
+ else
636
+ {
637
+ // pick a suitable name for the new function, avoiding collisions
638
+ if($newfuncname != '')
639
+ {
640
+ $xmlrpcfuncname = $newfuncname;
641
+ }
642
+ else
643
+ {
644
+ // take care to insure that methodname is translated to valid
645
+ // php function name
646
+ $xmlrpcfuncname = $prefix.'_'.preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
647
+ array('_', ''), $methodname);
648
+ }
649
+ while($buildit && function_exists($xmlrpcfuncname))
650
+ {
651
+ $xmlrpcfuncname .= 'x';
652
+ }
653
+
654
+ $msig = $msig[$signum];
655
+ $mdesc = '';
656
+ // if in 'offline' mode, get method description too.
657
+ // in online mode, favour speed of operation
658
+ if(!$buildit)
659
+ {
660
+ $msg = new $msgclass('system.methodHelp');
661
+ $msg->addparam(new $valclass($methodname));
662
+ $response =& $client->send($msg, $timeout, $protocol);
663
+ if (!$response->faultCode())
664
+ {
665
+ $mdesc = $response->value();
666
+ if ($client->return_type != 'phpvals')
667
+ {
668
+ $mdesc = $mdesc->scalarval();
669
+ }
670
+ }
671
+ }
672
+
673
+ $results = build_remote_method_wrapper_code($client, $methodname,
674
+ $xmlrpcfuncname, $msig, $mdesc, $timeout, $protocol, $simple_client_copy,
675
+ $prefix, $decode_php_objects, $encode_php_objects, $decode_fault,
676
+ $fault_response);
677
+
678
+ //print_r($code);
679
+ if ($buildit)
680
+ {
681
+ $allOK = 0;
682
+ eval($results['source'].'$allOK=1;');
683
+ // alternative
684
+ //$xmlrpcfuncname = create_function('$m', $innercode);
685
+ if($allOK)
686
+ {
687
+ return $xmlrpcfuncname;
688
+ }
689
+ else
690
+ {
691
+ error_log('XML-RPC: could not create function '.$xmlrpcfuncname.' to wrap remote method '.$methodname);
692
+ return false;
693
+ }
694
+ }
695
+ else
696
+ {
697
+ $results['function'] = $xmlrpcfuncname;
698
+ return $results;
699
+ }
700
+ }
701
+ }
702
+ }
703
+
704
+ /**
705
+ * Similar to wrap_xmlrpc_method, but will generate a php class that wraps
706
+ * all xmlrpc methods exposed by the remote server as own methods.
707
+ * For more details see wrap_xmlrpc_method.
708
+ * @param xmlrpc_client $client the client obj all set to query the desired server
709
+ * @param array $extra_options list of options for wrapped code
710
+ * @return mixed false on error, the name of the created class if all ok or an array with code, class name and comments (if the appropriatevoption is set in extra_options)
711
+ */
712
+ function wrap_xmlrpc_server($client, $extra_options=array())
713
+ {
714
+ $methodfilter = isset($extra_options['method_filter']) ? $extra_options['method_filter'] : '';
715
+ //$signum = isset($extra_options['signum']) ? (int)$extra_options['signum'] : 0;
716
+ $timeout = isset($extra_options['timeout']) ? (int)$extra_options['timeout'] : 0;
717
+ $protocol = isset($extra_options['protocol']) ? $extra_options['protocol'] : '';
718
+ $newclassname = isset($extra_options['new_class_name']) ? $extra_options['new_class_name'] : '';
719
+ $encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
720
+ $decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
721
+ $verbatim_client_copy = isset($extra_options['simple_client_copy']) ? !($extra_options['simple_client_copy']) : true;
722
+ $buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
723
+ $prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
724
+
725
+ $msgclass = $prefix.'msg';
726
+ //$valclass = $prefix.'val';
727
+ $decodefunc = 'php_'.$prefix.'_decode';
728
+
729
+ $msg = new $msgclass('system.listMethods');
730
+ $response =& $client->send($msg, $timeout, $protocol);
731
+ if($response->faultCode())
732
+ {
733
+ error_log('XML-RPC: could not retrieve method list from remote server');
734
+ return false;
735
+ }
736
+ else
737
+ {
738
+ $mlist = $response->value();
739
+ if ($client->return_type != 'phpvals')
740
+ {
741
+ $mlist = $decodefunc($mlist);
742
+ }
743
+ if(!is_array($mlist) || !count($mlist))
744
+ {
745
+ error_log('XML-RPC: could not retrieve meaningful method list from remote server');
746
+ return false;
747
+ }
748
+ else
749
+ {
750
+ // pick a suitable name for the new function, avoiding collisions
751
+ if($newclassname != '')
752
+ {
753
+ $xmlrpcclassname = $newclassname;
754
+ }
755
+ else
756
+ {
757
+ $xmlrpcclassname = $prefix.'_'.preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
758
+ array('_', ''), $client->server).'_client';
759
+ }
760
+ while($buildit && class_exists($xmlrpcclassname))
761
+ {
762
+ $xmlrpcclassname .= 'x';
763
+ }
764
+
765
+ /// @todo add function setdebug() to new class, to enable/disable debugging
766
+ $source = "class $xmlrpcclassname\n{\nvar \$client;\n\n";
767
+ $source .= "function $xmlrpcclassname()\n{\n";
768
+ $source .= build_client_wrapper_code($client, $verbatim_client_copy, $prefix);
769
+ $source .= "\$this->client =& \$client;\n}\n\n";
770
+ $opts = array('simple_client_copy' => 2, 'return_source' => true,
771
+ 'timeout' => $timeout, 'protocol' => $protocol,
772
+ 'encode_php_objs' => $encode_php_objects, 'prefix' => $prefix,
773
+ 'decode_php_objs' => $decode_php_objects
774
+ );
775
+ /// @todo build javadoc for class definition, too
776
+ foreach($mlist as $mname)
777
+ {
778
+ if ($methodfilter == '' || preg_match($methodfilter, $mname))
779
+ {
780
+ $opts['new_function_name'] = preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
781
+ array('_', ''), $mname);
782
+ $methodwrap = wrap_xmlrpc_method($client, $mname, $opts);
783
+ if ($methodwrap)
784
+ {
785
+ if (!$buildit)
786
+ {
787
+ $source .= $methodwrap['docstring'];
788
+ }
789
+ $source .= $methodwrap['source']."\n";
790
+ }
791
+ else
792
+ {
793
+ error_log('XML-RPC: will not create class method to wrap remote method '.$mname);
794
+ }
795
+ }
796
+ }
797
+ $source .= "}\n";
798
+ if ($buildit)
799
+ {
800
+ $allOK = 0;
801
+ eval($source.'$allOK=1;');
802
+ // alternative
803
+ //$xmlrpcfuncname = create_function('$m', $innercode);
804
+ if($allOK)
805
+ {
806
+ return $xmlrpcclassname;
807
+ }
808
+ else
809
+ {
810
+ error_log('XML-RPC: could not create class '.$xmlrpcclassname.' to wrap remote server '.$client->server);
811
+ return false;
812
+ }
813
+ }
814
+ else
815
+ {
816
+ return array('class' => $xmlrpcclassname, 'code' => $source, 'docstring' => '');
817
+ }
818
+ }
819
+ }
820
+ }
821
+
822
+ /**
823
+ * Given the necessary info, build php code that creates a new function to
824
+ * invoke a remote xmlrpc method.
825
+ * Take care that no full checking of input parameters is done to ensure that
826
+ * valid php code is emitted.
827
+ * Note: real spaghetti code follows...
828
+ * @access private
829
+ */
830
+ function build_remote_method_wrapper_code($client, $methodname, $xmlrpcfuncname,
831
+ $msig, $mdesc='', $timeout=0, $protocol='', $client_copy_mode=0, $prefix='xmlrpc',
832
+ $decode_php_objects=false, $encode_php_objects=false, $decode_fault=false,
833
+ $fault_response='')
834
+ {
835
+ $code = "function $xmlrpcfuncname (";
836
+ if ($client_copy_mode < 2)
837
+ {
838
+ // client copy mode 0 or 1 == partial / full client copy in emitted code
839
+ $innercode = build_client_wrapper_code($client, $client_copy_mode, $prefix);
840
+ $innercode .= "\$client->setDebug(\$debug);\n";
841
+ $this_ = '';
842
+ }
843
+ else
844
+ {
845
+ // client copy mode 2 == no client copy in emitted code
846
+ $innercode = '';
847
+ $this_ = 'this->';
848
+ }
849
+ $innercode .= "\$msg = new {$prefix}msg('$methodname');\n";
850
+
851
+ if ($mdesc != '')
852
+ {
853
+ // take care that PHP comment is not terminated unwillingly by method description
854
+ $mdesc = "/**\n* ".str_replace('*/', '* /', $mdesc)."\n";
855
+ }
856
+ else
857
+ {
858
+ $mdesc = "/**\nFunction $xmlrpcfuncname\n";
859
+ }
860
+
861
+ // param parsing
862
+ $plist = array();
863
+ $pcount = count($msig);
864
+ for($i = 1; $i < $pcount; $i++)
865
+ {
866
+ $plist[] = "\$p$i";
867
+ $ptype = $msig[$i];
868
+ if($ptype == 'i4' || $ptype == 'int' || $ptype == 'boolean' || $ptype == 'double' ||
869
+ $ptype == 'string' || $ptype == 'dateTime.iso8601' || $ptype == 'base64' || $ptype == 'null')
870
+ {
871
+ // only build directly xmlrpcvals when type is known and scalar
872
+ $innercode .= "\$p$i = new {$prefix}val(\$p$i, '$ptype');\n";
873
+ }
874
+ else
875
+ {
876
+ if ($encode_php_objects)
877
+ {
878
+ $innercode .= "\$p$i =& php_{$prefix}_encode(\$p$i, array('encode_php_objs'));\n";
879
+ }
880
+ else
881
+ {
882
+ $innercode .= "\$p$i =& php_{$prefix}_encode(\$p$i);\n";
883
+ }
884
+ }
885
+ $innercode .= "\$msg->addparam(\$p$i);\n";
886
+ $mdesc .= '* @param '.xmlrpc_2_php_type($ptype)." \$p$i\n";
887
+ }
888
+ if ($client_copy_mode < 2)
889
+ {
890
+ $plist[] = '$debug=0';
891
+ $mdesc .= "* @param int \$debug when 1 (or 2) will enable debugging of the underlying {$prefix} call (defaults to 0)\n";
892
+ }
893
+ $plist = implode(', ', $plist);
894
+ $mdesc .= '* @return '.xmlrpc_2_php_type($msig[0])." (or an {$prefix}resp obj instance if call fails)\n*/\n";
895
+
896
+ $innercode .= "\$res =& \${$this_}client->send(\$msg, $timeout, '$protocol');\n";
897
+ if ($decode_fault)
898
+ {
899
+ if (is_string($fault_response) && ((strpos($fault_response, '%faultCode%') !== false) || (strpos($fault_response, '%faultString%') !== false)))
900
+ {
901
+ $respcode = "str_replace(array('%faultCode%', '%faultString%'), array(\$res->faultCode(), \$res->faultString()), '".str_replace("'", "''", $fault_response)."')";
902
+ }
903
+ else
904
+ {
905
+ $respcode = var_export($fault_response, true);
906
+ }
907
+ }
908
+ else
909
+ {
910
+ $respcode = '$res';
911
+ }
912
+ if ($decode_php_objects)
913
+ {
914
+ $innercode .= "if (\$res->faultcode()) return $respcode; else return php_{$prefix}_decode(\$res->value(), array('decode_php_objs'));";
915
+ }
916
+ else
917
+ {
918
+ $innercode .= "if (\$res->faultcode()) return $respcode; else return php_{$prefix}_decode(\$res->value());";
919
+ }
920
+
921
+ $code = $code . $plist. ") {\n" . $innercode . "\n}\n";
922
+
923
+ return array('source' => $code, 'docstring' => $mdesc);
924
+ }
925
+
926
+ /**
927
+ * Given necessary info, generate php code that will rebuild a client object
928
+ * Take care that no full checking of input parameters is done to ensure that
929
+ * valid php code is emitted.
930
+ * @access private
931
+ */
932
+ function build_client_wrapper_code($client, $verbatim_client_copy, $prefix='xmlrpc')
933
+ {
934
+ $code = "\$client = new {$prefix}_client('".str_replace("'", "\'", $client->path).
935
+ "', '" . str_replace("'", "\'", $client->server) . "', $client->port);\n";
936
+
937
+ // copy all client fields to the client that will be generated runtime
938
+ // (this provides for future expansion or subclassing of client obj)
939
+ if ($verbatim_client_copy)
940
+ {
941
+ foreach($client as $fld => $val)
942
+ {
943
+ if($fld != 'debug' && $fld != 'return_type')
944
+ {
945
+ $val = var_export($val, true);
946
+ $code .= "\$client->$fld = $val;\n";
947
+ }
948
+ }
949
+ }
950
+ // only make sure that client always returns the correct data type
951
+ $code .= "\$client->return_type = '{$prefix}vals';\n";
952
+ //$code .= "\$client->setDebug(\$debug);\n";
953
+ return $code;
954
+ }
955
+ ?>
lib/Klarna/transport/xmlrpc-3.0.0.beta/lib/xmlrpcs.inc ADDED
@@ -0,0 +1,1246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // by Edd Dumbill (C) 1999-2002
3
+ // <edd@usefulinc.com>
4
+ // $Id: xmlrpcs.inc,v 1.71 2008/10/29 23:41:28 ggiunta Exp $
5
+
6
+ // Copyright (c) 1999,2000,2002 Edd Dumbill.
7
+ // All rights reserved.
8
+ //
9
+ // Redistribution and use in source and binary forms, with or without
10
+ // modification, are permitted provided that the following conditions
11
+ // are met:
12
+ //
13
+ // * Redistributions of source code must retain the above copyright
14
+ // notice, this list of conditions and the following disclaimer.
15
+ //
16
+ // * Redistributions in binary form must reproduce the above
17
+ // copyright notice, this list of conditions and the following
18
+ // disclaimer in the documentation and/or other materials provided
19
+ // with the distribution.
20
+ //
21
+ // * Neither the name of the "XML-RPC for PHP" nor the names of its
22
+ // contributors may be used to endorse or promote products derived
23
+ // from this software without specific prior written permission.
24
+ //
25
+ // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26
+ // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27
+ // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28
+ // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29
+ // REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30
+ // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31
+ // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32
+ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33
+ // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34
+ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35
+ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
36
+ // OF THE POSSIBILITY OF SUCH DAMAGE.
37
+
38
+ // XML RPC Server class
39
+ // requires: xmlrpc.inc
40
+
41
+ $GLOBALS['xmlrpcs_capabilities'] = array(
42
+ // xmlrpc spec: always supported
43
+ 'xmlrpc' => new xmlrpcval(array(
44
+ 'specUrl' => new xmlrpcval('http://www.xmlrpc.com/spec', 'string'),
45
+ 'specVersion' => new xmlrpcval(1, 'int')
46
+ ), 'struct'),
47
+ // if we support system.xxx functions, we always support multicall, too...
48
+ // Note that, as of 2006/09/17, the following URL does not respond anymore
49
+ 'system.multicall' => new xmlrpcval(array(
50
+ 'specUrl' => new xmlrpcval('http://www.xmlrpc.com/discuss/msgReader$1208', 'string'),
51
+ 'specVersion' => new xmlrpcval(1, 'int')
52
+ ), 'struct'),
53
+ // introspection: version 2! we support 'mixed', too
54
+ 'introspection' => new xmlrpcval(array(
55
+ 'specUrl' => new xmlrpcval('http://phpxmlrpc.sourceforge.net/doc-2/ch10.html', 'string'),
56
+ 'specVersion' => new xmlrpcval(2, 'int')
57
+ ), 'struct')
58
+ );
59
+
60
+ /* Functions that implement system.XXX methods of xmlrpc servers */
61
+ $_xmlrpcs_getCapabilities_sig=array(array($GLOBALS['xmlrpcStruct']));
62
+ $_xmlrpcs_getCapabilities_doc='This method lists all the capabilites that the XML-RPC server has: the (more or less standard) extensions to the xmlrpc spec that it adheres to';
63
+ $_xmlrpcs_getCapabilities_sdoc=array(array('list of capabilities, described as structs with a version number and url for the spec'));
64
+ function _xmlrpcs_getCapabilities($server, $m=null)
65
+ {
66
+ $outAr = $GLOBALS['xmlrpcs_capabilities'];
67
+ // NIL extension
68
+ if ($GLOBALS['xmlrpc_null_extension']) {
69
+ $outAr['nil'] = new xmlrpcval(array(
70
+ 'specUrl' => new xmlrpcval('http://www.ontosys.com/xml-rpc/extensions.php', 'string'),
71
+ 'specVersion' => new xmlrpcval(1, 'int')
72
+ ), 'struct');
73
+ }
74
+ return new xmlrpcresp(new xmlrpcval($outAr, 'struct'));
75
+ }
76
+
77
+ // listMethods: signature was either a string, or nothing.
78
+ // The useless string variant has been removed
79
+ $_xmlrpcs_listMethods_sig=array(array($GLOBALS['xmlrpcArray']));
80
+ $_xmlrpcs_listMethods_doc='This method lists all the methods that the XML-RPC server knows how to dispatch';
81
+ $_xmlrpcs_listMethods_sdoc=array(array('list of method names'));
82
+ function _xmlrpcs_listMethods($server, $m=null) // if called in plain php values mode, second param is missing
83
+ {
84
+
85
+ $outAr=array();
86
+ foreach($server->dmap as $key => $val)
87
+ {
88
+ $outAr[]=new xmlrpcval($key, 'string');
89
+ }
90
+ if($server->allow_system_funcs)
91
+ {
92
+ foreach($GLOBALS['_xmlrpcs_dmap'] as $key => $val)
93
+ {
94
+ $outAr[]=new xmlrpcval($key, 'string');
95
+ }
96
+ }
97
+ return new xmlrpcresp(new xmlrpcval($outAr, 'array'));
98
+ }
99
+
100
+ $_xmlrpcs_methodSignature_sig=array(array($GLOBALS['xmlrpcArray'], $GLOBALS['xmlrpcString']));
101
+ $_xmlrpcs_methodSignature_doc='Returns an array of known signatures (an array of arrays) for the method name passed. If no signatures are known, returns a none-array (test for type != array to detect missing signature)';
102
+ $_xmlrpcs_methodSignature_sdoc=array(array('list of known signatures, each sig being an array of xmlrpc type names', 'name of method to be described'));
103
+ function _xmlrpcs_methodSignature($server, $m)
104
+ {
105
+ // let accept as parameter both an xmlrpcval or string
106
+ if (is_object($m))
107
+ {
108
+ $methName=$m->getParam(0);
109
+ $methName=$methName->scalarval();
110
+ }
111
+ else
112
+ {
113
+ $methName=$m;
114
+ }
115
+ if(strpos($methName, "system.") === 0)
116
+ {
117
+ $dmap=$GLOBALS['_xmlrpcs_dmap']; $sysCall=1;
118
+ }
119
+ else
120
+ {
121
+ $dmap=$server->dmap; $sysCall=0;
122
+ }
123
+ if(isset($dmap[$methName]))
124
+ {
125
+ if(isset($dmap[$methName]['signature']))
126
+ {
127
+ $sigs=array();
128
+ foreach($dmap[$methName]['signature'] as $inSig)
129
+ {
130
+ $cursig=array();
131
+ foreach($inSig as $sig)
132
+ {
133
+ $cursig[]=new xmlrpcval($sig, 'string');
134
+ }
135
+ $sigs[]=new xmlrpcval($cursig, 'array');
136
+ }
137
+ $r=new xmlrpcresp(new xmlrpcval($sigs, 'array'));
138
+ }
139
+ else
140
+ {
141
+ // NB: according to the official docs, we should be returning a
142
+ // "none-array" here, which means not-an-array
143
+ $r=new xmlrpcresp(new xmlrpcval('undef', 'string'));
144
+ }
145
+ }
146
+ else
147
+ {
148
+ $r=new xmlrpcresp(0,$GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']);
149
+ }
150
+ return $r;
151
+ }
152
+
153
+ $_xmlrpcs_methodHelp_sig=array(array($GLOBALS['xmlrpcString'], $GLOBALS['xmlrpcString']));
154
+ $_xmlrpcs_methodHelp_doc='Returns help text if defined for the method passed, otherwise returns an empty string';
155
+ $_xmlrpcs_methodHelp_sdoc=array(array('method description', 'name of the method to be described'));
156
+ function _xmlrpcs_methodHelp($server, $m)
157
+ {
158
+ // let accept as parameter both an xmlrpcval or string
159
+ if (is_object($m))
160
+ {
161
+ $methName=$m->getParam(0);
162
+ $methName=$methName->scalarval();
163
+ }
164
+ else
165
+ {
166
+ $methName=$m;
167
+ }
168
+ if(strpos($methName, "system.") === 0)
169
+ {
170
+ $dmap=$GLOBALS['_xmlrpcs_dmap']; $sysCall=1;
171
+ }
172
+ else
173
+ {
174
+ $dmap=$server->dmap; $sysCall=0;
175
+ }
176
+ if(isset($dmap[$methName]))
177
+ {
178
+ if(isset($dmap[$methName]['docstring']))
179
+ {
180
+ $r=new xmlrpcresp(new xmlrpcval($dmap[$methName]['docstring']), 'string');
181
+ }
182
+ else
183
+ {
184
+ $r=new xmlrpcresp(new xmlrpcval('', 'string'));
185
+ }
186
+ }
187
+ else
188
+ {
189
+ $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']);
190
+ }
191
+ return $r;
192
+ }
193
+
194
+ $_xmlrpcs_multicall_sig = array(array($GLOBALS['xmlrpcArray'], $GLOBALS['xmlrpcArray']));
195
+ $_xmlrpcs_multicall_doc = 'Boxcar multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details';
196
+ $_xmlrpcs_multicall_sdoc = array(array('list of response structs, where each struct has the usual members', 'list of calls, with each call being represented as a struct, with members "methodname" and "params"'));
197
+ function _xmlrpcs_multicall_error($err)
198
+ {
199
+ if(is_string($err))
200
+ {
201
+ $str = $GLOBALS['xmlrpcstr']["multicall_${err}"];
202
+ $code = $GLOBALS['xmlrpcerr']["multicall_${err}"];
203
+ }
204
+ else
205
+ {
206
+ $code = $err->faultCode();
207
+ $str = $err->faultString();
208
+ }
209
+ $struct = array();
210
+ $struct['faultCode'] = new xmlrpcval($code, 'int');
211
+ $struct['faultString'] = new xmlrpcval($str, 'string');
212
+ return new xmlrpcval($struct, 'struct');
213
+ }
214
+
215
+ function _xmlrpcs_multicall_do_call($server, $call)
216
+ {
217
+ if($call->kindOf() != 'struct')
218
+ {
219
+ return _xmlrpcs_multicall_error('notstruct');
220
+ }
221
+ $methName = @$call->structmem('methodName');
222
+ if(!$methName)
223
+ {
224
+ return _xmlrpcs_multicall_error('nomethod');
225
+ }
226
+ if($methName->kindOf() != 'scalar' || $methName->scalartyp() != 'string')
227
+ {
228
+ return _xmlrpcs_multicall_error('notstring');
229
+ }
230
+ if($methName->scalarval() == 'system.multicall')
231
+ {
232
+ return _xmlrpcs_multicall_error('recursion');
233
+ }
234
+
235
+ $params = @$call->structmem('params');
236
+ if(!$params)
237
+ {
238
+ return _xmlrpcs_multicall_error('noparams');
239
+ }
240
+ if($params->kindOf() != 'array')
241
+ {
242
+ return _xmlrpcs_multicall_error('notarray');
243
+ }
244
+ $numParams = $params->arraysize();
245
+
246
+ $msg = new xmlrpcmsg($methName->scalarval());
247
+ for($i = 0; $i < $numParams; $i++)
248
+ {
249
+ if(!$msg->addParam($params->arraymem($i)))
250
+ {
251
+ $i++;
252
+ return _xmlrpcs_multicall_error(new xmlrpcresp(0,
253
+ $GLOBALS['xmlrpcerr']['incorrect_params'],
254
+ $GLOBALS['xmlrpcstr']['incorrect_params'] . ": probable xml error in param " . $i));
255
+ }
256
+ }
257
+
258
+ $result = $server->execute($msg);
259
+
260
+ if($result->faultCode() != 0)
261
+ {
262
+ return _xmlrpcs_multicall_error($result); // Method returned fault.
263
+ }
264
+
265
+ return new xmlrpcval(array($result->value()), 'array');
266
+ }
267
+
268
+ function _xmlrpcs_multicall_do_call_phpvals($server, $call)
269
+ {
270
+ if(!is_array($call))
271
+ {
272
+ return _xmlrpcs_multicall_error('notstruct');
273
+ }
274
+ if(!array_key_exists('methodName', $call))
275
+ {
276
+ return _xmlrpcs_multicall_error('nomethod');
277
+ }
278
+ if (!is_string($call['methodName']))
279
+ {
280
+ return _xmlrpcs_multicall_error('notstring');
281
+ }
282
+ if($call['methodName'] == 'system.multicall')
283
+ {
284
+ return _xmlrpcs_multicall_error('recursion');
285
+ }
286
+ if(!array_key_exists('params', $call))
287
+ {
288
+ return _xmlrpcs_multicall_error('noparams');
289
+ }
290
+ if(!is_array($call['params']))
291
+ {
292
+ return _xmlrpcs_multicall_error('notarray');
293
+ }
294
+
295
+ // this is a real dirty and simplistic hack, since we might have received a
296
+ // base64 or datetime values, but they will be listed as strings here...
297
+ $numParams = count($call['params']);
298
+ $pt = array();
299
+ foreach($call['params'] as $val)
300
+ $pt[] = php_2_xmlrpc_type(gettype($val));
301
+
302
+ $result = $server->execute($call['methodName'], $call['params'], $pt);
303
+
304
+ if($result->faultCode() != 0)
305
+ {
306
+ return _xmlrpcs_multicall_error($result); // Method returned fault.
307
+ }
308
+
309
+ return new xmlrpcval(array($result->value()), 'array');
310
+ }
311
+
312
+ function _xmlrpcs_multicall($server, $m)
313
+ {
314
+ $result = array();
315
+ // let accept a plain list of php parameters, beside a single xmlrpc msg object
316
+ if (is_object($m))
317
+ {
318
+ $calls = $m->getParam(0);
319
+ $numCalls = $calls->arraysize();
320
+ for($i = 0; $i < $numCalls; $i++)
321
+ {
322
+ $call = $calls->arraymem($i);
323
+ $result[$i] = _xmlrpcs_multicall_do_call($server, $call);
324
+ }
325
+ }
326
+ else
327
+ {
328
+ $numCalls=count($m);
329
+ for($i = 0; $i < $numCalls; $i++)
330
+ {
331
+ $result[$i] = _xmlrpcs_multicall_do_call_phpvals($server, $m[$i]);
332
+ }
333
+ }
334
+
335
+ return new xmlrpcresp(new xmlrpcval($result, 'array'));
336
+ }
337
+
338
+ $GLOBALS['_xmlrpcs_dmap']=array(
339
+ 'system.listMethods' => array(
340
+ 'function' => '_xmlrpcs_listMethods',
341
+ 'signature' => $_xmlrpcs_listMethods_sig,
342
+ 'docstring' => $_xmlrpcs_listMethods_doc,
343
+ 'signature_docs' => $_xmlrpcs_listMethods_sdoc),
344
+ 'system.methodHelp' => array(
345
+ 'function' => '_xmlrpcs_methodHelp',
346
+ 'signature' => $_xmlrpcs_methodHelp_sig,
347
+ 'docstring' => $_xmlrpcs_methodHelp_doc,
348
+ 'signature_docs' => $_xmlrpcs_methodHelp_sdoc),
349
+ 'system.methodSignature' => array(
350
+ 'function' => '_xmlrpcs_methodSignature',
351
+ 'signature' => $_xmlrpcs_methodSignature_sig,
352
+ 'docstring' => $_xmlrpcs_methodSignature_doc,
353
+ 'signature_docs' => $_xmlrpcs_methodSignature_sdoc),
354
+ 'system.multicall' => array(
355
+ 'function' => '_xmlrpcs_multicall',
356
+ 'signature' => $_xmlrpcs_multicall_sig,
357
+ 'docstring' => $_xmlrpcs_multicall_doc,
358
+ 'signature_docs' => $_xmlrpcs_multicall_sdoc),
359
+ 'system.getCapabilities' => array(
360
+ 'function' => '_xmlrpcs_getCapabilities',
361
+ 'signature' => $_xmlrpcs_getCapabilities_sig,
362
+ 'docstring' => $_xmlrpcs_getCapabilities_doc,
363
+ 'signature_docs' => $_xmlrpcs_getCapabilities_sdoc)
364
+ );
365
+
366
+ $GLOBALS['_xmlrpcs_occurred_errors'] = '';
367
+ $GLOBALS['_xmlrpcs_prev_ehandler'] = '';
368
+
369
+ /**
370
+ * Error handler used to track errors that occur during server-side execution of PHP code.
371
+ * This allows to report back to the client whether an internal error has occurred or not
372
+ * using an xmlrpc response object, instead of letting the client deal with the html junk
373
+ * that a PHP execution error on the server generally entails.
374
+ *
375
+ * NB: in fact a user defined error handler can only handle WARNING, NOTICE and USER_* errors.
376
+ *
377
+ */
378
+ function _xmlrpcs_errorHandler($errcode, $errstring, $filename=null, $lineno=null, $context=null)
379
+ {
380
+ // obey the @ protocol
381
+ if (error_reporting() == 0)
382
+ return;
383
+
384
+ //if($errcode != E_NOTICE && $errcode != E_WARNING && $errcode != E_USER_NOTICE && $errcode != E_USER_WARNING)
385
+ if($errcode != E_STRICT)
386
+ {
387
+ $GLOBALS['_xmlrpcs_occurred_errors'] = $GLOBALS['_xmlrpcs_occurred_errors'] . $errstring . "\n";
388
+ }
389
+ // Try to avoid as much as possible disruption to the previous error handling
390
+ // mechanism in place
391
+ if($GLOBALS['_xmlrpcs_prev_ehandler'] == '')
392
+ {
393
+ // The previous error handler was the default: all we should do is log error
394
+ // to the default error log (if level high enough)
395
+ if(ini_get('log_errors') && (intval(ini_get('error_reporting')) & $errcode))
396
+ {
397
+ error_log($errstring);
398
+ }
399
+ }
400
+ else
401
+ {
402
+ // Pass control on to previous error handler, trying to avoid loops...
403
+ if($GLOBALS['_xmlrpcs_prev_ehandler'] != '_xmlrpcs_errorHandler')
404
+ {
405
+ // NB: this code will NOT work on php < 4.0.2: only 2 params were used for error handlers
406
+ if(is_array($GLOBALS['_xmlrpcs_prev_ehandler']))
407
+ {
408
+ // the following works both with static class methods and plain object methods as error handler
409
+ call_user_func_array($GLOBALS['_xmlrpcs_prev_ehandler'], array($errcode, $errstring, $filename, $lineno, $context));
410
+ }
411
+ else
412
+ {
413
+ $GLOBALS['_xmlrpcs_prev_ehandler']($errcode, $errstring, $filename, $lineno, $context);
414
+ }
415
+ }
416
+ }
417
+ }
418
+
419
+ $GLOBALS['_xmlrpc_debuginfo']='';
420
+
421
+ /**
422
+ * Add a string to the debug info that can be later seralized by the server
423
+ * as part of the response message.
424
+ * Note that for best compatbility, the debug string should be encoded using
425
+ * the $GLOBALS['xmlrpc_internalencoding'] character set.
426
+ * @param string $m
427
+ * @access public
428
+ */
429
+ function xmlrpc_debugmsg($m)
430
+ {
431
+ $GLOBALS['_xmlrpc_debuginfo'] .= $m . "\n";
432
+ }
433
+
434
+ class xmlrpc_server
435
+ {
436
+ /**
437
+ * Array defining php functions exposed as xmlrpc methods by this server
438
+ * @access private
439
+ */
440
+ var $dmap=array();
441
+ /**
442
+ * Defines how functions in dmap will be invoked: either using an xmlrpc msg object
443
+ * or plain php values.
444
+ * valid strings are 'xmlrpcvals', 'phpvals' or 'epivals'
445
+ */
446
+ var $functions_parameters_type='xmlrpcvals';
447
+ /**
448
+ * Option used for fine-tuning the encoding the php values returned from
449
+ * functions registered in the dispatch map when the functions_parameters_types
450
+ * member is set to 'phpvals'
451
+ * @see php_xmlrpc_encode for a list of values
452
+ */
453
+ var $phpvals_encoding_options = array( 'auto_dates' );
454
+ /// controls wether the server is going to echo debugging messages back to the client as comments in response body. valid values: 0,1,2,3
455
+ var $debug = 1;
456
+ /**
457
+ * Controls behaviour of server when invoked user function throws an exception:
458
+ * 0 = catch it and return an 'internal error' xmlrpc response (default)
459
+ * 1 = catch it and return an xmlrpc response with the error corresponding to the exception
460
+ * 2 = allow the exception to float to the upper layers
461
+ */
462
+ var $exception_handling = 0;
463
+ /**
464
+ * When set to true, it will enable HTTP compression of the response, in case
465
+ * the client has declared its support for compression in the request.
466
+ */
467
+ var $compress_response = false;
468
+ /**
469
+ * List of http compression methods accepted by the server for requests.
470
+ * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
471
+ */
472
+ var $accepted_compression = array();
473
+ /// shall we serve calls to system.* methods?
474
+ var $allow_system_funcs = true;
475
+ /// list of charset encodings natively accepted for requests
476
+ var $accepted_charset_encodings = array();
477
+ /**
478
+ * charset encoding to be used for response.
479
+ * NB: if we can, we will convert the generated response from internal_encoding to the intended one.
480
+ * can be: a supported xml encoding (only UTF-8 and ISO-8859-1 at present, unless mbstring is enabled),
481
+ * null (leave unspecified in response, convert output stream to US_ASCII),
482
+ * 'default' (use xmlrpc library default as specified in xmlrpc.inc, convert output stream if needed),
483
+ * or 'auto' (use client-specified charset encoding or same as request if request headers do not specify it (unless request is US-ASCII: then use library default anyway).
484
+ * NB: pretty dangerous if you accept every charset and do not have mbstring enabled)
485
+ */
486
+ var $response_charset_encoding = '';
487
+ /**
488
+ * Storage for internal debug info
489
+ * @access private
490
+ */
491
+ var $debug_info = '';
492
+ /**
493
+ * Extra data passed at runtime to method handling functions. Used only by EPI layer
494
+ */
495
+ var $user_data = null;
496
+
497
+ /**
498
+ * @param array $dispmap the dispatch map withd efinition of exposed services
499
+ * @param boolean $servicenow set to false to prevent the server from runnung upon construction
500
+ */
501
+ function xmlrpc_server($dispMap=null, $serviceNow=true)
502
+ {
503
+ // if ZLIB is enabled, let the server by default accept compressed requests,
504
+ // and compress responses sent to clients that support them
505
+ if(function_exists('gzinflate'))
506
+ {
507
+ $this->accepted_compression = array('gzip', 'deflate');
508
+ $this->compress_response = true;
509
+ }
510
+
511
+ // by default the xml parser can support these 3 charset encodings
512
+ $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
513
+
514
+ // dispMap is a dispatch array of methods
515
+ // mapped to function names and signatures
516
+ // if a method
517
+ // doesn't appear in the map then an unknown
518
+ // method error is generated
519
+ /* milosch - changed to make passing dispMap optional.
520
+ * instead, you can use the class add_to_map() function
521
+ * to add functions manually (borrowed from SOAPX4)
522
+ */
523
+ if($dispMap)
524
+ {
525
+ $this->dmap = $dispMap;
526
+ if($serviceNow)
527
+ {
528
+ $this->service();
529
+ }
530
+ }
531
+ }
532
+
533
+ /**
534
+ * Set debug level of server.
535
+ * @param integer $in debug lvl: determines info added to xmlrpc responses (as xml comments)
536
+ * 0 = no debug info,
537
+ * 1 = msgs set from user with debugmsg(),
538
+ * 2 = add complete xmlrpc request (headers and body),
539
+ * 3 = add also all processing warnings happened during method processing
540
+ * (NB: this involves setting a custom error handler, and might interfere
541
+ * with the standard processing of the php function exposed as method. In
542
+ * particular, triggering an USER_ERROR level error will not halt script
543
+ * execution anymore, but just end up logged in the xmlrpc response)
544
+ * Note that info added at elevel 2 and 3 will be base64 encoded
545
+ * @access public
546
+ */
547
+ function setDebug($in)
548
+ {
549
+ $this->debug=$in;
550
+ }
551
+
552
+ /**
553
+ * Return a string with the serialized representation of all debug info
554
+ * @param string $charset_encoding the target charset encoding for the serialization
555
+ * @return string an XML comment (or two)
556
+ */
557
+ function serializeDebug($charset_encoding='')
558
+ {
559
+ // Tough encoding problem: which internal charset should we assume for debug info?
560
+ // It might contain a copy of raw data received from client, ie with unknown encoding,
561
+ // intermixed with php generated data and user generated data...
562
+ // so we split it: system debug is base 64 encoded,
563
+ // user debug info should be encoded by the end user using the INTERNAL_ENCODING
564
+ $out = '';
565
+ if ($this->debug_info != '')
566
+ {
567
+ $out .= "<!-- SERVER DEBUG INFO (BASE64 ENCODED):\n".base64_encode($this->debug_info)."\n-->\n";
568
+ }
569
+ if($GLOBALS['_xmlrpc_debuginfo']!='')
570
+ {
571
+
572
+ $out .= "<!-- DEBUG INFO:\n" . xmlrpc_encode_entitites(str_replace('--', '_-', $GLOBALS['_xmlrpc_debuginfo']), $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "\n-->\n";
573
+ // NB: a better solution MIGHT be to use CDATA, but we need to insert it
574
+ // into return payload AFTER the beginning tag
575
+ //$out .= "<![CDATA[ DEBUG INFO:\n\n" . str_replace(']]>', ']_]_>', $GLOBALS['_xmlrpc_debuginfo']) . "\n]]>\n";
576
+ }
577
+ return $out;
578
+ }
579
+
580
+ /**
581
+ * Execute the xmlrpc request, printing the response
582
+ * @param string $data the request body. If null, the http POST request will be examined
583
+ * @return xmlrpcresp the response object (usually not used by caller...)
584
+ * @access public
585
+ */
586
+ function service($data=null, $return_payload=false)
587
+ {
588
+ if ($data === null)
589
+ {
590
+ // workaround for a known bug in php ver. 5.2.2 that broke $HTTP_RAW_POST_DATA
591
+ $ver = phpversion();
592
+ if ($ver[0] >= 5)
593
+ {
594
+ $data = file_get_contents('php://input');
595
+ }
596
+ else
597
+ {
598
+ $data = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : '';
599
+ }
600
+ }
601
+ $raw_data = $data;
602
+
603
+ // reset internal debug info
604
+ $this->debug_info = '';
605
+
606
+ // Echo back what we received, before parsing it
607
+ if($this->debug > 1)
608
+ {
609
+ $this->debugmsg("+++GOT+++\n" . $data . "\n+++END+++");
610
+ }
611
+
612
+ $r = $this->parseRequestHeaders($data, $req_charset, $resp_charset, $resp_encoding);
613
+ if (!$r)
614
+ {
615
+ $r=$this->parseRequest($data, $req_charset);
616
+ }
617
+
618
+ // save full body of request into response, for more debugging usages
619
+ $r->raw_data = $raw_data;
620
+
621
+ if($this->debug > 2 && $GLOBALS['_xmlrpcs_occurred_errors'])
622
+ {
623
+ $this->debugmsg("+++PROCESSING ERRORS AND WARNINGS+++\n" .
624
+ $GLOBALS['_xmlrpcs_occurred_errors'] . "+++END+++");
625
+ }
626
+
627
+ $payload=$this->xml_header($resp_charset);
628
+ if($this->debug > 0)
629
+ {
630
+ $payload = $payload . $this->serializeDebug($resp_charset);
631
+ }
632
+
633
+ // G. Giunta 2006-01-27: do not create response serialization if it has
634
+ // already happened. Helps building json magic
635
+ if (empty($r->payload))
636
+ {
637
+ $r->serialize($resp_charset);
638
+ }
639
+ $payload = $payload . $r->payload;
640
+
641
+ if ($return_payload)
642
+ {
643
+ return $payload;
644
+ }
645
+
646
+ // if we get a warning/error that has output some text before here, then we cannot
647
+ // add a new header. We cannot say we are sending xml, either...
648
+ if(!headers_sent())
649
+ {
650
+ header('Content-Type: '.$r->content_type);
651
+ // we do not know if client actually told us an accepted charset, but if he did
652
+ // we have to tell him what we did
653
+ header("Vary: Accept-Charset");
654
+
655
+ // http compression of output: only
656
+ // if we can do it, and we want to do it, and client asked us to,
657
+ // and php ini settings do not force it already
658
+ $php_no_self_compress = !ini_get('zlib.output_compression') && (ini_get('output_handler') != 'ob_gzhandler');
659
+ if($this->compress_response && function_exists('gzencode') && $resp_encoding != ''
660
+ && $php_no_self_compress)
661
+ {
662
+ if(strpos($resp_encoding, 'gzip') !== false)
663
+ {
664
+ $payload = gzencode($payload);
665
+ header("Content-Encoding: gzip");
666
+ header("Vary: Accept-Encoding");
667
+ }
668
+ elseif (strpos($resp_encoding, 'deflate') !== false)
669
+ {
670
+ $payload = gzcompress($payload);
671
+ header("Content-Encoding: deflate");
672
+ header("Vary: Accept-Encoding");
673
+ }
674
+ }
675
+
676
+ // do not ouput content-length header if php is compressing output for us:
677
+ // it will mess up measurements
678
+ if($php_no_self_compress)
679
+ {
680
+ header('Content-Length: ' . (int)strlen($payload));
681
+ }
682
+ }
683
+ else
684
+ {
685
+ error_log('XML-RPC: '.__METHOD__.': http headers already sent before response is fully generated. Check for php warning or error messages');
686
+ }
687
+
688
+ print $payload;
689
+
690
+ // return request, in case subclasses want it
691
+ return $r;
692
+ }
693
+
694
+ /**
695
+ * Add a method to the dispatch map
696
+ * @param string $methodname the name with which the method will be made available
697
+ * @param string $function the php function that will get invoked
698
+ * @param array $sig the array of valid method signatures
699
+ * @param string $doc method documentation
700
+ * @param array $sigdoc the array of valid method signatures docs (one string per param, one for return type)
701
+ * @access public
702
+ */
703
+ function add_to_map($methodname,$function,$sig=null,$doc=false,$sigdoc=false)
704
+ {
705
+ $this->dmap[$methodname] = array(
706
+ 'function' => $function,
707
+ 'docstring' => $doc
708
+ );
709
+ if ($sig)
710
+ {
711
+ $this->dmap[$methodname]['signature'] = $sig;
712
+ }
713
+ if ($sigdoc)
714
+ {
715
+ $this->dmap[$methodname]['signature_docs'] = $sigdoc;
716
+ }
717
+ }
718
+
719
+ /**
720
+ * Verify type and number of parameters received against a list of known signatures
721
+ * @param array $in array of either xmlrpcval objects or xmlrpc type definitions
722
+ * @param array $sig array of known signatures to match against
723
+ * @access private
724
+ */
725
+ function verifySignature($in, $sig)
726
+ {
727
+ // check each possible signature in turn
728
+ if (is_object($in))
729
+ {
730
+ $numParams = $in->getNumParams();
731
+ }
732
+ else
733
+ {
734
+ $numParams = count($in);
735
+ }
736
+ foreach($sig as $cursig)
737
+ {
738
+ if(count($cursig)==$numParams+1)
739
+ {
740
+ $itsOK=1;
741
+ for($n=0; $n<$numParams; $n++)
742
+ {
743
+ if (is_object($in))
744
+ {
745
+ $p=$in->getParam($n);
746
+ if($p->kindOf() == 'scalar')
747
+ {
748
+ $pt=$p->scalartyp();
749
+ }
750
+ else
751
+ {
752
+ $pt=$p->kindOf();
753
+ }
754
+ }
755
+ else
756
+ {
757
+ $pt= $in[$n] == 'i4' ? 'int' : strtolower($in[$n]); // dispatch maps never use i4...
758
+ }
759
+
760
+ // param index is $n+1, as first member of sig is return type
761
+ if($pt != $cursig[$n+1] && $cursig[$n+1] != $GLOBALS['xmlrpcValue'])
762
+ {
763
+ $itsOK=0;
764
+ $pno=$n+1;
765
+ $wanted=$cursig[$n+1];
766
+ $got=$pt;
767
+ break;
768
+ }
769
+ }
770
+ if($itsOK)
771
+ {
772
+ return array(1,'');
773
+ }
774
+ }
775
+ }
776
+ if(isset($wanted))
777
+ {
778
+ return array(0, "Wanted ${wanted}, got ${got} at param ${pno}");
779
+ }
780
+ else
781
+ {
782
+ return array(0, "No method signature matches number of parameters");
783
+ }
784
+ }
785
+
786
+ /**
787
+ * Parse http headers received along with xmlrpc request. If needed, inflate request
788
+ * @return null on success or an xmlrpcresp
789
+ * @access private
790
+ */
791
+ function parseRequestHeaders(&$data, &$req_encoding, &$resp_encoding, &$resp_compression)
792
+ {
793
+ // check if $_SERVER is populated: it might have been disabled via ini file
794
+ // (this is true even when in CLI mode)
795
+ if (count($_SERVER) == 0)
796
+ {
797
+ error_log('XML-RPC: '.__METHOD__.': cannot parse request headers as $_SERVER is not populated');
798
+ }
799
+
800
+ if($this->debug > 1)
801
+ {
802
+ if(function_exists('getallheaders'))
803
+ {
804
+ $this->debugmsg(''); // empty line
805
+ foreach(getallheaders() as $name => $val)
806
+ {
807
+ $this->debugmsg("HEADER: $name: $val");
808
+ }
809
+ }
810
+
811
+ }
812
+
813
+ if(isset($_SERVER['HTTP_CONTENT_ENCODING']))
814
+ {
815
+ $content_encoding = str_replace('x-', '', $_SERVER['HTTP_CONTENT_ENCODING']);
816
+ }
817
+ else
818
+ {
819
+ $content_encoding = '';
820
+ }
821
+
822
+ // check if request body has been compressed and decompress it
823
+ if($content_encoding != '' && strlen($data))
824
+ {
825
+ if($content_encoding == 'deflate' || $content_encoding == 'gzip')
826
+ {
827
+ // if decoding works, use it. else assume data wasn't gzencoded
828
+ if(function_exists('gzinflate') && in_array($content_encoding, $this->accepted_compression))
829
+ {
830
+ if($content_encoding == 'deflate' && $degzdata = @gzuncompress($data))
831
+ {
832
+ $data = $degzdata;
833
+ if($this->debug > 1)
834
+ {
835
+ $this->debugmsg("\n+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" . $data . "\n+++END+++");
836
+ }
837
+ }
838
+ elseif($content_encoding == 'gzip' && $degzdata = @gzinflate(substr($data, 10)))
839
+ {
840
+ $data = $degzdata;
841
+ if($this->debug > 1)
842
+ $this->debugmsg("+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" . $data . "\n+++END+++");
843
+ }
844
+ else
845
+ {
846
+ $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_decompress_fail'], $GLOBALS['xmlrpcstr']['server_decompress_fail']);
847
+ return $r;
848
+ }
849
+ }
850
+ else
851
+ {
852
+ //error_log('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
853
+ $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_cannot_decompress'], $GLOBALS['xmlrpcstr']['server_cannot_decompress']);
854
+ return $r;
855
+ }
856
+ }
857
+ }
858
+
859
+ // check if client specified accepted charsets, and if we know how to fulfill
860
+ // the request
861
+ if ($this->response_charset_encoding == 'auto')
862
+ {
863
+ $resp_encoding = '';
864
+ if (isset($_SERVER['HTTP_ACCEPT_CHARSET']))
865
+ {
866
+ // here we should check if we can match the client-requested encoding
867
+ // with the encodings we know we can generate.
868
+ /// @todo we should parse q=0.x preferences instead of getting first charset specified...
869
+ $client_accepted_charsets = explode(',', strtoupper($_SERVER['HTTP_ACCEPT_CHARSET']));
870
+ // Give preference to internal encoding
871
+ $known_charsets = array($GLOBALS['xmlrpc_internalencoding'], 'UTF-8', 'ISO-8859-1', 'US-ASCII');
872
+ foreach ($known_charsets as $charset)
873
+ {
874
+ foreach ($client_accepted_charsets as $accepted)
875
+ if (strpos($accepted, $charset) === 0)
876
+ {
877
+ $resp_encoding = $charset;
878
+ break;
879
+ }
880
+ if ($resp_encoding)
881
+ break;
882
+ }
883
+ }
884
+ }
885
+ else
886
+ {
887
+ $resp_encoding = $this->response_charset_encoding;
888
+ }
889
+
890
+ if (isset($_SERVER['HTTP_ACCEPT_ENCODING']))
891
+ {
892
+ $resp_compression = $_SERVER['HTTP_ACCEPT_ENCODING'];
893
+ }
894
+ else
895
+ {
896
+ $resp_compression = '';
897
+ }
898
+
899
+ // 'guestimate' request encoding
900
+ /// @todo check if mbstring is enabled and automagic input conversion is on: it might mingle with this check???
901
+ $req_encoding = guess_encoding(isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : '',
902
+ $data);
903
+
904
+ return null;
905
+ }
906
+
907
+ /**
908
+ * Parse an xml chunk containing an xmlrpc request and execute the corresponding
909
+ * php function registered with the server
910
+ * @param string $data the xml request
911
+ * @param string $req_encoding (optional) the charset encoding of the xml request
912
+ * @return xmlrpcresp
913
+ * @access private
914
+ */
915
+ function parseRequest($data, $req_encoding='')
916
+ {
917
+ // 2005/05/07 commented and moved into caller function code
918
+ //if($data=='')
919
+ //{
920
+ // $data=$GLOBALS['HTTP_RAW_POST_DATA'];
921
+ //}
922
+
923
+ // G. Giunta 2005/02/13: we do NOT expect to receive html entities
924
+ // so we do not try to convert them into xml character entities
925
+ //$data = xmlrpc_html_entity_xlate($data);
926
+
927
+ $GLOBALS['_xh']=array();
928
+ $GLOBALS['_xh']['ac']='';
929
+ $GLOBALS['_xh']['stack']=array();
930
+ $GLOBALS['_xh']['valuestack'] = array();
931
+ $GLOBALS['_xh']['params']=array();
932
+ $GLOBALS['_xh']['pt']=array();
933
+ $GLOBALS['_xh']['isf']=0;
934
+ $GLOBALS['_xh']['isf_reason']='';
935
+ $GLOBALS['_xh']['method']=false; // so we can check later if we got a methodname or not
936
+ $GLOBALS['_xh']['rt']='';
937
+
938
+ // decompose incoming XML into request structure
939
+ if ($req_encoding != '')
940
+ {
941
+ if (!in_array($req_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
942
+ // the following code might be better for mb_string enabled installs, but
943
+ // makes the lib about 200% slower...
944
+ //if (!is_valid_charset($req_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
945
+ {
946
+ error_log('XML-RPC: '.__METHOD__.': invalid charset encoding of received request: '.$req_encoding);
947
+ $req_encoding = $GLOBALS['xmlrpc_defencoding'];
948
+ }
949
+ /// @BUG this will fail on PHP 5 if charset is not specified in the xml prologue,
950
+ // the encoding is not UTF8 and there are non-ascii chars in the text...
951
+ /// @todo use an ampty string for php 5 ???
952
+ $parser = xml_parser_create($req_encoding);
953
+ }
954
+ else
955
+ {
956
+ $parser = xml_parser_create();
957
+ }
958
+
959
+ xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
960
+ // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
961
+ // the xml parser to give us back data in the expected charset
962
+ // What if internal encoding is not in one of the 3 allowed?
963
+ // we use the broadest one, ie. utf8
964
+ // This allows to send data which is native in various charset,
965
+ // by extending xmlrpc_encode_entitites() and setting xmlrpc_internalencoding
966
+ if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
967
+ {
968
+ xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
969
+ }
970
+ else
971
+ {
972
+ xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
973
+ }
974
+
975
+ if ($this->functions_parameters_type != 'xmlrpcvals')
976
+ xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
977
+ else
978
+ xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
979
+ xml_set_character_data_handler($parser, 'xmlrpc_cd');
980
+ xml_set_default_handler($parser, 'xmlrpc_dh');
981
+ if(!xml_parse($parser, $data, 1))
982
+ {
983
+ // return XML error as a faultCode
984
+ $r=new xmlrpcresp(0,
985
+ $GLOBALS['xmlrpcerrxml']+xml_get_error_code($parser),
986
+ sprintf('XML error: %s at line %d, column %d',
987
+ xml_error_string(xml_get_error_code($parser)),
988
+ xml_get_current_line_number($parser), xml_get_current_column_number($parser)));
989
+ xml_parser_free($parser);
990
+ }
991
+ elseif ($GLOBALS['_xh']['isf'])
992
+ {
993
+ xml_parser_free($parser);
994
+ $r=new xmlrpcresp(0,
995
+ $GLOBALS['xmlrpcerr']['invalid_request'],
996
+ $GLOBALS['xmlrpcstr']['invalid_request'] . ' ' . $GLOBALS['_xh']['isf_reason']);
997
+ }
998
+ else
999
+ {
1000
+ xml_parser_free($parser);
1001
+ // small layering violation in favor of speed and memory usage:
1002
+ // we should allow the 'execute' method handle this, but in the
1003
+ // most common scenario (xmlrpcvals type server with some methods
1004
+ // registered as phpvals) that would mean a useless encode+decode pass
1005
+ if ($this->functions_parameters_type != 'xmlrpcvals' || (isset($this->dmap[$GLOBALS['_xh']['method']]['parameters_type']) && ($this->dmap[$GLOBALS['_xh']['method']]['parameters_type'] == 'phpvals')))
1006
+ {
1007
+ if($this->debug > 1)
1008
+ {
1009
+ $this->debugmsg("\n+++PARSED+++\n".var_export($GLOBALS['_xh']['params'], true)."\n+++END+++");
1010
+ }
1011
+ $r = $this->execute($GLOBALS['_xh']['method'], $GLOBALS['_xh']['params'], $GLOBALS['_xh']['pt']);
1012
+ }
1013
+ else
1014
+ {
1015
+ // build an xmlrpcmsg object with data parsed from xml
1016
+ $m=new xmlrpcmsg($GLOBALS['_xh']['method']);
1017
+ // now add parameters in
1018
+ for($i=0; $i<count($GLOBALS['_xh']['params']); $i++)
1019
+ {
1020
+ $m->addParam($GLOBALS['_xh']['params'][$i]);
1021
+ }
1022
+
1023
+ if($this->debug > 1)
1024
+ {
1025
+ $this->debugmsg("\n+++PARSED+++\n".var_export($m, true)."\n+++END+++");
1026
+ }
1027
+ $r = $this->execute($m);
1028
+ }
1029
+ }
1030
+ return $r;
1031
+ }
1032
+
1033
+ /**
1034
+ * Execute a method invoked by the client, checking parameters used
1035
+ * @param mixed $m either an xmlrpcmsg obj or a method name
1036
+ * @param array $params array with method parameters as php types (if m is method name only)
1037
+ * @param array $paramtypes array with xmlrpc types of method parameters (if m is method name only)
1038
+ * @return xmlrpcresp
1039
+ * @access private
1040
+ */
1041
+ function execute($m, $params=null, $paramtypes=null)
1042
+ {
1043
+ if (is_object($m))
1044
+ {
1045
+ $methName = $m->method();
1046
+ }
1047
+ else
1048
+ {
1049
+ $methName = $m;
1050
+ }
1051
+ $sysCall = $this->allow_system_funcs && (strpos($methName, "system.") === 0);
1052
+ $dmap = $sysCall ? $GLOBALS['_xmlrpcs_dmap'] : $this->dmap;
1053
+
1054
+ if(!isset($dmap[$methName]['function']))
1055
+ {
1056
+ // No such method
1057
+ return new xmlrpcresp(0,
1058
+ $GLOBALS['xmlrpcerr']['unknown_method'],
1059
+ $GLOBALS['xmlrpcstr']['unknown_method']);
1060
+ }
1061
+
1062
+ // Check signature
1063
+ if(isset($dmap[$methName]['signature']))
1064
+ {
1065
+ $sig = $dmap[$methName]['signature'];
1066
+ if (is_object($m))
1067
+ {
1068
+ list($ok, $errstr) = $this->verifySignature($m, $sig);
1069
+ }
1070
+ else
1071
+ {
1072
+ list($ok, $errstr) = $this->verifySignature($paramtypes, $sig);
1073
+ }
1074
+ if(!$ok)
1075
+ {
1076
+ // Didn't match.
1077
+ return new xmlrpcresp(
1078
+ 0,
1079
+ $GLOBALS['xmlrpcerr']['incorrect_params'],
1080
+ $GLOBALS['xmlrpcstr']['incorrect_params'] . ": ${errstr}"
1081
+ );
1082
+ }
1083
+ }
1084
+
1085
+ $func = $dmap[$methName]['function'];
1086
+ // let the 'class::function' syntax be accepted in dispatch maps
1087
+ if(is_string($func) && strpos($func, '::'))
1088
+ {
1089
+ $func = explode('::', $func);
1090
+ }
1091
+ // verify that function to be invoked is in fact callable
1092
+ if(!is_callable($func))
1093
+ {
1094
+ error_log("XML-RPC: ".__METHOD__.": function $func registered as method handler is not callable");
1095
+ return new xmlrpcresp(
1096
+ 0,
1097
+ $GLOBALS['xmlrpcerr']['server_error'],
1098
+ $GLOBALS['xmlrpcstr']['server_error'] . ": no function matches method"
1099
+ );
1100
+ }
1101
+
1102
+ // If debug level is 3, we should catch all errors generated during
1103
+ // processing of user function, and log them as part of response
1104
+ if($this->debug > 2)
1105
+ {
1106
+ $GLOBALS['_xmlrpcs_prev_ehandler'] = set_error_handler('_xmlrpcs_errorHandler');
1107
+ }
1108
+ try
1109
+ {
1110
+ // Allow mixed-convention servers
1111
+ if (is_object($m))
1112
+ {
1113
+ if($sysCall)
1114
+ {
1115
+ $r = call_user_func($func, $this, $m);
1116
+ }
1117
+ else
1118
+ {
1119
+ $r = call_user_func($func, $m);
1120
+ }
1121
+ if (!is_a($r, 'xmlrpcresp'))
1122
+ {
1123
+ error_log("XML-RPC: ".__METHOD__.": function $func registered as method handler does not return an xmlrpcresp object");
1124
+ if (is_a($r, 'xmlrpcval'))
1125
+ {
1126
+ $r = new xmlrpcresp($r);
1127
+ }
1128
+ else
1129
+ {
1130
+ $r = new xmlrpcresp(
1131
+ 0,
1132
+ $GLOBALS['xmlrpcerr']['server_error'],
1133
+ $GLOBALS['xmlrpcstr']['server_error'] . ": function does not return xmlrpcresp object"
1134
+ );
1135
+ }
1136
+ }
1137
+ }
1138
+ else
1139
+ {
1140
+ // call a 'plain php' function
1141
+ if($sysCall)
1142
+ {
1143
+ array_unshift($params, $this);
1144
+ $r = call_user_func_array($func, $params);
1145
+ }
1146
+ else
1147
+ {
1148
+ // 3rd API convention for method-handling functions: EPI-style
1149
+ if ($this->functions_parameters_type == 'epivals')
1150
+ {
1151
+ $r = call_user_func_array($func, array($methName, $params, $this->user_data));
1152
+ // mimic EPI behaviour: if we get an array that looks like an error, make it
1153
+ // an eror response
1154
+ if (is_array($r) && array_key_exists('faultCode', $r) && array_key_exists('faultString', $r))
1155
+ {
1156
+ $r = new xmlrpcresp(0, (integer)$r['faultCode'], (string)$r['faultString']);
1157
+ }
1158
+ else
1159
+ {
1160
+ // functions using EPI api should NOT return resp objects,
1161
+ // so make sure we encode the return type correctly
1162
+ $r = new xmlrpcresp(php_xmlrpc_encode($r, array('extension_api')));
1163
+ }
1164
+ }
1165
+ else
1166
+ {
1167
+ $r = call_user_func_array($func, $params);
1168
+ }
1169
+ }
1170
+ // the return type can be either an xmlrpcresp object or a plain php value...
1171
+ if (!is_a($r, 'xmlrpcresp'))
1172
+ {
1173
+ // what should we assume here about automatic encoding of datetimes
1174
+ // and php classes instances???
1175
+ $r = new xmlrpcresp(php_xmlrpc_encode($r, $this->phpvals_encoding_options));
1176
+ }
1177
+ }
1178
+ }
1179
+ catch(Exception $e)
1180
+ {
1181
+ // (barring errors in the lib) an uncatched exception happened
1182
+ // in the called function, we wrap it in a proper error-response
1183
+ switch($this->exception_handling)
1184
+ {
1185
+ case 2:
1186
+ throw $e;
1187
+ break;
1188
+ case 1:
1189
+ $r = new xmlrpcresp(0, $e->getCode(), $e->getMessage());
1190
+ break;
1191
+ default:
1192
+ $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_error'], $GLOBALS['xmlrpcstr']['server_error']);
1193
+ }
1194
+ }
1195
+ if($this->debug > 2)
1196
+ {
1197
+ // note: restore the error handler we found before calling the
1198
+ // user func, even if it has been changed inside the func itself
1199
+ if($GLOBALS['_xmlrpcs_prev_ehandler'])
1200
+ {
1201
+ set_error_handler($GLOBALS['_xmlrpcs_prev_ehandler']);
1202
+ }
1203
+ else
1204
+ {
1205
+ restore_error_handler();
1206
+ }
1207
+ }
1208
+ return $r;
1209
+ }
1210
+
1211
+ /**
1212
+ * add a string to the 'internal debug message' (separate from 'user debug message')
1213
+ * @param string $strings
1214
+ * @access private
1215
+ */
1216
+ function debugmsg($string)
1217
+ {
1218
+ $this->debug_info .= $string."\n";
1219
+ }
1220
+
1221
+ /**
1222
+ * @access private
1223
+ */
1224
+ function xml_header($charset_encoding='')
1225
+ {
1226
+ if ($charset_encoding != '')
1227
+ {
1228
+ return "<?xml version=\"1.0\" encoding=\"$charset_encoding\"?" . ">\n";
1229
+ }
1230
+ else
1231
+ {
1232
+ return "<?xml version=\"1.0\"?" . ">\n";
1233
+ }
1234
+ }
1235
+
1236
+ /**
1237
+ * A debugging routine: just echoes back the input packet as a string value
1238
+ * DEPRECATED!
1239
+ */
1240
+ function echoInput()
1241
+ {
1242
+ $r=new xmlrpcresp(new xmlrpcval( "'Aha said I: '" . $GLOBALS['HTTP_RAW_POST_DATA'], 'string'));
1243
+ print $r->serialize();
1244
+ }
1245
+ }
1246
+ ?>
lib/KlarnaCheckout/Checkout.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright 2012 Klarna AB
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ *
17
+ * Bootstrap file to include the klarna checkout library
18
+ *
19
+ * PHP version 5.3
20
+ *
21
+ * @category Payment
22
+ * @package Klarna_Checkout
23
+ * @author Klarna <support@klarna.com>
24
+ * @copyright 2012 Klarna AB
25
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
26
+ * @link http://integration.klarna.com/
27
+ */
28
+
29
+ define('KLARNA_CHECKOUT_DIR', dirname(__file__) . '/Checkout');
30
+
31
+ require_once KLARNA_CHECKOUT_DIR . '/ConnectorInterface.php';
32
+ require_once KLARNA_CHECKOUT_DIR . '/ResourceInterface.php';
33
+ require_once KLARNA_CHECKOUT_DIR . '/Connector.php';
34
+ require_once KLARNA_CHECKOUT_DIR . '/BasicConnector.php';
35
+ require_once KLARNA_CHECKOUT_DIR . '/Order.php';
36
+ require_once KLARNA_CHECKOUT_DIR . '/Digest.php';
37
+ require_once KLARNA_CHECKOUT_DIR . '/Exception.php';
38
+ require_once KLARNA_CHECKOUT_DIR . '/ConnectionErrorException.php';
39
+ require_once KLARNA_CHECKOUT_DIR . '/ConnectorException.php';
40
+ require_once KLARNA_CHECKOUT_DIR . '/UserAgent.php';
41
+
42
+ require_once KLARNA_CHECKOUT_DIR . '/HTTP/TransportInterface.php';
43
+ require_once KLARNA_CHECKOUT_DIR . '/HTTP/CURLHandleInterface.php';
44
+ require_once KLARNA_CHECKOUT_DIR . '/HTTP/Request.php';
45
+ require_once KLARNA_CHECKOUT_DIR . '/HTTP/Response.php';
46
+ require_once KLARNA_CHECKOUT_DIR . '/HTTP/Transport.php';
47
+ require_once KLARNA_CHECKOUT_DIR . '/HTTP/CURLTransport.php';
48
+ require_once KLARNA_CHECKOUT_DIR . '/HTTP/CURLHeaders.php';
49
+ require_once KLARNA_CHECKOUT_DIR . '/HTTP/CURLHandle.php';
50
+ require_once KLARNA_CHECKOUT_DIR . '/HTTP/CURLFactory.php';
lib/KlarnaCheckout/Checkout/BasicConnector.php ADDED
@@ -0,0 +1,320 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Copyright 2012 Klarna AB
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ *
18
+ * File containing the Klarna_Checkout_BasicConnector class
19
+ *
20
+ * PHP version 5.3
21
+ *
22
+ * @category Payment
23
+ * @package Klarna_Checkout
24
+ * @author Klarna <support@klarna.com>
25
+ * @copyright 2012 Klarna AB
26
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
27
+ * @link http://integration.klarna.com/
28
+ */
29
+
30
+ /**
31
+ * Basic implementation of the connector interface
32
+ *
33
+ * @category Payment
34
+ * @package Klarna_Checkout
35
+ * @author Rickard D. <rickard.dybeck@klarna.com>
36
+ * @author Christer G. <christer.gustavsson@klarna.com>
37
+ * @author David K. <david.keijser@klarna.com>
38
+ * @copyright 2012 Klarna AB
39
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
40
+ * @link http://integration.klarna.com/
41
+ */
42
+ class Klarna_Checkout_BasicConnector implements Klarna_Checkout_ConnectorInterface
43
+ {
44
+
45
+ /**
46
+ * Klarna_Checkout_HTTP_TransportInterface Implementation
47
+ *
48
+ * @var Klarna_Checkout_HTTP_TransportInterface
49
+ */
50
+ protected $http;
51
+
52
+ /**
53
+ * Digester class
54
+ *
55
+ * @var Klarna_Checkout_Digest
56
+ */
57
+ protected $digester;
58
+
59
+ /**
60
+ * Shared Secret used to sign requests
61
+ *
62
+ * @var string
63
+ */
64
+ private $_secret;
65
+
66
+ /**
67
+ * Create a new Checkout Connector
68
+ *
69
+ * @param Klarna_Checkout_HTTP_TransportInterface $http transport
70
+ * @param Klarna_Checkout_Digest $digester Digest Generator
71
+ * @param string $secret shared secret
72
+ */
73
+ public function __construct(
74
+ Klarna_Checkout_HTTP_TransportInterface $http,
75
+ Klarna_Checkout_Digest $digester,
76
+ $secret
77
+ ) {
78
+ $this->http = $http;
79
+ $this->digester = $digester;
80
+ $this->_secret = $secret;
81
+ }
82
+
83
+ /**
84
+ * Create the user agent identifier to use
85
+ *
86
+ * @return Klarna_Checkout_UserAgent
87
+ */
88
+ protected function userAgent()
89
+ {
90
+ return new Klarna_Checkout_UserAgent();
91
+ }
92
+
93
+ /**
94
+ * Applying the method on the specific resource
95
+ *
96
+ * @param string $method Http methods
97
+ * @param Klarna_Checkout_ResourceInterface $resource resource
98
+ * @param array $options Options
99
+ *
100
+ * @return mixed
101
+ */
102
+ public function apply(
103
+ $method,
104
+ Klarna_Checkout_ResourceInterface $resource,
105
+ array $options = null
106
+ ) {
107
+ switch ($method) {
108
+ case 'GET':
109
+ case 'POST':
110
+ return $this->handle($method, $resource, $options, array());
111
+ default:
112
+ throw new InvalidArgumentException(
113
+ "{$method} is not a valid HTTP method"
114
+ );
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Gets the underlying transport object
120
+ *
121
+ * @return Klarna_Checkout_HTTP_TransportInterface Transport object
122
+ */
123
+ public function getTransport()
124
+ {
125
+ return $this->http;
126
+ }
127
+
128
+ /**
129
+ * Set content (headers, payload) on a request
130
+ *
131
+ * @param Klarna_Checkout_ResourceInterface $resource Klarna Checkout Resource
132
+ * @param string $method HTTP Method
133
+ * @param string $payload Payload to send with the
134
+ * request
135
+ * @param string $url URL for request
136
+ *
137
+ * @return Klarna_Checkout_HTTP_Request
138
+ */
139
+ protected function createRequest(
140
+ Klarna_Checkout_ResourceInterface $resource,
141
+ $method,
142
+ $payload,
143
+ $url
144
+ ) {
145
+ // Generate the digest string
146
+ $digest = $this->digester->create($payload . $this->_secret);
147
+
148
+ $request = $this->http->createRequest($url);
149
+
150
+ $request->setMethod($method);
151
+
152
+ // Set HTTP Headers
153
+ $request->setHeader('User-Agent', (string)$this->userAgent());
154
+ $request->setHeader('Authorization', "Klarna {$digest}");
155
+ $request->setHeader('Accept', $resource->getContentType());
156
+ if (strlen($payload) > 0) {
157
+ $request->setHeader('Content-Type', $resource->getContentType());
158
+ $request->setData($payload);
159
+ }
160
+
161
+ return $request;
162
+ }
163
+
164
+ /**
165
+ * Get the url to use
166
+ *
167
+ * @param Klarna_Checkout_ResourceInterface $resource resource
168
+ * @param array $options Options
169
+ *
170
+ * @return string Url to use for HTTP requests
171
+ */
172
+ protected function getUrl(
173
+ Klarna_Checkout_ResourceInterface $resource, array $options
174
+ ) {
175
+ if (array_key_exists('url', $options)) {
176
+ return $options['url'];
177
+ }
178
+
179
+ return $resource->getLocation();
180
+ }
181
+
182
+ /**
183
+ * Get the data to use
184
+ *
185
+ * @param Klarna_Checkout_ResourceInterface $resource resource
186
+ * @param array $options Options
187
+ *
188
+ * @return array data to use for HTTP requests
189
+ */
190
+ protected function getData(
191
+ Klarna_Checkout_ResourceInterface $resource, array $options
192
+ ) {
193
+ if (array_key_exists('data', $options)) {
194
+ return $options['data'];
195
+ }
196
+
197
+ return $resource->marshal();
198
+ }
199
+
200
+ /**
201
+ * Throw an exception if the server responds with an error code.
202
+ *
203
+ * @param Klarna_Checkout_HTTP_Response $result HTTP Response object
204
+ *
205
+ * @throws Klarna_Checkout_HTTP_Status_Exception
206
+ * @return void
207
+ */
208
+ protected function verifyResponse(Klarna_Checkout_HTTP_Response $result)
209
+ {
210
+ // Error Status Code recieved. Throw an exception.
211
+ if ($result->getStatus() >= 400 && $result->getStatus() <= 599) {
212
+ throw new Klarna_Checkout_ConnectorException(
213
+ $result->getData(), $result->getStatus()
214
+ );
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Act upon the status of a response
220
+ *
221
+ * @param Klarna_Checkout_HTTP_Response $result response from server
222
+ * @param Klarna_Checkout_ResourceInterface $resource associated resource
223
+ * @param array $visited list of visited locations
224
+ *
225
+ * @return Klarna_Checkout_HTTP_Response
226
+ */
227
+ protected function handleResponse(
228
+ Klarna_Checkout_HTTP_Response $result,
229
+ Klarna_Checkout_ResourceInterface $resource,
230
+ array $visited = array()
231
+ ) {
232
+ // Check if we got an Error status code back
233
+ $this->verifyResponse($result);
234
+
235
+ $url = $result->getHeader('Location');
236
+ switch ($result->getStatus()) {
237
+ case 301:
238
+ // Update location and fallthrough
239
+ $resource->setLocation($url);
240
+ case 302:
241
+ // Don't fallthrough for other than GET
242
+ if ($result->getRequest()->getMethod() !== 'GET') {
243
+ break;
244
+ }
245
+ case 303:
246
+ // Detect eternal loops
247
+ if (in_array($url, $visited)) {
248
+ throw new Klarna_Checkout_ConnectorException(
249
+ 'Infinite redirect loop detected.',
250
+ -1
251
+ );
252
+ }
253
+ $visited[] = $url;
254
+ // Follow redirect
255
+ return $this->handle(
256
+ 'GET',
257
+ $resource,
258
+ array('url' => $url),
259
+ $visited
260
+ );
261
+ case 201:
262
+ // Update Location
263
+ $resource->setLocation($url);
264
+ break;
265
+ case 200:
266
+ // Update Data on resource
267
+ $json = json_decode($result->getData(), true);
268
+ if ($json === null) {
269
+ throw new Klarna_Checkout_ConnectorException(
270
+ 'Bad format on response content.',
271
+ -2
272
+ );
273
+ }
274
+ $resource->parse($json);
275
+ }
276
+
277
+ return $result;
278
+ }
279
+
280
+ /**
281
+ * Perform a HTTP Call on the supplied resource using the wanted method.
282
+ *
283
+ * @param string $method HTTP Method
284
+ * @param Klarna_Checkout_ResourceInterface $resource Klarna Order
285
+ * @param array $options Options
286
+ * @param array $visited list of visited locations
287
+ *
288
+ * @throws Klarna_Checkout_Exception if 4xx or 5xx response code.
289
+ * @return Result object containing status code and payload
290
+ */
291
+ protected function handle(
292
+ $method,
293
+ Klarna_Checkout_ResourceInterface $resource,
294
+ array $options = null,
295
+ array $visited = array()
296
+ ) {
297
+ if ($options === null) {
298
+ $options = array();
299
+ }
300
+
301
+ // Define the target URL
302
+ $url = $this->getUrl($resource, $options);
303
+
304
+ // Set a payload if it is a POST call.
305
+ $payload = '';
306
+ if ($method === 'POST') {
307
+ $payload = json_encode($this->getData($resource, $options));
308
+ }
309
+
310
+ // Create a HTTP Request object
311
+ $request = $this->createRequest($resource, $method, $payload, $url);
312
+ // $this->_setContent($request, $payload, $method);
313
+
314
+ // Execute the HTTP Request
315
+ $result = $this->http->send($request);
316
+
317
+ // Handle statuses appropriately.
318
+ return $this->handleResponse($result, $resource, $visited);
319
+ }
320
+ }
lib/KlarnaCheckout/Checkout/ConnectionErrorException.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright 2012 Klarna AB
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ *
17
+ * File containing the Klarna_Checkout_ConnectionErrorException class
18
+ *
19
+ * PHP version 5.3
20
+ *
21
+ * @category Payment
22
+ * @package Klarna_Checkout
23
+ * @author Klarna <support@klarna.com>
24
+ * @copyright 2012 Klarna AB
25
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
26
+ * @link http://integration.klarna.com/
27
+ */
28
+
29
+ /**
30
+ * Connection exception
31
+ *
32
+ * @category Payment
33
+ * @package Klarna_Checkout
34
+ * @author Rickard D. <rickard.dybeck@klarna.com>
35
+ * @author Christer G. <christer.gustavsson@klarna.com>
36
+ * @copyright 2012 Klarna AB
37
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
38
+ * @link http://integration.klarna.com/
39
+ */
40
+ class Klarna_Checkout_ConnectionErrorException extends Klarna_Checkout_Exception
41
+ {
42
+
43
+ }
lib/KlarnaCheckout/Checkout/Connector.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright 2012 Klarna AB
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ *
17
+ * File containing the Klarna_Checkout_Connector facade class
18
+ *
19
+ * PHP version 5.3
20
+ *
21
+ * @category Payment
22
+ * @package Klarna_Checkout
23
+ * @author Klarna <support@klarna.com>
24
+ * @copyright 2012 Klarna AB
25
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
26
+ * @link http://integration.klarna.com/
27
+ */
28
+
29
+ /**
30
+ * Connector factory
31
+ *
32
+ * @category Payment
33
+ * @package Klarna_Checkout
34
+ * @author Rickard D. <rickard.dybeck@klarna.com>
35
+ * @author Christer G. <christer.gustavsson@klarna.com>
36
+ * @author David K. <david.keijser@klarna.com>
37
+ * @copyright 2012 Klarna AB
38
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
39
+ * @link http://integration.klarna.com/
40
+ */
41
+ class Klarna_Checkout_Connector
42
+ {
43
+ /**
44
+ * Create a new Checkout Connector
45
+ *
46
+ * @param string $secret string used to sign requests
47
+ *
48
+ * @return Klarna_Checkout_ConnectorInterface
49
+ */
50
+ public static function create($secret)
51
+ {
52
+ return new Klarna_Checkout_BasicConnector(
53
+ Klarna_Checkout_HTTP_Transport::create(),
54
+ new Klarna_Checkout_Digest,
55
+ $secret
56
+ );
57
+ }
58
+ }
lib/KlarnaCheckout/Checkout/ConnectorException.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright 2012 Klarna AB
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ *
17
+ * File containing the Klarna_Checkout_ConnectorException class
18
+ *
19
+ * PHP version 5.3
20
+ *
21
+ * @category Payment
22
+ * @package Klarna_Checkout
23
+ * @author Klarna <support@klarna.com>
24
+ * @copyright 2012 Klarna AB
25
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
26
+ * @link http://integration.klarna.com/
27
+ */
28
+
29
+ /**
30
+ * Connector exception
31
+ *
32
+ * @category Payment
33
+ * @package Klarna_Checkout
34
+ * @author Rickard D. <rickard.dybeck@klarna.com>
35
+ * @author David K. <david.keijser@klarna.com>
36
+ * @copyright 2012 Klarna AB
37
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
38
+ * @link http://integration.klarna.com/
39
+ */
40
+ class Klarna_Checkout_ConnectorException extends Klarna_Checkout_Exception
41
+ {
42
+
43
+ }
lib/KlarnaCheckout/Checkout/ConnectorInterface.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Copyright 2012 Klarna AB
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ *
18
+ * File containing the Klarna_Checkout_Connector interface
19
+ *
20
+ * PHP version 5.3
21
+ *
22
+ * @category Payment
23
+ * @package Klarna_Checkout
24
+ * @author Klarna <support@klarna.com>
25
+ * @copyright 2012 Klarna AB
26
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
27
+ * @link http://integration.klarna.com/
28
+ */
29
+
30
+ /**
31
+ * Interface for the resource object
32
+ *
33
+ * @category Payment
34
+ * @package Klarna_Checkout
35
+ * @author Majid G. <majid.garmaroudi@klarna.com>
36
+ * @author David K. <david.keijser@klarna.com>
37
+ * @copyright 2012 Klarna AB
38
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
39
+ * @link http://integration.klarna.com/
40
+ */
41
+ interface Klarna_Checkout_ConnectorInterface
42
+ {
43
+ /**
44
+ * Applying the method on the specific resource
45
+ *
46
+ * @param string $method Http methods
47
+ * @param Klarna_Checkout_ResourceInterface $resource resource
48
+ * @param array $options Options
49
+ *
50
+ * @return void
51
+ */
52
+ public function apply(
53
+ $method,
54
+ Klarna_Checkout_ResourceInterface $resource,
55
+ array $options = null
56
+ );
57
+
58
+ /**
59
+ * Gets the underlying transport object
60
+ *
61
+ * @return Klarna_Checkout_HTTP_TransportInterface Transport object
62
+ */
63
+ public function getTransport();
64
+ }
lib/KlarnaCheckout/Checkout/Digest.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Copyright 2012 Klarna AB
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ *
18
+ * File containing the Klarna_Checkout_Digester class
19
+ *
20
+ * PHP version 5.3
21
+ *
22
+ * @category Payment
23
+ * @package Klarna_Checkout
24
+ * @author Klarna <support@klarna.com>
25
+ * @copyright 2012 Klarna AB
26
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
27
+ * @link http://integration.klarna.com/
28
+ */
29
+
30
+ /**
31
+ * Class to handle the digesting of hash string
32
+ *
33
+ * @category Payment
34
+ * @package Klarna_Checkout
35
+ * @author Rickard D. <rickard.dybeck@klarna.com>
36
+ * @author Christer G. <christer.gustavsson@klarna.com>
37
+ * @copyright 2012 Klarna AB
38
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
39
+ * @link http://integration.klarna.com/
40
+ */
41
+ class Klarna_Checkout_Digest
42
+ {
43
+ /**
44
+ * create a digest from a supplied string
45
+ *
46
+ * @param string $digestString string to hash
47
+ *
48
+ * @return string Base64 and SHA256 hashed string
49
+ */
50
+ public function create($digestString)
51
+ {
52
+ return base64_encode(hash('sha256', $digestString, true));
53
+ }
54
+ }
lib/KlarnaCheckout/Checkout/Exception.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Copyright 2012 Klarna AB
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ *
18
+ * File containing the Klarna_Checkout_Exception class
19
+ *
20
+ * PHP version 5.3
21
+ *
22
+ * @category Payment
23
+ * @package Klarna_Checkout
24
+ * @author Klarna <support@klarna.com>
25
+ * @copyright 2012 Klarna AB
26
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
27
+ * @link http://integration.klarna.com/
28
+ */
29
+
30
+ /**
31
+ * Basic exception class
32
+ *
33
+ * @category Payment
34
+ * @package Klarna_Checkout
35
+ * @author Majid G. <majid.garmaroudi@klarna.com>
36
+ * @author David K. <david.keijser@klarna.com>
37
+ * @copyright 2012 Klarna AB
38
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
39
+ * @link http://integration.klarna.com/
40
+ */
41
+ class Klarna_Checkout_Exception extends Exception
42
+ {
43
+
44
+ }
lib/KlarnaCheckout/Checkout/HTTP/CURLFactory.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright 2012 Klarna AB
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ *
17
+ * File containing the PHPUnit Klarna_HTTP_CURLTest test case
18
+ *
19
+ * PHP version 5.3
20
+ *
21
+ * @category Payment
22
+ * @package Payment_Klarna
23
+ * @subpackage Unit_Tests
24
+ * @author Klarna <support@klarna.com>
25
+ * @copyright 2012 Klarna AB
26
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
27
+ * @link http://integration.klarna.com/
28
+ */
29
+
30
+ /**
31
+ * Factory of cURL handles
32
+ *
33
+ * @category Payment
34
+ * @package Payment_Klarna
35
+ * @subpackage Unit_Tests
36
+ * @author Klarna <support@klarna.com>
37
+ * @copyright 2012 Klarna AB
38
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
39
+ * @link http://integration.klarna.com/
40
+ */
41
+ class Klarna_Checkout_HTTP_CURLFactory
42
+ {
43
+ /**
44
+ * Create a new cURL handle
45
+ *
46
+ * @return Klarna_Checkout_HTTP_CURLHandle
47
+ */
48
+ public function handle()
49
+ {
50
+ return new Klarna_Checkout_HTTP_CURLHandle();
51
+ }
52
+ }
lib/KlarnaCheckout/Checkout/HTTP/CURLHandle.php ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright 2012 Klarna AB
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ *
17
+ * File containing the Klarna_Checkout_HTTP_CURLHeaders class
18
+ *
19
+ * PHP version 5.3
20
+ *
21
+ * @category Payment
22
+ * @package Payment_Klarna
23
+ * @subpackage HTTP
24
+ * @author Klarna <support@klarna.com>
25
+ * @copyright 2012 Klarna AB
26
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
27
+ * @link http://integration.klarna.com/
28
+ */
29
+
30
+ /**
31
+ * A wrapper around the cURL functions
32
+ *
33
+ * @category Payment
34
+ * @package Payment_Klarna
35
+ * @subpackage HTTP
36
+ * @author David K. <david.keijser@klarna.com>
37
+ * @copyright 2012 Klarna AB
38
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
39
+ * @link http://integration.klarna.com/
40
+ */
41
+ class Klarna_Checkout_HTTP_CURLHandle
42
+ implements Klarna_Checkout_HTTP_CURLHandleInterface
43
+ {
44
+ /**
45
+ * cURL handle
46
+ * @var resource
47
+ */
48
+ private $_handle = null;
49
+
50
+ /**
51
+ * Create a new cURL handle
52
+ */
53
+ public function __construct()
54
+ {
55
+ if (!extension_loaded('curl')) {
56
+ throw new RuntimeException(
57
+ 'cURL extension is requred.'
58
+ );
59
+ }
60
+ $this->_handle = curl_init();
61
+ }
62
+
63
+ /**
64
+ * Set an option for the cURL transfer
65
+ *
66
+ * @param int $name option the set
67
+ * @param mixed $value the value to be set on option
68
+ *
69
+ * @return void
70
+ */
71
+ public function setOption($name, $value)
72
+ {
73
+ curl_setopt($this->_handle, $name, $value);
74
+ }
75
+
76
+ /**
77
+ * Perform the cURL session
78
+ *
79
+ * @return mixed response
80
+ */
81
+ public function execute()
82
+ {
83
+ return curl_exec($this->_handle);
84
+ }
85
+
86
+ /**
87
+ * Get information regarding this transfer
88
+ *
89
+ * @return array
90
+ */
91
+ public function getInfo()
92
+ {
93
+ return curl_getinfo($this->_handle);
94
+ }
95
+
96
+ /**
97
+ * Close the cURL session
98
+ *
99
+ * @return void
100
+ */
101
+ public function close()
102
+ {
103
+ curl_close($this->_handle);
104
+ }
105
+ }
lib/KlarnaCheckout/Checkout/HTTP/CURLHandleInterface.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright 2012 Klarna AB
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ *
17
+ * File containing the Klarna_Checkout_HTTP_CURLHeaders class
18
+ *
19
+ * PHP version 5.3
20
+ *
21
+ * @category Payment
22
+ * @package Payment_Klarna
23
+ * @subpackage HTTP
24
+ * @author Klarna <support@klarna.com>
25
+ * @copyright 2012 Klarna AB
26
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
27
+ * @link http://integration.klarna.com/
28
+ */
29
+
30
+ /**
31
+ * Defines a cURL handle interface
32
+ *
33
+ * @category Payment
34
+ * @package Payment_Klarna
35
+ * @subpackage HTTP
36
+ * @author David K. <david.keijser@klarna.com>
37
+ * @copyright 2012 Klarna AB
38
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
39
+ * @link http://integration.klarna.com/
40
+ */
41
+ interface Klarna_Checkout_HTTP_CURLHandleInterface
42
+ {
43
+ /**
44
+ * Set an option for the cURL transfer
45
+ *
46
+ * @param int $name option the set
47
+ * @param mixed $value the value to be set on option
48
+ *
49
+ * @return void
50
+ */
51
+ public function setOption($name, $value);
52
+
53
+ /**
54
+ * Perform the cURL session
55
+ *
56
+ * @return mixed response
57
+ */
58
+ public function execute();
59
+
60
+ /**
61
+ * Get information regarding this transfer
62
+ *
63
+ * @return array
64
+ */
65
+ public function getInfo();
66
+
67
+ /**
68
+ * Close the cURL session
69
+ *
70
+ * @return void
71
+ */
72
+ public function close();
73
+ }
lib/KlarnaCheckout/Checkout/HTTP/CURLHeaders.php ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Copyright 2012 Klarna AB
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ *
18
+ * File containing the Klarna_Checkout_HTTP_CURLHeaders class
19
+ *
20
+ * PHP version 5.3
21
+ *
22
+ * @category Payment
23
+ * @package Payment_Klarna
24
+ * @subpackage HTTP
25
+ * @author Klarna <support@klarna.com>
26
+ * @copyright 2012 Klarna AB
27
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
28
+ * @link http://integration.klarna.com/
29
+ */
30
+
31
+ /**
32
+ * A simple class handling the header callback for cURL.
33
+ *
34
+ * @category Payment
35
+ * @package Payment_Klarna
36
+ * @subpackage HTTP
37
+ * @author Klarna <support@klarna.com>
38
+ * @copyright 2012 Klarna AB
39
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
40
+ * @link http://integration.klarna.com/
41
+ */
42
+ class Klarna_Checkout_HTTP_CURLHeaders
43
+ {
44
+ /**
45
+ * Response headers, cleared for each request.
46
+ *
47
+ * @var array
48
+ */
49
+ protected $headers;
50
+
51
+ /**
52
+ * Initializes a new instance of the HTTP cURL class.
53
+ */
54
+ public function __construct()
55
+ {
56
+ $this->headers = array();
57
+ }
58
+
59
+ /**
60
+ * Callback method to handle custom headers.
61
+ *
62
+ * @param resource $curl the cURL resource.
63
+ * @param string $header the header data.
64
+ *
65
+ * @return int the number of bytes handled.
66
+ */
67
+ public function processHeader($curl, $header)
68
+ {
69
+ $curl = null;
70
+ //TODO replace with regexp, e.g. /^([^:]+):([^:]*)$/ ?
71
+ $pos = strpos($header, ':');
72
+ // Didn't find a colon.
73
+ if ($pos === false) {
74
+ // Not real header, abort.
75
+ return strlen($header);
76
+ }
77
+
78
+ $key = substr($header, 0, $pos);
79
+ $value = trim(substr($header, $pos+1));
80
+
81
+ $this->headers[$key] = trim($value);
82
+
83
+ return strlen($header);
84
+ }
85
+
86
+ /**
87
+ * Gets the accumulated headers.
88
+ *
89
+ * @return array
90
+ */
91
+ public function getHeaders()
92
+ {
93
+ return $this->headers;
94
+ }
95
+ }
lib/KlarnaCheckout/Checkout/HTTP/CURLTransport.php ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Copyright 2012 Klarna AB
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ *
18
+ * File containing the Klarna_Checkout_HTTP_CURLTransport class
19
+ *
20
+ * PHP version 5.3
21
+ *
22
+ * @category Payment
23
+ * @package Payment_Klarna
24
+ * @subpackage HTTP
25
+ * @author Klarna <support@klarna.com>
26
+ * @copyright 2012 Klarna AB AB
27
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
28
+ * @link http://integration.klarna.com/
29
+ */
30
+
31
+ /**
32
+ * Klarna HTTP transport implementation for cURL
33
+ *
34
+ * @category Payment
35
+ * @package Payment_Klarna
36
+ * @subpackage HTTP
37
+ * @author Klarna <support@klarna.com>
38
+ * @copyright 2012 Klarna AB
39
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
40
+ * @link http://integration.klarna.com/
41
+ */
42
+ class Klarna_Checkout_HTTP_CURLTransport
43
+ implements Klarna_Checkout_HTTP_TransportInterface
44
+ {
45
+ const DEFAULT_TIMEOUT = 10;
46
+
47
+ /**
48
+ * @var Klarna_Checkout_HTTP_CURLFactory
49
+ */
50
+ protected $curl;
51
+
52
+ /**
53
+ * Number of seconds before the connection times out.
54
+ *
55
+ * @var int
56
+ */
57
+ protected $timeout;
58
+
59
+ /**
60
+ * Initializes a new instance of the HTTP cURL class.
61
+ *
62
+ * @param Klarna_Checkout_HTTP_CURLFactory $curl factory to for curl handles
63
+ */
64
+ public function __construct(Klarna_Checkout_HTTP_CURLFactory $curl)
65
+ {
66
+ $this->curl = $curl;
67
+ $this->timeout = self::DEFAULT_TIMEOUT;
68
+ }
69
+
70
+ /**
71
+ * Sets the number of seconds until a connection times out.
72
+ *
73
+ * @param int $timeout number of seconds
74
+ *
75
+ * @return void
76
+ */
77
+ public function setTimeout($timeout)
78
+ {
79
+ $this->timeout = intval($timeout);
80
+ }
81
+
82
+ /**
83
+ * Gets the number of seconds before the connection times out.
84
+ *
85
+ * @return int timeout in number of seconds
86
+ */
87
+ public function getTimeout()
88
+ {
89
+ return $this->timeout;
90
+ }
91
+
92
+ /**
93
+ * Performs a HTTP request.
94
+ *
95
+ * @param Klarna_Checkout_HTTP_Request $request the HTTP request to send.
96
+ *
97
+ * @throws RuntimeException Thrown if a cURL handle cannot
98
+ * be initialized.
99
+ * @throws Klarna_Checkout_ConnectionErrorException Thrown for unspecified
100
+ * network or hardware issues.
101
+ * @return Klarna_Checkout_HTTP_Response
102
+ */
103
+ public function send(Klarna_Checkout_HTTP_Request $request)
104
+ {
105
+ $curl = $this->curl->handle();
106
+ if ($curl === false) {
107
+ throw new RuntimeException(
108
+ 'Failed to initialize a HTTP handle.'
109
+ );
110
+ }
111
+
112
+ $url = $request->getURL();
113
+ $curl->setOption(CURLOPT_URL, $url);
114
+
115
+ $method = $request->getMethod();
116
+ if ($method === 'POST') {
117
+ $curl->setOption(CURLOPT_POST, true);
118
+ $curl->setOption(CURLOPT_POSTFIELDS, $request->getData());
119
+ }
120
+
121
+ // Convert headers to cURL format.
122
+ $requestHeaders = array();
123
+ foreach ($request->getHeaders() as $key => $value) {
124
+ $requestHeaders[] = $key . ': ' . $value;
125
+ }
126
+
127
+ $curl->setOption(CURLOPT_HTTPHEADER, $requestHeaders);
128
+
129
+ $curl->setOption(CURLOPT_RETURNTRANSFER, true);
130
+ $curl->setOption(CURLOPT_CONNECTTIMEOUT, $this->timeout);
131
+
132
+ $curlHeaders = new Klarna_Checkout_HTTP_CURLHeaders();
133
+ $curl->setOption(
134
+ CURLOPT_HEADERFUNCTION,
135
+ array(&$curlHeaders, 'processHeader')
136
+ );
137
+
138
+ // TODO remove me when real cert is in place
139
+ $curl->setOption(CURLOPT_SSL_VERIFYPEER, false);
140
+
141
+ $payload = $curl->execute();
142
+ $info = $curl->getInfo();
143
+
144
+ $curl->close();
145
+
146
+ /*
147
+ * A failure occured if:
148
+ * payload is false (e.g. HTTP timeout?).
149
+ * info is false, then it has no HTTP status code.
150
+ */
151
+ if ($payload === false || $info === false) {
152
+ throw new Klarna_Checkout_ConnectionErrorException(
153
+ "Connection to '{$url}' failed."
154
+ );
155
+ }
156
+
157
+ $headers = $curlHeaders->getHeaders();
158
+
159
+ // Convert Content-Type into a normal header
160
+ $headers['Content-Type'] = $info['content_type'];
161
+
162
+ $response = new Klarna_Checkout_HTTP_Response(
163
+ $request, $headers, intval($info['http_code']), strval($payload)
164
+ );
165
+
166
+ return $response;
167
+ }
168
+
169
+ /**
170
+ * Creates a HTTP request object.
171
+ *
172
+ * @param string $url the request URL.
173
+ *
174
+ * @throws InvalidArgumentException If the specified argument
175
+ * is not of type string.
176
+ * @return Klarna_Checkout_HTTP_Request
177
+ */
178
+ public function createRequest($url)
179
+ {
180
+ return new Klarna_Checkout_HTTP_Request($url);
181
+ }
182
+ }
lib/KlarnaCheckout/Checkout/HTTP/Request.php ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Copyright 2012 Klarna AB
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ *
18
+ * File containing the Klarna_Checkout_HTTP_Request class
19
+ *
20
+ * PHP version 5.3
21
+ *
22
+ * @category Payment
23
+ * @package Payment_Klarna
24
+ * @subpackage HTTP
25
+ * @author Klarna <support@klarna.com>
26
+ * @copyright 2012 Klarna AB
27
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
28
+ * @link http://integration.klarna.com/
29
+ */
30
+
31
+ /**
32
+ * Klarna HTTP Request class
33
+ *
34
+ * @category Payment
35
+ * @package Payment_Klarna
36
+ * @subpackage HTTP
37
+ * @author Klarna <support@klarna.com>
38
+ * @copyright 2012 Klarna AB
39
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
40
+ * @link http://integration.klarna.com/
41
+ */
42
+ class Klarna_Checkout_HTTP_Request
43
+ {
44
+ /**
45
+ * @var string
46
+ */
47
+ protected $url;
48
+
49
+ /**
50
+ * @var string
51
+ */
52
+ protected $method;
53
+
54
+ /**
55
+ * @var array
56
+ */
57
+ protected $headers;
58
+
59
+ /**
60
+ * @var string
61
+ */
62
+ protected $data;
63
+
64
+ /**
65
+ * Initializes a new instance of the HTTP request class.
66
+ *
67
+ * @param string $url the request URL.
68
+ *
69
+ * @throws InvalidArgumentException If the specified argument
70
+ * is not of type string.
71
+ */
72
+ public function __construct($url)
73
+ {
74
+ $this->url = $url;
75
+ $this->method = 'GET';
76
+ $this->headers = array();
77
+ $this->data = '';
78
+ }
79
+
80
+ /**
81
+ * Gets the request URL.
82
+ *
83
+ * @return string the request URL.
84
+ */
85
+ public function getURL()
86
+ {
87
+ return $this->url;
88
+ }
89
+
90
+ /**
91
+ * Specifies the HTTP method used for the request.
92
+ *
93
+ * @param string $method a HTTP method.
94
+ *
95
+ * @throws InvalidArgumentException If the specified argument
96
+ * is not of type string.
97
+ * @return void
98
+ */
99
+ public function setMethod($method)
100
+ {
101
+ $this->method = strtoupper($method);
102
+ }
103
+
104
+ /**
105
+ * Gets the HTTP method used for the request.
106
+ *
107
+ * @return string a HTTP method
108
+ */
109
+ public function getMethod()
110
+ {
111
+ return $this->method;
112
+ }
113
+
114
+ /**
115
+ * Specifies a header for the request.
116
+ *
117
+ * @param string $name the header name
118
+ * @param mixed $value the header value
119
+ *
120
+ * @throws InvalidArgumentException If the argument name is not of type
121
+ * string or an empty string.
122
+ * @return void
123
+ */
124
+ public function setHeader($name, $value)
125
+ {
126
+ $this->headers[$name] = strval($value);
127
+ }
128
+
129
+ /**
130
+ * Gets a specific header for the request.
131
+ *
132
+ * @param string $name the header name
133
+ *
134
+ * @throws InvalidArgumentException If the specified argument
135
+ * is not of type string.
136
+ * @return string|null the header value or null if it doesn't exist
137
+ */
138
+ public function getHeader($name)
139
+ {
140
+ if (!array_key_exists($name, $this->headers)) {
141
+ return null;
142
+ }
143
+
144
+ return $this->headers[$name];
145
+ }
146
+
147
+ /**
148
+ * Gets the headers specified for the request.
149
+ *
150
+ * @return array
151
+ */
152
+ public function getHeaders()
153
+ {
154
+ return $this->headers;
155
+ }
156
+
157
+ /**
158
+ * Sets the data (payload) for the request.
159
+ *
160
+ * \code
161
+ * $request->setMethod('POST');
162
+ * $request->setData('some data');
163
+ * \endcode
164
+ *
165
+ * @param string $data the request payload
166
+ *
167
+ * @throws InvalidArgumentException If the specified argument
168
+ * is not of type string.
169
+ * @return void
170
+ */
171
+ public function setData($data)
172
+ {
173
+ $this->data = $data;
174
+ }
175
+
176
+ /**
177
+ * Gets the data (payload) for the request.
178
+ *
179
+ * @return string the request payload
180
+ */
181
+ public function getData()
182
+ {
183
+ return $this->data;
184
+ }
185
+ }
lib/KlarnaCheckout/Checkout/HTTP/Response.php ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Copyright 2012 Klarna AB
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ *
18
+ * File containing the Klarna_HTTP_Response class
19
+ *
20
+ * PHP version 5.3
21
+ *
22
+ * @category Payment
23
+ * @package Payment_Klarna
24
+ * @subpackage HTTP
25
+ * @author Klarna <support@klarna.com>
26
+ * @copyright 2012 Klarna AB
27
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
28
+ * @link http://integration.klarna.com/
29
+ */
30
+
31
+ /**
32
+ * Klarna HTTP Response class
33
+ *
34
+ * @category Payment
35
+ * @package Payment_Klarna
36
+ * @subpackage HTTP
37
+ * @author Klarna <support@klarna.com>
38
+ * @copyright 2012 Klarna AB
39
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
40
+ * @link http://integration.klarna.com/
41
+ */
42
+ class Klarna_Checkout_HTTP_Response
43
+ {
44
+ /**
45
+ * @var int
46
+ */
47
+ protected $status;
48
+
49
+ /**
50
+ * @var Klarna_Checkout_HTTP_Request
51
+ */
52
+ protected $request;
53
+
54
+ /**
55
+ * @var array
56
+ */
57
+ protected $headers;
58
+
59
+ /**
60
+ * @var string
61
+ */
62
+ protected $data;
63
+
64
+ /**
65
+ * Initializes a new instance of the HTTP response class.
66
+ *
67
+ * @param Klarna_Checkout_HTTP_Request $request the origin request.
68
+ * @param array $headers the response HTTP headers.
69
+ * @param int $status the HTTP status code.
70
+ * @param string $data the response payload.
71
+ */
72
+ public function __construct(
73
+ Klarna_Checkout_HTTP_Request $request, array $headers, $status, $data
74
+ ) {
75
+ $this->request = $request;
76
+ $this->headers = array();
77
+ foreach ($headers as $key => $value) {
78
+ $this->headers[strtolower($key)] = $value;
79
+ }
80
+ $this->status = $status;
81
+ $this->data = $data;
82
+ }
83
+
84
+ /**
85
+ * Gets the HTTP status code.
86
+ *
87
+ * @return int HTTP status code.
88
+ */
89
+ public function getStatus()
90
+ {
91
+ return $this->status;
92
+ }
93
+
94
+ /**
95
+ * Gets the HTTP request this response originated from.
96
+ *
97
+ * @return Klarna_Checkout_HTTP_Request
98
+ */
99
+ public function getRequest()
100
+ {
101
+ return $this->request;
102
+ }
103
+
104
+ /**
105
+ * Gets specified HTTP header.
106
+ *
107
+ * @param string $name the header name.
108
+ *
109
+ * @throws InvalidArgumentException If the specified argument
110
+ * is not of type string.
111
+ * @return string|null Null if header doesn't exist, else header value.
112
+ */
113
+ public function getHeader($name)
114
+ {
115
+ $name = strtolower($name);
116
+ if (!array_key_exists($name, $this->headers)) {
117
+ return null;
118
+ }
119
+
120
+ return $this->headers[$name];
121
+ }
122
+
123
+ /**
124
+ * Gets the headers specified for the response.
125
+ *
126
+ * @return array
127
+ */
128
+ public function getHeaders()
129
+ {
130
+ return $this->headers;
131
+ }
132
+
133
+ /**
134
+ * Gets the data (payload) for the response.
135
+ *
136
+ * @return string the response payload.
137
+ */
138
+ public function getData()
139
+ {
140
+ return $this->data;
141
+ }
142
+ }
lib/KlarnaCheckout/Checkout/HTTP/Transport.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright 2012 Klarna AB
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ *
17
+ * File containing the Transport factory
18
+ *
19
+ * PHP version 5.3
20
+ *
21
+ * @category Payment
22
+ * @package Payment_Klarna
23
+ * @subpackage Unit_Tests
24
+ * @author Klarna <support@klarna.com>
25
+ * @copyright 2012 Klarna AB
26
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
27
+ * @link http://integration.klarna.com/
28
+ */
29
+
30
+ /**
31
+ * Factory of HTTP Transport
32
+ *
33
+ * @category Payment
34
+ * @package Payment_Klarna
35
+ * @subpackage Unit_Tests
36
+ * @author David K. <david.keijser@klarna.com>
37
+ * @copyright 2012 Klarna AB
38
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
39
+ * @link http://integration.klarna.com/
40
+ */
41
+ class Klarna_Checkout_HTTP_Transport
42
+ {
43
+ /**
44
+ * Create a new transport instance
45
+ *
46
+ * @return Klarna_Checkout_HTTP_TransportInterface
47
+ */
48
+ public static function create()
49
+ {
50
+ return new Klarna_Checkout_HTTP_CURLTransport(
51
+ new Klarna_Checkout_HTTP_CURLFactory
52
+ );
53
+ }
54
+ }
lib/KlarnaCheckout/Checkout/HTTP/TransportInterface.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Copyright 2012 Klarna AB
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ *
18
+ * File containing the Klarna_Checkout_HTTP_TransportInterface interface
19
+ *
20
+ * PHP version 5.3
21
+ *
22
+ * @category Payment
23
+ * @package Payment_Klarna
24
+ * @subpackage Interfaces
25
+ * @author Klarna <support@klarna.com>
26
+ * @copyright 2012 Klarna AB
27
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
28
+ * @link http://integration.klarna.com/
29
+ */
30
+
31
+ /**
32
+ * Interface for a Klarna HTTP Transport object
33
+ *
34
+ * @category Payment
35
+ * @package Payment_Klarna
36
+ * @subpackage Interfaces
37
+ * @author Klarna <support@klarna.com>
38
+ * @copyright 2012 Klarna AB
39
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
40
+ * @link http://integration.klarna.com/
41
+ */
42
+ interface Klarna_Checkout_HTTP_TransportInterface
43
+ {
44
+ /**
45
+ * Specifies the number of seconds before the connection times out.
46
+ *
47
+ * @param int $timeout number of seconds
48
+ *
49
+ * @throws InvalidArgumentException If the specified argument
50
+ * is not of type integer.
51
+ * @return void
52
+ */
53
+ public function setTimeout($timeout);
54
+
55
+ /**
56
+ * Gets the number of seconds before the connection times out.
57
+ *
58
+ * @return int timeout in number of seconds
59
+ */
60
+ public function getTimeout();
61
+
62
+ /**
63
+ * Performs a HTTP request.
64
+ *
65
+ * @param Klarna_Checkout_HTTP_Request $request the HTTP request to send.
66
+ *
67
+ * @throws Klarna_Checkout_ConnectionErrorException Thrown for unspecified
68
+ * network or hardware issues.
69
+ * @return Klarna_Checkout_HTTP_Response
70
+ */
71
+ public function send(Klarna_Checkout_HTTP_Request $request);
72
+
73
+ /**
74
+ * Creates a HTTP request object.
75
+ *
76
+ * @param string $url the request URL.
77
+ *
78
+ * @throws InvalidArgumentException If the specified argument
79
+ * is not of type string.
80
+ * @return Klarna_Checkout_HTTP_Request
81
+ */
82
+ public function createRequest($url);
83
+ }
lib/KlarnaCheckout/Checkout/Order.php ADDED
@@ -0,0 +1,259 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Copyright 2012 Klarna AB
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ *
18
+ * File containing the Klarna_Checkout_Order class
19
+ *
20
+ * PHP version 5.3
21
+ *
22
+ * @category Payment
23
+ * @package Klarna_Checkout
24
+ * @author Klarna <support@klarna.com>
25
+ * @copyright 2012 Klarna AB
26
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
27
+ * @link http://integration.klarna.com/
28
+ */
29
+
30
+ /**
31
+ * Implementation of the order resource
32
+ *
33
+ * @category Payment
34
+ * @package Klarna_Checkout
35
+ * @author Majid G. <majid.garmaroudi@klarna.com>
36
+ * @author David K. <david.keijser@klarna.com>
37
+ * @copyright 2012 Klarna AB
38
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
39
+ * @link http://integration.klarna.com/
40
+ */
41
+ class Klarna_Checkout_Order
42
+ implements Klarna_Checkout_ResourceInterface, ArrayAccess
43
+ {
44
+ /**
45
+ * Base URI that is used to create order resources
46
+ *
47
+ * @var string
48
+ */
49
+ public static $baseUri = null;
50
+
51
+ /**
52
+ * Content Type to use
53
+ *
54
+ * @var string
55
+ */
56
+ public static $contentType = null;
57
+
58
+ /**
59
+ * URI of remote resource
60
+ *
61
+ * @var string
62
+ */
63
+ private $_location;
64
+
65
+ /**
66
+ * Order data
67
+ *
68
+ * @var array
69
+ */
70
+ private $_data = array();
71
+
72
+ /**
73
+ * Connector
74
+ *
75
+ * @var Klarna_Checkout_ConnectorInterface
76
+ */
77
+ protected $connector;
78
+
79
+ /**
80
+ * Create a new Order object
81
+ *
82
+ * @param Klarna_Checkout_ConnectorInterface $connector connector to use
83
+ * @param string $uri uri of resource
84
+ *
85
+ * @return void
86
+ */
87
+ public function __construct(
88
+ Klarna_Checkout_ConnectorInterface $connector,
89
+ $uri = null
90
+ ) {
91
+ $this->connector = $connector;
92
+ if ($uri !== null) {
93
+ $this->setLocation($uri);
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Get the URL of the resource
99
+ *
100
+ * @return string
101
+ */
102
+ public function getLocation()
103
+ {
104
+ return $this->_location;
105
+ }
106
+
107
+ /**
108
+ * Set the URL of the resource
109
+ *
110
+ * @param string $location URL of the resource
111
+ *
112
+ * @return void
113
+ */
114
+ public function setLocation($location)
115
+ {
116
+ $this->_location = strval($location);
117
+ }
118
+
119
+ /**
120
+ * Return content type of the resource
121
+ *
122
+ * @return string Content type
123
+ */
124
+ public function getContentType()
125
+ {
126
+ return self::$contentType;
127
+ }
128
+
129
+ /**
130
+ * Replace resource data
131
+ *
132
+ * @param array $data data
133
+ *
134
+ * @return void
135
+ */
136
+ public function parse(array $data)
137
+ {
138
+ $this->_data = $data;
139
+ }
140
+
141
+ /**
142
+ * Basic representation of the object
143
+ *
144
+ * @return array Data
145
+ */
146
+ public function marshal()
147
+ {
148
+ return $this->_data;
149
+ }
150
+
151
+ /**
152
+ * Create a new order
153
+ *
154
+ * @param array $data data to initialise order resource with
155
+ *
156
+ * @return void
157
+ */
158
+ public function create(array $data)
159
+ {
160
+ $options = array(
161
+ 'url' => self::$baseUri,
162
+ 'data' => $data
163
+ );
164
+
165
+ $this->connector->apply('POST', $this, $options);
166
+ }
167
+
168
+ /**
169
+ * Fetch order data
170
+ *
171
+ * @return void
172
+ */
173
+ public function fetch()
174
+ {
175
+ $options = array(
176
+ 'url' => $this->_location
177
+ );
178
+ $this->connector->apply('GET', $this, $options);
179
+ }
180
+
181
+ /**
182
+ * Update order data
183
+ *
184
+ * @param array $data data to update order resource with
185
+ *
186
+ * @return void
187
+ */
188
+ public function update(
189
+ array $data
190
+ ) {
191
+ $options = array(
192
+ 'url' => $this->_location,
193
+ 'data' => $data
194
+ );
195
+ $this->connector->apply('POST', $this, $options);
196
+ }
197
+
198
+ /**
199
+ * Get value of a key
200
+ *
201
+ * @param string $key Key
202
+ *
203
+ * @return mixed data
204
+ */
205
+ public function offsetGet($key)
206
+ {
207
+ if (!is_string($key)) {
208
+ throw new InvalidArgumentException("Key must be string");
209
+ }
210
+
211
+ return $this->_data[$key];
212
+ }
213
+
214
+ /**
215
+ * Set value of a key
216
+ *
217
+ * @param string $key Key
218
+ * @param mixed $value Value of the key
219
+ *
220
+ * @return void
221
+ */
222
+ public function offsetSet($key, $value)
223
+ {
224
+ if (!is_string($key)) {
225
+ throw new InvalidArgumentException("Key must be string");
226
+ }
227
+
228
+ $value = print_r($value, true);
229
+ throw new RuntimeException(
230
+ "Use update function to change values. trying to set $key to $value"
231
+ );
232
+ }
233
+
234
+ /**
235
+ * Check if a key exists in the resource
236
+ *
237
+ * @param string $key key
238
+ *
239
+ * @return boolean
240
+ */
241
+ public function offsetExists($key)
242
+ {
243
+ return array_key_exists($key, $this->_data);
244
+ }
245
+
246
+ /**
247
+ * Unset the value of a key
248
+ *
249
+ * @param string $key key
250
+ *
251
+ * @return void
252
+ */
253
+ public function offsetUnset($key)
254
+ {
255
+ throw new RuntimeException(
256
+ "unset of fields not supported. trying to unset $key"
257
+ );
258
+ }
259
+ }
lib/KlarnaCheckout/Checkout/ResourceInterface.php ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Copyright 2012 Klarna AB
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ *
18
+ * File containing the Klarna_Checkout_Resource interface
19
+ *
20
+ * PHP version 5.3
21
+ *
22
+ * @category Payment
23
+ * @package Klarna_Checkout
24
+ * @author Klarna <support@klarna.com>
25
+ * @copyright 2012 Klarna AB
26
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
27
+ * @link http://integration.klarna.com/
28
+ */
29
+
30
+ /**
31
+ * Interface for the resource object
32
+ *
33
+ * @category Payment
34
+ * @package Klarna_Checkout
35
+ * @author Majid G. <majid.garmaroudi@klarna.com>
36
+ * @author David K. <david.keijser@klarna.com>
37
+ * @copyright 2012 Klarna AB
38
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
39
+ * @link http://integration.klarna.com/
40
+ */
41
+ interface Klarna_Checkout_ResourceInterface
42
+ {
43
+ /**
44
+ * Get the URL of the resource
45
+ *
46
+ * @return string
47
+ */
48
+ public function getLocation();
49
+
50
+ /**
51
+ * Set the URL of the resource
52
+ *
53
+ * @param string $location URL of the resource
54
+ *
55
+ * @return void
56
+ */
57
+ public function setLocation($location);
58
+
59
+ /**
60
+ * Return content type of the resource
61
+ *
62
+ * @return string Content type
63
+ */
64
+ public function getContentType();
65
+
66
+ /**
67
+ * Update resource with the new data
68
+ *
69
+ * @param array $data data
70
+ *
71
+ * @return void
72
+ */
73
+ public function parse(array $data);
74
+
75
+ /**
76
+ * Basic representation of the object
77
+ *
78
+ * @return array data
79
+ */
80
+ public function marshal();
81
+ }
lib/KlarnaCheckout/Checkout/UserAgent.php ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Copyright 2012 Klarna AB
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ *
18
+ * File containing the Klarna_Checkout_UserAgent class
19
+ *
20
+ * PHP version 5.3
21
+ *
22
+ * @category Payment
23
+ * @package Klarna_Checkout
24
+ * @author Klarna <support@klarna.com>
25
+ * @copyright 2012 Klarna AB
26
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
27
+ * @link http://integration.klarna.com/
28
+ */
29
+
30
+ /**
31
+ * UserAgent string builder
32
+ *
33
+ * @category Payment
34
+ * @package Klarna_Checkout
35
+ * @author David K. <david.keijser@klarna.com>
36
+ * @copyright 2012 Klarna AB
37
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
38
+ * @link http://integration.klarna.com/
39
+ */
40
+ class Klarna_Checkout_UserAgent
41
+ {
42
+ /**
43
+ * Components of the user-agent
44
+ *
45
+ * @var array
46
+ */
47
+ private $_fields;
48
+
49
+ /**
50
+ * Initialise user-agent with default fields
51
+ */
52
+ public function __construct()
53
+ {
54
+
55
+ $this->_fields = array(
56
+ 'Library' => array(
57
+ 'name' => 'Klarna.ApiWrapper',
58
+ 'version' => '1.1.0',
59
+ ),
60
+ 'OS' => array(
61
+ 'name' => php_uname('s'),
62
+ 'version' => php_uname('r')
63
+ ),
64
+ 'Language' => array(
65
+ 'name' => 'PHP',
66
+ 'version' => phpversion()
67
+ ),
68
+ 'Module' => array(
69
+ 'name' => 'KlarnaCheckout.MagentoModule',
70
+ 'version' => '1.0.3'
71
+ )
72
+ );
73
+ }
74
+
75
+ /**
76
+ * Add a new field to the user agent
77
+ *
78
+ * @param string $field Name of field
79
+ * @param array $data data array with name, version and possibly options
80
+ *
81
+ * @return void
82
+ */
83
+ public function addField($field, array $data)
84
+ {
85
+ if (array_key_exists($field, $this->_fields)) {
86
+ throw new Klarna_Checkout_Exception(
87
+ "Unable to redefine field {$field}"
88
+ );
89
+ }
90
+ $this->_fields[$field] = $data;
91
+ }
92
+
93
+ /**
94
+ * Serialise fields to a user agent string
95
+ *
96
+ * @return string
97
+ */
98
+ public function __toString()
99
+ {
100
+ $parts = array();
101
+ foreach ($this->_fields as $key => $value) {
102
+ $parts[] = "$key/{$value['name']}_{$value['version']}";
103
+ if (array_key_exists('options', $value)) {
104
+ $parts[] = '(' . implode(' ; ', $value['options']) . ')';
105
+ }
106
+ }
107
+ return implode(' ', $parts);
108
+ }
109
+ }
package.xml ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>Avenla_KlarnaCheckout</name>
4
+ <version>1.0.3</version>
5
+ <stability>stable</stability>
6
+ <license uri="http://productdownloads.avenla.com/magento-modules/klarna-checkout/">Custom license by Avenla Oy</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>Klarna Checkout payment module</summary>
10
+ <description>http://productdownloads.avenla.com/magento-modules/klarna-checkout/&#xD;
11
+ &#xD;
12
+ For questions and support - klarna-support@avenla.com&#xD;
13
+ </description>
14
+ <notes>Klarna Checkout module</notes>
15
+ <authors><author><name>Avenla Oy</name><user>Avenla</user><email>info@avenla.fi</email></author></authors>
16
+ <date>2014-06-13</date>
17
+ <time>08:21:38</time>
18
+ <contents><target name="magecommunity"><dir name="Avenla"><dir name="KlarnaCheckout"><dir name="Block"><dir name="Adminhtml"><dir name="System"><dir name="Config"><dir name="Fieldset"><file name="Info.php" hash="3d0fd774a6116513e4d413a078e80848"/></dir></dir></dir></dir><dir name="KCO"><file name="Confirmation.php" hash="61e58e00070fa76b9f067619e0db5ba5"/><file name="Info.php" hash="2af961abb2eb0609b84f8da5281332ad"/></dir><file name="KCO.php" hash="1abf22f4408a872572c1f8babed730d6"/></dir><dir name="Helper"><file name="Api.php" hash="3e2446b1520479046f82ad895ab81165"/><file name="Data.php" hash="7cdfce7ea47880243c4ff7be1fbb42c5"/></dir><dir name="Model"><file name="Api.php" hash="0b72aba4740d21bfd566404a31451c27"/><file name="Config.php" hash="1d6e519fcfa81f4691b8390b4140db11"/><file name="KCO.php" hash="692cb0acaa1d8cfa5a4d972e730fb5f8"/><file name="Observer.php" hash="39eeedd245eb2907d8a472b9769787fb"/><file name="Order.php" hash="86df514f6d5fbfc0b7baf002896a406f"/><dir name="Source"><file name="Countries.php" hash="0440b3ecc5528cf42dbb937eb7d9c0cb"/><file name="Orderlocale.php" hash="a88769cf211991c70a0f51185bc53fb0"/><file name="Servermode.php" hash="df4420b3831b1a1e1ec04daa53711a4d"/><file name="Taxclass.php" hash="ff40a1943352031fd7971688b805be05"/></dir></dir><dir name="controllers"><file name="KCOController.php" hash="a79ea804166678c8ade66142d0979b67"/></dir><dir name="etc"><file name="config.xml" hash="721392e523c5e73dba78b24202ca88b3"/><file name="system.xml" hash="2e027dca202ecae47542dafc1e50f0fe"/></dir></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="template"><dir name="KCO"><file name="info.phtml" hash="ad24867ca2930f515a0905a5db6f8d15"/><dir name="system"><dir name="config"><dir name="fieldset"><file name="info.phtml" hash="36a21983fcc4816b7c8a6b8fe9f2e78c"/></dir></dir></dir></dir></dir></dir></dir></dir><dir name="frontend"><dir name="base"><dir name="default"><dir name="template"><dir name="KCO"><file name="KCO.phtml" hash="27f81d18d4cf0b593f85f65cce93f6ba"/><dir name="cart"><file name="crosssell.phtml" hash="e7f78cb99e14ab680b794524b8666812"/><file name="shipping.phtml" hash="d9870590d2d9198de22b9b995c33a8a5"/></dir><file name="cart.phtml" hash="a23ccf697e854e3b541b5a19a8c1e808"/><file name="info.phtml" hash="a87d8ee70c92a10445fa864a63d042ba"/><file name="link.phtml" hash="7dadce1567975585dddd51bc904b95fa"/><dir name="onepage"><file name="link.phtml" hash="0e8ddf5a7e9a0f6ccd7b274170103ac7"/></dir></dir></dir><dir name="layout"><file name="KCO.xml" hash="2ffde82bc08dc156b4645adb779beda5"/></dir></dir></dir></dir></target><target name="magelib"><dir name="Klarna"><file name="Country.php" hash="40360c3964fc6193e7fb922845eb419b"/><file name="Currency.php" hash="a709407428d86521cc0775cbeb281523"/><file name="Encoding.php" hash="f8f8e744303ff7ce7f4b226331e7bdcf"/><file name="Exceptions.php" hash="933b2811e910da817743766cdde8ffeb"/><file name="Flags.php" hash="b407a1373adf5f172bef3e3d01e81cdb"/><file name="Klarna.php" hash="dc69f2c10e9b65999057bc04246c3fe8"/><file name="Language.php" hash="8e40aed0dece8f0a3fbe07e37451af16"/><dir name="checkout"><file name="checkouthtml.intf.php" hash="81e0740254585f2af67895df28093d35"/><file name="threatmetrix.class.php" hash="6ce1bfded90b6b3bb329176ff4c7acac"/></dir><dir name="examples"><file name="activate.php" hash="85a84a8eaeec830949551dfd98fe0236"/><file name="activateInvoice.php" hash="cfac2ed57d0c79679fb35ca723eaffab"/><file name="activatePart.php" hash="b46475c2d9f5b326ff2cd65586a1de6e"/><file name="activateReservation.php" hash="bd9b0e5717eaa32ebf6b5d31ad66e5d4"/><file name="addTransaction.php" hash="6426b7965de733bb17ddc7f74ee3949f"/><file name="calc_monthly_cost.php" hash="30e78c4d40db82643396f9289749a898"/><file name="cancelReservation.php" hash="ae8d00e19c671f6cfe8454a5bb590ab1"/><file name="changeReservation.php" hash="43d9486d29326a8c33a4fda466612149"/><file name="checkOrderStatus.php" hash="bef76f120b3e486790d78b50f4101701"/><file name="creditInvoice.php" hash="8edca171c9f7be2a3c15b23ff166048e"/><file name="creditPart.php" hash="5497b9307da7cccadbf4a6c0dc943876"/><file name="deleteInvoice.php" hash="6d2a6e53c64e7a1aacb79c775a46da5b"/><file name="emailInvoice.php" hash="764fbd5fab4f52dacd767a3f7ac17234"/><file name="fetchPClasses.php" hash="aac08dc4d9cfece6ac9e9a9c4e7b20cc"/><file name="getAddresses.php" hash="ebd79224ea702c92e99a407a53d42da0"/><file name="getPClasses.php" hash="8e444c98a7c63b82512af1f4ce35ac65"/><file name="invoiceAddress.php" hash="97475403b1db4264c0add60930380434"/><file name="invoiceAmount.php" hash="2f5dc326549b179cf99c27518b715e88"/><file name="invoicePartAmount.php" hash="87bf68e855bfdd4f6ba3547091104c97"/><file name="reserveAmount.php" hash="1a97ef5c39f5a2c6d97660504cbbfa10"/><file name="reserveOCR.php" hash="9349b5ea9807060cba4b5f3fcd14369f"/><file name="returnAmount.php" hash="02caca4d76b6d055d504023c770961a4"/><file name="sendInvoice.php" hash="9139eef562746840d09f9d9d5a465c21"/><file name="splitReservation.php" hash="0e09421de645d97c3b74485d51edacfd"/><file name="update.php" hash="3e3561165f4c6727731afe451b0f87d6"/><file name="updateChargeAmount.php" hash="34de26a7f6a089bdb18f67eae1f903c7"/><file name="updateGoodsQty.php" hash="e4f9ee36f56b47924ba35cda500cf13e"/><file name="updateOrderNo.php" hash="cdd594a386072f7bffbf05d2ecdcb9b7"/></dir><file name="klarnaaddr.php" hash="b090d4b0a819fdb7cc58815a8643de18"/><file name="klarnacalc.php" hash="3ab728bf3889240b881f282122fd25fd"/><file name="klarnaconfig.php" hash="a3a4011dccda60fcf3567f56254699ff"/><file name="klarnapclass.php" hash="26a8fd1df0806788f6719cb894787f76"/><dir name="pclasses"><file name="jsonstorage.class.php" hash="e59e526d9dcc17d1ca92ded188750672"/><file name="mysqlstorage.class.php" hash="896a5cd4edf461ef517e0bb2e328707b"/><file name="sqlstorage.class.php" hash="bb7ba4359e7e69424c9f0ecdafc0c9ae"/><file name="storage.intf.php" hash="26db57484b6db3d717c0150ffaa87b19"/><file name="xmlstorage.class.php" hash="4f13939bf8d73f2724f858dd17f219de"/></dir><dir name="transport"><dir name="xmlrpc-3.0.0.beta"><dir name="lib"><file name="xmlrpc.inc" hash="5a74ea2a831648febc9b2c8f809b252c"/><file name="xmlrpc_wrappers.inc" hash="5aa00141ead09fc5498d9a3c9fcab888"/><file name="xmlrpcs.inc" hash="158b97bda79333e9b40793d876b6e98f"/></dir></dir></dir></dir><dir name="KlarnaCheckout"><dir name="Checkout"><file name="BasicConnector.php" hash="42afc7257646279bbc9239e5e427a76d"/><file name="ConnectionErrorException.php" hash="352ae3f5bbb18c04f0a9d3ca1be30faf"/><file name="Connector.php" hash="767cd6181abcd144ea8b2d1738f0a617"/><file name="ConnectorException.php" hash="fe4017579c8757798e018fb0755496c0"/><file name="ConnectorInterface.php" hash="0ebfd5610fec6ddce7b7fd0f45f3d1d6"/><file name="Digest.php" hash="d7c2ea4ff1f22c8919685aad7e4e9c8b"/><file name="Exception.php" hash="31a95e8212f01d149265f75e20b31bcb"/><dir name="HTTP"><file name="CURLFactory.php" hash="591493c487797a80fd76f4f80ca598d0"/><file name="CURLHandle.php" hash="2d58ba553ffddafdcc9c80bea0c2c5ee"/><file name="CURLHandleInterface.php" hash="df5e3ddf82deb09164c75249c7235756"/><file name="CURLHeaders.php" hash="2a900efb8591defaf9230368403c975f"/><file name="CURLTransport.php" hash="c35a9910a04007372c2d6673f4657b89"/><file name="Request.php" hash="180f04ffd522b713b4261087a7e60c68"/><file name="Response.php" hash="86993580c2a11b14bab4af6328caa6de"/><file name="Transport.php" hash="4aa96d5086e105d79b0f97593fbe5d9b"/><file name="TransportInterface.php" hash="3d6aab36479741136c9dee1bbcf3914e"/></dir><file name="Order.php" hash="568242a47b838ae9abf8438a4bc7301e"/><file name="ResourceInterface.php" hash="55fc925edc0f69a89f2d79e348152a2c"/><file name="UserAgent.php" hash="a553b9c2cef0503fe328e56b7bb9a4fe"/></dir><file name="Checkout.php" hash="6e16bb237ecfc54f086a0d533a8e69e3"/></dir></target><target name="mageskin"><dir name="frontend"><dir name="base"><dir name="default"><dir name="KCO"><file name="dropdown.png" hash="a408e177ff3130bdb4bb491935700df4"/><file name="kco.css" hash="3fcde3265ba399760925e3033e42e106"/></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Avenla_KlarnaCheckout.xml" hash="22b47fd5cc2c12370457a51ffff752d6"/></dir></target><target name="magelocale"><dir name="fi_FI"><file name="Avenla_KlarnaCheckout.csv" hash="d226e6331799155c6ceb05e13fa0b388"/></dir></target></contents>
19
+ <compatible/>
20
+ <dependencies><required><php><min>5.2.16</min><max>5.5.0</max></php></required></dependencies>
21
+ </package>
skin/frontend/base/default/KCO/dropdown.png ADDED
Binary file
skin/frontend/base/default/KCO/kco.css ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * This file is released under a custom license by Avenla Oy.
3
+ * All rights reserved
4
+ *
5
+ * License and more information can be found at http://productdownloads.avenla.com/magento-modules/klarna-checkout/
6
+ * For questions and support - klarna-support@avenla.com
7
+ *
8
+ * @category Avenla
9
+ * @package Avenla_KlarnaCheckout
10
+ * @copyright Copyright (c) Avenla Oy
11
+ * @link http://www.avenla.fi
12
+ */
13
+
14
+ /**
15
+ * Avenla KlarnaCheckout
16
+ *
17
+ * @category Avenla
18
+ * @package Avenla_KlarnaCheckout
19
+ */
20
+
21
+ #klarnaWrapper {position: relative;}
22
+ .klarnaLink, #klarnaMsg{
23
+ background-color:#FFF;
24
+ text-align:center;
25
+ border:1px solid #1e7ec8;
26
+ display:block;
27
+ padding:20px 5px;
28
+ margin-bottom:10px;
29
+ border-radius:3px;
30
+ }
31
+
32
+ #klarnaMsg{margin-bottom:0px;}
33
+ .klarna_link_text{margin:10px 0px 0px 0px;font-weight:bold;}
34
+ #klarnaMsg h2{
35
+ color:#1e7ec8;
36
+ font-weight:bold;
37
+ margin:0px;
38
+ }
39
+ .klarnaLink:hover{border:1px solid #8CCBFC;}
40
+ #klarnaWrapper{position:relative;}
41
+ .klarna_alert{font-weight:bold; color:red;}
42
+ #klarnaOverlay{
43
+ background-color:#FFF;
44
+ position:absolute;
45
+ height:100%;
46
+ width:100%;
47
+ opacity:0.5;
48
+ }
49
+
50
+ .cart .payments ul li {
51
+ display:block;
52
+ float:left;
53
+ border-top-right-radius:8px;
54
+ border-top-left-radius:8px;
55
+ margin-right:5px;
56
+ border-right: 1px solid #d0cbc1;
57
+ padding:10px;
58
+ border-bottom:0px;
59
+ background: #eeeeee;
60
+ }
61
+
62
+ .cart .payments ul{display:block;margin:0;}
63
+ .cart .totals_row{margin: 0 0 18px;}
64
+ .cart .payments ul li a, .responsive-payments ul li a{
65
+ display:block;
66
+ text-decoration:none;
67
+ font: 18px "Helvetica Neue",Helvetica,Arial,sans-serif;
68
+ font-weight: 700;
69
+ color: #31393E;
70
+ }
71
+ .cart .payments ul li.active a{color:#fff;}
72
+ .cart .payments ul li.active{
73
+ position:relative;
74
+ z-index:20;
75
+ background: rgb(178,225,255);
76
+ background: -moz-linear-gradient(top, rgba(178,225,255,1) 0%, rgba(102,182,252,1) 100%);
77
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(178,225,255,1)), color-stop(100%,rgba(102,182,252,1)));
78
+ background: -webkit-linear-gradient(top, rgba(178,225,255,1) 0%,rgba(102,182,252,1) 100%);
79
+ background: -o-linear-gradient(top, rgba(178,225,255,1) 0%,rgba(102,182,252,1) 100%);
80
+ background: -ms-linear-gradient(top, rgba(178,225,255,1) 0%,rgba(102,182,252,1) 100%);
81
+ background: linear-gradient(to bottom, rgba(178,225,255,1) 0%,rgba(102,182,252,1) 100%);
82
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#b2e1ff', endColorstr='#66b6fc',GradientType=0 );
83
+ margin-bottom: -1px;
84
+ margin-top: 1px;
85
+ }
86
+
87
+ .cart .payments ul li:hover{
88
+ background: rgb(222,239,255);
89
+ background: -moz-linear-gradient(top, rgba(222,239,255,1) 0%, rgba(152,190,222,1) 100%);
90
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(222,239,255,1)), color-stop(100%,rgba(152,190,222,1)));
91
+ background: -webkit-linear-gradient(top, rgba(222,239,255,1) 0%,rgba(152,190,222,1) 100%);
92
+ background: -o-linear-gradient(top, rgba(222,239,255,1) 0%,rgba(152,190,222,1) 100%);
93
+ background: -ms-linear-gradient(top, rgba(222,239,255,1) 0%,rgba(152,190,222,1) 100%);
94
+ background: linear-gradient(to bottom, rgba(222,239,255,1) 0%,rgba(152,190,222,1) 100%);
95
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#deefff', endColorstr='#98bede',GradientType=0 );
96
+ }
97
+
98
+ .payment_container{
99
+ padding:5px;
100
+ overflow: hidden;
101
+ clear:both;
102
+ margin-top:0px;
103
+ position: relative;
104
+ -webkit-border-radius: 2px;
105
+ -moz-border-radius: 2px;
106
+ border: 1px solid #d0cbc1;
107
+ }
108
+
109
+ .sp-methods{
110
+ background:none !important;
111
+ margin:0px !important;
112
+ padding:0px !important;
113
+ }
114
+ .cart .crosssell{margin: 0 0 18px;}
115
+ .cart .cart-collaterals .col2-set, .cart .totals{width:auto;float:none;}
116
+ .cart .cart-collaterals .col2-set .col-2{width: 48.5%;}
117
+ .cart .crosssell{overflow:hidden;}
118
+ .cart .crosssell li.item{float:left;margin-right:10px;}
119
+ .paymentMenuContainer {position:relative;}
120
+ .responsive-payments ul{overflow:hidden;border:1px solid #ddd;border-radius:3px 0px 0px 3px;margin-bottom:10px !important;}
121
+ .responsive-payments .paymentnavi-toggle{display:block;position:absolute;right:0;height:40px;width:40px;background:url('dropdown.png') no-repeat;}
122
+ .responsive-payments .paymentnavi-toggle:hover{cursor:pointer;}
123
+ .responsive-payments ul li{
124
+ padding:8px 40px 8px 0px !important;
125
+ background:#fff;
126
+ float:none !important;
127
+ text-align:center;
128
+ border-bottom:1px solid #ddd;
129
+ display:none;
130
+ transition:0.4s;
131
+ }
132
+ .responsive-payments ul li:hover {box-shadow:inset 0px 0px 5px 5px rgba(0,0,0,0.025);}
133
+ .responsive-payments ul li.active{display:block !important;}
134
+ .klarna-part-payment{margin-top:10px;}