PageRenderTime 54ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/www/shop/engine/Shopware/Plugins/Default/Frontend/PigmbhKlarnaPayment/api/klarnacalc.php

https://bitbucket.org/weberlars/sot-shopware
PHP | 672 lines | 281 code | 60 blank | 331 comment | 70 complexity | a4b88322d8d059606320bf647437acea MD5 | raw file
Possible License(s): AGPL-3.0, MIT, BSD-3-Clause, LGPL-2.1, LGPL-3.0
  1. <?php
  2. /**
  3. * Copyright 2010 KLARNA AB. All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without modification, are
  6. * permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this list of
  9. * conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice, this list
  12. * of conditions and the following disclaimer in the documentation and/or other materials
  13. * provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY KLARNA AB "AS IS" AND ANY EXPRESS OR IMPLIED
  16. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  17. * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KLARNA AB OR
  18. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  19. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  20. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  21. * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  22. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  23. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24. *
  25. * The views and conclusions contained in the software and documentation are those of the
  26. * authors and should not be interpreted as representing official policies, either expressed
  27. * or implied, of KLARNA AB.
  28. *
  29. * @package KlarnaAPI
  30. */
  31. /**
  32. * KlarnaCalc provides methods to calculate part payment functions.
  33. *
  34. * All rates are yearly rates, but they are calculated monthly. So
  35. * a rate of 9 % is used 0.75% monthly. The first is the one we specify
  36. * to the customers, and the second one is the one added each month to
  37. * the account. The IRR uses the same notation.
  38. *
  39. * The APR is however calculated by taking the monthly rate and raising
  40. * it to the 12 power. This is according to the EU law, and will give
  41. * very large numbers if the $pval is small compared to the $fee and
  42. * the amount of months you repay is small as well.
  43. *
  44. * All functions work in discrete mode, and the time interval is the
  45. * mythical evenly divided month. There is no way to calculate APR in
  46. * days without using integrals and other hairy math. So don't try.
  47. * The amount of days between actual purchase and the first bill can
  48. * of course vary between 28 and 61 days, but all calculations in this
  49. * class assume this time is exactly and that is ok since this will only
  50. * overestimate the APR and all examples in EU law uses whole months as well.
  51. *
  52. * @package KlarnaAPI
  53. * @version 2.1.2
  54. * @since 2011-09-13
  55. * @link http://integration.klarna.com/
  56. * @copyright Copyright (c) 2010 Klarna AB (http://klarna.com)
  57. */
  58. class KlarnaCalc {
  59. /**
  60. * This constant tells the irr function when to stop.
  61. * If the calculation error is lower than this the calculation is done.
  62. *
  63. * @var float
  64. */
  65. protected static $accuracy = 0.01;
  66. /**
  67. * Calculates the midpoint between two points. Used by divide and conquer.
  68. *
  69. * @param float $a
  70. * @param float $b
  71. * @return float
  72. */
  73. private static function midpoint($a, $b) {
  74. return (($a+$b)/2);
  75. }
  76. /**
  77. * npv - Net Present Value
  78. * Calculates the difference between the initial loan to the customer
  79. * and the individual payments adjusted for the inverse of the interest
  80. * rate. The variable we are searching for is $rate and if $pval,
  81. * $payarray and $rate is perfectly balanced this function returns 0.0.
  82. *
  83. * @param float $pval initial loan to customer (in any currency)
  84. * @param array $payarray array of monthly payments from the customer
  85. * @param float $rate interest rate per year in %
  86. * @param int $fromdayone do we count interest from the first day yes(1)/no(0).
  87. * @return float
  88. */
  89. private static function npv($pval, $payarray, $rate, $fromdayone) {
  90. $month = $fromdayone;
  91. foreach($payarray as $payment) {
  92. $pval -= $payment / pow (1 + $rate/(12*100.0), $month++);
  93. }
  94. return ($pval);
  95. }
  96. /**
  97. * This function uses divide and conquer to numerically find the IRR,
  98. * Internal Rate of Return. It starts of by trying a low of 0% and a
  99. * high of 100%. If this isn't enough it will double the interval up
  100. * to 1000000%. Note that this is insanely high, and if you try to convert
  101. * an IRR that high to an APR you will get even more insane values,
  102. * so feed this function good data.
  103. *
  104. * Return values: float irr if it was possible to find a rate that gets
  105. * npv closer to 0 than $accuracy.
  106. * int -1 The sum of the payarray is less than the lent
  107. * amount, $pval. Hellooooooo. Impossible.
  108. * int -2 the IRR is way to high, giving up.
  109. *
  110. * This algorithm works in logarithmic time no matter what inputs you give
  111. * and it will come to a good answer within ~30 steps.
  112. *
  113. * @param float $pval initial loan to customer (in any currency)
  114. * @param array $payarray array of monthly payments from the customer
  115. * @param int $fromdayone do we count interest from the first day yes(1)/no(0).
  116. * @return float
  117. */
  118. private static function irr($pval, $payarray, $fromdayone) {
  119. $low = 0.0;
  120. $high = 100.0;
  121. $lowval = self::npv($pval, $payarray, $low, $fromdayone);
  122. $highval = self::npv($pval, $payarray, $high, $fromdayone);
  123. // The sum of $payarray is smaller than $pval, impossible!
  124. if($lowval > 0.0) {
  125. return (-1);
  126. }
  127. // Standard divide and conquer.
  128. do {
  129. $mid = self::midpoint($low, $high);
  130. $midval = self::npv($pval, $payarray, $mid, $fromdayone);
  131. if(abs($midval) < self::$accuracy) {
  132. //we are close enough
  133. return ($mid);
  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. }
  142. else if($midval >= 0.0) {
  143. // irr is between low and mid
  144. $high = $mid;
  145. $highval = $midval;
  146. }
  147. else {
  148. // irr is between mid and high
  149. $low = $mid;
  150. $lowval = $midval;
  151. }
  152. } while ($high < 1000000);
  153. // bad input, insanely high interest. APR will be INSANER!
  154. return (-2);
  155. }
  156. /**
  157. * IRR is not the same thing as APR, Annual Percentage Rate. The
  158. * IRR is per time period, i.e. 1 month, and the APR is per year,
  159. * and note that that you need to raise to the power of 12, not
  160. * mutliply by 12.
  161. *
  162. * This function turns an IRR into an APR.
  163. *
  164. * If you feed it a value of 100%, yes the APR will be millions!
  165. * If you feed it a value of 9%, it will be 9.3806%.
  166. * That is the nature of this math and you can check the wiki
  167. * page for APR for more info.
  168. *
  169. * @param float $irr Internal Rate of Return, expressed yearly, in %
  170. * @return float Annual Percentage Rate, in %
  171. */
  172. private static function irr2apr($irr) {
  173. return (100 * (pow (1 + $irr / (12 * 100.0), 12) - 1));
  174. }
  175. /**
  176. * This is a simplified model of how our paccengine works if
  177. * a client always pays their bills. It adds interest and fees
  178. * and checks minimum payments. It will run until the value
  179. * of the account reaches 0, and return an array of all the
  180. * individual payments. Months is the amount of months to run
  181. * the simulation. Important! Don't feed it too few months or
  182. * the whole loan won't be paid off, but the other functions
  183. * should handle this correctly.
  184. *
  185. * Giving it too many months has no bad effects, or negative
  186. * amount of months which means run forever, but it will stop
  187. * as soon as the account is paid in full.
  188. *
  189. * Depending if the account is a base account or not, the
  190. * payment has to be 1/24 of the capital amount.
  191. *
  192. * The payment has to be at least $minpay, unless the capital
  193. * amount + interest + fee is less than $minpay; in that case
  194. * that amount is paid and the function returns since the client
  195. * no longer owes any money.
  196. *
  197. * @param float $pval initial loan to customer (in any currency)
  198. * @param float $rate interest rate per year in %
  199. * @param float $fee monthly invoice fee
  200. * @param float $minpay minimum monthly payment allowed for this country.
  201. * @param float $payment payment the client to pay each month
  202. * @param int $months amount of months to run (-1 => infinity)
  203. * @param boolean $base is it a base account?
  204. * @return array An array of monthly payments for the customer.
  205. */
  206. private static function fulpacc($pval, $rate, $fee, $minpay, $payment, $months, $base) {
  207. $bal = $pval;
  208. $payarray = array();
  209. while(($months != 0) && ($bal > self::$accuracy)) {
  210. $interest = $bal * $rate / (100.0 * 12);
  211. $newbal = $bal + $interest + $fee;
  212. if($minpay >= $newbal || $payment >= $newbal) {
  213. $payarray[] = $newbal;
  214. return $payarray;
  215. }
  216. $newpay = max($payment, $minpay);
  217. if($base) {
  218. $newpay = max($newpay, $bal/24.0 + $fee + $interest);
  219. }
  220. $bal = $newbal - $newpay;
  221. $payarray[] = $newpay;
  222. $months -= 1;
  223. }
  224. return $payarray;
  225. }
  226. /**
  227. * Calculates how much you have to pay each month if you want to
  228. * pay exactly the same amount each month. The interesting input
  229. * is the amount of $months.
  230. *
  231. * It does not include the fee so add that later.
  232. *
  233. * Return value: monthly payment.
  234. *
  235. * @param float $pval principal value
  236. * @param int $months months to pay of in
  237. * @param float $rate interest rate in % as before
  238. * @return float monthly payment
  239. */
  240. private static function annuity($pval, $months, $rate) {
  241. if($months == 0) {
  242. return $pval;
  243. }
  244. if($rate == 0) {
  245. return $pval/$months;
  246. }
  247. $p = $rate / (100.0*12);
  248. return $pval * $p / (1 - pow((1+$p), -$months));
  249. }
  250. /**
  251. * How many months does it take to pay off a loan if I pay
  252. * exactly $monthly each month? It might actually go faster
  253. * if you hit the minimum payments, but this function returns
  254. * the longest amount of months.
  255. *
  256. * This function _does_ not include the fee, so remove the fee
  257. * from the monthly before sending it into this function.
  258. *
  259. * Return values: float $months
  260. * int -1 you are not paying more than
  261. * the interest. infinity
  262. * int -2 $fromdayone has to be 0 or 1
  263. *
  264. * $fromdayone should be 0 for pay_in_X_months since the interest
  265. * won't be applied on the first invoice. In all other cases use 1.
  266. *
  267. * @param float $pval principal value
  268. * @param float $monthly payment/month (-fee)
  269. * @param float $rate interest rate in %
  270. * @param int $fromdayone do we count interest from day one? [0, 1]
  271. * @return float months it takes (round it up)
  272. */
  273. private static function fixed($pval, $monthly, $rate, $fromdayone) {
  274. $p = $rate / (100.0*12);
  275. $f = 1 + $p;
  276. if($fromdayone == 0) {
  277. if( $f < $pval * $p / $monthly ) {
  278. return -1;
  279. }
  280. // this might be wrong. check it.
  281. // it seems to give the right output.
  282. return 1 - log($f - $pval * $p / $monthly) / log($f);
  283. }
  284. else if($fromdayone == 1) {
  285. if(1.0 < $pval * $p / $monthly ) {
  286. return -1;
  287. }
  288. return -log(1.0 - $pval * $p / $monthly) / log($f);
  289. }
  290. else {
  291. return -2;
  292. }
  293. }
  294. /**
  295. * Calculate the APR for an annuity given the following inputs.
  296. *
  297. * If you give it bad inputs, it will return negative values.
  298. *
  299. * @param float $pval principal value
  300. * @param int $months months to pay off in
  301. * @param float $rate interest rate in % as before
  302. * @param float $fee monthly fee
  303. * @param float $minpay minimum payment per month
  304. * @return float APR in %
  305. */
  306. private static function apr_annuity($pval, $months, $rate, $fee, $minpay) {
  307. $payment = self::annuity($pval, $months, $rate) + $fee;
  308. if($payment < 0) {
  309. return $payment;
  310. }
  311. $payarray = self::fulpacc($pval, $rate, $fee, $minpay, $payment, $months, false);
  312. $apr = self::irr2apr(self::irr($pval, $payarray, 1));
  313. return $apr;
  314. }
  315. /**
  316. * Calculate the APR given a fixed payment each month.
  317. *
  318. * If you give it bad inputs, it will return negative values.
  319. *
  320. * @param float $pval principal value
  321. * @param float $payment monthly payment for client
  322. * @param float $rate interest rate in % as before
  323. * @param float $fee monthly fee
  324. * @param float $minpay minimum payment per month
  325. * @return float APR in %
  326. */
  327. private static function apr_fixed($pval, $payment, $rate, $fee, $minpay) {
  328. $months = self::fixed($pval, $payment-$fee, $rate, 1);
  329. if($months < 0) {
  330. return $months;
  331. }
  332. $months = ceil($months);
  333. $payarray = self::fulpacc($pval, $rate, $fee, $minpay, $payment, $months, false);
  334. $apr = self::irr2apr(self::irr($pval, $payarray, 1));
  335. return $apr;
  336. }
  337. /**
  338. * Calculates APR for a campaign where you give $free months to
  339. * the client and there is no interest on the first invoice.
  340. * The only new input is $free, and if you give "Pay in Jan"
  341. * in November, then $free = 2.
  342. *
  343. * The more free months you give, the lower the APR so it does
  344. * matter.
  345. *
  346. * This function basically pads the $payarray with zeros in the
  347. * beginning (but there is some more magic as well).
  348. *
  349. * @param float $pval principal value
  350. * @param float $payment monthly payment for client
  351. * @param float $rate interest rate in % as before
  352. * @param float $fee monthly fee
  353. * @param float $minpay minimum payment per month
  354. * @param int $free free months
  355. * @return float APR in %
  356. */
  357. private static function apr_payin_X_months($pval, $payment, $rate, $fee, $minpay, $free) {
  358. $firstpay = $payment; //this used to be buggy. use this line.
  359. $months = self::fixed($pval, $payment-$fee, $rate, 0);
  360. if($months < 0) {
  361. return $months;
  362. }
  363. $months = ceil($months);
  364. $farray = array();
  365. while($free--) {
  366. $farray[] = 0.0;
  367. }
  368. $pval += $fee;
  369. $farray[] = $firstpay;
  370. $pval -= $firstpay;
  371. $payarray = self::fulpacc($pval, $rate, $fee, $minpay, $payment, $months, false);
  372. $newarray = array_merge($farray, $payarray);
  373. $apr = self::irr2apr(self::irr($pval, $newarray, 1));
  374. return $apr;
  375. }
  376. /**
  377. * Grabs the array of all monthly payments for specified PClass.
  378. *
  379. * <b>Flags can be either</b>:<br>
  380. * {@link KlarnaFlags::CHECKOUT_PAGE}<br>
  381. * {@link KlarnaFlags::PRODUCT_PAGE}<br>
  382. *
  383. * @param float $sum The sum for the order/product.
  384. * @param KlarnaPClass $pclass {@link KlarnaPClass PClass} used to calculate the APR.
  385. * @param int $flags Indicates if it is the checkout or a product page.
  386. * @throws KlarnaException
  387. * @return array An array of monthly payments.
  388. */
  389. private static function get_payarr($sum, $pclass, $flags) {
  390. $monthsfee = (($flags === KlarnaFlags::CHECKOUT_PAGE) ? $pclass->getInvoiceFee() : 0);
  391. $startfee = (($flags === KlarnaFlags::CHECKOUT_PAGE) ? $pclass->getStartFee() : 0);
  392. //Include start fee in sum
  393. $sum += $startfee;
  394. $base = ($pclass->getType() === KlarnaPClass::ACCOUNT);
  395. $lowest = self::get_lowest_payment_for_account($pclass->getCountry());
  396. if($flags == KlarnaFlags::CHECKOUT_PAGE) {
  397. $minpay = ($pclass->getType() === KlarnaPClass::ACCOUNT) ? $lowest : 0;
  398. }
  399. else {
  400. $minpay = 0;
  401. }
  402. $payment = self::annuity($sum, $pclass->getMonths(), $pclass->getInterestRate());
  403. //Add monthly fee
  404. $payment += $monthsfee;
  405. return self::fulpacc($sum, $pclass->getInterestRate(), $monthsfee, $minpay, $payment, $pclass->getMonths(), $base);
  406. }
  407. /**
  408. * Calculates APR for the specified values.<br>
  409. * Result is rounded with two decimals.<br>
  410. *
  411. * <b>Flags can be either</b>:<br>
  412. * {@link KlarnaFlags::CHECKOUT_PAGE}<br>
  413. * {@link KlarnaFlags::PRODUCT_PAGE}<br>
  414. *
  415. * @param float $sum The sum for the order/product.
  416. * @param KlarnaPClass $pclass {@link KlarnaPClass PClass} used to calculate the APR.
  417. * @param int $flags Indicates if it is the checkout or a product page.
  418. * @param int $free Number of free months.
  419. * @throws KlarnaException
  420. * @return float APR in %
  421. */
  422. public static function calc_apr($sum, $pclass, $flags, $free = 0) {
  423. if(!is_numeric($sum)) {
  424. throw new KlarnaException('Error in ' . __METHOD__ . ': Argument sum is not numeric!');
  425. }
  426. else if(is_numeric($sum) && (!is_int($sum) || !is_float($sum))) {
  427. $sum = floatval($sum);
  428. }
  429. if(!($pclass instanceof KlarnaPClass)) {
  430. throw new KlarnaException('Error in ' . __METHOD__ . ': Supplied PClass is not a PClass object!');
  431. }
  432. if(!is_numeric($free)) {
  433. throw new KlarnaException('Error in ' . __METHOD__ . ': Argument free is not an integer!');
  434. }
  435. else if(is_numeric($free) && !is_int($free)) {
  436. $free = intval($free);
  437. }
  438. if($free < 0) {
  439. throw new KlarnaException('Error in ' . __METHOD__ . ': Number of free months must be positive or zero!');
  440. }
  441. if(is_numeric($flags) && !is_int($flags)) {
  442. $flags = intval($flags);
  443. }
  444. if(!is_numeric($flags) || !in_array($flags, array(KlarnaFlags::CHECKOUT_PAGE, KlarnaFlags::PRODUCT_PAGE))) {
  445. throw new KlarnaException('Error in ' . __METHOD__ . ': Flags argument invalid!');
  446. }
  447. $monthsfee = (($flags === KlarnaFlags::CHECKOUT_PAGE) ? $pclass->getInvoiceFee() : 0);
  448. $startfee = (($flags === KlarnaFlags::CHECKOUT_PAGE) ? $pclass->getStartFee() : 0);
  449. //Include start fee in sum
  450. $sum += $startfee;
  451. $lowest = self::get_lowest_payment_for_account($pclass->getCountry());
  452. if($flags == KlarnaFlags::CHECKOUT_PAGE) {
  453. $minpay = ($pclass->getType() === KlarnaPClass::ACCOUNT) ? $lowest : 0;
  454. }
  455. else {
  456. $minpay = 0;
  457. }
  458. //add monthly fee
  459. $payment = self::annuity($sum, $pclass->getMonths(), $pclass->getInterestRate()) + $monthsfee;
  460. //echo "annuity $payment, $sum " . $pclass->getMonths() . " " . $pclass->getInterestRate() . "\n";
  461. $type = $pclass->getType();
  462. switch($type) {
  463. case KlarnaPClass::CAMPAIGN:
  464. case KlarnaPClass::ACCOUNT:
  465. $apr = self::apr_annuity($sum, $pclass->getMonths(), $pclass->getInterestRate(), $pclass->getInvoiceFee(), $minpay);
  466. break;
  467. case KlarnaPClass::SPECIAL:
  468. $apr = self::apr_payin_X_months($sum, $payment, $pclass->getInterestRate(), $pclass->getInvoiceFee(), $minpay, $free);
  469. break;
  470. case KlarnaPClass::FIXED:
  471. $apr = self::apr_fixed($sum, $payment, $pclass->getInterestRate(), $pclass->getInvoiceFee(), $minpay);
  472. break;
  473. default:
  474. throw new KlarnaException('Error in ' . __METHOD__ . ': Unknown PClass type! ('.$type.')');
  475. }
  476. return round($apr, 2);
  477. }
  478. /**
  479. * Calculates the total credit purchase cost.<br>
  480. * The result is rounded up, depending on the pclass country.<br>
  481. *
  482. * <b>Flags can be either</b>:<br>
  483. * {@link KlarnaFlags::CHECKOUT_PAGE}<br>
  484. * {@link KlarnaFlags::PRODUCT_PAGE}<br>
  485. *
  486. * @param float $sum The sum for the order/product.
  487. * @param KlarnaPClass $pclass {@link KlarnaPClass PClass} used to calculate total credit cost.
  488. * @param int $flags Indicates if it is the checkout or a product page.
  489. * @throws KlarnaException
  490. * @return float Total credit purchase cost.
  491. */
  492. public static function total_credit_purchase_cost($sum, $pclass, $flags) {
  493. if(!is_numeric($sum)) {
  494. throw new KlarnaException('Error in ' . __METHOD__ . ': Argument sum is not numeric!');
  495. }
  496. else if(is_numeric($sum) && (!is_int($sum) || !is_float($sum))) {
  497. $sum = floatval($sum);
  498. }
  499. if(!($pclass instanceof KlarnaPClass)) {
  500. throw new KlarnaException('Error in ' . __METHOD__ . ': Supplied PClass is not a PClass object!');
  501. }
  502. if(is_numeric($flags) && !is_int($flags)) {
  503. $flags = intval($flags);
  504. }
  505. if(!is_numeric($flags) || !in_array($flags, array(KlarnaFlags::CHECKOUT_PAGE, KlarnaFlags::PRODUCT_PAGE))) {
  506. throw new KlarnaException('Error in ' . __METHOD__ . ': Flags argument invalid!');
  507. }
  508. $payarr = self::get_payarr($sum, $pclass, $flags);
  509. $credit_cost = 0;
  510. foreach($payarr as $pay) {
  511. $credit_cost += $pay;
  512. }
  513. return self::pRound($credit_cost, $pclass->getCountry());
  514. }
  515. /**
  516. * Calculates the monthly cost for the specified pclass.<br>
  517. * The result is rounded up to the correct value depending on the pclass country.<br>
  518. *
  519. * Example:<br>
  520. * <ul>
  521. * <li>In product view, round monthly cost with max 0.5 or 0.1 depending on currency.<br>
  522. * <ul>
  523. * <li>10.50 SEK rounds to 11 SEK</li>
  524. * <li>10.49 SEK rounds to 10 SEK</li>
  525. * <li> 8.55 EUR rounds to 8.6 EUR</li>
  526. * <li> 8.54 EUR rounds to 8.5 EUR</li>
  527. * </ul></li>
  528. * <li>
  529. * In checkout, round the monthly cost to have 2 decimals.<br>
  530. * For example 10.57 SEK/per m???nad
  531. * </li>
  532. * </ul>
  533. *
  534. * <b>Flags can be either</b>:<br>
  535. * {@link KlarnaFlags::CHECKOUT_PAGE}<br>
  536. * {@link KlarnaFlags::PRODUCT_PAGE}<br>
  537. *
  538. * @param int $sum The sum for the order/product.
  539. * @param KlarnaPClass $pclass {@link KlarnaPClass PClass} used to calculate monthly cost.
  540. * @param int $flags Indicates if it is the checkout or a product page.
  541. * @throws KlarnaException
  542. * @return float The monthly cost.
  543. */
  544. public static function calc_monthly_cost($sum, $pclass, $flags) {
  545. if(!is_numeric($sum)) {
  546. throw new KlarnaException('Error in ' . __METHOD__ . ': Argument sum is not numeric!');
  547. }
  548. else if(is_numeric($sum) && (!is_int($sum) || !is_float($sum))) {
  549. $sum = floatval($sum);
  550. }
  551. if(!($pclass instanceof KlarnaPClass)) {
  552. throw new KlarnaException('Error in ' . __METHOD__ . ': Supplied PClass is not a PClass object!');
  553. }
  554. if(is_numeric($flags) && !is_int($flags)) {
  555. $flags = intval($flags);
  556. }
  557. if(!is_numeric($flags) || !in_array($flags, array(KlarnaFlags::CHECKOUT_PAGE, KlarnaFlags::PRODUCT_PAGE))) {
  558. throw new KlarnaException('Error in ' . __METHOD__ . ': Flags argument invalid!');
  559. }
  560. $payarr = self::get_payarr($sum, $pclass, $flags);
  561. $value = isset($payarr[0]) ? ($payarr[0]) : 0;
  562. return (KlarnaFlags::CHECKOUT_PAGE == $flags) ? round($value, 2) : self::pRound($value, $pclass->getCountry());
  563. }
  564. /**
  565. * Returns the lowest monthly payment for Klarna Account.
  566. *
  567. * @param int $country {@link KlarnaCountry Country} constant.
  568. * @throws KlarnaException
  569. * @return int|float Lowest monthly payment.
  570. */
  571. public static function get_lowest_payment_for_account($country) {
  572. switch ($country) {
  573. case KlarnaCountry::SE:
  574. $lowest_monthly_payment = 50.0;
  575. break;
  576. case KlarnaCountry::NO:
  577. $lowest_monthly_payment = 95.0;
  578. break;
  579. case KlarnaCountry::FI:
  580. $lowest_monthly_payment = 8.95;
  581. break;
  582. case KlarnaCountry::DK:
  583. $lowest_monthly_payment = 89.0;
  584. break;
  585. case KlarnaCountry::DE:
  586. case KlarnaCountry::NL:
  587. $lowest_monthly_payment = 6.95;
  588. break;
  589. default:
  590. throw new KlarnaException('Error in ' . __METHOD__ . ': Not allowed for this country!');
  591. }
  592. return $lowest_monthly_payment;
  593. }
  594. /**
  595. * Rounds a value depending on the specified country.
  596. *
  597. * @param int|float $value The value to be rounded.
  598. * @param int $country {@link KlarnaCountry} constant.
  599. * @return float|int
  600. */
  601. public static function pRound($value, $country) {
  602. $multiply = 1; //Round to closest integer
  603. switch($country) {
  604. case KlarnaCountry::FI:
  605. case KlarnaCountry::DE:
  606. case KlarnaCountry::NL:
  607. $multiply = 10; //Round to closest decimal
  608. break;
  609. }
  610. return floor(($value*$multiply)+0.5)/$multiply;
  611. }
  612. }