Post by Shinpe » Fri Jun 03, 2016 4:57 pm

Hello guys,
I just installed a new payment method but i'm having a bit of trouble with it.
The thing is that the payment gateway only supports 2 decimal places and open cart sends up to 4 decimal places so we got an error message.
My question is, is there a way to round up the value before it gets send to the gateway?

Best regards,
Levi

New member

Posts

Joined
Wed Jun 24, 2015 1:03 am

Post by inactiveaccount9912 » Fri Jun 03, 2016 6:38 pm

You will have to edit the controller of that payment method and change the total sent. Without knowing the code or the extension noone can help you. If that extension makes a total match check in its callback method, you will also have to round the order total there so it matches the total sent to the payment processor.

Expert Member

Posts

Joined
Fri May 14, 2010 2:36 am

Post by Shinpe » Fri Jun 03, 2016 6:54 pm

Here is the code for the controller.

Code: Select all

<?php
class ControllerPaymentMeowallet extends Controller
{
    public function index()
    {
        $this->language->load('payment/meowallet');

        $data['button_confirm'] = $this->language->get('button_confirm');
        $data['action']         = $this->url->link('payment/meowallet/redirect', 'SSL');
        $data['testmode']       = $this->config->get('meowallet_environment');
        $data['text_testmode']  = $this->language->get('text_testmode');

        return $this->load->view('default/template/payment/meowallet.tpl', $data);
    }

    public function redirect()
    {
        $this->load->model('checkout/order');
        $this->load->model('payment/meowallet');

        $confirm_url = $this->url->link('payment/meowallet/success');
        $cancel_url  = $this->url->link('checkout/checkout', '', 'SSL');
        $products    = $this->cart->getProducts();
        $order_info  = $this->model_checkout_order->getOrder($this->session->data['order_id']);

        try
        {
            $checkout = $this->model_payment_meowallet->createCheckout($order_info, $products, $confirm_url, $cancel_url);
        }
        catch (\Exception $e)
        {
            $this->log->write($e->getMessage());
            $this->response->redirect($this->url->link('payment/meowallet/error'));
        }

        $url = sprintf('%s%s%s=%s', $checkout->url_redirect,
                                    false === strpos($checkout->url_redirect, '?') ? '?' : '&',
                                    'locale', $this->getLocaleCode());

        $this->response->redirect($url);
    }


    public function error()
    {
        $this->language->load('payment/meowallet');

        $data['breadcrumbs'][]   = array('text' => $this->language->get('text_home'),
                                         'href' => $this->url->link('common/home'));
        $data['breadcrumbs'][]   = array('text' => $this->language->get('text_cart'),
                                         'href' => $this->url->link('checkout/cart'));

        $data['heading_title']   = 'MEO Wallet';
        $data['text_error']      = $this->language->get('error_message');
        $data['continue']        = $this->url->link('checkout/checkout', '', 'SSL');
        $data['button_continue'] = $this->language->get('continue');
        $data['content_bottom']  = '';

        $data['header']          = $this->load->controller('common/header');
        $data['column_left']     = $this->load->controller('common/column_left');
        $data['column_right']    = $this->load->controller('common/column_right');
        $data['footer']          = $this->load->controller('common/footer');

        $this->response->setOutput($this->load->view('default/template/payment/meowallet_error.tpl', $data));
    }

    public function success()
    {
        if (isset($this->request->get['checkoutid']))
        {
            $id = $this->request->get['checkoutid'];

            $this->load->model('payment/meowallet');
            $this->model_payment_meowallet->confirmCheckout($id);
        }

        $this->response->redirect($this->url->link('checkout/success'));

    }

    public function callback()
    {
        $callback = $this->getRequestPayload();

        try
        {
            $this->load->model('payment/meowallet');
            $this->model_payment_meowallet->processCallback($callback);
        }
        catch (\InvalidArgumentException $e)
        {
            $this->log->write("MEOWallet received invalid callback. Request data: '$callback'");
            http_response_code(400);
        }
        catch (\Exception $e)
        {
            $this->log->write('MEOWallet error processing callback. Reason: '.$e->getMessage());
            http_response_code(500);
        }
    }

    private function getRequestPayload()
    {
        return file_get_contents('php://input');
    }

    private function getLocaleCode()
    {
        $this->load->model('localisation/language');
        $languages  = $this->model_localisation_language->getLanguages();
        $languageId = $this->config->get('config_language_id');
        $localeCode = 'pt_PT';

        foreach ($languages as $language)
        {
            if ($language['language_id'] == $languageId)
            {
                # sample data: en_US.UTF-8,en_US,en-gb,english
                $_locale = explode(',', $language['locale']);

                if (count($_locale) > 1)
                {
                    $localeCode = $_locale[2];
                }

                break;
            }
        }

        return $localeCode;
    }
}
Hope this help

New member

Posts

Joined
Wed Jun 24, 2015 1:03 am

Post by Shinpe » Fri Jun 03, 2016 6:56 pm

And here is the model file

Code: Select all

<?php
class ModelPaymentMeowallet extends Model
{
    const PRODUCTION_ENVIRONMENT_ID    = 0;
    const PRODUCTION_SERVICE_ENDPOINT  = 'https://services.wallet.pt/api/v2';
    const SANDBOX_ENVIRONMENT_ID       = 1;
    const SANDBOX_SERVICE_ENDPOINT     = 'https://services.sandbox.meowallet.pt/api/v2';

    public function getMethod($address, round($total, 2, PHP_ROUND_HALF_DOWN))
    {
        
        $available  = true;
        $currencies = array('EUR');
        $authToken  = $this->getAPIToken();

        if ( empty($authToken) )
        {
            $available = false;
        }

        if (false === in_array(strtoupper($this->currency->getCode()), $currencies))
        {
            $available = false;
        }

        if ($available)
        {
            return array(
                'code'       => 'meowallet',
                'title'      => $this->config->get('meowallet_title'),
                'sort_order' => $this->config->get('meo_wallet_sort_order'),
                'terms'      => ''
            );
        }

        return array();
    }

    public function logger($message)
    {
        if ($this->config->get('meowallet_debug'))
        {
            $log = new Log('meowallet.log');
            $log->write($message);
        }
    }

    protected function getAPIToken()
    {
        $environment = $this->config->get('meowallet_environment');
        $key         = sprintf('meowallet_%s_api_token', $environment == self::SANDBOX_ENVIRONMENT_ID ? 'sandbox' : 'production');

        return $this->config->get($key);
    }

    protected function getServiceEndpoint($path = null)
    {
        $environment = $this->config->get('meowallet_environment');
        $url         = null;

        switch ($environment)
        {
            case static::SANDBOX_ENVIRONMENT_ID:
                $url = static::SANDBOX_SERVICE_ENDPOINT;
                break;
            case static::PRODUCTION_ENVIRONMENT_ID:
                $url = static::PRODUCTION_SERVICE_ENDPOINT;
                break;
        }

        if ( empty($url) )
        {
            throw new \UnexpectedValueException("Invalid environment configuration, set as '$environment'");
        }

        return sprintf('%s/%s', $url, $path);
    }

    public function createCheckout($order_info, $products, $url_confirm, $url_cancel)
    {
        $address = sprintf("%s %s", html_entity_decode($order_info['shipping_address_1'], ENT_QUOTES, 'UTF-8'),
                                    html_entity_decode($order_info['shipping_address_2'], ENT_QUOTES, 'UTF-8'));
        $client  = array('name'  => sprintf("%s %s", html_entity_decode($order_info['shipping_firstname'], ENT_QUOTES, 'UTF-8'),
                                                     html_entity_decode($order_info['shipping_lastname'], ENT_QUOTES, 'UTF-8')),
                         'email' => $order_info['email']);

        $client['phone']   = $order_info['telephone'];
        $client['address'] = array('country'    => $order_info['shipping_iso_code_2'],
                                   'city'       => html_entity_decode($order_info['shipping_city'], ENT_QUOTES, 'UTF-8'),
                                   'postalcode' => html_entity_decode($order_info['shipping_postcode'], ENT_QUOTES, 'UTF-8'),
                                   'address'    => trim($address));

        $payment = array('client'        => $client,
                         'amount'        => $order_info['total'],
                         'currency'      => $order_info['currency_code'],
                         'items'         => array(),
                         'ext_invoiceid' => $order_info['order_id']);

        foreach ($products as $product)
        {
            $payment['items'][] = array('ref'   => $product['product_id'],
                                        'name'  => $product['name'],
                                        'descr' => $product['name'],
                                        'qt'    => $product['quantity']);
        }

        $request_data = json_encode(array('payment'     => $payment,
                                          'url_confirm' => $url_confirm,
                                          'url_cancel'  => $url_cancel));

        $authToken    = $this->getAPIToken();
        $headers      = array(
            'Authorization: WalletPT ' . $authToken,
            'Content-Type: application/json',
            'Content-Length: ' . strlen($request_data)
        );

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
        curl_setopt($ch, CURLOPT_URL, $this->getServiceEndpoint('checkout'));
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
        curl_setopt($ch, CURLOPT_POSTFIELDS, $request_data);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        $response = json_decode( curl_exec($ch) );

        if (curl_errno($ch))
        {
            $message = sprintf('MEOWallet Could not create procheckout. Reason: %s', curl_error($ch));
            throw new \Exception($message);
        }

        if (   false == is_object($response)
            || false == property_exists($response, 'id')
            || false == property_exists($response, 'url_redirect')
        )
        {
            $response_data = var_export($response);
            $message       = sprintf('MEOWallet Could not create procheckout. Service response: %s', $response_data);

            throw new \Exception($message);
        }

        $this->db->query(sprintf("INSERT INTO `" . DB_PREFIX . "meowallet_order` VALUES (%d, '%s')",
                                 (int) $this->db->escape($order_info['order_id']), $this->db->escape($response->id)));

        return $response;
    }

    public function processCallback($verbatim_callback)
    {
        if (false === $this->isValidCallback($verbatim_callback))
        {
            throw new \InvalidArgumentException('Invalid callback');
        }

        $callback = json_decode($verbatim_callback);

        $this->logger(sprintf("MEOWallet callback for invoice_id '%s' with status '%s'",
                                  $callback->ext_invoiceid, $callback->operation_status));

        try
        {
            $this->processPayment($callback->operation_id,
                                  $callback->ext_invoiceid,
                                  $callback->operation_status,
                                  $callback->amount,
                                  $callback->method);
        }
        catch (\Exception $e)
        {
            throw $e;
        }
    }

    public function confirmCheckout($checkout_id)
    {
        $order_id = $this->getCheckoutOrderId($checkout_id);

        if ( null == $order_id )
        {
            throw new \InvalidArgumentException("MEOWallet no checkout '$checkout_id' found!");
        }

        $this->load->model('checkout/order');

        $order_info = $this->model_checkout_order->getOrder($order_id);

        if ( ! $order_info )
        {
            throw new \InvalidArgumentException("MEOWallet no order with id '$order_id' found!");
        }

        if ( ! $order_info['order_status_id'] )
        {
            $order_status = $this->config->get('meowallet_pending_order_status_id');
            $this->model_checkout_order->addOrderHistory($order_info, $order_status, "MEOWallet transaction id $checkout_id", true);
        }
    }

    private function isValidCallback($data)
    {
        $authToken    = $this->getAPIToken();
        $headers      = array(
            'Authorization: WalletPT ' . $authToken,
            'Content-Type: application/json',
            'Content-Length: ' . strlen($data));

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
        curl_setopt($ch, CURLOPT_URL, $this->getServiceEndpoint('callback/verify'));
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        $response = curl_exec($ch);

        if (0 == strcasecmp('true', $response))
        {
            return true;
        }

        if (0 != strcasecmp('false', $response))
        {
            throw new \InvalidArgumentException("MEOWallet callback validation returned unexpected response '$response'");
        }

        return false;
    }

    private function processPayment($transaction_id, $order_id, $status, $amount, $method)
    {
        $this->logger(sprintf("Processing payment for transaction_id: '%s' order_id '%s' with status '%s', amount '%s'",
                              $transaction_id, $order_id, $status, $amount));

        $this->load->model('checkout/order');
        $order_info = $this->model_checkout_order->getOrder($order_id);

        if ( ! $order_info )
        {
            throw new \InvalidArgumentException("MEOWallet no order with id '$order_id' found!");
        }

        switch ($status)
        {
            case 'COMPLETED':
                $is_partial_payment = ((float)$amount != $this->currency->format($order_info['total'],
                                        $order_info['currency_code'],
                                        $order_info['currency_value'],
                                        false));

                if ($is_partial_payment)
                {
                    throw new \InvalidArgumentException('MEOWallet amount paid mismatch order value');
                }

                $orderStatus = $this->config->get('meowallet_completed_order_status_id');

                break;

            case 'FAIL':
                /**
                 * If transaction fails and it wasn't previously marked as pending, there is no need to inform user or the merchant.
                 * Since the user will be redirected to checkout page to retry the payment.
                 */

                $orderStatus = null;

                if ( $order_info['order_status_id'] )
                {
                    $orderStatus = $this->config->get('meowallet_failed_order_status_id');
                }

                break;

            case 'NEW':
            case 'PENDING':
                $orderStatus = $this->config->get('meowallet_pending_order_status_id');

                break;

            default:
                throw new \InvalidArgumentException("MEOWallet payment operation status '$status' not handled by this module!");
        }

        $comment = "MEO Wallet payment status is $status. Checkout ID: $transaction_id Method: $method";

        $this->model_checkout_order->addOrderHistory($order_info['order_id'], $orderStatus, $comment);
    }

    private function getCheckoutOrderId($checkout_id)
    {
        $qry = $this->db->query("SELECT order_id FROM `" . DB_PREFIX . "meowallet_order`
                                 WHERE `meowallet_checkout_id` = '" . $this->db->escape($checkout_id) . "' LIMIT 1");

        $orderId = null;
        if ( $qry->num_rows )
        {
            $orderId = $qry->row['order_id'];
        }

        return $orderId;
    }
}

New member

Posts

Joined
Wed Jun 24, 2015 1:03 am

Post by inactiveaccount9912 » Fri Jun 03, 2016 7:44 pm

In the model change

Code: Select all

$order_info['total']
to

Code: Select all

round($order_info['total'], 2)

Expert Member

Posts

Joined
Fri May 14, 2010 2:36 am

Post by Shinpe » Fri Jun 03, 2016 9:33 pm

YES! it works!
Thank you so much.
Keep being awsome!

New member

Posts

Joined
Wed Jun 24, 2015 1:03 am
Who is online

Users browsing this forum: No registered users and 54 guests