From f4750ca67aaf8790842f37b883b2d896b9c1ecf5 Mon Sep 17 00:00:00 2001 From: Ed Mark Date: Mon, 23 May 2016 14:43:47 +0300 Subject: [PATCH 1/2] remove old --- DIEGIMAS.txt | 9 - README.md | 20 - README.txt | 9 - ext/modules/payment/paysera/callback.php | 75 -- .../english/modules/payment/paysera.php | 49 - includes/modules/payment/paysera.php | 562 ----------- includes/modules/payment/paysera/WebToPay.php | 910 ------------------ 7 files changed, 1634 deletions(-) delete mode 100644 DIEGIMAS.txt delete mode 100644 README.md delete mode 100644 README.txt delete mode 100644 ext/modules/payment/paysera/callback.php delete mode 100644 includes/languages/english/modules/payment/paysera.php delete mode 100644 includes/modules/payment/paysera.php delete mode 100644 includes/modules/payment/paysera/WebToPay.php diff --git a/DIEGIMAS.txt b/DIEGIMAS.txt deleted file mode 100644 index 5578a8a..0000000 --- a/DIEGIMAS.txt +++ /dev/null @@ -1,9 +0,0 @@ -Diegimas - -1. Padarykite atsargine kopija[nebutina]. -2. Nukopijuokite failus i osCommerce direktoija. -3. Administracijoje idiekite aktyvuokite mokejimo buda, pasirinkite ji, paspauskite radagavima ir iveskite reikalingus duomenis. - -Kontaktai - -Kilus klausimams prasome kreiptis i pagalba@mokejimai.lt diff --git a/README.md b/README.md deleted file mode 100644 index 8a2e3c3..0000000 --- a/README.md +++ /dev/null @@ -1,20 +0,0 @@ -plugin-oscommerce -================= - -Paysera.com payment gateway plugin for osCommerce - -Requirements ------------- - -- osCommerce - -Installation ------------- - -1. Download this repository as zip and extract "ext,includes" folders into osCommerce main directory. -2. In admin panel activate payment method, select it, press edit and fill in all information. - -Contacts --------- - -If any problems occur please feel free to seek help via support@paysera.com \ No newline at end of file diff --git a/README.txt b/README.txt deleted file mode 100644 index 52c42c4..0000000 --- a/README.txt +++ /dev/null @@ -1,9 +0,0 @@ -Installation - -1. BACKUP ALL FILES[optional]. -2. Copy files to osCommerce directory. -3. In admin panel active payment method, select it, press edit and fill in all information. - -Contacts - -If any problems occur please feel free to seek help via support@paysera.com diff --git a/ext/modules/payment/paysera/callback.php b/ext/modules/payment/paysera/callback.php deleted file mode 100644 index 0c9d494..0000000 --- a/ext/modules/payment/paysera/callback.php +++ /dev/null @@ -1,75 +0,0 @@ - MODULE_PAYMENT_PAYSERA_PROJECT, - 'sign_password' => MODULE_PAYMENT_PAYSERA_SIGNATURE, - )); - - if ($response['status'] == 1) { - $order_query = tep_db_query(' - SELECT `orders_status`, `currency`, `currency_value` - FROM ' . TABLE_ORDERS . ' - WHERE - `orders_id` = ' . intval($response['orderid']) - ); - - if (tep_db_num_rows($order_query) <= 0) { - throw new WebToPayException('Order not found!'); - } - - $order = tep_db_fetch_array($order_query); - - if ($order['currency'] != $response['currency']) { - throw new WebToPayException('Bad currency!'); - } - - $total_query = tep_db_query(' - SELECT `value` - FROM ' . TABLE_ORDERS_TOTAL . ' - WHERE - `orders_id` = ' . intval($response['orderid']) . ' - AND `class` = "ot_total" - LIMIT 1' - ); - - $total = tep_db_fetch_array($total_query); - - - if (intval(number_format($total['value'], 2, '', '')) > ($response['amount'])) { - throw new WebToPayException('Bad amount!'); - } - - $sql_data_array = array(); - $sql_data_array['orders_id'] = $response['orderid']; - $sql_data_array['date_added'] = 'now()'; - $sql_data_array['customer_notified'] = '0'; - $sql_data_array['comments'] = print_r($_REQUEST, true); - - $sql_data_array['orders_status_id'] = MODULE_PAYMENT_PAYSERA_ORDER_STATUS_ID; - tep_db_query(' - UPDATE ' . TABLE_ORDERS . ' - SET - `orders_status` = ' . intval(MODULE_PAYMENT_PAYSERA_ORDER_STATUS_ID) . ', - `last_modified` = NOW() - WHERE - `orders_id` = ' . intval($response['orderid']) - ); - - tep_db_perform(TABLE_ORDERS_STATUS_HISTORY, $sql_data_array); - - echo 'OK'; - - } - -} catch (Exception $e) { - echo get_class($e) . ': ' . $e->getMessage(); -} - - diff --git a/includes/languages/english/modules/payment/paysera.php b/includes/languages/english/modules/payment/paysera.php deleted file mode 100644 index a6cb6af..0000000 --- a/includes/languages/english/modules/payment/paysera.php +++ /dev/null @@ -1,49 +0,0 @@ -code = 'paysera'; - $this->title = MODULE_PAYMENT_PAYSERA_TEXT_TITLE; - $this->description = MODULE_PAYMENT_PAYSERA_TEXT_DESCRIPTION; - $this->sort_order = MODULE_PAYMENT_PAYSERA_SORT_ORDER; - $this->enabled = ((MODULE_PAYMENT_PAYSERA_STATUS == 'True') ? true : false); - - $libwebtopay_dir = substr(__FILE__, 0, -4) .'/WebToPay.php'; - if ( !is_file($libwebtopay_dir) ) { - throw new Exception('LibWebToPay library not found in '. $libwebtopay_dir .'. '. "\n" .' - You may download it from here: http://bitbucket.org/webtopay/libwebtopay/raw/default/WebToPay.php'); - } - require_once $libwebtopay_dir; - - $this->form_action_url = 'https://www.mokejimai.lt/pay/'; - } - - - function update_status() { - global $order; - - if ( ($this->enabled == true) && ((int)MODULE_PAYMENT_PAYPAL_STANDARD_ZONE > 0) ) { - $check_flag = false; - $check_query = tep_db_query("select zone_id from " . TABLE_ZONES_TO_GEO_ZONES . " where geo_zone_id = '" . MODULE_PAYMENT_PAYPAL_STANDARD_ZONE . "' and zone_country_id = '" . $order->billing['country']['id'] . "' order by zone_id"); - while ($check = tep_db_fetch_array($check_query)) { - if ($check['zone_id'] < 1) { - $check_flag = true; - break; - } elseif ($check['zone_id'] == $order->billing['zone_id']) { - $check_flag = true; - break; - } - } - - if ($check_flag == false) { - $this->enabled = false; - } - } - } - - function javascript_validation() { - return false; - } - - function pre_confirmation_check() { - global $cartID, $cart; - - if (empty($cart->cartID)) { - $cartID = $cart->cartID = $cart->generate_cart_id(); - } - - if (!tep_session_is_registered('cartID')) { - tep_session_register('cartID'); - } - } - - function selection() { - global $cart_Paysera_ID; - - if (tep_session_is_registered('cart_Paysera_ID')) { - $order_id = substr($cart_Paysera_ID, strpos($cart_Paysera_ID, '-')+1); - - $check_query = tep_db_query('select orders_id from ' . TABLE_ORDERS_STATUS_HISTORY . ' where orders_id = "' . (int)$order_id . '" limit 1'); - - if (tep_db_num_rows($check_query) < 1) { - tep_db_query('delete from ' . TABLE_ORDERS . ' where orders_id = "' . (int)$order_id . '"'); - tep_db_query('delete from ' . TABLE_ORDERS_TOTAL . ' where orders_id = "' . (int)$order_id . '"'); - tep_db_query('delete from ' . TABLE_ORDERS_STATUS_HISTORY . ' where orders_id = "' . (int)$order_id . '"'); - tep_db_query('delete from ' . TABLE_ORDERS_PRODUCTS . ' where orders_id = "' . (int)$order_id . '"'); - tep_db_query('delete from ' . TABLE_ORDERS_PRODUCTS_ATTRIBUTES . ' where orders_id = "' . (int)$order_id . '"'); - tep_db_query('delete from ' . TABLE_ORDERS_PRODUCTS_DOWNLOAD . ' where orders_id = "' . (int)$order_id . '"'); - - tep_session_unregister('cart_Paysera_ID'); - } - } - - return array('id' => $this->code, - 'module' => $this->title); - } - - function confirmation() { - global $cartID, $cart_Paysera_ID, $customer_id, $languages_id, $order, $order_total_modules; - - if (tep_session_is_registered('cartID')) { - $insert_order = false; - - if (tep_session_is_registered('cart_Paysera_ID')) { - $order_id = substr($cart_Paysera_ID, strpos($cart_Paysera_ID, '-')+1); - - $curr_check = tep_db_query("select currency from " . TABLE_ORDERS . " where orders_id = '" . (int)$order_id . "'"); - $curr = tep_db_fetch_array($curr_check); - - if ( ($curr['currency'] != $order->info['currency']) || ($cartID != substr($cart_Paysera_ID, 0, strlen($cartID))) ) { - $check_query = tep_db_query('select orders_id from ' . TABLE_ORDERS_STATUS_HISTORY . ' where orders_id = "' . (int)$order_id . '" limit 1'); - - if (tep_db_num_rows($check_query) < 1) { - tep_db_query('delete from ' . TABLE_ORDERS . ' where orders_id = "' . (int)$order_id . '"'); - tep_db_query('delete from ' . TABLE_ORDERS_TOTAL . ' where orders_id = "' . (int)$order_id . '"'); - tep_db_query('delete from ' . TABLE_ORDERS_STATUS_HISTORY . ' where orders_id = "' . (int)$order_id . '"'); - tep_db_query('delete from ' . TABLE_ORDERS_PRODUCTS . ' where orders_id = "' . (int)$order_id . '"'); - tep_db_query('delete from ' . TABLE_ORDERS_PRODUCTS_ATTRIBUTES . ' where orders_id = "' . (int)$order_id . '"'); - tep_db_query('delete from ' . TABLE_ORDERS_PRODUCTS_DOWNLOAD . ' where orders_id = "' . (int)$order_id . '"'); - } - - $insert_order = true; - } - } else { - $insert_order = true; - } - - if ($insert_order == true) { - $order_totals = array(); - if (is_array($order_total_modules->modules)) { - reset($order_total_modules->modules); - while (list(, $value) = each($order_total_modules->modules)) { - $class = substr($value, 0, strrpos($value, '.')); - if ($GLOBALS[$class]->enabled) { - for ($i=0, $n=sizeof($GLOBALS[$class]->output); $i<$n; $i++) { - if (tep_not_null($GLOBALS[$class]->output[$i]['title']) && tep_not_null($GLOBALS[$class]->output[$i]['text'])) { - $order_totals[] = array('code' => $GLOBALS[$class]->code, - 'title' => $GLOBALS[$class]->output[$i]['title'], - 'text' => $GLOBALS[$class]->output[$i]['text'], - 'value' => $GLOBALS[$class]->output[$i]['value'], - 'sort_order' => $GLOBALS[$class]->sort_order); - } - } - } - } - } - - $sql_data_array = array('customers_id' => $customer_id, - 'customers_name' => $order->customer['firstname'] . ' ' . $order->customer['lastname'], - 'customers_company' => $order->customer['company'], - 'customers_street_address' => $order->customer['street_address'], - 'customers_suburb' => $order->customer['suburb'], - 'customers_city' => $order->customer['city'], - 'customers_postcode' => $order->customer['postcode'], - 'customers_state' => $order->customer['state'], - 'customers_country' => $order->customer['country']['title'], - 'customers_telephone' => $order->customer['telephone'], - 'customers_email_address' => $order->customer['email_address'], - 'customers_address_format_id' => $order->customer['format_id'], - 'delivery_name' => $order->delivery['firstname'] . ' ' . $order->delivery['lastname'], - 'delivery_company' => $order->delivery['company'], - 'delivery_street_address' => $order->delivery['street_address'], - 'delivery_suburb' => $order->delivery['suburb'], - 'delivery_city' => $order->delivery['city'], - 'delivery_postcode' => $order->delivery['postcode'], - 'delivery_state' => $order->delivery['state'], - 'delivery_country' => $order->delivery['country']['title'], - 'delivery_address_format_id' => $order->delivery['format_id'], - 'billing_name' => $order->billing['firstname'] . ' ' . $order->billing['lastname'], - 'billing_company' => $order->billing['company'], - 'billing_street_address' => $order->billing['street_address'], - 'billing_suburb' => $order->billing['suburb'], - 'billing_city' => $order->billing['city'], - 'billing_postcode' => $order->billing['postcode'], - 'billing_state' => $order->billing['state'], - 'billing_country' => $order->billing['country']['title'], - 'billing_address_format_id' => $order->billing['format_id'], - 'payment_method' => $order->info['payment_method'], - 'cc_type' => $order->info['cc_type'], - 'cc_owner' => $order->info['cc_owner'], - 'cc_number' => $order->info['cc_number'], - 'cc_expires' => $order->info['cc_expires'], - 'date_purchased' => 'now()', - 'orders_status' => $order->info['order_status'], - 'currency' => $order->info['currency'], - 'currency_value' => $order->info['currency_value']); - - tep_db_perform(TABLE_ORDERS, $sql_data_array); - - $insert_id = tep_db_insert_id(); - - for ($i=0, $n=sizeof($order_totals); $i<$n; $i++) { - $sql_data_array = array('orders_id' => $insert_id, - 'title' => $order_totals[$i]['title'], - 'text' => $order_totals[$i]['text'], - 'value' => $order_totals[$i]['value']*$order->info['currency_value'], - 'class' => $order_totals[$i]['code'], - 'sort_order' => $order_totals[$i]['sort_order']); - - tep_db_perform(TABLE_ORDERS_TOTAL, $sql_data_array); - } - - for ($i=0, $n=sizeof($order->products); $i<$n; $i++) { - $sql_data_array = array('orders_id' => $insert_id, - 'products_id' => tep_get_prid($order->products[$i]['id']), - 'products_model' => $order->products[$i]['model'], - 'products_name' => $order->products[$i]['name'], - 'products_price' => $order->products[$i]['price'], - 'final_price' => $order->products[$i]['final_price'], - 'products_tax' => $order->products[$i]['tax'], - 'products_quantity' => $order->products[$i]['qty']); - - tep_db_perform(TABLE_ORDERS_PRODUCTS, $sql_data_array); - - $order_products_id = tep_db_insert_id(); - - $attributes_exist = '0'; - if (isset($order->products[$i]['attributes'])) { - $attributes_exist = '1'; - for ($j=0, $n2=sizeof($order->products[$i]['attributes']); $j<$n2; $j++) { - if (DOWNLOAD_ENABLED == 'true') { - $attributes_query = "select popt.products_options_name, poval.products_options_values_name, pa.options_values_price, pa.price_prefix, pad.products_attributes_maxdays, pad.products_attributes_maxcount , pad.products_attributes_filename - from " . TABLE_PRODUCTS_OPTIONS . " popt, " . TABLE_PRODUCTS_OPTIONS_VALUES . " poval, " . TABLE_PRODUCTS_ATTRIBUTES . " pa - left join " . TABLE_PRODUCTS_ATTRIBUTES_DOWNLOAD . " pad - on pa.products_attributes_id=pad.products_attributes_id - where pa.products_id = '" . $order->products[$i]['id'] . "' - and pa.options_id = '" . $order->products[$i]['attributes'][$j]['option_id'] . "' - and pa.options_id = popt.products_options_id - and pa.options_values_id = '" . $order->products[$i]['attributes'][$j]['value_id'] . "' - and pa.options_values_id = poval.products_options_values_id - and popt.language_id = '" . $languages_id . "' - and poval.language_id = '" . $languages_id . "'"; - $attributes = tep_db_query($attributes_query); - } else { - $attributes = tep_db_query("select popt.products_options_name, poval.products_options_values_name, pa.options_values_price, pa.price_prefix from " . TABLE_PRODUCTS_OPTIONS . " popt, " . TABLE_PRODUCTS_OPTIONS_VALUES . " poval, " . TABLE_PRODUCTS_ATTRIBUTES . " pa where pa.products_id = '" . $order->products[$i]['id'] . "' and pa.options_id = '" . $order->products[$i]['attributes'][$j]['option_id'] . "' and pa.options_id = popt.products_options_id and pa.options_values_id = '" . $order->products[$i]['attributes'][$j]['value_id'] . "' and pa.options_values_id = poval.products_options_values_id and popt.language_id = '" . $languages_id . "' and poval.language_id = '" . $languages_id . "'"); - } - $attributes_values = tep_db_fetch_array($attributes); - - $sql_data_array = array('orders_id' => $insert_id, - 'orders_products_id' => $order_products_id, - 'products_options' => $attributes_values['products_options_name'], - 'products_options_values' => $attributes_values['products_options_values_name'], - 'options_values_price' => $attributes_values['options_values_price'], - 'price_prefix' => $attributes_values['price_prefix']); - - tep_db_perform(TABLE_ORDERS_PRODUCTS_ATTRIBUTES, $sql_data_array); - - if ((DOWNLOAD_ENABLED == 'true') && isset($attributes_values['products_attributes_filename']) && tep_not_null($attributes_values['products_attributes_filename'])) { - $sql_data_array = array('orders_id' => $insert_id, - 'orders_products_id' => $order_products_id, - 'orders_products_filename' => $attributes_values['products_attributes_filename'], - 'download_maxdays' => $attributes_values['products_attributes_maxdays'], - 'download_count' => $attributes_values['products_attributes_maxcount']); - - tep_db_perform(TABLE_ORDERS_PRODUCTS_DOWNLOAD, $sql_data_array); - } - } - } - } - - $cart_Paysera_ID = $cartID . '-' . $insert_id; - tep_session_register('cart_Paysera_ID'); - } - } - - return false; - } - - - function process_button() { - global $order, $currencies, $currency, $customer_id, $cart_Paysera_ID; - - $orderid = substr($cart_Paysera_ID, strpos($cart_Paysera_ID, '-')+1); - - $customer = $GLOBALS['order']->customer; - $paytext = 'Apmokejimas uz prekes ir paslaugas (uz nr. [order_nr]) ([site_name]). Uzsako: ' . $customer['firstname'] . ' ' . $customer['lastname'] . ', ' . $customer['street_address'] . ''; - - $arrLTReplace = array( - - '?' => 'A', - '?' => 'C', - '?' => 'E', - '?' => 'E', - '?' => 'I', - '?' => 'S', - '?' => 'U', - '?' => 'U', - '?' => 'Z', - '?' => 'a', - '?' => 'c', - '?' => 'e', - '?' => 'e', - '?' => 'i', - '?' => 's', - '?' => 'u', - '?' => 'u', - '?' => 'z' - - ); - - $process_button_string = ''; - - if(strcmp($currency,substr(MODULE_PAYMENT_PAYSERA_CURRENCY,-3)) != 0 && MODULE_PAYMENT_PAYSERA_CURRENCY !== 'Selected Currency') { - - echo 'Chosen currency is disabled for this payment module. '; - - $process_button_string .= ''; - - } else { - - try { - $request = WebToPay::buildRequest(array( - 'projectid' => MODULE_PAYMENT_PAYSERA_PROJECT, - 'sign_password' => MODULE_PAYMENT_PAYSERA_SIGNATURE, - 'orderid' => $orderid, - - 'amount' => number_format($order->info['total'] * $order->info['currency_value'], 2, '.', '') * 100, - 'currency' => $currency, - - 'lang' => substr($_SESSION['language'],0, 3), - - 'p_firstname' => strtr($order->customer['firstname'], $arrLTReplace), - 'p_lastname' => strtr($order->customer['lastname'], $arrLTReplace), - 'p_email' => strtr($order->customer['email_address'], $arrLTReplace), - 'p_street' => strtr($order->customer['street_address'], $arrLTReplace), - 'p_city' => strtr($order->customer['city'], $arrLTReplace), - 'p_state' => strtr($order->customer['state'], $arrLTReplace), - 'p_zip' => strtr($order->customer['postcode'], $arrLTReplace), - 'p_countrycode' => strtr($order->customer['country']['iso_code_2'], $arrLTReplace), - - 'accepturl' => tep_href_link(FILENAME_CHECKOUT_PROCESS, '', 'SSL'), - 'cancelurl' => tep_href_link(FILENAME_CHECKOUT_PAYMENT, '', 'SSL'), - 'callbackurl' => tep_href_link('ext/modules/payment/paysera/callback.php', '' , 'SSL', true, true, true), - - 'test' => (MODULE_PAYMENT_PAYSERA_TESTING == 'Yes') ? '1' : '0', - )); - } - catch (WebToPayException $e) { - echo $e->getMessage(); - } - } - - if($request) { - foreach( $request as $field=>$value) { - $process_button_string .= tep_draw_hidden_field($field, $value); - } - } else { - $process_button_string .= tep_draw_hidden_field('cancelurl', tep_href_link(FILENAME_CHECKOUT_PAYMENT, '', 'SSL'));; - } - - return $process_button_string; - } - - - function before_process() { - global $customer_id, $order, $order_totals, $sendto, $billto, $languages_id, $payment, $currencies, $cart, $cart_Paysera_ID; - global $$payment; - - $GLOBALS['order']->customer['email_address'] .= "\nDont\nSend\nEmail\nPlease\n:)"; - - $order_id = substr($cart_Paysera_ID, strpos($cart_Paysera_ID, '-')+1); - - $check_query = tep_db_query("select orders_status from " . TABLE_ORDERS . " where orders_id = '" . (int)$order_id . "'"); - - $sql_data_array = array('orders_id' => $order_id, - 'date_added' => 'now()', - 'customer_notified' => (SEND_EMAILS == 'true') ? '1' : '0', - 'comments' => $order->info['comments']); - - tep_db_perform(TABLE_ORDERS_STATUS_HISTORY, $sql_data_array); - - $products_ordered = ''; - $subtotal = 0; - $total_tax = 0; - - for ($i=0, $n=sizeof($order->products); $i<$n; $i++) { - // Stock Update - Joao Correia - if (STOCK_LIMITED == 'true') { - if (DOWNLOAD_ENABLED == 'true') { - $stock_query_raw = "SELECT products_quantity, pad.products_attributes_filename - FROM " . TABLE_PRODUCTS . " p - LEFT JOIN " . TABLE_PRODUCTS_ATTRIBUTES . " pa - ON p.products_id=pa.products_id - LEFT JOIN " . TABLE_PRODUCTS_ATTRIBUTES_DOWNLOAD . " pad - ON pa.products_attributes_id=pad.products_attributes_id - WHERE p.products_id = '" . tep_get_prid($order->products[$i]['id']) . "'"; - // Will work with only one option for downloadable products - // otherwise, we have to build the query dynamically with a loop - $products_attributes = $order->products[$i]['attributes']; - if (is_array($products_attributes)) { - $stock_query_raw .= " AND pa.options_id = '" . $products_attributes[0]['option_id'] . "' AND pa.options_values_id = '" . $products_attributes[0]['value_id'] . "'"; - } - $stock_query = tep_db_query($stock_query_raw); - } - else{ - $stock_query = tep_db_query("select products_quantity from " . TABLE_PRODUCTS . " where products_id = '" . tep_get_prid($order->products[$i]['id']) . "'"); - } - if (tep_db_num_rows($stock_query) > 0) { - $stock_values = tep_db_fetch_array($stock_query); - // do not decrement quantities if products_attributes_filename exists - if ((DOWNLOAD_ENABLED != 'true') || (!$stock_values['products_attributes_filename'])) { - $stock_left = $stock_values['products_quantity'] - $order->products[$i]['qty']; - } - else { - $stock_left = $stock_values['products_quantity']; - } - tep_db_query("update " . TABLE_PRODUCTS . " set products_quantity = '" . $stock_left . "' where products_id = '" . tep_get_prid($order->products[$i]['id']) . "'"); - if ( ($stock_left < 1) && (STOCK_ALLOW_CHECKOUT == 'false') ) { - tep_db_query("update " . TABLE_PRODUCTS . " set products_status = '0' where products_id = '" . tep_get_prid($order->products[$i]['id']) . "'"); - } - } - } - - // Update products_ordered (for bestsellers list) - tep_db_query("update " . TABLE_PRODUCTS . " set products_ordered = products_ordered + " . sprintf('%d', $order->products[$i]['qty']) . " where products_id = '" . tep_get_prid($order->products[$i]['id']) . "'"); - - //------insert customer choosen option to order-------- - $attributes_exist = '0'; - $products_ordered_attributes = ''; - if (isset($order->products[$i]['attributes'])) { - $attributes_exist = '1'; - for ($j=0, $n2=sizeof($order->products[$i]['attributes']); $j<$n2; $j++) { - if (DOWNLOAD_ENABLED == 'true') { - $attributes_query = "select popt.products_options_name, poval.products_options_values_name, pa.options_values_price, pa.price_prefix, pad.products_attributes_maxdays, pad.products_attributes_maxcount , pad.products_attributes_filename - from " . TABLE_PRODUCTS_OPTIONS . " popt, " . TABLE_PRODUCTS_OPTIONS_VALUES . " poval, " . TABLE_PRODUCTS_ATTRIBUTES . " pa - left join " . TABLE_PRODUCTS_ATTRIBUTES_DOWNLOAD . " pad - on pa.products_attributes_id=pad.products_attributes_id - where pa.products_id = '" . $order->products[$i]['id'] . "' - and pa.options_id = '" . $order->products[$i]['attributes'][$j]['option_id'] . "' - and pa.options_id = popt.products_options_id - and pa.options_values_id = '" . $order->products[$i]['attributes'][$j]['value_id'] . "' - and pa.options_values_id = poval.products_options_values_id - and popt.language_id = '" . $languages_id . "' - and poval.language_id = '" . $languages_id . "'"; - $attributes = tep_db_query($attributes_query); - } - else{ - $attributes = tep_db_query("select popt.products_options_name, poval.products_options_values_name, pa.options_values_price, pa.price_prefix from " . TABLE_PRODUCTS_OPTIONS . " popt, " . TABLE_PRODUCTS_OPTIONS_VALUES . " poval, " . TABLE_PRODUCTS_ATTRIBUTES . " pa where pa.products_id = '" . $order->products[$i]['id'] . "' and pa.options_id = '" . $order->products[$i]['attributes'][$j]['option_id'] . "' and pa.options_id = popt.products_options_id and pa.options_values_id = '" . $order->products[$i]['attributes'][$j]['value_id'] . "' and pa.options_values_id = poval.products_options_values_id and popt.language_id = '" . $languages_id . "' and poval.language_id = '" . $languages_id . "'"); - } - $attributes_values = tep_db_fetch_array($attributes); - - $products_ordered_attributes .= "\n\t" . $attributes_values['products_options_name'] . ' ' . $attributes_values['products_options_values_name']; - } - } - //------insert customer choosen option eof ---- - $total_weight += ($order->products[$i]['qty'] * $order->products[$i]['weight']); - $total_tax += tep_calculate_tax($total_products_price, $products_tax) * $order->products[$i]['qty']; - $total_cost += $total_products_price; - - $products_ordered .= $order->products[$i]['qty'] . ' x ' . $order->products[$i]['name'] . ' (' . $order->products[$i]['model'] . ') = ' . $currencies->display_price($order->products[$i]['final_price'], $order->products[$i]['tax'], $order->products[$i]['qty']) . $products_ordered_attributes . "\n"; - } - - // load the after_process function from the payment modules - $this->after_process(); - - $cart->reset(true); - - // unregister session variables used during checkout - tep_session_unregister('sendto'); - tep_session_unregister('billto'); - tep_session_unregister('shipping'); - tep_session_unregister('payment'); - tep_session_unregister('comments'); - - tep_session_unregister('cart_Paysera_ID'); - - tep_redirect(tep_href_link(FILENAME_CHECKOUT_SUCCESS, '', 'SSL')); - } - - function after_process() { - return false; - } - - function output_error() { - return false; - } - - - function check() { - if (!isset($this->_check)) { - $check_query = tep_db_query("select configuration_value from " . TABLE_CONFIGURATION . " where configuration_key = 'MODULE_PAYMENT_PAYSERA_STATUS'"); - $this->_check = tep_db_num_rows($check_query); - } - return $this->_check; - } - - - function install() { - $this->remove(); - - /* Hack */ - global $language, $module_type; - include_once(DIR_FS_CATALOG_LANGUAGES.$language.'/modules/'.$module_type.'/paysera.php'); - /*/Hack */ - - $field = tep_db_fetch_array(tep_db_query("SELECT `currencies_id` FROM " . TABLE_CURRENCIES . " WHERE `code` = 'LTL' LIMIT 1")); - if( !$field['currencies_id'] ){ - tep_db_query("insert into " . TABLE_CURRENCIES . "(`title`, `code`, `symbol_left`, `symbol_right`, `decimal_point`, `thousands_point`, `decimal_places`, `value`, `last_updated`) values ('Lithuania litas', 'LTL', 'Lt', '', '.', ',', '2', '2.38189947', NOW())"); - } - - tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('".MODULE_PAYMENT_PAYSERA_STATUS_TITLE."', 'MODULE_PAYMENT_PAYSERA_STATUS', 'True', '".MODULE_PAYMENT_PAYSERA_STATUS_DESCRIPTION."', '6', '3', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now())"); - tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('".MODULE_PAYMENT_PAYSERA_PROJECT_TITLE."', 'MODULE_PAYMENT_PAYSERA_PROJECT', '', '".MODULE_PAYMENT_PAYSERA_PROJECT_DESCRIPTION."', '6', '4', now())"); - tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('".MODULE_PAYMENT_PAYSERA_SIGNATURE_TITLE."', 'MODULE_PAYMENT_PAYSERA_SIGNATURE', '', '".MODULE_PAYMENT_PAYSERA_SIGNATURE_DESCRIPTION."', '6', '4', now())"); - tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('".MODULE_PAYMENT_PAYSERA_CURRENCY_TITLE."', 'MODULE_PAYMENT_PAYSERA_CURRENCY', 'Selected Currency', '".MODULE_PAYMENT_PAYSERA_CURRENCY_DESCRIPTION."', '6', '6', 'tep_cfg_select_option(array(\'Selected Currency\',\'Only USD\',\'Only LTL\',\'Only CAD\',\'Only EUR\',\'Only GBP\',\'Only JPY\'), ', now())"); - tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, use_function, set_function, date_added) values ('".MODULE_PAYMENT_PAYSERA_ZONE_TITLE."', 'MODULE_PAYMENT_PAYSERA_ZONE', '0', '".MODULE_PAYMENT_PAYSERA_ZONE_DESCRIPTION."', '6', '2', 'tep_get_zone_class_title', 'tep_cfg_pull_down_zone_classes(', now())"); - tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('".MODULE_PAYMENT_PAYSERA_SORT_ORDER_TITLE."', 'MODULE_PAYMENT_PAYSERA_SORT_ORDER', '0', '".MODULE_PAYMENT_PAYSERA_SORT_ORDER_DESCRIPTION."', '6', '0', now())"); - tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('".MODULE_PAYMENT_PAYSERA_TESTING_TITLE."', 'MODULE_PAYMENT_PAYSERA_TESTING', 'No', '".MODULE_PAYMENT_PAYSERA_TESTING_DESCRIPTION."', '6', '0', 'tep_cfg_select_option(array(\'Yes\', \'No\'), ', now())"); - tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, use_function, date_added) values ('Set Order Status', 'MODULE_PAYMENT_PAYSERA_ORDER_STATUS_ID', '0', 'Set the status of orders made with this payment module to this value', '6', '0', 'tep_cfg_pull_down_order_statuses(', 'tep_get_order_status_name', now())"); - - $d = dir(DIR_FS_CATALOG_LANGUAGES); - while ( false !== ($entry = $d->read()) ) { - if( $entry != "." && $entry != ".." && is_dir($d->path.$entry) && is_file($d->path.$entry.'/modules/'.$module_type.'/paysera.php') ){ - $langFile = implode('', file($d->path.$entry.'/modules/'.$module_type.'/paysera.php')); - preg_match("/MODULE_PAYMENT_PAYSERA_ORDERS_STATUS_20.*['\"]\s*,\s*['\"]([^\"']+)/", $langFile, $constant); - - $language_query = tep_db_query("SELECT languages_id from " . TABLE_LANGUAGES . " WHERE directory = '" . $entry . "'"); - $languageData = tep_db_fetch_array($language_query); - - if( $languageData ){ - tep_db_query("insert into " . TABLE_ORDERS_STATUS . " (orders_status_id, language_id, orders_status_name) values (20, '".$languageData['languages_id']."', '".trim($constant[1])."')"); - } - } - } - $d->close(); - - tep_db_query("ALTER TABLE ".TABLE_ORDERS." ADD `SSID` VARCHAR( 40 ) NOT NULL"); - } - - - function remove() { - tep_db_query("DELETE FROM " . TABLE_CONFIGURATION . " WHERE `configuration_key` IN ('" . implode("', '", $this->keys()) . "')"); - tep_db_query("DELETE FROM " . TABLE_ORDERS_STATUS . " WHERE `orders_status_id` = '20'"); - - $res = tep_db_query("SHOW FIELDS FROM " . TABLE_ORDERS); - while ($field = tep_db_fetch_array($res)) { - if( $field['Field'] == 'SSID' ){ - tep_db_query("ALTER TABLE " . TABLE_ORDERS . " DROP `SSID` "); - break; - } - } - } - - - function keys() { - return array('MODULE_PAYMENT_PAYSERA_STATUS', 'MODULE_PAYMENT_PAYSERA_TESTING', 'MODULE_PAYMENT_PAYSERA_PROJECT', 'MODULE_PAYMENT_PAYSERA_SIGNATURE', 'MODULE_PAYMENT_PAYSERA_CURRENCY', 'MODULE_PAYMENT_PAYSERA_ZONE', 'MODULE_PAYMENT_PAYSERA_SORT_ORDER', 'MODULE_PAYMENT_PAYSERA_ORDER_STATUS_ID'); - } - - - function tep_remove_order($order_id, $restock = false) { - if ($restock) { - $order_query = tep_db_query("SELECT products_id, products_quantity from " . TABLE_ORDERS_PRODUCTS . " where orders_id = '" . (int)$order_id . "'"); - while ($order = tep_db_fetch_array($order_query)) { - tep_db_query("UPDATE " . TABLE_PRODUCTS . " SET products_quantity = products_quantity + " . $order['products_quantity'] . ", products_ordered = products_ordered - " . $order['products_quantity'] . " where products_id = '" . (int)$order['products_id'] . "'"); - } - } - - tep_db_query("DELETE FROM " . TABLE_ORDERS . " WHERE orders_id = '" . (int)$order_id . "'"); - tep_db_query("DELETE FROM " . TABLE_ORDERS_PRODUCTS . " WHERE orders_id = '" . (int)$order_id . "'"); - tep_db_query("DELETE FROM " . TABLE_ORDERS_PRODUCTS_ATTRIBUTES . " WHERE orders_id = '" . (int)$order_id . "'"); - tep_db_query("DELETE FROM " . TABLE_ORDERS_STATUS_HISTORY . " WHERE orders_id = '" . (int)$order_id . "'"); - tep_db_query("DELETE FROM " . TABLE_ORDERS_TOTAL . " WHERE orders_id = '" . (int)$order_id . "'"); - } - - } - - diff --git a/includes/modules/payment/paysera/WebToPay.php b/includes/modules/payment/paysera/WebToPay.php deleted file mode 100644 index 64cf2fc..0000000 --- a/includes/modules/payment/paysera/WebToPay.php +++ /dev/null @@ -1,910 +0,0 @@ -. - * - * @package WebToPay - * @author EVP International - * @license http://www.gnu.org/licenses/lgpl.html - * @version 1.3.3 - * @link http://www.webtopay.com/ - */ - -class WebToPay { - - /** - * WebToPay Library version. - */ - const VERSION = '1.3.3'; - - - /** - * Server URL where all requests should go. - */ - const PAY_URL = 'https://www.mokejimai.lt/pay/'; - - - /** - * SMS answer url. - */ - const SMS_ANSWER_URL = 'https://www.mokejimai.lt/psms/respond/'; - - - /** - * Prefix for callback data. - */ - const PREFIX = 'wp_'; - - /** - * Identifies what verification method was used. - * - * Values can be: - * - false not verified - * - RESPONSE only response parameters are verified - * - SS1 SS1 verification - * - SS2 SS2 verification - */ - public static $verified = false; - - - /** - * If true, check SS2 if false, skip to SS1 - */ - private static $SS2 = true; - - - /** - * Toggle SS2 checking. Usualy you don't need to use this method, because - * by default first SS2 support are checked and if it doesn't work, - * fallback to SS1. - * - * Use this method if your server supports SS2, but you want to use SS1. - */ - public static function toggleSS2($value) { - self::$SS2 = (bool) $value; - } - - - /** - * Throw exception. - * - * @param string $code - * @return void - */ - public static function throwResponseError($code) { - $errors = array( - '0x1' => self::_('mokėjimo suma per maža'), - '0x2' => self::_('mokėjimo suma per didelė'), - '0x3' => self::_('nurodyta valiuta neaptarnaujama'), - '0x4' => self::_('nėra sumos arba valiutos'), - '0x6' => self::_('klaidos kodas nebenaudojamas'), - '0x7' => self::_('išjungtas testavimo režimas'), - '0x8' => self::_('jūs uždraudėte šį mokėjimo būdą'), - '0x9' => self::_('blogas "paytext" kintamojo kodavimas (turi būti utf-8)'), - '0x10' => self::_('tuščias arba neteisingai užpildytas "orderid"'), - '0x11' => self::_('mokėjimas negalimas, kol projektas nepatvirtintas arba jeigu jis yra blokuotas'), - '0x12' => self::_('negautas "projectid" parametras, nors jis yra privalomas'), - '0x13' => self::_('"accepturl", "cancellurl" arba "callbacurl" skiriasi nuo projekte patvirtintų adresų'), - '0x14' => self::_('blogai sugeneruotas paraštas ("sign" parametras)'), - '0x15' => self::_('klaidingi kai kurie iš perduotų parametrų'), - '0x15x0' => self::_('neteisingas vienas iš šių parametrų: cancelurl, accepturl, callbackurl'), - ); - - if (isset($errors[$code])) { - $msg = $errors[$code]; - } - else { - $msg = self::_('Nenumatyta klaida'); - } - - throw new WebToPayException($msg); - } - - - /** - * Returns specification array for request. - * - * @return array - */ - public static function getRequestSpec() { - // Array structure: - // * name – request item name. - // * maxlen – max allowed value for item. - // * required – is this item is required. - // * user – if true, user can set value of this item, if false - // item value is generated. - // * isrequest – if true, item will be included in request array, if - // false, item only be used internaly and will not be - // included in outgoing request array. - // * regexp – regexp to test item value. - return array( - array('projectid', 11, true, true, true, '/^\d+$/'), - array('orderid', 40, true, true, true, ''), - array('lang', 3, false, true, true, '/^[a-z]{3}$/i'), - array('amount', 11, false, true, true, '/^\d+$/'), - array('currency', 3, false, true, true, '/^[a-z]{3}$/i'), - array('accepturl', 255, true, true, true, ''), - array('cancelurl', 255, true, true, true, ''), - array('callbackurl', 255, true, true, true, ''), - array('payment', 20, false, true, true, ''), - array('country', 2, false, true, true, '/^[a-z_]{2}$/i'), - array('paytext', 255, false, true, true, ''), - array('p_firstname', 255, false, true, true, ''), - array('p_lastname', 255, false, true, true, ''), - array('p_email', 255, false, true, true, ''), - array('p_street', 255, false, true, true, ''), - array('p_city', 255, false, true, true, ''), - array('p_state', 20, false, true, true, ''), - array('p_zip', 20, false, true, true, ''), - array('p_countrycode', 2, false, true, true, '/^[a-z]{2}$/i'), - array('sign', 255, true, false, true, ''), - array('sign_password', 255, true, true, false, ''), - array('only_payments', 0, false, true, true, ''), - array('disalow_payments', 0, false, true, true, ''), - array('repeat_request', 1, false, false, true, '/^[01]$/'), - array('test', 1, false, true, true, '/^[01]$/'), - array('version', 9, true, false, true, '/^\d+\.\d+$/'), - ); - } - - - /** - * Returns specification array for repeat request. - * - * @return array - */ - public static function getRepeatRequestSpec() { - // Array structure: - // * name – request item name. - // * maxlen – max allowed value for item. - // * required – is this item is required. - // * user – if true, user can set value of this item, if false - // item value is generated. - // * isrequest – if true, item will be included in request array, if - // false, item only be used internaly and will not be - // included in outgoing request array. - // * regexp – regexp to test item value. - return array( - array('projectid', 11, true, true, true, '/^\d+$/'), - array('orderid', 40, true, true, true, ''), - array('sign', 255, true, false, true, ''), - array('sign_password', 255, true, true, false, ''), - array('repeat_request', 1, true, false, true, '/^1$/'), - array('version', 9, true, false, true, '/^\d+\.\d+$/'), - ); - } - - - /** - * Returns specification array for makro response. - * - * @return array - */ - public static function getMakroResponseSpec() { - // Array structure: - // * name – request item name. - // * maxlen – max allowed value for item. - // * required – is this item is required in response. - // * mustcheck – this item must be checked by user. - // * isresponse – if false, item must not be included in response array. - // * regexp – regexp to test item value. - return array( - 'projectid' => array(11, true, true, true, '/^\d+$/'), - 'orderid' => array(40, false, false, true, ''), - 'lang' => array(3, false, false, true, '/^[a-z]{3}$/i'), - 'amount' => array(11, false, false, true, '/^\d+$/'), - 'currency' => array(3, false, false, true, '/^[a-z]{3}$/i'), - 'payment' => array(20, false, false, true, ''), - 'country' => array(2, false, false, true, '/^[a-z_]{2}$/i'), - 'paytext' => array(0, false, false, true, ''), - '_ss2' => array(0, true, false, true, ''), - '_ss1' => array(0, false, false, true, ''), - 'name' => array(255, false, false, true, ''), - 'surename' => array(255, false, false, true, ''), - 'status' => array(255, false, false, true, ''), - 'error' => array(20, false, false, true, ''), - 'test' => array(1, false, false, true, '/^[01]$/'), - - 'p_email' => array(0, false, false, true, ''), - 'payamount' => array(0, false, false, true, ''), - 'paycurrency' => array(0, false, false, true, ''), - - 'version' => array(9, true, false, true, '/^\d+\.\d+$/'), - - 'sign_password' => array(255, false, true, false, ''), - ); - } - - - - /** - * Returns specification array for mikro response. - * - * @return array - */ - public static function getMikroResponseSpec() { - // Array structure: - // * name – request item name. - // * maxlen – max allowed value for item. - // * required – is this item is required in response. - // * mustcheck – this item must be checked by user. - // * isresponse – if false, item must not be included in response array. - // * regexp – regexp to test item value. - return array( - 'to' => array(0, true, false, true, ''), - 'sms' => array(0, true, false, true, ''), - 'from' => array(0, true, false, true, ''), - 'operator' => array(0, true, false, true, ''), - 'amount' => array(0, true, false, true, ''), - 'currency' => array(0, true, false, true, ''), - 'country' => array(0, true, false, true, ''), - 'id' => array(0, true, false, true, ''), - '_ss2' => array(0, true, false, true, ''), - '_ss1' => array(0, true, false, true, ''), - 'test' => array(0, true, false, true, ''), - 'key' => array(0, true, false, true, ''), - //'version' => array(9, true, false, true, '/^\d+\.\d+$/'), - ); - } - - /** - * Checks user given request data array. - * - * If any errors occurs, WebToPayException will be raised. - * - * This method returns validated request array. Returned array contains - * only those items from $data, that are needed. - * - * @param array $data - * @return array - */ - public static function checkRequestData($data, $specs) { - $request = array(); - foreach ($specs as $spec) { - list($name, $maxlen, $required, $user, $isrequest, $regexp) = $spec; - if (!$user) continue; - if ($required && !isset($data[$name])) { - $e = new WebToPayException( - self::_("'%s' is required but missing.", $name), - WebToPayException::E_MISSING); - $e->setField($name); - throw $e; - } - - if (!empty($data[$name])) { - if ($maxlen && strlen($data[$name]) > $maxlen) { - $e = new WebToPayException( - self::_("'%s' value '%s' is too long, %d characters allowed.", - $name, $data[$name], $maxlen), - WebToPayException::E_MAXLEN); - $e->setField($name); - throw $e; - } - - if ('' != $regexp && !preg_match($regexp, $data[$name])) { - $e = new WebToPayException( - self::_("'%s' value '%s' is invalid.", $name, $data[$name]), - WebToPayException::E_REGEXP); - $e->setField($name); - throw $e; - } - } - - if ($isrequest && isset($data[$name])) { - $request[$name] = $data[$name]; - } - } - - return $request; - } - - - /** - * Puts signature on request data array. - * - * @param string $request - * @param string $password - * @return string - */ - public static function signRequest($request, $password) { - $fields = array( - 'projectid', 'orderid', 'lang', 'amount', 'currency', - 'accepturl', 'cancelurl', 'callbackurl', 'payment', 'country', - 'p_firstname', 'p_lastname', 'p_email', 'p_street', - 'p_city', 'p_state', 'p_zip', 'p_countrycode', 'test', - 'version' - ); - $data = ''; - foreach ($fields as $key) { - if (isset($request[$key]) && trim($request[$key]) != '') { - $data .= $request[$key]; - } - } - $request['sign'] = md5($data . $password); - - return $request; - } - - - /** - * Builds request data array. - * - * This method checks all given data and generates correct request data - * array or raises WebToPayException on failure. - * - * Method accepts single parameter $data of array type. All possible array - * keys are described here: - * https://www.mokejimai.lt/makro_specifikacija.html - * - * @param array $data Information about current payment request. - * @return array - */ - public static function buildRequest($data) { - $specs = self::getRequestSpec(); - $request = self::checkRequestData($data, $specs); - $version = explode('.', self::VERSION); - $request['version'] = $version[0].'.'.$version[1]; - $request = self::signRequest($request, $data['sign_password']); - return $request; - } - - - /** - * Builds repeat request data array. - * - * This method checks all given data and generates correct request data - * array or raises WebToPayException on failure. - * - * Method accepts single parameter $data of array type. All possible array - * keys are described here: - * https://www.mokejimai.lt/makro_specifikacija.html - * - * @param array $data Information about current payment request. - * @return array - */ - public static function buildRepeatRequest($data) { - $specs = self::getRepeatRequestSpec(); - $request = self::checkRequestData($data, $specs); - $request['repeat_request'] = '1'; - $version = explode('.', self::VERSION); - $request['version'] = $version[0].'.'.$version[1]; - $request = self::signRequest($request, $data['sign_password']); - return $request; - } - - /** - * Download certificate from webtopay.com. - * - * @param string $cert - * @return string - */ - public static function getCert($cert) { - $fp = fsockopen("downloads.webtopay.com", 80, $errno, $errstr, 30); - if (!$fp) { - throw new WebToPayException( - self::_('Payment check: Can\'t get cert from '. - 'downloads.webtopay.com/download/%s', - $cert), - WebToPayException::E_INVALID); - return false; - } - - $out = "GET /download/" . $cert . " HTTP/1.1\r\n"; - $out .= "Host: downloads.webtopay.com\r\n"; - $out .= "Connection: Close\r\n\r\n"; - - $content = ''; - - fwrite($fp, $out); - while (!feof($fp)) $content .= fgets($fp, 8192); - fclose($fp); - - list($header, $content) = explode("\r\n\r\n", $content, 2); - - return $content; - } - - /** - * Check is response certificate is valid - * - * @param string $response - * @param string $cert - * @return bool - */ - public static function checkResponseCert($response, $cert='public.key') { - $pKeyP = self::getCert($cert); - if (!$pKeyP) { - throw new WebToPayException( - self::_('Can\'t get openssl public key for %s', $cert), - WebToPayException::E_INVALID); - } - - $_SS2 = ''; - foreach ($response as $key => $value) { - if ($key!='_ss2') $_SS2 .= "{$value}|"; - } - $ok = openssl_verify($_SS2, base64_decode($response['_ss2']), $pKeyP); - - if ($ok !== 1) { - throw new WebToPayException( - self::_('Can\'t verify SS2 for %s', $cert), - WebToPayException::E_INVALID); - } - - return true; - } - - public static function checkResponseData($response, $mustcheck_data, $specs) { - $resp_keys = array(); - foreach ($specs as $name => $spec) { - list($maxlen, $required, $mustcheck, $is_response, $regexp) = $spec; - if ($required && !isset($response[$name])) { - $e = new WebToPayException( - self::_("'%s' is required but missing.", $name), - WebToPayException::E_MISSING); - $e->setField($name); - throw $e; - } - - if ($mustcheck) { - if (!isset($mustcheck_data[$name])) { - $e = new WebToPayException( - self::_("'%s' must exists in array of second parameter ". - "of checkResponse() method.", $name), - WebToPayException::E_USER_PARAMS); - $e->setField($name); - throw $e; - } - - if ($is_response) { - if ($response[$name] != $mustcheck_data[$name]) { - $e = new WebToPayException( - self::_("'%s' yours and requested value is not ". - "equal ('%s' != '%s') ", - $name, $mustcheck_data[$name], $response[$name]), - WebToPayException::E_INVALID); - $e->setField($name); - throw $e; - } - } - } - - if (!empty($response[$name])) { - if ($maxlen && strlen($response[$name]) > $maxlen) { - $e = new WebToPayException( - self::_("'%s' value '%s' is too long, %d characters allowed.", - $name, $response[$name], $maxlen), - WebToPayException::E_MAXLEN); - $e->setField($name); - throw $e; - } - - if ('' != $regexp && !preg_match($regexp, $response[$name])) { - $e = new WebToPayException( - self::_("'%s' value '%s' is invalid.", $name, $response[$name]), - WebToPayException::E_REGEXP); - $e->setField($name); - throw $e; - } - } - - if (isset($response[$name])) { - $resp_keys[] = $name; - } - } - - // Filter only parameters passed from webtopay - $_response = array(); - foreach (array_keys($response) as $key) { - if (in_array($key, $resp_keys)) { - $_response[$key] = $response[$key]; - } - } - - return $_response; - } - - - /** - * Check if SS2 checking is available and enabled. - * - * @return bool - */ - public static function useSS2() { - if (!self::$SS2) return false; - if (!function_exists('openssl_pkey_get_public')) return false; - return true; - } - - - /** - * Check for SS1, which is not depend on openssl functions. - * - * @param array $response - * @param string $passwd - * @param int $orderid - * @return bool - */ - public static function checkSS1($response, $passwd, $orderid) { - if (32 != strlen($passwd)) { - $passwd = md5($passwd); - } - - $_SS1 = array( - $passwd, - $orderid, - intval($response['test']), - 1 - ); - - $_SS1 = implode('|', $_SS1); - if ($response['_ss1'] != md5($_SS1)) { - throw new WebToPayException( - self::_('Can\'t verify SS1'), - WebToPayException::E_INVALID); - } - - return true; - } - - - /** - * Return type and specification of given response array. - * - * @param array $response - * @return array($type, $specs) - */ - public static function getSpecsForResponse($response) { - if ( - isset($response['to']) && - isset($response['from']) && - isset($response['sms']) && - !isset($response['projectid']) - ) - { - $type = 'mikro'; - $specs = self::getMikroResponseSpec(); - } - else { - $type = 'makro'; - $specs = self::getMakroResponseSpec(); - } - - return array($type, $specs); - } - - - public static function getPrefixed($data, $prefix) { - if (empty($prefix)) return $data; - $ret = array(); - foreach ($data as $key => $val) { - if (strpos($key, $prefix) === 0 && strlen($key) > 3) { - $ret[substr($key, 3)] = $val; - } - } - return $ret; - } - - - /** - * Checks and validates response from WebToPay server. - * - * This function accepts both mikro and makro responses. - * - * First parameter usualy should by $_GET array. - * - * Description about response can be found here: - * makro: https://www.mokejimai.lt/makro_specifikacija.html - * mikro: https://www.mokejimai.lt/mikro_mokejimu_specifikacija_SMS.html - * - * If response is not correct, WebToPayException will be raised. - * - * @param array $response Response array. - * @param array $user_data - * @return void - */ - public static function checkResponse($response, $user_data=array()) { - self::$verified = false; - - $response = self::getPrefixed($response, self::PREFIX); - - // *get* response type (makro|mikro) - list($type, $specs) = self::getSpecsForResponse($response); - - try { - // *check* response - $version = explode('.', self::VERSION); - $version = $version[0].'.'.$version[1]; - if ('makro' == $type && $response['version'] != $version) { - throw new WebToPayException( - self::_('Incompatible library and response versions: ' . - 'libwebtopay %s, response %s', self::VERSION, $response['version']), - WebToPayException::E_INVALID); - } - - if ('makro' == $type && $response['projectid'] != $user_data['projectid']) { - throw new WebToPayException( - self::_('Bad projectid: ' . - 'libwebtopay %s, response %s', self::VERSION, $response['version']), - WebToPayException::E_INVALID); - } - - if ('makro' == $type) { - self::$verified = 'RESPONSE VERSION '.$response['version'].' OK'; - } - - $orderid = 'makro' == $type ? $response['orderid'] : $response['id']; - $password = $user_data['sign_password']; - - // *check* SS2 - if (self::useSS2()) { - $cert = 'public.key'; - if (self::checkResponseCert($response, $cert)) { - self::$verified = 'SS2 public.key'; - } - } - - // *check* SS1 - else if (self::checkSS1($response, $password, $orderid)) { - self::$verified = 'SS1'; - } - - // *check* status - if ('makro' == $type && '1' != $response['status']) { - throw new WebToPayException( - self::_('Returned transaction status is %d, successful status '. - 'should be 1.', $response['status']), - WebToPayException::E_INVALID); - } - - } - - catch (WebToPayException $e) { - if (isset($user_data['log'])) { - self::log('ERR', - self::responseToLog($type, $response) . - ' ('. get_class($e).': '. $e->getMessage().')', - $user_data['log']); - } - throw $e; - } - - if (isset($user_data['log'])) { - self::log('OK', self::responseToLog($type, $response), $user_data['log']); - } - - return $response; - } - - public static function responseToLog($type, $req) { - if ('mikro' == $type) { - return self::mikroResponseToLog($req); - } - else { - return self::makroResponseToLog($req); - } - } - - public static function mikroResponseToLog($req) { - $ret = array(); - foreach (array('to', 'from', 'id', 'sms') as $key) { - $ret[] = $key.':"'.$req[$key].'"'; - } - - return 'MIKRO '.implode(', ', $ret); - } - - public static function makroResponseToLog($req) { - $ret = array(); - foreach (array('projectid', 'orderid', 'payment') as $key) { - $ret[] = $key.':"'.$req[$key].'"'; - } - - return 'MAKRO '.implode(', ', $ret); - } - - public static function mikroAnswerToLog($answer) { - $ret = array(); - foreach (array('id', 'msg') as $key) { - $ret[] = $key.':"'.$answer[$key].'"'; - } - - return 'MIKRO [answer] '.implode(', ', $ret); - } - - public static function log($type, $msg, $logfile=null) { - if (!isset($logfile)) { - return false; - } - - $fp = @fopen($logfile, 'a'); - if (!$fp) { - throw new WebToPayException( - self::_('Can\'t write to logfile: %s', $logfile), WebToPayException::E_LOG); - } - - $logline = array(); - - // *log* type - $logline[] = $type; - - // *log* REMOTE_ADDR - if (isset($_SERVER['REMOTE_ADDR'])) { - $logline[] = $_SERVER['REMOTE_ADDR']; - } - else { - $logline[] = '-'; - } - - // *log* datetime - $logline[] = date('[Y-m-d H:i:s O]'); - - // *log* version - $logline[] = 'v'.self::VERSION.':'; - - // *log* message - $logline[] = $msg; - - $logline = implode(' ', $logline)."\n"; - fwrite($fp, $logline); - fclose($fp); - - // clear big log file - if (filesize($logfile) > 1024 * 1024 * pi()) { - copy($logfile, $logfile.'.old'); - unlink($logfile); - } - } - - - /** - * Sends SMS answer. - * - * @param array $answer - * @return void - */ - public static function smsAnswer($answer) { - $url = parse_url(self::SMS_ANSWER_URL); - if ('https' == $url['scheme']) { - $host = 'ssl://'.$url['host']; - $port = 443; - } - else { - $host = $url['host']; - $port = 80; - } - - try { - $fp = fsockopen($host, $port, $errno, $errstr, 30); - if (!$fp) { - throw new WebToPayException( - self::_('Can\'t connect to %s', self::SMS_ANSWER_URL), - WebToPayException::E_SMS_ANSWER); - } - - $data = array( - 'id' => $answer['id'], - 'msg' => $answer['msg'], - 'transaction' => md5($answer['sign_password'].'|'.$answer['id']), - ); - - $query = $url['path'].'?'.http_build_query($data); - - $out = "GET " . $query . " HTTP/1.1\r\n"; - $out .= "Host: ".$url['host']."\r\n"; - $out .= "Connection: Close\r\n\r\n"; - - $content = ''; - - fwrite($fp, $out); - while (!feof($fp)) $content .= fgets($fp, 8192); - fclose($fp); - - list($header, $content) = explode("\r\n\r\n", $content, 2); - - $content = trim($content); - if (strpos($content, 'OK') !== 0) { - throw new WebToPayException( - self::_('Error: %s', $content), - WebToPayException::E_SMS_ANSWER); - } - } - - catch (WebToPayException $e) { - if (isset($answer['log'])) { - self::log('ERR', - self::mikroAnswerToLog($answer). - ' ('. get_class($e).': '. $e->getMessage().')', - $answer['log']); - } - throw $e; - } - - if (isset($answer['log'])) { - self::log('OK', self::mikroAnswerToLog($answer), $answer['log']); - } - - } - - - /** - * I18n support. - */ - public static function _() { - $args = func_get_args(); - if (sizeof($args) > 1) { - return call_user_func_array('sprintf', $args); - } - else { - return $args[0]; - } - } - -} - - - -class WebToPayException extends Exception { - - /** - * Missing field. - */ - const E_MISSING = 1; - - /** - * Invalid field value. - */ - const E_INVALID = 2; - - /** - * Max length exceeded. - */ - const E_MAXLEN = 3; - - /** - * Regexp for field value doesn't match. - */ - const E_REGEXP = 4; - - /** - * Missing or invalid user given parameters. - */ - const E_USER_PARAMS = 5; - - /** - * Logging errors - */ - const E_LOG = 6; - - /** - * SMS answer errors - */ - const E_SMS_ANSWER = 7; - - - - protected $field_name = false; - - public function setField($field_name) { - $this->field_name = $field_name; - } - - public function getField() { - return $this->field_name; - } - -} - From a6ce8c83e6f8f8e70f88a45c5900dc5c0a23e448 Mon Sep 17 00:00:00 2001 From: Ed Mark Date: Mon, 23 May 2016 14:44:22 +0300 Subject: [PATCH 2/2] new version --- DIEGIMAS.txt | 9 + README.md | 20 + README.txt | 9 + ext/modules/payment/paysera/callback.php | 75 + .../english/modules/payment/paysera.php | 49 + includes/modules/payment/paysera.php | 562 ++++ includes/modules/payment/paysera/WebToPay.php | 2357 +++++++++++++++++ 7 files changed, 3081 insertions(+) create mode 100644 DIEGIMAS.txt create mode 100644 README.md create mode 100644 README.txt create mode 100644 ext/modules/payment/paysera/callback.php create mode 100644 includes/languages/english/modules/payment/paysera.php create mode 100644 includes/modules/payment/paysera.php create mode 100644 includes/modules/payment/paysera/WebToPay.php diff --git a/DIEGIMAS.txt b/DIEGIMAS.txt new file mode 100644 index 0000000..19a0b18 --- /dev/null +++ b/DIEGIMAS.txt @@ -0,0 +1,9 @@ +Diegimas + +1. Padarykite atsargine kopija[nebutina]. +2. Nukopijuokite failus i osCommerce direktoija. +3. Administracijoje idiekite aktyvuokite mokejimo buda, pasirinkite ji, paspauskite radagavima ir iveskite reikalingus duomenis. + +Kontaktai + +Kilus klausimams prasome kreiptis i pagalba@paysera.lt diff --git a/README.md b/README.md new file mode 100644 index 0000000..8a2e3c3 --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +plugin-oscommerce +================= + +Paysera.com payment gateway plugin for osCommerce + +Requirements +------------ + +- osCommerce + +Installation +------------ + +1. Download this repository as zip and extract "ext,includes" folders into osCommerce main directory. +2. In admin panel activate payment method, select it, press edit and fill in all information. + +Contacts +-------- + +If any problems occur please feel free to seek help via support@paysera.com \ No newline at end of file diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..52c42c4 --- /dev/null +++ b/README.txt @@ -0,0 +1,9 @@ +Installation + +1. BACKUP ALL FILES[optional]. +2. Copy files to osCommerce directory. +3. In admin panel active payment method, select it, press edit and fill in all information. + +Contacts + +If any problems occur please feel free to seek help via support@paysera.com diff --git a/ext/modules/payment/paysera/callback.php b/ext/modules/payment/paysera/callback.php new file mode 100644 index 0000000..0c9d494 --- /dev/null +++ b/ext/modules/payment/paysera/callback.php @@ -0,0 +1,75 @@ + MODULE_PAYMENT_PAYSERA_PROJECT, + 'sign_password' => MODULE_PAYMENT_PAYSERA_SIGNATURE, + )); + + if ($response['status'] == 1) { + $order_query = tep_db_query(' + SELECT `orders_status`, `currency`, `currency_value` + FROM ' . TABLE_ORDERS . ' + WHERE + `orders_id` = ' . intval($response['orderid']) + ); + + if (tep_db_num_rows($order_query) <= 0) { + throw new WebToPayException('Order not found!'); + } + + $order = tep_db_fetch_array($order_query); + + if ($order['currency'] != $response['currency']) { + throw new WebToPayException('Bad currency!'); + } + + $total_query = tep_db_query(' + SELECT `value` + FROM ' . TABLE_ORDERS_TOTAL . ' + WHERE + `orders_id` = ' . intval($response['orderid']) . ' + AND `class` = "ot_total" + LIMIT 1' + ); + + $total = tep_db_fetch_array($total_query); + + + if (intval(number_format($total['value'], 2, '', '')) > ($response['amount'])) { + throw new WebToPayException('Bad amount!'); + } + + $sql_data_array = array(); + $sql_data_array['orders_id'] = $response['orderid']; + $sql_data_array['date_added'] = 'now()'; + $sql_data_array['customer_notified'] = '0'; + $sql_data_array['comments'] = print_r($_REQUEST, true); + + $sql_data_array['orders_status_id'] = MODULE_PAYMENT_PAYSERA_ORDER_STATUS_ID; + tep_db_query(' + UPDATE ' . TABLE_ORDERS . ' + SET + `orders_status` = ' . intval(MODULE_PAYMENT_PAYSERA_ORDER_STATUS_ID) . ', + `last_modified` = NOW() + WHERE + `orders_id` = ' . intval($response['orderid']) + ); + + tep_db_perform(TABLE_ORDERS_STATUS_HISTORY, $sql_data_array); + + echo 'OK'; + + } + +} catch (Exception $e) { + echo get_class($e) . ': ' . $e->getMessage(); +} + + diff --git a/includes/languages/english/modules/payment/paysera.php b/includes/languages/english/modules/payment/paysera.php new file mode 100644 index 0000000..a6cb6af --- /dev/null +++ b/includes/languages/english/modules/payment/paysera.php @@ -0,0 +1,49 @@ +code = 'paysera'; + $this->title = MODULE_PAYMENT_PAYSERA_TEXT_TITLE; + $this->description = MODULE_PAYMENT_PAYSERA_TEXT_DESCRIPTION; + $this->sort_order = MODULE_PAYMENT_PAYSERA_SORT_ORDER; + $this->enabled = ((MODULE_PAYMENT_PAYSERA_STATUS == 'True') ? true : false); + + $libwebtopay_dir = substr(__FILE__, 0, -4) .'/WebToPay.php'; + if ( !is_file($libwebtopay_dir) ) { + throw new Exception('LibWebToPay library not found in '. $libwebtopay_dir .'. '. "\n" .' + You may download it from here: http://bitbucket.org/webtopay/libwebtopay/raw/default/WebToPay.php'); + } + require_once $libwebtopay_dir; + + $this->form_action_url = 'https://www.paysera.com/pay/'; + } + + + function update_status() { + global $order; + + if ( ($this->enabled == true) && ((int)MODULE_PAYMENT_PAYPAL_STANDARD_ZONE > 0) ) { + $check_flag = false; + $check_query = tep_db_query("select zone_id from " . TABLE_ZONES_TO_GEO_ZONES . " where geo_zone_id = '" . MODULE_PAYMENT_PAYPAL_STANDARD_ZONE . "' and zone_country_id = '" . $order->billing['country']['id'] . "' order by zone_id"); + while ($check = tep_db_fetch_array($check_query)) { + if ($check['zone_id'] < 1) { + $check_flag = true; + break; + } elseif ($check['zone_id'] == $order->billing['zone_id']) { + $check_flag = true; + break; + } + } + + if ($check_flag == false) { + $this->enabled = false; + } + } + } + + function javascript_validation() { + return false; + } + + function pre_confirmation_check() { + global $cartID, $cart; + + if (empty($cart->cartID)) { + $cartID = $cart->cartID = $cart->generate_cart_id(); + } + + if (!tep_session_is_registered('cartID')) { + tep_session_register('cartID'); + } + } + + function selection() { + global $cart_Paysera_ID; + + if (tep_session_is_registered('cart_Paysera_ID')) { + $order_id = substr($cart_Paysera_ID, strpos($cart_Paysera_ID, '-')+1); + + $check_query = tep_db_query('select orders_id from ' . TABLE_ORDERS_STATUS_HISTORY . ' where orders_id = "' . (int)$order_id . '" limit 1'); + + if (tep_db_num_rows($check_query) < 1) { + tep_db_query('delete from ' . TABLE_ORDERS . ' where orders_id = "' . (int)$order_id . '"'); + tep_db_query('delete from ' . TABLE_ORDERS_TOTAL . ' where orders_id = "' . (int)$order_id . '"'); + tep_db_query('delete from ' . TABLE_ORDERS_STATUS_HISTORY . ' where orders_id = "' . (int)$order_id . '"'); + tep_db_query('delete from ' . TABLE_ORDERS_PRODUCTS . ' where orders_id = "' . (int)$order_id . '"'); + tep_db_query('delete from ' . TABLE_ORDERS_PRODUCTS_ATTRIBUTES . ' where orders_id = "' . (int)$order_id . '"'); + tep_db_query('delete from ' . TABLE_ORDERS_PRODUCTS_DOWNLOAD . ' where orders_id = "' . (int)$order_id . '"'); + + tep_session_unregister('cart_Paysera_ID'); + } + } + + return array('id' => $this->code, + 'module' => $this->title); + } + + function confirmation() { + global $cartID, $cart_Paysera_ID, $customer_id, $languages_id, $order, $order_total_modules; + + if (tep_session_is_registered('cartID')) { + $insert_order = false; + + if (tep_session_is_registered('cart_Paysera_ID')) { + $order_id = substr($cart_Paysera_ID, strpos($cart_Paysera_ID, '-')+1); + + $curr_check = tep_db_query("select currency from " . TABLE_ORDERS . " where orders_id = '" . (int)$order_id . "'"); + $curr = tep_db_fetch_array($curr_check); + + if ( ($curr['currency'] != $order->info['currency']) || ($cartID != substr($cart_Paysera_ID, 0, strlen($cartID))) ) { + $check_query = tep_db_query('select orders_id from ' . TABLE_ORDERS_STATUS_HISTORY . ' where orders_id = "' . (int)$order_id . '" limit 1'); + + if (tep_db_num_rows($check_query) < 1) { + tep_db_query('delete from ' . TABLE_ORDERS . ' where orders_id = "' . (int)$order_id . '"'); + tep_db_query('delete from ' . TABLE_ORDERS_TOTAL . ' where orders_id = "' . (int)$order_id . '"'); + tep_db_query('delete from ' . TABLE_ORDERS_STATUS_HISTORY . ' where orders_id = "' . (int)$order_id . '"'); + tep_db_query('delete from ' . TABLE_ORDERS_PRODUCTS . ' where orders_id = "' . (int)$order_id . '"'); + tep_db_query('delete from ' . TABLE_ORDERS_PRODUCTS_ATTRIBUTES . ' where orders_id = "' . (int)$order_id . '"'); + tep_db_query('delete from ' . TABLE_ORDERS_PRODUCTS_DOWNLOAD . ' where orders_id = "' . (int)$order_id . '"'); + } + + $insert_order = true; + } + } else { + $insert_order = true; + } + + if ($insert_order == true) { + $order_totals = array(); + if (is_array($order_total_modules->modules)) { + reset($order_total_modules->modules); + while (list(, $value) = each($order_total_modules->modules)) { + $class = substr($value, 0, strrpos($value, '.')); + if ($GLOBALS[$class]->enabled) { + for ($i=0, $n=sizeof($GLOBALS[$class]->output); $i<$n; $i++) { + if (tep_not_null($GLOBALS[$class]->output[$i]['title']) && tep_not_null($GLOBALS[$class]->output[$i]['text'])) { + $order_totals[] = array('code' => $GLOBALS[$class]->code, + 'title' => $GLOBALS[$class]->output[$i]['title'], + 'text' => $GLOBALS[$class]->output[$i]['text'], + 'value' => $GLOBALS[$class]->output[$i]['value'], + 'sort_order' => $GLOBALS[$class]->sort_order); + } + } + } + } + } + + $sql_data_array = array('customers_id' => $customer_id, + 'customers_name' => $order->customer['firstname'] . ' ' . $order->customer['lastname'], + 'customers_company' => $order->customer['company'], + 'customers_street_address' => $order->customer['street_address'], + 'customers_suburb' => $order->customer['suburb'], + 'customers_city' => $order->customer['city'], + 'customers_postcode' => $order->customer['postcode'], + 'customers_state' => $order->customer['state'], + 'customers_country' => $order->customer['country']['title'], + 'customers_telephone' => $order->customer['telephone'], + 'customers_email_address' => $order->customer['email_address'], + 'customers_address_format_id' => $order->customer['format_id'], + 'delivery_name' => $order->delivery['firstname'] . ' ' . $order->delivery['lastname'], + 'delivery_company' => $order->delivery['company'], + 'delivery_street_address' => $order->delivery['street_address'], + 'delivery_suburb' => $order->delivery['suburb'], + 'delivery_city' => $order->delivery['city'], + 'delivery_postcode' => $order->delivery['postcode'], + 'delivery_state' => $order->delivery['state'], + 'delivery_country' => $order->delivery['country']['title'], + 'delivery_address_format_id' => $order->delivery['format_id'], + 'billing_name' => $order->billing['firstname'] . ' ' . $order->billing['lastname'], + 'billing_company' => $order->billing['company'], + 'billing_street_address' => $order->billing['street_address'], + 'billing_suburb' => $order->billing['suburb'], + 'billing_city' => $order->billing['city'], + 'billing_postcode' => $order->billing['postcode'], + 'billing_state' => $order->billing['state'], + 'billing_country' => $order->billing['country']['title'], + 'billing_address_format_id' => $order->billing['format_id'], + 'payment_method' => $order->info['payment_method'], + 'cc_type' => $order->info['cc_type'], + 'cc_owner' => $order->info['cc_owner'], + 'cc_number' => $order->info['cc_number'], + 'cc_expires' => $order->info['cc_expires'], + 'date_purchased' => 'now()', + 'orders_status' => $order->info['order_status'], + 'currency' => $order->info['currency'], + 'currency_value' => $order->info['currency_value']); + + tep_db_perform(TABLE_ORDERS, $sql_data_array); + + $insert_id = tep_db_insert_id(); + + for ($i=0, $n=sizeof($order_totals); $i<$n; $i++) { + $sql_data_array = array('orders_id' => $insert_id, + 'title' => $order_totals[$i]['title'], + 'text' => $order_totals[$i]['text'], + 'value' => $order_totals[$i]['value']*$order->info['currency_value'], + 'class' => $order_totals[$i]['code'], + 'sort_order' => $order_totals[$i]['sort_order']); + + tep_db_perform(TABLE_ORDERS_TOTAL, $sql_data_array); + } + + for ($i=0, $n=sizeof($order->products); $i<$n; $i++) { + $sql_data_array = array('orders_id' => $insert_id, + 'products_id' => tep_get_prid($order->products[$i]['id']), + 'products_model' => $order->products[$i]['model'], + 'products_name' => $order->products[$i]['name'], + 'products_price' => $order->products[$i]['price'], + 'final_price' => $order->products[$i]['final_price'], + 'products_tax' => $order->products[$i]['tax'], + 'products_quantity' => $order->products[$i]['qty']); + + tep_db_perform(TABLE_ORDERS_PRODUCTS, $sql_data_array); + + $order_products_id = tep_db_insert_id(); + + $attributes_exist = '0'; + if (isset($order->products[$i]['attributes'])) { + $attributes_exist = '1'; + for ($j=0, $n2=sizeof($order->products[$i]['attributes']); $j<$n2; $j++) { + if (DOWNLOAD_ENABLED == 'true') { + $attributes_query = "select popt.products_options_name, poval.products_options_values_name, pa.options_values_price, pa.price_prefix, pad.products_attributes_maxdays, pad.products_attributes_maxcount , pad.products_attributes_filename + from " . TABLE_PRODUCTS_OPTIONS . " popt, " . TABLE_PRODUCTS_OPTIONS_VALUES . " poval, " . TABLE_PRODUCTS_ATTRIBUTES . " pa + left join " . TABLE_PRODUCTS_ATTRIBUTES_DOWNLOAD . " pad + on pa.products_attributes_id=pad.products_attributes_id + where pa.products_id = '" . $order->products[$i]['id'] . "' + and pa.options_id = '" . $order->products[$i]['attributes'][$j]['option_id'] . "' + and pa.options_id = popt.products_options_id + and pa.options_values_id = '" . $order->products[$i]['attributes'][$j]['value_id'] . "' + and pa.options_values_id = poval.products_options_values_id + and popt.language_id = '" . $languages_id . "' + and poval.language_id = '" . $languages_id . "'"; + $attributes = tep_db_query($attributes_query); + } else { + $attributes = tep_db_query("select popt.products_options_name, poval.products_options_values_name, pa.options_values_price, pa.price_prefix from " . TABLE_PRODUCTS_OPTIONS . " popt, " . TABLE_PRODUCTS_OPTIONS_VALUES . " poval, " . TABLE_PRODUCTS_ATTRIBUTES . " pa where pa.products_id = '" . $order->products[$i]['id'] . "' and pa.options_id = '" . $order->products[$i]['attributes'][$j]['option_id'] . "' and pa.options_id = popt.products_options_id and pa.options_values_id = '" . $order->products[$i]['attributes'][$j]['value_id'] . "' and pa.options_values_id = poval.products_options_values_id and popt.language_id = '" . $languages_id . "' and poval.language_id = '" . $languages_id . "'"); + } + $attributes_values = tep_db_fetch_array($attributes); + + $sql_data_array = array('orders_id' => $insert_id, + 'orders_products_id' => $order_products_id, + 'products_options' => $attributes_values['products_options_name'], + 'products_options_values' => $attributes_values['products_options_values_name'], + 'options_values_price' => $attributes_values['options_values_price'], + 'price_prefix' => $attributes_values['price_prefix']); + + tep_db_perform(TABLE_ORDERS_PRODUCTS_ATTRIBUTES, $sql_data_array); + + if ((DOWNLOAD_ENABLED == 'true') && isset($attributes_values['products_attributes_filename']) && tep_not_null($attributes_values['products_attributes_filename'])) { + $sql_data_array = array('orders_id' => $insert_id, + 'orders_products_id' => $order_products_id, + 'orders_products_filename' => $attributes_values['products_attributes_filename'], + 'download_maxdays' => $attributes_values['products_attributes_maxdays'], + 'download_count' => $attributes_values['products_attributes_maxcount']); + + tep_db_perform(TABLE_ORDERS_PRODUCTS_DOWNLOAD, $sql_data_array); + } + } + } + } + + $cart_Paysera_ID = $cartID . '-' . $insert_id; + tep_session_register('cart_Paysera_ID'); + } + } + + return false; + } + + + function process_button() { + global $order, $currencies, $currency, $customer_id, $cart_Paysera_ID; + + $orderid = substr($cart_Paysera_ID, strpos($cart_Paysera_ID, '-')+1); + + $customer = $GLOBALS['order']->customer; + $paytext = 'Apmokejimas uz prekes ir paslaugas (uz nr. [order_nr]) ([site_name]). Uzsako: ' . $customer['firstname'] . ' ' . $customer['lastname'] . ', ' . $customer['street_address'] . ''; + + $arrLTReplace = array( + + '?' => 'A', + '?' => 'C', + '?' => 'E', + '?' => 'E', + '?' => 'I', + '?' => 'S', + '?' => 'U', + '?' => 'U', + '?' => 'Z', + '?' => 'a', + '?' => 'c', + '?' => 'e', + '?' => 'e', + '?' => 'i', + '?' => 's', + '?' => 'u', + '?' => 'u', + '?' => 'z' + + ); + + $process_button_string = ''; + + if(strcmp($currency,substr(MODULE_PAYMENT_PAYSERA_CURRENCY,-3)) != 0 && MODULE_PAYMENT_PAYSERA_CURRENCY !== 'Selected Currency') { + + echo 'Chosen currency is disabled for this payment module. '; + + $process_button_string .= ''; + + } else { + + try { + $request = WebToPay::buildRequest(array( + 'projectid' => MODULE_PAYMENT_PAYSERA_PROJECT, + 'sign_password' => MODULE_PAYMENT_PAYSERA_SIGNATURE, + 'orderid' => $orderid, + + 'amount' => number_format($order->info['total'] * $order->info['currency_value'], 2, '.', '') * 100, + 'currency' => $currency, + + 'lang' => substr($_SESSION['language'],0, 3), + + 'p_firstname' => strtr($order->customer['firstname'], $arrLTReplace), + 'p_lastname' => strtr($order->customer['lastname'], $arrLTReplace), + 'p_email' => strtr($order->customer['email_address'], $arrLTReplace), + 'p_street' => strtr($order->customer['street_address'], $arrLTReplace), + 'p_city' => strtr($order->customer['city'], $arrLTReplace), + 'p_state' => strtr($order->customer['state'], $arrLTReplace), + 'p_zip' => strtr($order->customer['postcode'], $arrLTReplace), + 'p_countrycode' => strtr($order->customer['country']['iso_code_2'], $arrLTReplace), + + 'accepturl' => tep_href_link(FILENAME_CHECKOUT_PROCESS, '', 'SSL'), + 'cancelurl' => tep_href_link(FILENAME_CHECKOUT_PAYMENT, '', 'SSL'), + 'callbackurl' => tep_href_link('ext/modules/payment/paysera/callback.php', '' , 'SSL', true, true, true), + + 'test' => (MODULE_PAYMENT_PAYSERA_TESTING == 'Yes') ? '1' : '0', + )); + } + catch (WebToPayException $e) { + echo $e->getMessage(); + } + } + + if($request) { + foreach( $request as $field=>$value) { + $process_button_string .= tep_draw_hidden_field($field, $value); + } + } else { + $process_button_string .= tep_draw_hidden_field('cancelurl', tep_href_link(FILENAME_CHECKOUT_PAYMENT, '', 'SSL'));; + } + + return $process_button_string; + } + + + function before_process() { + global $customer_id, $order, $order_totals, $sendto, $billto, $languages_id, $payment, $currencies, $cart, $cart_Paysera_ID; + global $$payment; + + $GLOBALS['order']->customer['email_address'] .= "\nDont\nSend\nEmail\nPlease\n:)"; + + $order_id = substr($cart_Paysera_ID, strpos($cart_Paysera_ID, '-')+1); + + $check_query = tep_db_query("select orders_status from " . TABLE_ORDERS . " where orders_id = '" . (int)$order_id . "'"); + + $sql_data_array = array('orders_id' => $order_id, + 'date_added' => 'now()', + 'customer_notified' => (SEND_EMAILS == 'true') ? '1' : '0', + 'comments' => $order->info['comments']); + + tep_db_perform(TABLE_ORDERS_STATUS_HISTORY, $sql_data_array); + + $products_ordered = ''; + $subtotal = 0; + $total_tax = 0; + + for ($i=0, $n=sizeof($order->products); $i<$n; $i++) { + // Stock Update - Joao Correia + if (STOCK_LIMITED == 'true') { + if (DOWNLOAD_ENABLED == 'true') { + $stock_query_raw = "SELECT products_quantity, pad.products_attributes_filename + FROM " . TABLE_PRODUCTS . " p + LEFT JOIN " . TABLE_PRODUCTS_ATTRIBUTES . " pa + ON p.products_id=pa.products_id + LEFT JOIN " . TABLE_PRODUCTS_ATTRIBUTES_DOWNLOAD . " pad + ON pa.products_attributes_id=pad.products_attributes_id + WHERE p.products_id = '" . tep_get_prid($order->products[$i]['id']) . "'"; + // Will work with only one option for downloadable products + // otherwise, we have to build the query dynamically with a loop + $products_attributes = $order->products[$i]['attributes']; + if (is_array($products_attributes)) { + $stock_query_raw .= " AND pa.options_id = '" . $products_attributes[0]['option_id'] . "' AND pa.options_values_id = '" . $products_attributes[0]['value_id'] . "'"; + } + $stock_query = tep_db_query($stock_query_raw); + } + else{ + $stock_query = tep_db_query("select products_quantity from " . TABLE_PRODUCTS . " where products_id = '" . tep_get_prid($order->products[$i]['id']) . "'"); + } + if (tep_db_num_rows($stock_query) > 0) { + $stock_values = tep_db_fetch_array($stock_query); + // do not decrement quantities if products_attributes_filename exists + if ((DOWNLOAD_ENABLED != 'true') || (!$stock_values['products_attributes_filename'])) { + $stock_left = $stock_values['products_quantity'] - $order->products[$i]['qty']; + } + else { + $stock_left = $stock_values['products_quantity']; + } + tep_db_query("update " . TABLE_PRODUCTS . " set products_quantity = '" . $stock_left . "' where products_id = '" . tep_get_prid($order->products[$i]['id']) . "'"); + if ( ($stock_left < 1) && (STOCK_ALLOW_CHECKOUT == 'false') ) { + tep_db_query("update " . TABLE_PRODUCTS . " set products_status = '0' where products_id = '" . tep_get_prid($order->products[$i]['id']) . "'"); + } + } + } + + // Update products_ordered (for bestsellers list) + tep_db_query("update " . TABLE_PRODUCTS . " set products_ordered = products_ordered + " . sprintf('%d', $order->products[$i]['qty']) . " where products_id = '" . tep_get_prid($order->products[$i]['id']) . "'"); + + //------insert customer choosen option to order-------- + $attributes_exist = '0'; + $products_ordered_attributes = ''; + if (isset($order->products[$i]['attributes'])) { + $attributes_exist = '1'; + for ($j=0, $n2=sizeof($order->products[$i]['attributes']); $j<$n2; $j++) { + if (DOWNLOAD_ENABLED == 'true') { + $attributes_query = "select popt.products_options_name, poval.products_options_values_name, pa.options_values_price, pa.price_prefix, pad.products_attributes_maxdays, pad.products_attributes_maxcount , pad.products_attributes_filename + from " . TABLE_PRODUCTS_OPTIONS . " popt, " . TABLE_PRODUCTS_OPTIONS_VALUES . " poval, " . TABLE_PRODUCTS_ATTRIBUTES . " pa + left join " . TABLE_PRODUCTS_ATTRIBUTES_DOWNLOAD . " pad + on pa.products_attributes_id=pad.products_attributes_id + where pa.products_id = '" . $order->products[$i]['id'] . "' + and pa.options_id = '" . $order->products[$i]['attributes'][$j]['option_id'] . "' + and pa.options_id = popt.products_options_id + and pa.options_values_id = '" . $order->products[$i]['attributes'][$j]['value_id'] . "' + and pa.options_values_id = poval.products_options_values_id + and popt.language_id = '" . $languages_id . "' + and poval.language_id = '" . $languages_id . "'"; + $attributes = tep_db_query($attributes_query); + } + else{ + $attributes = tep_db_query("select popt.products_options_name, poval.products_options_values_name, pa.options_values_price, pa.price_prefix from " . TABLE_PRODUCTS_OPTIONS . " popt, " . TABLE_PRODUCTS_OPTIONS_VALUES . " poval, " . TABLE_PRODUCTS_ATTRIBUTES . " pa where pa.products_id = '" . $order->products[$i]['id'] . "' and pa.options_id = '" . $order->products[$i]['attributes'][$j]['option_id'] . "' and pa.options_id = popt.products_options_id and pa.options_values_id = '" . $order->products[$i]['attributes'][$j]['value_id'] . "' and pa.options_values_id = poval.products_options_values_id and popt.language_id = '" . $languages_id . "' and poval.language_id = '" . $languages_id . "'"); + } + $attributes_values = tep_db_fetch_array($attributes); + + $products_ordered_attributes .= "\n\t" . $attributes_values['products_options_name'] . ' ' . $attributes_values['products_options_values_name']; + } + } + //------insert customer choosen option eof ---- + $total_weight += ($order->products[$i]['qty'] * $order->products[$i]['weight']); + $total_tax += tep_calculate_tax($total_products_price, $products_tax) * $order->products[$i]['qty']; + $total_cost += $total_products_price; + + $products_ordered .= $order->products[$i]['qty'] . ' x ' . $order->products[$i]['name'] . ' (' . $order->products[$i]['model'] . ') = ' . $currencies->display_price($order->products[$i]['final_price'], $order->products[$i]['tax'], $order->products[$i]['qty']) . $products_ordered_attributes . "\n"; + } + + // load the after_process function from the payment modules + $this->after_process(); + + $cart->reset(true); + + // unregister session variables used during checkout + tep_session_unregister('sendto'); + tep_session_unregister('billto'); + tep_session_unregister('shipping'); + tep_session_unregister('payment'); + tep_session_unregister('comments'); + + tep_session_unregister('cart_Paysera_ID'); + + tep_redirect(tep_href_link(FILENAME_CHECKOUT_SUCCESS, '', 'SSL')); + } + + function after_process() { + return false; + } + + function output_error() { + return false; + } + + + function check() { + if (!isset($this->_check)) { + $check_query = tep_db_query("select configuration_value from " . TABLE_CONFIGURATION . " where configuration_key = 'MODULE_PAYMENT_PAYSERA_STATUS'"); + $this->_check = tep_db_num_rows($check_query); + } + return $this->_check; + } + + + function install() { + $this->remove(); + + /* Hack */ + global $language, $module_type; + include_once(DIR_FS_CATALOG_LANGUAGES.$language.'/modules/'.$module_type.'/paysera.php'); + /*/Hack */ + + $field = tep_db_fetch_array(tep_db_query("SELECT `currencies_id` FROM " . TABLE_CURRENCIES . " WHERE `code` = 'LTL' LIMIT 1")); + if( !$field['currencies_id'] ){ + tep_db_query("insert into " . TABLE_CURRENCIES . "(`title`, `code`, `symbol_left`, `symbol_right`, `decimal_point`, `thousands_point`, `decimal_places`, `value`, `last_updated`) values ('Lithuania litas', 'LTL', 'Lt', '', '.', ',', '2', '2.38189947', NOW())"); + } + + tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('".MODULE_PAYMENT_PAYSERA_STATUS_TITLE."', 'MODULE_PAYMENT_PAYSERA_STATUS', 'True', '".MODULE_PAYMENT_PAYSERA_STATUS_DESCRIPTION."', '6', '3', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now())"); + tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('".MODULE_PAYMENT_PAYSERA_PROJECT_TITLE."', 'MODULE_PAYMENT_PAYSERA_PROJECT', '', '".MODULE_PAYMENT_PAYSERA_PROJECT_DESCRIPTION."', '6', '4', now())"); + tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('".MODULE_PAYMENT_PAYSERA_SIGNATURE_TITLE."', 'MODULE_PAYMENT_PAYSERA_SIGNATURE', '', '".MODULE_PAYMENT_PAYSERA_SIGNATURE_DESCRIPTION."', '6', '4', now())"); + tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('".MODULE_PAYMENT_PAYSERA_CURRENCY_TITLE."', 'MODULE_PAYMENT_PAYSERA_CURRENCY', 'Selected Currency', '".MODULE_PAYMENT_PAYSERA_CURRENCY_DESCRIPTION."', '6', '6', 'tep_cfg_select_option(array(\'Selected Currency\',\'Only USD\',\'Only LTL\',\'Only CAD\',\'Only EUR\',\'Only GBP\',\'Only JPY\'), ', now())"); + tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, use_function, set_function, date_added) values ('".MODULE_PAYMENT_PAYSERA_ZONE_TITLE."', 'MODULE_PAYMENT_PAYSERA_ZONE', '0', '".MODULE_PAYMENT_PAYSERA_ZONE_DESCRIPTION."', '6', '2', 'tep_get_zone_class_title', 'tep_cfg_pull_down_zone_classes(', now())"); + tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('".MODULE_PAYMENT_PAYSERA_SORT_ORDER_TITLE."', 'MODULE_PAYMENT_PAYSERA_SORT_ORDER', '0', '".MODULE_PAYMENT_PAYSERA_SORT_ORDER_DESCRIPTION."', '6', '0', now())"); + tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('".MODULE_PAYMENT_PAYSERA_TESTING_TITLE."', 'MODULE_PAYMENT_PAYSERA_TESTING', 'No', '".MODULE_PAYMENT_PAYSERA_TESTING_DESCRIPTION."', '6', '0', 'tep_cfg_select_option(array(\'Yes\', \'No\'), ', now())"); + tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, use_function, date_added) values ('Set Order Status', 'MODULE_PAYMENT_PAYSERA_ORDER_STATUS_ID', '0', 'Set the status of orders made with this payment module to this value', '6', '0', 'tep_cfg_pull_down_order_statuses(', 'tep_get_order_status_name', now())"); + + $d = dir(DIR_FS_CATALOG_LANGUAGES); + while ( false !== ($entry = $d->read()) ) { + if( $entry != "." && $entry != ".." && is_dir($d->path.$entry) && is_file($d->path.$entry.'/modules/'.$module_type.'/paysera.php') ){ + $langFile = implode('', file($d->path.$entry.'/modules/'.$module_type.'/paysera.php')); + preg_match("/MODULE_PAYMENT_PAYSERA_ORDERS_STATUS_20.*['\"]\s*,\s*['\"]([^\"']+)/", $langFile, $constant); + + $language_query = tep_db_query("SELECT languages_id from " . TABLE_LANGUAGES . " WHERE directory = '" . $entry . "'"); + $languageData = tep_db_fetch_array($language_query); + + if( $languageData ){ + tep_db_query("insert into " . TABLE_ORDERS_STATUS . " (orders_status_id, language_id, orders_status_name) values (20, '".$languageData['languages_id']."', '".trim($constant[1])."')"); + } + } + } + $d->close(); + + tep_db_query("ALTER TABLE ".TABLE_ORDERS." ADD `SSID` VARCHAR( 40 ) NOT NULL"); + } + + + function remove() { + tep_db_query("DELETE FROM " . TABLE_CONFIGURATION . " WHERE `configuration_key` IN ('" . implode("', '", $this->keys()) . "')"); + tep_db_query("DELETE FROM " . TABLE_ORDERS_STATUS . " WHERE `orders_status_id` = '20'"); + + $res = tep_db_query("SHOW FIELDS FROM " . TABLE_ORDERS); + while ($field = tep_db_fetch_array($res)) { + if( $field['Field'] == 'SSID' ){ + tep_db_query("ALTER TABLE " . TABLE_ORDERS . " DROP `SSID` "); + break; + } + } + } + + + function keys() { + return array('MODULE_PAYMENT_PAYSERA_STATUS', 'MODULE_PAYMENT_PAYSERA_TESTING', 'MODULE_PAYMENT_PAYSERA_PROJECT', 'MODULE_PAYMENT_PAYSERA_SIGNATURE', 'MODULE_PAYMENT_PAYSERA_CURRENCY', 'MODULE_PAYMENT_PAYSERA_ZONE', 'MODULE_PAYMENT_PAYSERA_SORT_ORDER', 'MODULE_PAYMENT_PAYSERA_ORDER_STATUS_ID'); + } + + + function tep_remove_order($order_id, $restock = false) { + if ($restock) { + $order_query = tep_db_query("SELECT products_id, products_quantity from " . TABLE_ORDERS_PRODUCTS . " where orders_id = '" . (int)$order_id . "'"); + while ($order = tep_db_fetch_array($order_query)) { + tep_db_query("UPDATE " . TABLE_PRODUCTS . " SET products_quantity = products_quantity + " . $order['products_quantity'] . ", products_ordered = products_ordered - " . $order['products_quantity'] . " where products_id = '" . (int)$order['products_id'] . "'"); + } + } + + tep_db_query("DELETE FROM " . TABLE_ORDERS . " WHERE orders_id = '" . (int)$order_id . "'"); + tep_db_query("DELETE FROM " . TABLE_ORDERS_PRODUCTS . " WHERE orders_id = '" . (int)$order_id . "'"); + tep_db_query("DELETE FROM " . TABLE_ORDERS_PRODUCTS_ATTRIBUTES . " WHERE orders_id = '" . (int)$order_id . "'"); + tep_db_query("DELETE FROM " . TABLE_ORDERS_STATUS_HISTORY . " WHERE orders_id = '" . (int)$order_id . "'"); + tep_db_query("DELETE FROM " . TABLE_ORDERS_TOTAL . " WHERE orders_id = '" . (int)$order_id . "'"); + } + + } + + diff --git a/includes/modules/payment/paysera/WebToPay.php b/includes/modules/payment/paysera/WebToPay.php new file mode 100644 index 0000000..714496b --- /dev/null +++ b/includes/modules/payment/paysera/WebToPay.php @@ -0,0 +1,2357 @@ +. + * + * @package WebToPay + * @author EVP International + * @license http://www.gnu.org/licenses/lgpl.html + * @version 1.6 + * @link http://www.webtopay.com/ + */ + + +/** + * Contains static methods for most used scenarios. + */ +class WebToPay { + + /** + * WebToPay Library version. + */ + const VERSION = '1.6'; + + /** + * Server URL where all requests should go. + */ + const PAY_URL = 'https://www.paysera.com/pay/'; + + /** + * Server URL where all non-lithuanian language requests should go. + */ + const PAYSERA_PAY_URL = 'https://www.paysera.com/pay/'; + + /** + * Server URL where we can get XML with payment method data. + */ + const XML_URL = 'https://www.paysera.com/new/api/paymentMethods/'; + + /** + * SMS answer url. + */ + const SMS_ANSWER_URL = 'https://www.paysera.com/psms/respond/'; + + /** + * Builds request data array. + * + * This method checks all given data and generates correct request data + * array or raises WebToPayException on failure. + * + * Possible keys: + * https://www.paysera.com/makro_specifikacija.html + * + * @param array $data Information about current payment request + * + * @return array + * + * @throws WebToPayException on data validation error + */ + public static function buildRequest($data) { + if (!isset($data['sign_password']) || !isset($data['projectid'])) { + throw new WebToPayException('sign_password or projectid is not provided'); + } + $password = $data['sign_password']; + $projectId = $data['projectid']; + unset($data['sign_password']); + unset($data['projectid']); + + $factory = new WebToPay_Factory(array('projectId' => $projectId, 'password' => $password)); + $requestBuilder = $factory->getRequestBuilder(); + return $requestBuilder->buildRequest($data); + } + + + /** + * Builds request and redirects user to payment window with generated request data + * + * Possible array keys are described here: + * https://www.paysera.com/makro_specifikacija.html + * + * @param array $data Information about current payment request. + * @param boolean $exit if true, exits after sending Location header; default false + * + * @throws WebToPayException on data validation error + */ + public static function redirectToPayment($data, $exit = false) { + if (!isset($data['sign_password']) || !isset($data['projectid'])) { + throw new WebToPayException('sign_password or projectid is not provided'); + } + $password = $data['sign_password']; + $projectId = $data['projectid']; + unset($data['sign_password']); + unset($data['projectid']); + + $factory = new WebToPay_Factory(array('projectId' => $projectId, 'password' => $password)); + $url = $factory->getRequestBuilder() + ->buildRequestUrlFromData($data); + + if (headers_sent()) { + echo ''; + } else { + header("Location: $url", true); + } + printf( + 'Redirecting to %s. Please wait.', + htmlentities($url, ENT_QUOTES, 'UTF-8'), + htmlentities($url, ENT_QUOTES, 'UTF-8') + ); + if ($exit) { + exit(); + } + } + + /** + * Builds repeat request data array. + * + * This method checks all given data and generates correct request data + * array or raises WebToPayException on failure. + * + * Method accepts single parameter $data of array type. All possible array + * keys are described here: + * https://www.paysera.com/makro_specifikacija.html + * + * @param array $data Information about current payment request + * + * @return array + * + * @throws WebToPayException on data validation error + */ + public static function buildRepeatRequest($data) { + if (!isset($data['sign_password']) || !isset($data['projectid']) || !isset($data['orderid'])) { + throw new WebToPayException('sign_password, projectid or orderid is not provided'); + } + $password = $data['sign_password']; + $projectId = $data['projectid']; + $orderId = $data['orderid']; + + $factory = new WebToPay_Factory(array('projectId' => $projectId, 'password' => $password)); + $requestBuilder = $factory->getRequestBuilder(); + return $requestBuilder->buildRepeatRequest($orderId); + } + + /** + * Returns payment url. Argument is same as lang parameter in request data + * + * @param string $language + * @return string $url + */ + public static function getPaymentUrl($language = 'LIT') { + return (in_array($language, array('lt', 'lit', 'LIT'))) + ? self::PAY_URL + : self::PAYSERA_PAY_URL; + } + + /** + * Parses response from WebToPay server and validates signs. + * + * This function accepts both micro and macro responses. + * + * First parameter usualy should be $_GET array. + * + * Description about response can be found here: + * makro: https://www.paysera.com/makro_specifikacija.html + * mikro: https://www.paysera.com/mikro_mokejimu_specifikacija_SMS.html + * + * If response is not correct, WebToPayException will be raised. + * + * @param array $query Response array + * @param array $userData + * + * @return array + * + * @throws WebToPayException + * @deprecated use validateAndParseData() and check status code yourself + */ + public static function checkResponse($query, $userData = array()) { + $projectId = isset($userData['projectid']) ? $userData['projectid'] : null; + $password = isset($userData['sign_password']) ? $userData['sign_password'] : null; + $logFile = isset($userData['log']) ? $userData['log'] : null; + + try { + $data = self::validateAndParseData($query, $projectId, $password); + if ($data['type'] == 'macro' && $data['status'] != 1) { + throw new WebToPayException('Expected status code 1', WebToPayException::E_DEPRECATED_USAGE); + } + + if ($logFile) { + self::log('OK', http_build_query($data, null, '&'), $logFile); + } + return $data; + + } catch (WebToPayException $exception) { + if ($logFile && $exception->getCode() != WebToPayException::E_DEPRECATED_USAGE) { + self::log('ERR', $exception . "\nQuery: " . http_build_query($query, null, '&'), $logFile); + } + throw $exception; + } + } + + /** + * Parses request (query) data and validates its signature. + * + * @param array $query usually $_GET + * @param integer $projectId + * @param string $password + * + * @return array + * + * @throws WebToPayException + */ + public static function validateAndParseData(array $query, $projectId, $password) { + $factory = new WebToPay_Factory(array('projectId' => $projectId, 'password' => $password)); + $validator = $factory->getCallbackValidator(); + $data = $validator->validateAndParseData($query); + return $data; + } + + /** + * Sends SMS answer + * + * @param array $userData + * + * @throws WebToPayException + * @throws WebToPay_Exception_Validation + */ + public static function smsAnswer($userData) { + if (!isset($userData['id']) || !isset($userData['msg']) || !isset($userData['sign_password'])) { + throw new WebToPay_Exception_Validation('id, msg and sign_password are required'); + } + + $smsId = $userData['id']; + $text = $userData['msg']; + $password = $userData['sign_password']; + $logFile = isset($userData['log']) ? $userData['log'] : null; + + try { + + $factory = new WebToPay_Factory(array('password' => $password)); + $factory->getSmsAnswerSender()->sendAnswer($smsId, $text); + + if ($logFile) { + self::log('OK', 'SMS ANSWER ' . $smsId . ' ' . $text, $logFile); + } + + } catch (WebToPayException $e) { + if ($logFile) { + self::log('ERR', 'SMS ANSWER ' . $e, $logFile); + } + throw $e; + } + + } + + + /** + * Gets available payment methods for project. Gets methods min and max amounts in specified currency. + * + * @param integer $projectId + * @param string $currency + * + * @return WebToPay_PaymentMethodList + * + * @throws WebToPayException + */ + public static function getPaymentMethodList($projectId, $currency = 'EUR') { + $factory = new WebToPay_Factory(array('projectId' => $projectId)); + return $factory->getPaymentMethodListProvider()->getPaymentMethodList($currency); + } + + /** + * Logs to file. Just skips logging if file is not writeable + * + * @param string $type + * @param string $msg + * @param string $logfile + */ + protected static function log($type, $msg, $logfile) { + $fp = @fopen($logfile, 'a'); + if (!$fp) { + return; + } + + $logline = array( + $type, + isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '-', + date('[Y-m-d H:i:s O]'), + 'v' . self::VERSION . ':', + $msg + ); + + $logline = implode(' ', $logline)."\n"; + fwrite($fp, $logline); + fclose($fp); + + // clear big log file + if (filesize($logfile) > 1024 * 1024 * pi()) { + copy($logfile, $logfile.'.old'); + unlink($logfile); + } + } +} + + + + +/** + * Base exception class for all exceptions in this library + */ +class WebToPayException extends Exception { + + /** + * Missing field. + */ + const E_MISSING = 1; + + /** + * Invalid field value. + */ + const E_INVALID = 2; + + /** + * Max length exceeded. + */ + const E_MAXLEN = 3; + + /** + * Regexp for field value doesn't match. + */ + const E_REGEXP = 4; + + /** + * Missing or invalid user given parameters. + */ + const E_USER_PARAMS = 5; + + /** + * Logging errors + */ + const E_LOG = 6; + + /** + * SMS answer errors + */ + const E_SMS_ANSWER = 7; + + /** + * Macro answer errors + */ + const E_STATUS = 8; + + /** + * Library errors - if this happens, bug-report should be sent; also you can check for newer version + */ + const E_LIBRARY = 9; + + /** + * Errors in remote service - it returns some invalid data + */ + const E_SERVICE = 10; + + /** + * Deprecated usage errors + */ + const E_DEPRECATED_USAGE = 11; + + /** + * @var string|boolean + */ + protected $fieldName = false; + + /** + * Sets field which failed + * + * @param string $fieldName + */ + public function setField($fieldName) { + $this->fieldName = $fieldName; + } + + /** + * Gets field which failed + * + * @return string|boolean false + */ + public function getField() { + return $this->fieldName; + } +} + +/** + * Parses and validates callbacks + */ +class WebToPay_CallbackValidator { + + /** + * @var WebToPay_Sign_SignCheckerInterface + */ + protected $signer; + + /** + * @var WebToPay_Util + */ + protected $util; + + /** + * @var integer + */ + protected $projectId; + + /** + * Constructs object + * + * @param integer $projectId + * @param WebToPay_Sign_SignCheckerInterface $signer + * @param WebToPay_Util $util + */ + public function __construct($projectId, WebToPay_Sign_SignCheckerInterface $signer, WebToPay_Util $util) { + $this->signer = $signer; + $this->util = $util; + $this->projectId = $projectId; + } + + /** + * Parses callback parameters from query parameters and checks if sign is correct. + * Request has parameter "data", which is signed and holds all callback parameters + * + * @param array $requestData + * + * @return array Parsed callback parameters + * + * @throws WebToPayException + * @throws WebToPay_Exception_Callback + */ + public function validateAndParseData(array $requestData) { + if (!$this->signer->checkSign($requestData)) { + throw new WebToPay_Exception_Callback('Invalid sign parameters, check $_GET length limit'); + } + + if (!isset($requestData['data'])) { + throw new WebToPay_Exception_Callback('"data" parameter not found'); + } + $data = $requestData['data']; + + $queryString = $this->util->decodeSafeUrlBase64($data); + $request = $this->util->parseHttpQuery($queryString); + + if (!isset($request['projectid'])) { + throw new WebToPay_Exception_Callback( + 'Project ID not provided in callback', + WebToPayException::E_INVALID + ); + } + + if ((string) $request['projectid'] !== (string) $this->projectId) { + throw new WebToPay_Exception_Callback( + sprintf('Bad projectid: %s, should be: %s', $request['projectid'], $this->projectId), + WebToPayException::E_INVALID + ); + } + + if (!isset($request['type']) || !in_array($request['type'], array('micro', 'macro'))) { + $micro = ( + isset($request['to']) + && isset($request['from']) + && isset($request['sms']) + ); + $request['type'] = $micro ? 'micro' : 'macro'; + } + + return $request; + } + + /** + * Checks data to have all the same parameters provided in expected array + * + * @param array $data + * @param array $expected + * + * @throws WebToPayException + */ + public function checkExpectedFields(array $data, array $expected) { + foreach ($expected as $key => $value) { + $passedValue = isset($data[$key]) ? $data[$key] : null; + if ($passedValue != $value) { + throw new WebToPayException( + sprintf('Field %s is not as expected (expected %s, got %s)', $key, $value, $passedValue) + ); + } + } + } +} + +/** + * Wrapper class to group payment methods. Each country can have several payment method groups, each of them + * have one or more payment methods. + */ +class WebToPay_PaymentMethodGroup { + /** + * Some unique (in the scope of country) key for this group + * + * @var string + */ + protected $groupKey; + + /** + * Translations array for this group. Holds associative array of group title by country codes. + * + * @var array + */ + protected $translations; + + /** + * Holds actual payment methods + * + * @var WebToPay_PaymentMethod[] + */ + protected $paymentMethods; + + /** + * Default language for titles + * + * @var string + */ + protected $defaultLanguage; + + /** + * Constructs object + * + * @param string $groupKey + * @param array $translations + * @param string $defaultLanguage + */ + public function __construct($groupKey, array $translations = array(), $defaultLanguage = 'lt') { + $this->groupKey = $groupKey; + $this->translations = $translations; + $this->defaultLanguage = $defaultLanguage; + $this->paymentMethods = array(); + } + + /** + * Sets default language for titles. + * Returns itself for fluent interface + * + * @param string $language + * + * @return WebToPay_PaymentMethodGroup + */ + public function setDefaultLanguage($language) { + $this->defaultLanguage = $language; + foreach ($this->paymentMethods as $paymentMethod) { + $paymentMethod->setDefaultLanguage($language); + } + return $this; + } + + /** + * Gets default language for titles + * + * @return string + */ + public function getDefaultLanguage() { + return $this->defaultLanguage; + } + + /** + * Gets title of the group. Tries to get title in specified language. If it is not found or if language is not + * specified, uses default language, given to constructor. + * + * @param string [Optional] $languageCode + * + * @return string + */ + public function getTitle($languageCode = null) { + if ($languageCode !== null && isset($this->translations[$languageCode])) { + return $this->translations[$languageCode]; + } elseif (isset($this->translations[$this->defaultLanguage])) { + return $this->translations[$this->defaultLanguage]; + } else { + return $this->groupKey; + } + } + + /** + * Returns group key + * + * @return string + */ + public function getKey() { + return $this->groupKey; + } + + /** + * Returns available payment methods for this group + * + * @return WebToPay_PaymentMethod[] + */ + public function getPaymentMethods() { + return $this->paymentMethods; + } + + + /** + * Adds new payment method for this group. + * If some other payment method with specified key was registered earlier, overwrites it. + * Returns given payment method + * + * @param WebToPay_PaymentMethod $paymentMethod + * + * @return WebToPay_PaymentMethod + */ + public function addPaymentMethod(WebToPay_PaymentMethod $paymentMethod) { + return $this->paymentMethods[$paymentMethod->getKey()] = $paymentMethod; + } + + /** + * Gets payment method object with key. If no payment method with such key is found, returns null. + * + * @param string $key + * + * @return null|WebToPay_PaymentMethod + */ + public function getPaymentMethod($key) { + return isset($this->paymentMethods[$key]) ? $this->paymentMethods[$key] : null; + } + + /** + * Returns new group instance with only those payment methods, which are available for provided amount. + * + * @param integer $amount + * @param string $currency + * + * @return WebToPay_PaymentMethodGroup + */ + public function filterForAmount($amount, $currency) { + $group = new WebToPay_PaymentMethodGroup($this->groupKey, $this->translations, $this->defaultLanguage); + foreach ($this->getPaymentMethods() as $paymentMethod) { + if ($paymentMethod->isAvailableForAmount($amount, $currency)) { + $group->addPaymentMethod($paymentMethod); + } + } + return $group; + } + + /** + * Returns new country instance with only those payment methods, which are returns or not iban number after payment + * + * @param boolean $isIban + * + * @return WebToPay_PaymentMethodGroup + */ + public function filterForIban($isIban = true) { + $group = new WebToPay_PaymentMethodGroup($this->groupKey, $this->translations, $this->defaultLanguage); + foreach ($this->getPaymentMethods() as $paymentMethod) { + if ($paymentMethod->isIban() == $isIban) { + $group->addPaymentMethod($paymentMethod); + } + } + return $group; + } + + /** + * Returns whether this group has no payment methods + * + * @return boolean + */ + public function isEmpty() { + return count($this->paymentMethods) === 0; + } + + /** + * Loads payment methods from given XML node + * + * @param SimpleXMLElement $groupNode + */ + public function fromXmlNode($groupNode) { + foreach ($groupNode->payment_type as $paymentTypeNode) { + $key = (string) $paymentTypeNode->attributes()->key; + $titleTranslations = array(); + foreach ($paymentTypeNode->title as $titleNode) { + $titleTranslations[(string) $titleNode->attributes()->language] = (string) $titleNode; + } + $logoTranslations = array(); + foreach ($paymentTypeNode->logo_url as $logoNode) { + if ((string) $logoNode !== '') { + $logoTranslations[(string) $logoNode->attributes()->language] = (string) $logoNode; + } + } + $minAmount = null; + $maxAmount = null; + $currency = null; + $isIban = false; + $baseCurrency = null; + if (isset($paymentTypeNode->min)) { + $minAmount = (int) $paymentTypeNode->min->attributes()->amount; + $currency = (string) $paymentTypeNode->min->attributes()->currency; + } + if (isset($paymentTypeNode->max)) { + $maxAmount = (int) $paymentTypeNode->max->attributes()->amount; + $currency = (string) $paymentTypeNode->max->attributes()->currency; + } + + if (isset($paymentTypeNode->is_iban)) { + $isIban = (int) $paymentTypeNode->is_iban; + } + if (isset($paymentTypeNode->base_currency)) { + $baseCurrency = (string) $paymentTypeNode->base_currency; + } + $this->addPaymentMethod($this->createPaymentMethod( + $key, $minAmount, $maxAmount, $currency, $logoTranslations, $titleTranslations, $isIban, $baseCurrency + )); + } + } + + /** + * Method to create new payment method instances. Overwrite if you have to use some other subclass. + * + * @param string $key + * @param integer $minAmount + * @param integer $maxAmount + * @param string $currency + * @param array $logoList + * @param array $titleTranslations + * @param bool $isIban + * @param null $baseCurrency + * + * @return WebToPay_PaymentMethod + */ + protected function createPaymentMethod( + $key, $minAmount, $maxAmount, $currency, array $logoList = array(), array $titleTranslations = array(), + $isIban = false, $baseCurrency = null + ) { + return new WebToPay_PaymentMethod( + $key, $minAmount, $maxAmount, $currency, $logoList, $titleTranslations, $this->defaultLanguage, + $isIban, $baseCurrency + ); + } +} + +/** + * Loads data about payment methods and constructs payment method list object from that data + * You need SimpleXML support to use this feature + */ +class WebToPay_PaymentMethodListProvider { + + /** + * @var integer + */ + protected $projectId; + + /** + * @var WebToPay_WebClient + */ + protected $webClient; + + /** + * Holds constructed method lists by currency + * + * @var WebToPay_PaymentMethodList[] + */ + protected $methodListCache = array(); + + /** + * Builds various request URLs + * + * @var WebToPay_UrlBuilder $urlBuilder + */ + protected $urlBuilder; + + /** + * Constructs object + * + * @param integer $projectId + * @param WebToPay_WebClient $webClient + * @param WebToPay_UrlBuilder $urlBuilder + * + * @throws WebToPayException if SimpleXML is not available + */ + public function __construct( + $projectId, + WebToPay_WebClient $webClient, + WebToPay_UrlBuilder $urlBuilder + ) + { + $this->projectId = $projectId; + $this->webClient = $webClient; + $this->urlBuilder = $urlBuilder; + + if (!function_exists('simplexml_load_string')) { + throw new WebToPayException('You have to install libxml to use payment methods API'); + } + } + + /** + * Gets payment method list for specified currency + * + * @param string $currency + * + * @return WebToPay_PaymentMethodList + * + * @throws WebToPayException + */ + public function getPaymentMethodList($currency) { + if (!isset($this->methodListCache[$currency])) { + $xmlAsString = $this->webClient->get($this->urlBuilder->buildForPaymentsMethodList($this->projectId, $currency)); + $useInternalErrors = libxml_use_internal_errors(false); + $rootNode = simplexml_load_string($xmlAsString); + libxml_clear_errors(); + libxml_use_internal_errors($useInternalErrors); + if (!$rootNode) { + throw new WebToPayException('Unable to load XML from remote server'); + } + $methodList = new WebToPay_PaymentMethodList($this->projectId, $currency); + $methodList->fromXmlNode($rootNode); + $this->methodListCache[$currency] = $methodList; + } + return $this->methodListCache[$currency]; + } +} + +/** + * Raised on error in callback + */ +class WebToPay_Exception_Callback extends WebToPayException { + +} + +/** + * Raised if configuration is incorrect + */ +class WebToPay_Exception_Configuration extends WebToPayException { + +} + + +/** + * Raised on validation error in passed data when building the request + */ +class WebToPay_Exception_Validation extends WebToPayException { + + public function __construct($message, $code = 0, $field = null, Exception $previousException = null) { + parent::__construct($message, $code, $previousException); + if ($field) { + $this->setField($field); + } + } +} + +/** + * Class to hold information about payment method + */ +class WebToPay_PaymentMethod { + /** + * Assigned key for this payment method + * + * @var string + */ + protected $key; + + /** + * Logo url list by language. Usually logo is same for all languages, but exceptions exist + * + * @var array + */ + protected $logoList; + + /** + * Title list by language + * + * @var array + */ + protected $titleTranslations; + + /** + * Default language to use for titles + * + * @var string + */ + protected $defaultLanguage; + + /** + * @var boolean + */ + protected $isIban; + + /** + * @var string + */ + protected $baseCurrency; + + /** + * Constructs object + * + * @param string $key + * @param integer $minAmount + * @param integer $maxAmount + * @param string $currency + * @param array $logoList + * @param array $titleTranslations + * @param string $defaultLanguage + * @param bool $isIban + * @param string $baseCurrency + */ + public function __construct( + $key, $minAmount, $maxAmount, $currency, array $logoList = array(), array $titleTranslations = array(), + $defaultLanguage = 'lt', $isIban = false, $baseCurrency = null + ) { + $this->key = $key; + $this->minAmount = $minAmount; + $this->maxAmount = $maxAmount; + $this->currency = $currency; + $this->logoList = $logoList; + $this->titleTranslations = $titleTranslations; + $this->defaultLanguage = $defaultLanguage; + $this->isIban = $isIban; + $this->baseCurrency = $baseCurrency; + } + + /** + * Sets default language for titles. + * Returns itself for fluent interface + * + * @param string $language + * + * @return WebToPay_PaymentMethod + */ + public function setDefaultLanguage($language) { + $this->defaultLanguage = $language; + return $this; + } + + /** + * Gets default language for titles + * + * @return string + */ + public function getDefaultLanguage() { + return $this->defaultLanguage; + } + + /** + * Get assigned payment method key + * + * @return string + */ + public function getKey() { + return $this->key; + } + + /** + * Gets logo url for this payment method. Uses specified language or default one. + * If logotype is not found for specified language, null is returned. + * + * @param string [Optional] $languageCode + * + * @return string|null + */ + public function getLogoUrl($languageCode = null) { + if ($languageCode !== null && isset($this->logoList[$languageCode])) { + return $this->logoList[$languageCode]; + } elseif (isset($this->logoList[$this->defaultLanguage])) { + return $this->logoList[$this->defaultLanguage]; + } else { + return null; + } + } + + /** + * Gets title for this payment method. Uses specified language or default one. + * + * @param string [Optional] $languageCode + * + * @return string + */ + public function getTitle($languageCode = null) { + if ($languageCode !== null && isset($this->titleTranslations[$languageCode])) { + return $this->titleTranslations[$languageCode]; + } elseif (isset($this->titleTranslations[$this->defaultLanguage])) { + return $this->titleTranslations[$this->defaultLanguage]; + } else { + return $this->key; + } + } + + /** + * Checks if this payment method can be used for specified amount. + * Throws exception if currency checked is not the one, for which payment method list was downloaded. + * + * @param integer $amount + * @param string $currency + * + * @return boolean + * + * @throws WebToPayException + */ + public function isAvailableForAmount($amount, $currency) { + if ($this->currency !== $currency) { + throw new WebToPayException( + 'Currencies does not match. You have to get payment types for the currency you are checking. Given currency: ' + . $currency . ', available currency: ' . $this->currency + ); + } + return ( + ($this->minAmount === null || $amount >= $this->minAmount) + && ($this->maxAmount === null || $amount <= $this->maxAmount) + ); + } + + /** + * Returns min amount for this payment method. If no min amount is specified, returns empty string. + * + * @return string + */ + public function getMinAmountAsString() { + return $this->minAmount === null ? '' : ($this->minAmount . ' ' . $this->currency); + } + + /** + * Returns max amount for this payment method. If no max amount is specified, returns empty string. + * + * @return string + */ + public function getMaxAmountAsString() { + return $this->maxAmount === null ? '' : ($this->maxAmount . ' ' . $this->currency); + } + + /** + * Set if this method returns IBAN number after payment + * + * @param boolean $isIban + */ + public function setIsIban($isIban) { + $this->isIban = $isIban == 1; + } + + /** + * Get if this method returns IBAN number after payment + * + * @return bool + */ + public function isIban() { + return $this->isIban; + } + + /** + * Setter of BaseCurrency + * + * @param string $baseCurrency + */ + public function setBaseCurrency($baseCurrency) + { + $this->baseCurrency = $baseCurrency; + } + + /** + * Getter of BaseCurrency + * + * @return string + */ + public function getBaseCurrency() + { + return $this->baseCurrency; + } +} + + +/** + * Utility class + */ +class WebToPay_Util { + + /** + * Decodes url-safe-base64 encoded string + * Url-safe-base64 is same as base64, but + is replaced to - and / to _ + * + * @param string $encodedText + * + * @return string + */ + public function decodeSafeUrlBase64($encodedText) { + return base64_decode(strtr($encodedText, array('-' => '+', '_' => '/'))); + } + + /** + * Encodes string to url-safe-base64 + * Url-safe-base64 is same as base64, but + is replaced to - and / to _ + * + * @param string $text + * + * @return string + */ + public function encodeSafeUrlBase64($text) { + return strtr(base64_encode($text), array('+' => '-', '/' => '_')); + } + + /** + * Parses HTTP query to array + * + * @param string $query + * + * @return array + */ + public function parseHttpQuery($query) { + $params = array(); + parse_str($query, $params); + if (get_magic_quotes_gpc()) { + $params = $this->stripSlashesRecursively($params); + } + return $params; + } + + /** + * Strips slashes recursively, so this method can be used on arrays with more than one level + * + * @param mixed $data + * + * @return mixed + */ + protected function stripSlashesRecursively($data) { + if (is_array($data)) { + $result = array(); + foreach ($data as $key => $value) { + $result[stripslashes($key)] = $this->stripSlashesRecursively($value); + } + return $result; + } else { + return stripslashes($data); + } + } +} + +/** + * Class with all information about available payment methods for some project, optionally filtered by some amount. + */ +class WebToPay_PaymentMethodList { + /** + * Holds available payment countries + * + * @var WebToPay_PaymentMethodCountry[] + */ + protected $countries; + + /** + * Default language for titles + * + * @var string + */ + protected $defaultLanguage; + + /** + * Project ID, to which this method list is valid + * + * @var integer + */ + protected $projectId; + + /** + * Currency for min and max amounts in this list + * + * @var string + */ + protected $currency; + + /** + * If this list is filtered for some amount, this field defines it + * + * @var integer + */ + protected $amount; + + /** + * Constructs object + * + * @param integer $projectId + * @param string $currency currency for min and max amounts in this list + * @param string $defaultLanguage + * @param integer $amount null if this list is not filtered by amount + */ + public function __construct($projectId, $currency, $defaultLanguage = 'lt', $amount = null) { + $this->projectId = $projectId; + $this->countries = array(); + $this->defaultLanguage = $defaultLanguage; + $this->currency = $currency; + $this->amount = $amount; + } + + /** + * Sets default language for titles. + * Returns itself for fluent interface + * + * @param string $language + * + * @return WebToPay_PaymentMethodList + */ + public function setDefaultLanguage($language) { + $this->defaultLanguage = $language; + foreach ($this->countries as $country) { + $country->setDefaultLanguage($language); + } + return $this; + } + + /** + * Gets default language for titles + * + * @return string + */ + public function getDefaultLanguage() { + return $this->defaultLanguage; + } + + /** + * Gets project ID for this payment method list + * + * @return integer + */ + public function getProjectId() { + return $this->projectId; + } + + /** + * Gets currency for min and max amounts in this list + * + * @return string + */ + public function getCurrency() { + return $this->currency; + } + + /** + * Gets whether this list is already filtered for some amount + * + * @return boolean + */ + public function isFiltered() { + return $this->amount !== null; + } + + /** + * Returns available countries + * + * @return WebToPay_PaymentMethodCountry[] + */ + public function getCountries() { + return $this->countries; + } + + /** + * Adds new country to payment methods. If some other country with same code was registered earlier, overwrites it. + * Returns added country instance + * + * @param WebToPay_PaymentMethodCountry $country + * + * @return WebToPay_PaymentMethodCountry + */ + public function addCountry(WebToPay_PaymentMethodCountry $country) { + return $this->countries[$country->getCode()] = $country; + } + + /** + * Gets country object with specified country code. If no country with such country code is found, returns null. + * + * @param string $countryCode + * + * @return null|WebToPay_PaymentMethodCountry + */ + public function getCountry($countryCode) { + return isset($this->countries[$countryCode]) ? $this->countries[$countryCode] : null; + } + + /** + * Returns new payment method list instance with only those payment methods, which are available for provided + * amount. + * Returns itself, if list is already filtered and filter amount matches the given one. + * + * @param integer $amount + * @param string $currency + * + * @return WebToPay_PaymentMethodList + * + * @throws WebToPayException if this list is already filtered and not for provided amount + */ + public function filterForAmount($amount, $currency) { + if ($currency !== $this->currency) { + throw new WebToPayException( + 'Currencies do not match. Given currency: ' . $currency . ', currency in list: ' . $this->currency + ); + } + if ($this->isFiltered()) { + if ($this->amount === $amount) { + return $this; + } else { + throw new WebToPayException('This list is already filtered, use unfiltered list instead'); + } + } else { + $list = new WebToPay_PaymentMethodList($this->projectId, $currency, $this->defaultLanguage, $amount); + foreach ($this->getCountries() as $country) { + $country = $country->filterForAmount($amount, $currency); + if (!$country->isEmpty()) { + $list->addCountry($country); + } + } + return $list; + } + } + + /** + * Loads countries from given XML node + * + * @param SimpleXMLElement $xmlNode + */ + public function fromXmlNode($xmlNode) { + foreach ($xmlNode->country as $countryNode) { + $titleTranslations = array(); + foreach ($countryNode->title as $titleNode) { + $titleTranslations[(string) $titleNode->attributes()->language] = (string) $titleNode; + } + $this->addCountry($this->createCountry((string) $countryNode->attributes()->code, $titleTranslations)) + ->fromXmlNode($countryNode); + } + } + + /** + * Method to create new country instances. Overwrite if you have to use some other country subtype. + * + * @param string $countryCode + * @param array $titleTranslations + * + * @return WebToPay_PaymentMethodCountry + */ + protected function createCountry($countryCode, array $titleTranslations = array()) { + return new WebToPay_PaymentMethodCountry($countryCode, $titleTranslations, $this->defaultLanguage); + } +} + +/** + * Sends answer to SMS payment if it was not provided with response to callback + */ +class WebToPay_SmsAnswerSender { + + /** + * @var string + */ + protected $password; + + /** + * @var WebToPay_WebClient + */ + protected $webClient; + + /** + * @var WebToPay_UrlBuilder $urlBuilder + */ + protected $urlBuilder; + + /** + * Constructs object + * + * @param string $password + * @param WebToPay_WebClient $webClient + * @param WebToPay_UrlBuilder $urlBuilder + */ + public function __construct( + $password, + WebToPay_WebClient $webClient, + WebToPay_UrlBuilder $urlBuilder + ) { + $this->password = $password; + $this->webClient = $webClient; + $this->urlBuilder = $urlBuilder; + } + + /** + * Sends answer by sms ID get from callback. Answer can be send only if it was not provided + * when responding to callback + * + * @param integer $smsId + * @param string $text + * + * @throws WebToPayException + */ + public function sendAnswer($smsId, $text) { + $content = $this->webClient->get($this->urlBuilder->buildForSmsAnswer(), array( + 'id' => $smsId, + 'msg' => $text, + 'transaction' => md5($this->password . '|' . $smsId), + )); + if (strpos($content, 'OK') !== 0) { + throw new WebToPayException( + sprintf('Error: %s', $content), + WebToPayException::E_SMS_ANSWER + ); + } + } +} + + +/** + * Creates objects. Also caches to avoid creating several instances of same objects + */ +class WebToPay_Factory { + + const ENV_PRODUCTION = 'production'; + const ENV_SANDBOX = 'sandbox'; + + /** + * @var array + */ + protected static $defaultConfiguration = array( + 'routes' => array( + self::ENV_PRODUCTION => array( + 'publicKey' => 'http://www.paysera.com/download/public.key', + 'payment' => 'https://www.paysera.com/pay/', + 'paymentMethodList' => 'https://www.paysera.com/new/api/paymentMethods/', + 'smsAnswer' => 'https://www.paysera.com/psms/respond/', + ), + self::ENV_SANDBOX => array( + 'publicKey' => 'http://sandbox.paysera.com/download/public.key', + 'payment' => 'https://sandbox.paysera.com/pay/', + 'paymentMethodList' => 'https://sandbox.paysera.com/new/api/paymentMethods/', + 'smsAnswer' => 'https://sandbox.paysera.com/psms/respond/', + ), + ) + ); + + /** + * @var string + */ + protected $environment; + + /** + * @var array + */ + protected $configuration; + + /** + * @var WebToPay_WebClient + */ + protected $webClient = null; + + /** + * @var WebToPay_CallbackValidator + */ + protected $callbackValidator = null; + + /** + * @var WebToPay_RequestBuilder + */ + protected $requestBuilder = null; + + /** + * @var WebToPay_Sign_SignCheckerInterface + */ + protected $signer = null; + + /** + * @var WebToPay_SmsAnswerSender + */ + protected $smsAnswerSender = null; + + /** + * @var WebToPay_PaymentMethodListProvider + */ + protected $paymentMethodListProvider = null; + + /** + * @var WebToPay_Util + */ + protected $util = null; + + /** + * @var WebToPay_UrlBuilder + */ + protected $urlBuilder = null; + + + /** + * Constructs object. + * Configuration keys: projectId, password + * They are required only when some object being created needs them, + * if they are not found at that moment - exception is thrown + * + * @param array $configuration + */ + public function __construct(array $configuration = array()) { + + $this->configuration = array_merge(self::$defaultConfiguration, $configuration); + $this->environment = self::ENV_PRODUCTION; + } + + /** + * If passed true the factory will use sandbox when constructing URLs + * + * @param $enableSandbox + * @return self + */ + public function useSandbox($enableSandbox) + { + if ($enableSandbox) { + $this->environment = self::ENV_SANDBOX; + } else { + $this->environment = self::ENV_PRODUCTION; + } + return $this; + } + + /** + * Creates or gets callback validator instance + * + * @return WebToPay_CallbackValidator + * + * @throws WebToPay_Exception_Configuration + */ + public function getCallbackValidator() { + if ($this->callbackValidator === null) { + if (!isset($this->configuration['projectId'])) { + throw new WebToPay_Exception_Configuration('You have to provide project ID'); + } + $this->callbackValidator = new WebToPay_CallbackValidator( + $this->configuration['projectId'], + $this->getSigner(), + $this->getUtil() + ); + } + return $this->callbackValidator; + } + + /** + * Creates or gets request builder instance + * + * @throws WebToPay_Exception_Configuration + * + * @return WebToPay_RequestBuilder + */ + public function getRequestBuilder() { + if ($this->requestBuilder === null) { + if (!isset($this->configuration['password'])) { + throw new WebToPay_Exception_Configuration('You have to provide project password to sign request'); + } + if (!isset($this->configuration['projectId'])) { + throw new WebToPay_Exception_Configuration('You have to provide project ID'); + } + $this->requestBuilder = new WebToPay_RequestBuilder( + $this->configuration['projectId'], + $this->configuration['password'], + $this->getUtil(), + $this->getUrlBuilder() + ); + } + return $this->requestBuilder; + } + + /** + * @return WebToPay_UrlBuilder + */ + public function getUrlBuilder() { + if ($this->urlBuilder === null) { + $this->urlBuilder = new WebToPay_UrlBuilder( + $this->configuration, + $this->environment + ); + } + return $this->urlBuilder; + } + + /** + * Creates or gets SMS answer sender instance + * + * @throws WebToPay_Exception_Configuration + * + * @return WebToPay_SmsAnswerSender + */ + public function getSmsAnswerSender() { + if ($this->smsAnswerSender === null) { + if (!isset($this->configuration['password'])) { + throw new WebToPay_Exception_Configuration('You have to provide project password'); + } + $this->smsAnswerSender = new WebToPay_SmsAnswerSender( + $this->configuration['password'], + $this->getWebClient(), + $this->getUrlBuilder() + ); + } + return $this->smsAnswerSender; + } + + /** + * Creates or gets payment list provider instance + * + * @throws WebToPay_Exception_Configuration + * + * @return WebToPay_PaymentMethodListProvider + */ + public function getPaymentMethodListProvider() { + if ($this->paymentMethodListProvider === null) { + if (!isset($this->configuration['projectId'])) { + throw new WebToPay_Exception_Configuration('You have to provide project ID'); + } + $this->paymentMethodListProvider = new WebToPay_PaymentMethodListProvider( + $this->configuration['projectId'], + $this->getWebClient(), + $this->getUrlBuilder() + + ); + } + return $this->paymentMethodListProvider; + } + + /** + * Creates or gets signer instance. Chooses SS2 signer if openssl functions are available, SS1 in other case + * + * @throws WebToPay_Exception_Configuration + * + * @return WebToPay_Sign_SignCheckerInterface + * + * @throws WebToPayException + */ + protected function getSigner() { + if ($this->signer === null) { + if (function_exists('openssl_pkey_get_public')) { + $webClient = $this->getWebClient(); + $publicKey = $webClient->get($this->getUrlBuilder()->buildForPublicKey()); + if (!$publicKey) { + throw new WebToPayException('Cannot download public key from WebToPay website'); + } + $this->signer = new WebToPay_Sign_SS2SignChecker($publicKey, $this->getUtil()); + } else { + if (!isset($this->configuration['password'])) { + throw new WebToPay_Exception_Configuration( + 'You have to provide project password if OpenSSL is unavailable' + ); + } + $this->signer = new WebToPay_Sign_SS1SignChecker($this->configuration['password']); + } + } + return $this->signer; + } + + /** + * Creates or gets web client instance + * + * @throws WebToPay_Exception_Configuration + * + * @return WebToPay_WebClient + */ + protected function getWebClient() { + if ($this->webClient === null) { + $this->webClient = new WebToPay_WebClient(); + } + return $this->webClient; + } + + /** + * Creates or gets util instance + * + * @throws WebToPay_Exception_Configuration + * + * @return WebToPay_Util + */ + protected function getUtil() { + if ($this->util === null) { + $this->util = new WebToPay_Util(); + } + return $this->util; + } +} + + +/** + * Sign checker which checks SS1 signature. SS1 does not depend on SSL functions + */ +class WebToPay_Sign_SS1SignChecker implements WebToPay_Sign_SignCheckerInterface { + + /** + * @var string + */ + protected $projectPassword; + + /** + * Constructs object + * + * @param string $projectPassword + */ + public function __construct($projectPassword) { + $this->projectPassword = $projectPassword; + } + + /** + * Check for SS1, which is not depend on openssl functions. + * + * @param array $request + * + * @return boolean + * + * @throws WebToPay_Exception_Callback + */ + public function checkSign(array $request) { + if (!isset($request['data']) || !isset($request['ss1'])) { + throw new WebToPay_Exception_Callback('Not enough parameters in callback. Possible version mismatch'); + } + + return md5($request['data'] . $this->projectPassword) === $request['ss1']; + } +} + +/** + * Checks SS2 signature. Depends on SSL functions + */ +class WebToPay_Sign_SS2SignChecker implements WebToPay_Sign_SignCheckerInterface { + + /** + * @var string + */ + protected $publicKey; + + /** + * @var WebToPay_Util + */ + protected $util; + + /** + * Constructs object + * + * @param string $publicKey + * @param WebToPay_Util $util + */ + public function __construct($publicKey, WebToPay_Util $util) { + $this->publicKey = $publicKey; + $this->util = $util; + } + + /** + * Checks signature + * + * @param array $request + * + * @return boolean + * + * @throws WebToPay_Exception_Callback + */ + public function checkSign(array $request) { + if (!isset($request['data']) || !isset($request['ss2'])) { + throw new WebToPay_Exception_Callback('Not enough parameters in callback. Possible version mismatch'); + } + + $ss2 = $this->util->decodeSafeUrlBase64($request['ss2']); + $ok = openssl_verify($request['data'], $ss2, $this->publicKey); + return $ok === 1; + } +} + +/** + * Interface for sign checker + */ +interface WebToPay_Sign_SignCheckerInterface { + + /** + * Checks whether request is signed properly + * + * @param array $request + * + * @return boolean + */ + public function checkSign(array $request); +} + +/** + * Simple web client + */ +class WebToPay_WebClient { + + /** + * Gets page contents by specified URI. Adds query data if provided to the URI + * Ignores status code of the response and header fields + * + * @param string $uri + * @param array $queryData + * + * @return string + * + * @throws WebToPayException + */ + public function get($uri, array $queryData = array()) { + if (count($queryData) > 0) { + $uri .= strpos($uri, '?') === false ? '?' : '&'; + $uri .= http_build_query($queryData, null, '&'); + } + $url = parse_url($uri); + if ('https' == $url['scheme']) { + $host = 'ssl://'.$url['host']; + $port = 443; + } else { + $host = $url['host']; + $port = 80; + } + + $fp = fsockopen($host, $port, $errno, $errstr, 30); + if (!$fp) { + throw new WebToPayException(sprintf('Cannot connect to %s', $uri), WebToPayException::E_INVALID); + } + + if(isset($url['query'])) { + $data = $url['path'].'?'.$url['query']; + } else { + $data = $url['path']; + } + + $out = "GET " . $data . " HTTP/1.0\r\n"; + $out .= "Host: ".$url['host']."\r\n"; + $out .= "Connection: Close\r\n\r\n"; + + $content = ''; + + fwrite($fp, $out); + while (!feof($fp)) $content .= fgets($fp, 8192); + fclose($fp); + + list($header, $content) = explode("\r\n\r\n", $content, 2); + + return trim($content); + } +} + +/** + * Payment method configuration for some country + */ +class WebToPay_PaymentMethodCountry { + /** + * @var string + */ + protected $countryCode; + + /** + * Holds available payment types for this country + * + * @var WebToPay_PaymentMethodGroup[] + */ + protected $groups; + + /** + * Default language for titles + * + * @var string + */ + protected $defaultLanguage; + + /** + * Translations array for this country. Holds associative array of country title by language codes. + * + * @var array + */ + protected $titleTranslations; + + /** + * Constructs object + * + * @param string $countryCode + * @param array $titleTranslations + * @param string $defaultLanguage + */ + public function __construct($countryCode, $titleTranslations, $defaultLanguage = 'lt') { + $this->countryCode = $countryCode; + $this->defaultLanguage = $defaultLanguage; + $this->titleTranslations = $titleTranslations; + $this->groups = array(); + } + + /** + * Sets default language for titles. + * Returns itself for fluent interface + * + * @param string $language + * + * @return WebToPay_PaymentMethodCountry + */ + public function setDefaultLanguage($language) { + $this->defaultLanguage = $language; + foreach ($this->groups as $group) { + $group->setDefaultLanguage($language); + } + return $this; + } + + /** + * Gets title of the group. Tries to get title in specified language. If it is not found or if language is not + * specified, uses default language, given to constructor. + * + * @param string [Optional] $languageCode + * + * @return string + */ + public function getTitle($languageCode = null) { + if ($languageCode !== null && isset($this->titleTranslations[$languageCode])) { + return $this->titleTranslations[$languageCode]; + } elseif (isset($this->titleTranslations[$this->defaultLanguage])) { + return $this->titleTranslations[$this->defaultLanguage]; + } else { + return $this->countryCode; + } + } + + /** + * Gets default language for titles + * + * @return string + */ + public function getDefaultLanguage() { + return $this->defaultLanguage; + } + + /** + * Gets country code + * + * @return string + */ + public function getCode() { + return $this->countryCode; + } + + /** + * Adds new group to payment methods for this country. + * If some other group was registered earlier with same key, overwrites it. + * Returns given group + * + * @param WebToPay_PaymentMethodGroup $group + * + * @return WebToPay_PaymentMethodGroup + */ + public function addGroup(WebToPay_PaymentMethodGroup $group) { + return $this->groups[$group->getKey()] = $group; + } + + /** + * Gets group object with specified group key. If no group with such key is found, returns null. + * + * @param string $groupKey + * + * @return null|WebToPay_PaymentMethodGroup + */ + public function getGroup($groupKey) { + return isset($this->groups[$groupKey]) ? $this->groups[$groupKey] : null; + } + + /** + * Returns payment method groups registered for this country. + * + * @return WebToPay_PaymentMethodGroup[] + */ + public function getGroups() { + return $this->groups; + } + + /** + * Gets payment methods in all groups + * + * @return WebToPay_PaymentMethod[] + */ + public function getPaymentMethods() { + $paymentMethods = array(); + foreach ($this->groups as $group) { + $paymentMethods = array_merge($paymentMethods, $group->getPaymentMethods()); + } + return $paymentMethods; + } + + /** + * Returns new country instance with only those payment methods, which are available for provided amount. + * + * @param integer $amount + * @param string $currency + * + * @return WebToPay_PaymentMethodCountry + */ + public function filterForAmount($amount, $currency) { + $country = new WebToPay_PaymentMethodCountry($this->countryCode, $this->titleTranslations, $this->defaultLanguage); + foreach ($this->getGroups() as $group) { + $group = $group->filterForAmount($amount, $currency); + if (!$group->isEmpty()) { + $country->addGroup($group); + } + } + return $country; + } + + /** + * Returns new country instance with only those payment methods, which are returns or not iban number after payment + * + * @param boolean $isIban + * + * @return WebToPay_PaymentMethodCountry + */ + public function filterForIban($isIban = true) { + $country = new WebToPay_PaymentMethodCountry($this->countryCode, $this->titleTranslations, $this->defaultLanguage); + foreach ($this->getGroups() as $group) { + $group = $group->filterForIban($isIban); + if (!$group->isEmpty()) { + $country->addGroup($group); + } + } + return $country; + } + + /** + * Returns whether this country has no groups + * + * @return boolean + */ + public function isEmpty() { + return count($this->groups) === 0; + } + + /** + * Loads groups from given XML node + * + * @param SimpleXMLElement $countryNode + */ + public function fromXmlNode($countryNode) { + foreach ($countryNode->payment_group as $groupNode) { + $key = (string) $groupNode->attributes()->key; + $titleTranslations = array(); + foreach ($groupNode->title as $titleNode) { + $titleTranslations[(string) $titleNode->attributes()->language] = (string) $titleNode; + } + $this->addGroup($this->createGroup($key, $titleTranslations))->fromXmlNode($groupNode); + } + } + + /** + * Method to create new group instances. Overwrite if you have to use some other group subtype. + * + * @param string $groupKey + * @param array $translations + * + * @return WebToPay_PaymentMethodGroup + */ + protected function createGroup($groupKey, array $translations = array()) { + return new WebToPay_PaymentMethodGroup($groupKey, $translations, $this->defaultLanguage); + } +} + + +/** + * Used to build a complete request URL. + * + * Class WebToPay_UrlBuilder + */ +class WebToPay_UrlBuilder { + + const PLACEHOLDER_KEY = '[domain]'; + + /** + * @var array + */ + protected $configuration = array(); + + /** + * @var string + */ + protected $environment; + + /** + * @var array + */ + protected $environmentSettings; + + /** + * @param array $configuration + * @param string $environment + */ + function __construct($configuration, $environment) + { + $this->configuration = $configuration; + $this->environment = $environment; + $this->environmentSettings = $this->configuration['routes'][$this->environment]; + } + + /** + * Builds a complete request URL based on the provided parameters + * + * @param $request + * @param null $language + * @return string + */ + public function buildForRequest($request, $language = null) { + return $this->createUrlFromRequestAndLanguage($request); + } + + /** + * Builds a complete URL for payment list API + * + * @param int $projectId + * @param string $currency + * @return string + */ + public function buildForPaymentsMethodList($projectId, $currency) { + $route = $this->environmentSettings['paymentMethodList']; + return $route . $projectId . '/currency:' . $currency; + } + + /** + * Builds a complete URL for Sms Answer + * + * @return string + */ + public function buildForSmsAnswer() { + $route = $this->environmentSettings['smsAnswer']; + return $route; + } + + /** + * Build the url to the public key + * + * @return string + */ + public function buildForPublicKey() { + $route = $this->environmentSettings['publicKey']; + return $route; + } + + /** + * Creates an URL from the request and data provided. + * + * @param array $request + * @return string + */ + protected function createUrlFromRequestAndLanguage($request) { + $url = $this->getPaymentUrl() . '?' . http_build_query($request, null, '&'); + return preg_replace('/[\r\n]+/is', '', $url); + } + + /** + * Returns payment url. Argument is same as lang parameter in request data + * + * @return string $url + */ + protected function getPaymentUrl() { + $route = $this->environmentSettings['payment']; + return $route; + } +} + + +/** + * Builds and signs requests + */ +class WebToPay_RequestBuilder { + + /** + * @var string + */ + protected $projectPassword; + + /** + * @var WebToPay_Util + */ + protected $util; + + /** + * @var integer + */ + protected $projectId; + + + /** + * @var WebToPay_UrlBuilder $urlBuilder + */ + protected $urlBuilder; + + /** + * Constructs object + * + * @param integer $projectId + * @param string $projectPassword + * @param WebToPay_Util $util + * @param WebToPay_UrlBuilder $urlBuilder + */ + public function __construct( + $projectId, + $projectPassword, + WebToPay_Util $util, + WebToPay_UrlBuilder $urlBuilder + ) + { + $this->projectId = $projectId; + $this->projectPassword = $projectPassword; + $this->util = $util; + $this->urlBuilder = $urlBuilder; + } + + /** + * Builds request data array. + * + * This method checks all given data and generates correct request data + * array or raises WebToPayException on failure. + * + * @param array $data information about current payment request + * + * @return array + * + * @throws WebToPayException + */ + public function buildRequest($data) { + $this->validateRequest($data, self::getRequestSpec()); + $data['version'] = WebToPay::VERSION; + $data['projectid'] = $this->projectId; + unset($data['repeat_request']); + return $this->createRequest($data); + } + + /** + * Builds the full request url (including the protocol and the domain) + * + * @param array $data + * @return string + */ + public function buildRequestUrlFromData($data) { + $language = isset($data['lang']) ? $data['lang'] : null; + $request = $this->buildRequest($data); + return $this->urlBuilder->buildForRequest($request, $language); + } + + /** + * Builds repeat request data array. + * + * This method checks all given data and generates correct request data + * array or raises WebToPayException on failure. + * + * @param string $orderId order id of repeated request + * + * @return array + * + * @throws WebToPayException + */ + public function buildRepeatRequest($orderId) { + $data['orderid'] = $orderId; + $data['version'] = WebToPay::VERSION; + $data['projectid'] = $this->projectId; + $data['repeat_request'] = '1'; + return $this->createRequest($data); + } + + /** + * Builds the full request url for a repeated request (including the protocol and the domain) + * + * @param string $orderId order id of repeated request + * @return string + */ + public function buildRepeatRequestUrlFromOrderId($orderId) { + $request = $this->buildRepeatRequest($orderId); + return $this->urlBuilder->buildForRequest($request); + } + + /** + * Checks data to be valid by passed specification + * + * @param array $data + * @param array $specs + * + * @throws WebToPay_Exception_Validation + */ + protected function validateRequest($data, $specs) { + foreach ($specs as $spec) { + list($name, $maxlen, $required, $regexp) = $spec; + if ($required && !isset($data[$name])) { + throw new WebToPay_Exception_Validation( + sprintf("'%s' is required but missing.", $name), + WebToPayException::E_MISSING, + $name + ); + } + + if (!empty($data[$name])) { + if ($maxlen && strlen($data[$name]) > $maxlen) { + throw new WebToPay_Exception_Validation(sprintf( + "'%s' value is too long (%d), %d characters allowed.", + $name, + strlen($data[$name]), + $maxlen + ), WebToPayException::E_MAXLEN, $name); + } + + if ($regexp !== '' && !preg_match($regexp, $data[$name])) { + throw new WebToPay_Exception_Validation( + sprintf("'%s' value '%s' is invalid.", $name, $data[$name]), + WebToPayException::E_REGEXP, + $name + ); + } + } + } + } + + /** + * Makes request data array from parameters, also generates signature + * + * @param array $request + * + * @return array + */ + protected function createRequest(array $request) { + $data = $this->util->encodeSafeUrlBase64(http_build_query($request, null, '&')); + return array( + 'data' => $data, + 'sign' => md5($data . $this->projectPassword), + ); + } + + /** + * Returns specification of fields for request. + * + * Array structure: + * name – request item name + * maxlen – max allowed value for item + * required – is this item is required + * regexp – regexp to test item value + * + * @return array + */ + protected static function getRequestSpec() { + return array( + array('orderid', 40, true, ''), + array('accepturl', 255, true, ''), + array('cancelurl', 255, true, ''), + array('callbackurl', 255, true, ''), + array('lang', 3, false, '/^[a-z]{3}$/i'), + array('amount', 11, false, '/^\d+$/'), + array('currency', 3, false, '/^[a-z]{3}$/i'), + array('payment', 20, false, ''), + array('country', 2, false, '/^[a-z_]{2}$/i'), + array('paytext', 255, false, ''), + array('p_firstname', 255, false, ''), + array('p_lastname', 255, false, ''), + array('p_email', 255, false, ''), + array('p_street', 255, false, ''), + array('p_city', 255, false, ''), + array('p_state', 20, false, ''), + array('p_zip', 20, false, ''), + array('p_countrycode', 2, false, '/^[a-z]{2}$/i'), + array('test', 1, false, '/^[01]$/'), + array('time_limit', 19, false, '/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/'), + ); + } +} \ No newline at end of file