shop.balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

klarna_invoice.php (17516B)


      1 <?php
      2 class ControllerExtensionPaymentKlarnaInvoice extends Controller {
      3 	public function index() {
      4 		$this->load->model('checkout/order');
      5 
      6 		$order_info = $this->model_checkout_order->getOrder($this->session->data['order_id']);
      7 
      8 		if ($order_info) {
      9 			$this->load->language('extension/payment/klarna_invoice');
     10 
     11 			$data['days'] = array();
     12 
     13 			for ($i = 1; $i <= 31; $i++) {
     14 				$data['days'][] = array(
     15 					'text'  => sprintf('%02d', $i),
     16 					'value' => $i
     17 				);
     18 			}
     19 
     20 			$data['months'] = array();
     21 
     22 			for ($i = 1; $i <= 12; $i++) {
     23 				$data['months'][] = array(
     24 					'text'  => sprintf('%02d', $i),
     25 					'value' => $i
     26 				);
     27 			}
     28 
     29 			$data['years'] = array();
     30 
     31 			for ($i = date('Y'); $i >= 1900; $i--) {
     32 				$data['years'][] = array(
     33 					'text'  => $i,
     34 					'value' => $i
     35 				);
     36 			}
     37 
     38 			// Store Taxes to send to Klarna
     39 			$total_data = array();
     40 			$total = 0;
     41 
     42 			$this->load->model('setting/extension');
     43 
     44 			$sort_order = array();
     45 
     46 			$results = $this->model_setting_extension->getExtensions('total');
     47 
     48 			foreach ($results as $key => $value) {
     49 				$sort_order[$key] = $this->config->get('total_' . $value['code'] . '_sort_order');
     50 			}
     51 
     52 			array_multisort($sort_order, SORT_ASC, $results);
     53 
     54 			$klarna_tax = array();
     55 
     56 			foreach ($results as $result) {
     57 				if ($this->config->get($result['code'] . '_status')) {
     58 					$this->load->model('extension/total/' . $result['code']);
     59 
     60 					$taxes = array();
     61 					
     62 					// We have to put the totals in an array so that they pass by reference.
     63 					$this->{'model_extension_total_' . $result['code']}->getTotal(array("totals"=>$total_data, "total"=>$total, "taxes"=>$taxes));
     64 
     65 					$amount = 0;
     66 
     67 					foreach ($taxes as $tax_id => $value) {
     68 						$amount += $value;
     69 					}
     70 
     71 					$klarna_tax[$result['code']] = $amount;
     72 				}
     73 			}
     74 
     75 			foreach ($total_data as $key => $value) {
     76 				$sort_order[$key] = $value['sort_order'];
     77 
     78 				if (isset($klarna_tax[$value['code']])) {
     79 					if ($klarna_tax[$value['code']]) {
     80 						$total_data[$key]['tax_rate'] = abs($klarna_tax[$value['code']] / $value['value'] * 100);
     81 					} else {
     82 						$total_data[$key]['tax_rate'] = 0;
     83 					}
     84 				} else {
     85 					$total_data[$key]['tax_rate'] = '0';
     86 				}
     87 			}
     88 
     89 			$this->session->data['klarna'][$this->session->data['order_id']] = $total_data;
     90 
     91 			// Order must have identical shipping and billing address or have no shipping address at all
     92 			if ($this->cart->hasShipping() && !($order_info['payment_firstname'] == $order_info['shipping_firstname'] && $order_info['payment_lastname'] == $order_info['shipping_lastname'] && $order_info['payment_address_1'] == $order_info['shipping_address_1'] && $order_info['payment_address_2'] == $order_info['shipping_address_2'] && $order_info['payment_postcode'] == $order_info['shipping_postcode'] && $order_info['payment_city'] == $order_info['shipping_city'] && $order_info['payment_zone_id'] == $order_info['shipping_zone_id'] && $order_info['payment_zone_code'] == $order_info['shipping_zone_code'] && $order_info['payment_country_id'] == $order_info['shipping_country_id'] && $order_info['payment_country'] == $order_info['shipping_country'] && $order_info['payment_iso_code_3'] == $order_info['shipping_iso_code_3'])) {
     93 				$data['error_warning'] = $this->language->get('error_address_match');
     94 			} else {
     95 				$data['error_warning'] = '';
     96 			}
     97 
     98 			$klarna_invoice = $this->config->get('payment_klarna_invoice');
     99 
    100 			$data['merchant'] = $klarna_invoice[$order_info['payment_iso_code_3']]['merchant'];
    101 			$data['phone_number'] = $order_info['telephone'];
    102 
    103 			if ($order_info['payment_iso_code_3'] == 'DEU' || $order_info['payment_iso_code_3'] == 'NLD') {
    104 				$address = $this->splitAddress($order_info['payment_address_1']);
    105 
    106 				$data['street'] = $address[0];
    107 				$data['street_number'] = $address[1];
    108 				$data['street_extension'] = $address[2];
    109 
    110 				if ($order_info['payment_iso_code_3'] == 'DEU') {
    111 					$data['street_number'] = trim($address[1] . ' ' . $address[2]);
    112 				}
    113 			} else {
    114 				$data['street'] = '';
    115 				$data['street_number'] = '';
    116 				$data['street_extension'] = '';
    117 			}
    118 
    119 			$data['company'] = $order_info['payment_company'];
    120 			$data['iso_code_2'] = $order_info['payment_iso_code_2'];
    121 			$data['iso_code_3'] = $order_info['payment_iso_code_3'];
    122 
    123 			// Get the invoice fee
    124 			$query = $this->db->query("SELECT `value` FROM `" . DB_PREFIX . "order_total` WHERE `order_id` = " . (int)$order_info['order_id'] . " AND `code` = 'klarna_fee'");
    125 
    126 			if ($query->num_rows && !$query->row['value']) {
    127 				$data['klarna_fee'] = $query->row['value'];
    128 			} else {
    129 				$data['klarna_fee'] = '';
    130 			}
    131 
    132 			return $this->load->view('extension/payment/klarna_invoice', $data);
    133 		}
    134 	}
    135 
    136 	public function send() {
    137 		$this->load->language('extension/payment/klarna_invoice');
    138 
    139 		$json = array();
    140 
    141 		$this->load->model('checkout/order');
    142 
    143 		$order_info = $this->model_checkout_order->getOrder($this->session->data['order_id']);
    144 
    145 		// Order must have identical shipping and billing address or have no shipping address at all
    146 		if ($order_info) {
    147 			if ($order_info['payment_iso_code_3'] == 'DEU' && empty($this->request->post['deu_terms'])) {
    148 				$json['error'] = $this->language->get('error_deu_terms');
    149 			}
    150 
    151 			if ($this->cart->hasShipping() && !($order_info['payment_firstname'] == $order_info['shipping_firstname'] && $order_info['payment_lastname'] == $order_info['shipping_lastname'] && $order_info['payment_address_1'] == $order_info['shipping_address_1'] && $order_info['payment_address_2'] == $order_info['shipping_address_2'] && $order_info['payment_postcode'] == $order_info['shipping_postcode'] && $order_info['payment_city'] == $order_info['shipping_city'] && $order_info['payment_zone_id'] == $order_info['shipping_zone_id'] && $order_info['payment_zone_code'] == $order_info['shipping_zone_code'] && $order_info['payment_country_id'] == $order_info['shipping_country_id'] && $order_info['payment_country'] == $order_info['shipping_country'] && $order_info['payment_iso_code_3'] == $order_info['shipping_iso_code_3'])) {
    152 				$json['error'] = $this->language->get('error_address_match');
    153 			}
    154 
    155 			if (!$json) {
    156 				$klarna_invoice = $this->config->get('payment_klarna_invoice');
    157 
    158 				if ($klarna_invoice[$order_info['payment_iso_code_3']]['server'] == 'live') {
    159 					$url = 'https://payment.klarna.com/';
    160 				} else {
    161 					$url = 'https://payment.testdrive.klarna.com/';
    162 				}
    163 
    164 				$country_to_currency = array(
    165 					'NOR' => 'NOK',
    166 					'SWE' => 'SEK',
    167 					'FIN' => 'EUR',
    168 					'DNK' => 'DKK',
    169 					'DEU' => 'EUR',
    170 					'NLD' => 'EUR'
    171 				);
    172 
    173 				switch ($order_info['payment_iso_code_3']) {
    174 					// Sweden
    175 					case 'SWE':
    176 						$country = 209;
    177 						$language = 138;
    178 						$encoding = 2;
    179 						$currency = 0;
    180 						break;
    181 					// Finland
    182 					case 'FIN':
    183 						$country = 73;
    184 						$language = 37;
    185 						$encoding = 4;
    186 						$currency = 2;
    187 						break;
    188 					// Denmark
    189 					case 'DNK':
    190 						$country = 59;
    191 						$language = 27;
    192 						$encoding = 5;
    193 						$currency = 3;
    194 						break;
    195 					// Norway
    196 					case 'NOR':
    197 						$country = 164;
    198 						$language = 97;
    199 						$encoding = 3;
    200 						$currency = 1;
    201 						break;
    202 					// Germany
    203 					case 'DEU':
    204 						$country = 81;
    205 						$language = 28;
    206 						$encoding = 6;
    207 						$currency = 2;
    208 						break;
    209 					// Netherlands
    210 					case 'NLD':
    211 						$country = 154;
    212 						$language = 101;
    213 						$encoding = 7;
    214 						$currency = 2;
    215 						break;
    216 				}
    217 
    218 				if (isset($this->request->post['street'])) {
    219 					$street = $this->request->post['street'];
    220 				} else {
    221 					$street = $order_info['payment_address_1'];
    222 				}
    223 
    224 				if (isset($this->request->post['house_no'])) {
    225 					$house_no = $this->request->post['house_no'];
    226 				} else {
    227 					$house_no = '';
    228 				}
    229 
    230 				if (isset($this->request->post['house_ext'])) {
    231 					$house_ext = $this->request->post['house_ext'];
    232 				} else {
    233 					$house_ext = '';
    234 				}
    235 
    236 				$address = array(
    237 					'email'           => $order_info['email'],
    238 					'telno'           => $this->request->post['phone_no'],
    239 					'cellno'          => '',
    240 					'fname'           => $order_info['payment_firstname'],
    241 					'lname'           => $order_info['payment_lastname'],
    242 					'company'         => $order_info['payment_company'],
    243 					'careof'          => '',
    244 					'street'          => $street,
    245 					'house_number'    => $house_no,
    246 					'house_extension' => $house_ext,
    247 					'zip'             => $order_info['payment_postcode'],
    248 					'city'            => $order_info['payment_city'],
    249 					'country'         => $country,
    250 				);
    251 
    252 				$product_query = $this->db->query("SELECT `name`, `model`, `price`, `quantity`, `tax` / `price` * 100 AS 'tax_rate' FROM `" . DB_PREFIX . "order_product` WHERE `order_id` = " . (int)$order_info['order_id'] . " UNION ALL SELECT '', `code`, `amount`, '1', 0.00 FROM `" . DB_PREFIX . "order_voucher` WHERE `order_id` = " . (int)$order_info['order_id']);
    253 
    254 				foreach ($product_query->rows as $product) {
    255 					$goods_list[] = array(
    256 						'qty'   => (int)$product['quantity'],
    257 						'goods' => array(
    258 							'artno'    => $product['model'],
    259 							'title'    => $product['name'],
    260 							'price'    => (int)str_replace('.', '', $this->currency->format($product['price'], $country_to_currency[$order_info['payment_iso_code_3']], '', false)),
    261 							'vat'      => (float)$product['tax_rate'],
    262 							'discount' => 0.0,
    263 							'flags'    => 0
    264 						)
    265 					);
    266 				}
    267 
    268 				if (isset($this->session->data['klarna'][$this->session->data['order_id']])) {
    269 					$totals = $this->session->data['klarna'][$this->session->data['order_id']];
    270 				} else {
    271 					$totals = array();
    272 				}
    273 
    274 				foreach ($totals as $total) {
    275 					if ($total['code'] != 'sub_total' && $total['code'] != 'tax' && $total['code'] != 'total') {
    276 						$goods_list[] = array(
    277 							'qty'   => 1,
    278 							'goods' => array(
    279 								'artno'    => '',
    280 								'title'    => $total['title'],
    281 								'price'    => (int)str_replace('.', '', $this->currency->format($total['value'], $country_to_currency[$order_info['payment_iso_code_3']], '', false)),
    282 								'vat'      => (float)$total['tax_rate'],
    283 								'discount' => 0.0,
    284 								'flags'    => 0
    285 							)
    286 						);
    287 					}
    288 				}
    289 
    290 				$digest = '';
    291 
    292 				foreach ($goods_list as $goods) {
    293 					$digest .= utf8_decode(htmlspecialchars(html_entity_decode($goods['goods']['title'], ENT_COMPAT, 'UTF-8'))) . ':';
    294 				}
    295 
    296 				$digest = base64_encode(pack('H*', hash('sha256', $digest . $klarna_invoice[$order_info['payment_iso_code_3']]['secret'])));
    297 
    298 				if (isset($this->request->post['pno'])) {
    299 					$pno = $this->request->post['pno'];
    300 				} else {
    301 					$pno = sprintf('%02d', (int)$this->request->post['pno_day']) . sprintf('%02d', (int)$this->request->post['pno_month']) . (int)$this->request->post['pno_year'];
    302 				}
    303 
    304 				$pclass = -1;
    305 
    306 				if (isset($this->request->post['gender']) && ($order_info['payment_iso_code_3'] == 'DEU' || $order_info['payment_iso_code_3'] == 'NLD')) {
    307 					$gender = (int)$this->request->post['gender'];
    308 				} else {
    309 					$gender = '';
    310 				}
    311 
    312 				$transaction = array(
    313 					'4.1',
    314 					'API:OPENCART:' . VERSION,
    315 					$pno,
    316 					$gender,
    317 					'',
    318 					'',
    319 					(string)$order_info['order_id'],
    320 					'',
    321 					$address,
    322 					$address,
    323 					$order_info['ip'],
    324 					0,
    325 					$currency,
    326 					$country,
    327 					$language,
    328 					(int)$klarna_invoice[$order_info['payment_iso_code_3']]['merchant'],
    329 					$digest,
    330 					$encoding,
    331 					$pclass,
    332 					$goods_list,
    333 					$order_info['comment'],
    334 					array('delay_adjust' => 1),
    335 					array(),
    336 					array(),
    337 					array(),
    338 					array(),
    339 					array()
    340 				);
    341 
    342 				$xml  = '<methodCall>';
    343 				$xml .= '  <methodName>add_invoice</methodName>';
    344 				$xml .= '  <params>';
    345 
    346 				foreach ($transaction as $parameter)  {
    347 					$xml .= '    <param><value>' . $this->constructXmlrpc($parameter) . '</value></param>';
    348 				}
    349 
    350 				$xml .= '  </params>';
    351 				$xml .= '</methodCall>';
    352 
    353 				$header = array();
    354 
    355 				$header[] = 'Content-Type: text/xml';
    356 				$header[] = 'Content-Length: ' . strlen($xml);
    357 
    358 				$curl = curl_init();
    359 
    360 				curl_setopt($curl, CURLOPT_URL, $url);
    361 				curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    362 				curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1);
    363 				curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST');
    364 				curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    365 				curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
    366 				curl_setopt($curl, CURLOPT_POSTFIELDS, $xml);
    367 
    368 				$response = curl_exec($curl);
    369 
    370 				if (curl_errno($curl)) {
    371 					$log = new Log('klarna_invoice.log');
    372 					$log->write('HTTP Error for order #' . $order_info['order_id'] . '. Code: ' . curl_errno($curl) . ' message: ' . curl_error($curl));
    373 
    374 					$json['error'] = $this->language->get('error_network');
    375 				} else {
    376 					preg_match('/<member><name>faultString<\/name><value><string>(.+)<\/string><\/value><\/member>/', $response, $match);
    377 
    378 					if (isset($match[1])) {
    379 						preg_match('/<member><name>faultCode<\/name><value><int>([0-9]+)<\/int><\/value><\/member>/', $response, $match2);
    380 
    381 						$log = new Log('klarna_invoice.log');
    382 						$log->write('Failed to create an invoice for order #' . $order_info['order_id'] . '. Message: ' . utf8_encode($match[1]) . ' Code: ' . $match2[1]);
    383 
    384 						$json['error'] = utf8_encode($match[1]);
    385 					} else {
    386 						$xml = new DOMDocument();
    387 						$xml->loadXML($response);
    388 
    389 						$invoice_number = $xml->getElementsByTagName('string')->item(0)->nodeValue;
    390 						$klarna_order_status = $xml->getElementsByTagName('int')->item(0)->nodeValue;
    391 
    392 						if ($klarna_order_status == '1') {
    393 							$order_status = $klarna_invoice[$order_info['payment_iso_code_3']]['accepted_status_id'];
    394 						} elseif ($klarna_order_status == '2') {
    395 							$order_status = $klarna_invoice[$order_info['payment_iso_code_3']]['pending_status_id'];
    396 						} else {
    397 							$order_status = $this->config->get('config_order_status_id');
    398 						}
    399 
    400 						$comment = sprintf($this->language->get('text_comment'), $invoice_number, $this->config->get('config_currency'), $country_to_currency[$order_info['payment_iso_code_3']], $this->currency->getValue($country_to_currency[$order_info['payment_iso_code_3']]));
    401 
    402 						$this->model_checkout_order->addOrderHistory($this->session->data['order_id'], $order_status, $comment, 1);
    403 
    404 						$json['redirect'] = $this->url->link('checkout/success');
    405 					}
    406 				}
    407 
    408 				curl_close($curl);
    409 			}
    410 		}
    411 
    412 		$this->response->addHeader('Content-Type: application/json');
    413 		$this->response->setOutput(json_encode($json));
    414 	}
    415 
    416 	private function constructXmlrpc($data) {
    417 		$type = gettype($data);
    418 
    419 		switch ($type) {
    420 			case 'boolean':
    421 				if ($data == true) {
    422 					$value = 1;
    423 				} else {
    424 					$value = false;
    425 				}
    426 
    427 				$xml = '<boolean>' . $value . '</boolean>';
    428 				break;
    429 			case 'integer':
    430 				$xml = '<int>' . (int)$data . '</int>';
    431 				break;
    432 			case 'double':
    433 				$xml = '<double>' . (float)$data . '</double>';
    434 				break;
    435 			case 'string':
    436 					$xml = '<string>' . htmlspecialchars($data) . '</string>';
    437 				break;
    438 			case 'array':
    439 				// is numeric ?
    440 				if ($data === array_values($data)) {
    441 					$xml = '<array><data>';
    442 
    443 					foreach ($data as $value) {
    444 						$xml .= '<value>' . $this->constructXmlrpc($value) . '</value>';
    445 					}
    446 
    447 					$xml .= '</data></array>';
    448 
    449 				} else {
    450 					// array is associative
    451 					$xml = '<struct>';
    452 
    453 					foreach ($data as $key => $value) {
    454 						$xml .= '<member>';
    455 						$xml .= '  <name>' . htmlspecialchars($key) . '</name>';
    456 						$xml .= '  <value>' . $this->constructXmlrpc($value) . '</value>';
    457 						$xml .= '</member>';
    458 					}
    459 
    460 					$xml .= '</struct>';
    461 				}
    462 
    463 				break;
    464 			default:
    465 				$xml = '<nil/>';
    466 				break;
    467 		}
    468 
    469 		return $xml;
    470 	}
    471 
    472 	private function splitAddress($address) {
    473 		$numbers = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
    474 
    475 		$characters = array('-', '/', ' ', '#', '.', 'a', 'b', 'c', 'd', 'e',
    476 						'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
    477 						'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A',
    478 						'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
    479 						'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
    480 						'X', 'Y', 'Z');
    481 
    482 		$specialchars = array('-', '/', ' ', '#', '.');
    483 
    484 		$num_pos = $this->strposArr($address, $numbers, 2);
    485 
    486 		$street_name = substr($address, 0, $num_pos);
    487 
    488 		$street_name = trim($street_name);
    489 
    490 		$number_part = substr($address, $num_pos);
    491 
    492 		$number_part = trim($number_part);
    493 
    494 		$ext_pos = $this->strposArr($number_part, $characters, 0);
    495 
    496 		if ($ext_pos != '') {
    497 			$house_number = substr($number_part, 0, $ext_pos);
    498 
    499 			$house_extension = substr($number_part, $ext_pos);
    500 
    501 			$house_extension = str_replace($specialchars, '', $house_extension);
    502 		} else {
    503 			$house_number = $number_part;
    504 			$house_extension = '';
    505 		}
    506 
    507 		return array($street_name, $house_number, $house_extension);
    508 	}
    509 
    510 	private function strposArr($haystack, $needle, $where) {
    511 		$defpos = 10000;
    512 
    513 		if (!is_array($needle)) {
    514 			$needle = array($needle);
    515 		}
    516 
    517 		foreach ($needle as $what) {
    518 			if (($pos = strpos($haystack, $what, $where)) !== false) {
    519 				if ($pos < $defpos) {
    520 					$defpos = $pos;
    521 				}
    522 			}
    523 		}
    524 
    525 		return $defpos;
    526 	}
    527 }