lib/Ajde/Shop/Transaction/Provider/Wedeal.php

Summary

Maintainability
D
2 days
Test Coverage
<?php

class Ajde_Shop_Transaction_Provider_Wedeal extends Ajde_Shop_Transaction_Provider
{
    private static $_debug = false;
    private static $_api_url = 'www.paydutch.nl';
    private static $_api_path = '/api/processreq.aspx';

    public function getName()
    {
        return 'iDeal';
    }

    public function getLogo()
    {
        return MEDIA_DIR.'core/shop/ideal.png';
    }

    public function usePostProxy()
    {
        return false;
    }

    public function getRedirectUrl($description = null)
    {
        $transaction = $this->getTransaction();

        $request = [
            'type'           => 'transaction',
            'transactionreq' => [
                'username'    => config('shop.transaction.wedeal.username'),
                'password'    => config('shop.transaction.wedeal.password'),
                'reference'   => $transaction->secret,
                'description' => config('app.id').': '.Ajde_Component_String::makePlural($transaction->shipment_itemsqty,
                        'item'),
                'amount'      => str_replace('.', ',', (string) $transaction->payment_amount),
                'methodcode'  => '0101',
                'maxcount'    => '1',
                'test'        => $this->isSandbox() ? 'true' : 'false',
                'successurl'  => config('app.rootUrl').'shop/transaction:callback/wedeal.html',
                'failurl'     => config('app.rootUrl').'shop/transaction:callback/wedeal.html',
            ],
        ];
        $res = $this->sendRequest($request, true);

        if ($res['success'] === true) {
            $url = $res['response'];
            if (substr($url, 0, 7) === 'http://' || substr($url, 0, 8) === 'https://') {
                return $this->ping($url) ? $res['response'] : false;
            } else {
                Ajde_Log::log('Wedeal::getRedirectUrl() returned no URL but: '.$res['response']);

                return false;
            }
        } else {
            Ajde_Log::log('Wedeal::getRedirectUrl() returned no URL but: '.$res['response']);

            return false;
        }
    }

    public function getRedirectParams($description = null)
    {
        return [];
    }

    public function updatePayment()
    {
        $request = Ajde::app()->getRequest();

        $username = $request->getParam('Username');
        $password = $request->getParam('Password');
        $id = $request->getParam('ID');
        $secret = $request->getParam('Reference');
        $paymentMethod = $request->getParam('PaymentMethod');
        $state = $request->getParam('PaymentState');
        $description = $request->getParam('Description');

        if ($username != config('shop.transaction.wedeal.callbackUsername')) {
            Ajde_Log::log('Invalid username for callback of transaction '.$secret);

            return false;
        }
        if ($password != config('shop.transaction.wedeal.callbackPassword')) {
            Ajde_Log::log('Invalid password for callback of transaction '.$secret);

            return false;
        }

        $transaction = new TransactionModel();
        if (!$transaction->loadByField('secret', $secret)) {
            Ajde_Log::log('Could not find transaction for PayPal payment with txn id '.$txn_id.' and transaction secret '.$secret);
        }

        $request = [
            'type'     => 'query',
            'merchant' => [
                'username'  => config('shop.transaction.wedeal.username'),
                'password'  => config('shop.transaction.wedeal.password'),
                'reference' => $secret,
            ],
        ];

        // Pause a little before request is made to allow for processing on provider
        // as this request will be made synchronously after payment
        sleep(3);

        $res = $this->sendRequest($request);

        if ($res['success'] === true) {
            $response = $res['response']->paymentinfo;
            $count = (int) $res['response']->count;

            // get transaction details
            if ($count == 0) {
                $transaction->payment_status = 'refused';
                $transaction->save();
                Ajde_Log::log('iDeal callback didn\'t return any transaction for '.$secret);
            } elseif (self::isPaid((string) $response->state)) {
                if ((string) $response->id != $id) {
                    Ajde_Log::log('IDs don\'t match for iDeal callback of transaction '.$secret);
                } else {
                    $details = 'AMOUNT: '.(string) $response->amount.PHP_EOL.
                        'PAYER_NAME: '.(string) $response->consumername.PHP_EOL.
                        'PAYER_ACCOUNT: '.(string) $response->consumeraccount.PHP_EOL.
                        'PAYER_CITY: '.(string) $response->consumercity.PHP_EOL.
                        'PAYER_COUNTRY: '.(string) $response->consumercountry.PHP_EOL.
                        'WEDEAL_ID: '.(string) $response->id;
                    $transaction->payment_details = $details;
                    $transaction->payment_status = 'completed';
                    $transaction->save();

                    return [
                        'success'     => true,
                        'transaction' => $transaction,
                    ];
                }
            } elseif (self::isRefused((string) $response->state)) {
                $transaction->payment_status = 'refused';
                $transaction->save();
                Ajde_Log::log('iDeal payment refused with state '.(string) $response->state);
            } else {
                Ajde_Log::log('iDeal payment callback called with state '.(string) $response->state.' but no status change for transaction '.$secret.' detected');
            }
        } else {
            Ajde_Log::log('Wedeal::updatePayment() failed because: '.$res['response']);
        }

        return [
            'success'     => false,
            'transaction' => $transaction,
        ];
    }

    // Helpers

    private function sendRequest($request, $asRaw = false)
    {
        if (self::$_debug) {
            Ajde_Log::log('INPUT DATA: '.var_export($request, true));
        }

        $xml = self::buildXML($request);
        $url = fsockopen('ssl://'.self::$_api_url, 443);

        if ($url === false) {
            return [
                'success'  => false,
                'response' => 'iDeal foutmelding: Kan niet verbinden',
            ];
        }

        $data = $xml->saveXML();
        $length = strlen($data);

        if (self::$_debug) {
            Ajde_Log::log('REQUEST XML: '.var_export($data, true));
        }

        $post = 'GET '.self::$_api_path." HTTP/1.0\n";
        $post .= "Content-Length: $length\n";
        $post .= "Content-Type: text/xml\n";
        $post .= "Connection: Close\n\n";
        $post .= "$data\n\n";

        fwrite($url, $post);

        $response = '';
        while (!feof($url)) {
            $response .= fgets($url, 1024);
        }

        fclose($url);

        if (self::$_debug) {
            Ajde_Log::log('RESPONSE DATA: '.var_export($response, true));
        }

        if ($asRaw) {
            $contentLenght = strpos($response, PHP_EOL.'Content-Length:') + 1;
            $nextLine = strpos($response, PHP_EOL, $contentLenght);
            $result = trim(substr($response, $nextLine));
        } else {
            if (strpos($response, '<?xml') === false) {
                return [
                    'success'  => false,
                    'response' => 'iDeal foutmelding: Ongeldig antwoord',
                ];
            }

            $start = strpos($response, '<?xml');
            $response = substr($response, $start);

            $xml = new DOMDocument();
            $xml->loadXML($response);

            $result = simplexml_import_dom($xml);

            if (self::$_debug) {
                Ajde_Log::log('OUTPUT XML: '.var_export($result, true));
            }

            if ($result->error) {
                return [
                    'success'  => false,
                    'response' => "iDeal foutmelding ($result->error): ".self::getError($result->error),
                ];
            }
        }

        return [
            'success'  => true,
            'response' => $result,
        ];
    }

    private static function getError($errno)
    {
        switch ($errno) {
            case '0000':
                return 'Onbekend bericht';
            case '0100':
                return 'Test-omgeving is niet geactiveerd';
            case '0200':
                return 'Live-omgeving is niet geactiveerd';
            case '1100':
                return 'Banklist aanvraag mislukt';
            case '2000':
                return 'Transactie open';
            case '2100':
                return 'Transactie succesvol';
            case '2200':
                return 'Transactie mislukt';
            case '2300':
                return 'Transactie verlopen';
            case '2400':
                return 'Transactie niet gevonden';
            case '2500':
                return 'Bedrag is te laag. Het minimum zijn uw transactiekosten.';
            case '3000':
                return 'Login succesvol';
            case '3100':
                return 'Merchant bestaat niet';
            case '3200':
                return 'Wachtwoord onjuist';
            case '3300':
                return 'Merchant is geblokkeerd';
            case '4000':
                return 'Het bedrag is niet in centen. Gebruik dus geen , of .';
            case '9000':
                return 'SSL niet gebruikt, gebruik HTTPS';
        }
    }

    private static function isPaid($status)
    {
        $status = strtolower($status);
        if ($status == 'register' || $status == 'processing' || $status == 'cancelled' || $status == 'failed') {
            return false;
        } else {
            return true;
        }
    }

    private static function isRefused($status)
    {
        $status = strtolower($status);
        if ($status == 'cancelled' || $status == 'failed') {
            return false;
        } else {
            return true;
        }
    }

    /**
     * @param $request
     *
     * @return DOMDocument
     */
    private static function buildXML($request)
    {
        $xml = new DOMDocument();
        $reqelm = $xml->createElement('request');
        self::appendData($reqelm, $xml, $request);
        $xml->appendChild($reqelm);

        return $xml;
    }

    private static function appendData(&$element, $xml, $data)
    {
        foreach ($data as $k => $v) {
            if (is_array($v)) {
                $child = $xml->createElement($k);
                self::appendData($child, $xml, $v);
            } else {
                $child = $xml->createElement($k, $v);
            }
            $element->appendChild($child);
        }
    }
}