<?php
namespace Payum\Core\Bridge\PlainPhp\Security;

use Payum\Core\Exception\InvalidArgumentException;
use Payum\Core\Security\HttpRequestVerifierInterface;
use Payum\Core\Security\TokenInterface;
use Payum\Core\Security\Util\RequestTokenVerifier;
use Payum\Core\Storage\StorageInterface;

class HttpRequestVerifier implements HttpRequestVerifierInterface
{
    /**
     * @var \Payum\Core\Storage\StorageInterface
     */
    protected $tokenStorage;

    /**
     * @var string
     */
    protected $tokenParameter;

    /**
     * @param StorageInterface $tokenStorage
     * @param string           $tokenParameter
     */
    public function __construct(StorageInterface $tokenStorage, $tokenParameter = 'payum_token')
    {
        $this->tokenStorage = $tokenStorage;
        $this->tokenParameter = (string) $tokenParameter;
    }

    /**
     * {@inheritDoc}
     */
    public function verify($httpRequest)
    {
        if (false == is_array($httpRequest)) {
            throw new InvalidArgumentException('Invalid request given. In most cases you have to pass $_REQUEST array.');
        }

        if (false == isset($httpRequest[$this->tokenParameter])) {
            throw new InvalidArgumentException(sprintf('Token parameter `%s` was not found in in the http request.', $this->tokenParameter));
        }

        if ($httpRequest[$this->tokenParameter] instanceof TokenInterface) {
            return $httpRequest[$this->tokenParameter];
        }

        if (false == $token = $this->tokenStorage->find($httpRequest[$this->tokenParameter])) {
            throw new InvalidArgumentException(sprintf('A token with hash `%s` could not be found.', $httpRequest[$this->tokenParameter]));
        }

        /** @var $token TokenInterface */
        if (!RequestTokenVerifier::isValid($_SERVER['REQUEST_URI'], $token->getTargetUrl())) {
            throw new InvalidArgumentException(sprintf('The current url %s not match target url %s set in the token.', $_SERVER['REQUEST_URI'], $token->getTargetUrl()));
        }

        return $token;
    }

    /**
     * {@inheritDoc}
     */
    public function invalidate(TokenInterface $token)
    {
        $this->tokenStorage->delete($token);
    }
}


<?php
namespace Payum\Payex\Action;

use Payum\Core\Action\ActionInterface;
use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Core\Model\PaymentInterface;
use Payum\Core\Request\Convert;
use Payum\Payex\Api\OrderApi;

class ConvertPaymentAction implements ActionInterface
{
    /**
     * {@inheritDoc}
     *
     * @param Convert $request
     */
    public function execute($request)
    {
        RequestNotSupportedException::assertSupports($this, $request);

        /** @var PaymentInterface $payment */
        $payment = $request->getSource();

        $details = ArrayObject::ensureArrayObject($payment->getDetails());
        $details['price'] = $payment->getTotalAmount();
        $details['priceArgList'] = '';
        $details['vat'] = 0;
        $details['currency'] = $payment->getCurrencyCode();
        $details['orderId'] = $payment->getNumber();
        $details['productNumber'] = 'n\a';
        $details['purchaseOperation'] = OrderApi::PURCHASEOPERATION_SALE;
        $details['view'] = OrderApi::VIEW_CREDITCARD;
        $details['description'] = $payment->getDescription();
        $details['clientIdentifier'] = '';
        $details['additionalValues'] = '';
        $details['agreementRef'] = '';
        $details['clientLanguage'] = isset($details['clientLanguage']) ? $details['clientLanguage'] : 'en-US';
        $details['autoPay'] = false;

        $request->setResult((array) $details);
    }

    /**
     * {@inheritDoc}
     */
    public function supports($request)
    {
        return
            $request instanceof Convert &&
            $request->getSource() instanceof PaymentInterface &&
            $request->getTo() == 'array'
        ;
    }
}


<?php
namespace Payum\Paypal\ProHosted\Nvp\Action;

use Payum\Core\Action\ActionInterface;
use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Core\Request\GetStatusInterface;
use Payum\Paypal\ProHosted\Nvp\Api;

class StatusAction implements ActionInterface
{
    /**
     * {@inheritDoc}
     *
     * @param GetStatusInterface $request
     */
    public function execute($request)
    {
        RequestNotSupportedException::assertSupports($this, $request);

        $model = ArrayObject::ensureArrayObject($request->getModel());

        foreach (range(0, 9) as $index) {
            if ($model['L_ERRORCODE'.$index]) {
                $request->markFailed();

                return;
            }
        }

        if (isset($model['CANCELLED'])) {
            $request->markCanceled();

            return;
        }

        if (null === $paymentStatus = $model['PAYMENTSTATUS']) {
            $request->markUnknown();

            return;
        }

        $refundStatuses = [
            Api::PAYMENTSTATUS_REFUNDED,
            Api::PAYMENTSTATUS_PARTIALLY_REFUNDED,
        ];

        if (in_array($paymentStatus, $refundStatuses)) {
            $request->markRefunded();

            return;
        }

        if ($paymentStatus == Api::PAYMENTSTATUS_COMPLETED) {
            $request->markCaptured();

            return;
        }

        $pendingStatuses = [
            Api::PAYMENTSTATUS_IN_PROGRESS,
            Api::PAYMENTSTATUS_PENDING,
        ];

        if (in_array($paymentStatus, $pendingStatuses)) {
            if (Api::PENDINGREASON_AUTHORIZATION == $model['PENDINGREASON']) {
                $request->markAuthorized();

                return;
            }
        }

        if ($paymentStatus == Api::PAYMENTSTATUS_PENDING) {
            $request->markPending();

            return;
        }

        $failedStatuses = array(
            Api::PAYMENTSTATUS_FAILED,
            Api::PAYMENTSTATUS_EXPIRED,
            Api::PAYMENTSTATUS_DENIED,
            Api::PAYMENTSTATUS_CANCELED_REVERSAL,
        );

        if (in_array($paymentStatus, $failedStatuses)) {
            $request->markFailed();

            return;
        }
    }

    /**
     * {@inheritDoc}
     */
    public function supports($request)
    {
        return
            $request instanceof GetStatusInterface &&
            $request->getModel() instanceof \ArrayAccess
        ;
    }
}


<?php
namespace Payum\Paypal\ExpressCheckout\Nvp\Action;

use Payum\Core\Action\ActionInterface;
use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Core\GatewayAwareInterface;
use Payum\Core\GatewayAwareTrait;
use Payum\Core\Request\Cancel;
use Payum\Core\Request\Sync;
use Payum\Paypal\ExpressCheckout\Nvp\Api;
use Payum\Paypal\ExpressCheckout\Nvp\Request\Api\ManageRecurringPaymentsProfileStatus;

class CancelRecurringPaymentsProfileAction implements ActionInterface, GatewayAwareInterface
{
    use GatewayAwareTrait;

    /**
     * {@inheritDoc}
     */
    public function execute($request)
    {
        /** @var $request Cancel */
        RequestNotSupportedException::assertSupports($this, $request);

        $model = ArrayObject::ensureArrayObject($request->getModel());
        $model->validateNotEmpty(array('PROFILEID', 'BILLINGPERIOD'));

        $cancelDetails = new ArrayObject([
            'PROFILEID' => $model['PROFILEID'],
            'ACTION' => Api::RECURRINGPAYMENTACTION_CANCEL,
        ]);

        $this->gateway->execute(new ManageRecurringPaymentsProfileStatus($cancelDetails));
        $this->gateway->execute(new Sync($request->getModel()));
    }

    /**
     * {@inheritDoc}
     */
    public function supports($request)
    {
        if (false == ($request instanceof Cancel && $request->getModel() instanceof \ArrayAccess)) {
            return false;
        }

        // it must take into account only recurring payments, common payments must be cancelled by another action.
        $model = $request->getModel();
        return isset($model['BILLINGPERIOD']);
    }
}


<?php
namespace Payum\Klarna\Invoice\Action;

use Payum\Core\Action\ActionInterface;
use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Core\GatewayAwareInterface;
use Payum\Core\GatewayAwareTrait;
use Payum\Core\Request\Authorize;
use Payum\Klarna\Invoice\Request\Api\ReserveAmount;

class AuthorizeAction implements ActionInterface, GatewayAwareInterface
{
    use GatewayAwareTrait;

    /**
     * {@inheritDoc}
     *
     * @param Authorize $request
     */
    public function execute($request)
    {
        RequestNotSupportedException::assertSupports($this, $request);

        $details = ArrayObject::ensureArrayObject($request->getModel());

        if (false == $details['rno']) {
            $this->gateway->execute(new ReserveAmount($details));
        }
    }

    /**
     * {@inheritDoc}
     */
    public function supports($request)
    {
        return
            $request instanceof Authorize &&
            $request->getModel() instanceof \ArrayAccess
        ;
    }
}


<?php

namespace Payum\Paypal\Rest\Action;

use PayPal\Api\Payment as PaypalPayment;
use Payum\Core\Action\ActionInterface;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Core\GatewayAwareInterface;
use Payum\Core\GatewayAwareTrait;
use Payum\Core\Request\Sync;

class SyncAction implements ActionInterface, GatewayAwareInterface
{
    use GatewayAwareTrait;

    /**
     * {@inheritDoc}
     */
    public function execute($request)
    {
        /** @var $request Sync */
        RequestNotSupportedException::assertSupports($this, $request);

        /** @var PaypalPayment $model */
        $model = $request->getModel();

        $payment = PaypalPayment::get($model->id);

        $model->fromArray($payment->toArray());
    }

    /**
     * {@inheritDoc}
     */
    public function supports($request)
    {
        return
            $request instanceof Sync &&
            $request->getModel() instanceof PaypalPayment
        ;
    }
}


<?php
namespace Payum\Payex\Api;

class RecurringApi extends BaseApi
{
    const PERIODTYPE_HOURS = 1;

    const PERIODTYPE_DAILY = 2;

    const PERIODTYPE_WEEKLY = 3;

    const PERIODTYPE_MONTHLY = 4;

    const PERIODTYPE_QUARTERLY = 5;

    const PERIODTYPE_YEARLY = 6;

    const RECURRINGSTATUS_RECURRING = 1;

    const RECURRINGSTATUS_STOPPEDBYMERCHANT = 2;

    const RECURRINGSTATUS_STOPPEDBYADMIN = 3;

    const RECURRINGSTATUS_STOPPEDBYCLIENT = 4;

    const RECURRINGSTATUS_STOPPEDBYSYSTEM = 5;

    const RECURRINGSTATUS_FAILED = 6;

    /**
     * @link http://www.payexpim.com/technical-reference/pxrecurring/pxrecurring-start/
     *
     * @param array $parameters
     *
     * @return array
     */
    public function start(array $parameters)
    {
        $parameters['accountNumber'] = $this->options['account_number'];

        if (isset($parameters['orderId'])) {
            $parameters['orderID'] = $parameters['orderId'];
            unset($parameters['orderId']);
        }

        //Deprecated, set to blank.
        $parameters['notifyUrl'] = '';

        $parameters['hash'] = $this->calculateHash($parameters, array(
            'accountNumber',
            'agreementRef',
            'startDate',
            'periodType',
            'period',
            'alertPeriod',
            'price',
            'productNumber',
            'orderID',
            'description',
            'notifyUrl',
        ));

        return $this->call('Start', $parameters, $this->getPxRecurringWsdl());
    }

    /**
     * @link http://www.payexpim.com/technical-reference/pxrecurring/pxrecurring-stop/
     *
     * @param array $parameters
     *
     * @return array
     */
    public function stop(array $parameters)
    {
        $parameters['accountNumber'] = $this->options['account_number'];

        $parameters['hash'] = $this->calculateHash($parameters, array(
            'accountNumber',
            'agreementRef',
        ));

        return $this->call('Stop', $parameters, $this->getPxRecurringWsdl());
    }

    /**
     * @link http://www.payexpim.com/technical-reference/pxrecurring/pxrecurring-stop/
     *
     * @param array $parameters
     *
     * @return array
     */
    public function check(array $parameters)
    {
        $parameters['accountNumber'] = $this->options['account_number'];

        $parameters['hash'] = $this->calculateHash($parameters, array(
            'accountNumber',
            'agreementRef',
        ));

        return $this->call('Check', $parameters, $this->getPxRecurringWsdl());
    }

    /**
     * {@inheritDoc}
     */
    protected function getPxRecurringWsdl()
    {
        return $this->options['sandbox'] ?
            'https://test-external.payex.com/pxagreement/pxrecurring.asmx?wsdl' :
            'https://external.payex.com/pxagreement/pxrecurring.asmx?wsdl'
        ;
    }
}


<?php
namespace Payum\Paypal\Ipn;

use Http\Message\MessageFactory;
use Payum\Core\Exception\Http\HttpException;
use Payum\Core\Exception\InvalidArgumentException;
use Payum\Core\HttpClientInterface;

/**
 * @link https://www.x.com/developers/paypal/documentation-tools/ipn/integration-guide/IPNIntro
 */
class Api
{
    /**
     * It sends back if the message originated with PayPal.
     */
    const NOTIFY_VERIFIED = 'VERIFIED';

    /**
     * if there is any discrepancy with what was originally sent
     */
    const NOTIFY_INVALID = 'INVALID';

    const CMD_NOTIFY_VALIDATE = '_notify-validate';

    /**
     * @var HttpClientInterface
     */
    protected $client;

    /**
     * @var MessageFactory
     */
    protected $messageFactory;

    /**
     * @var array
     */
    protected $options;

    /**
     * @param array               $options
     * @param HttpClientInterface $client
     * @param MessageFactory      $messageFactory
     */
    public function __construct(array $options, HttpClientInterface $client, MessageFactory $messageFactory)
    {
        $this->client = $client;
        $this->messageFactory = $messageFactory;

        $this->options = $options;

        if (false == (isset($this->options['sandbox']) && is_bool($this->options['sandbox']))) {
            throw new InvalidArgumentException('The boolean sandbox option must be set.');
        }
    }

    /**
     * @param array $fields
     *
     * @return string
     */
    public function notifyValidate(array $fields)
    {
        $fields['cmd'] = self::CMD_NOTIFY_VALIDATE;

        $headers = array(
            'Content-Type' => 'application/x-www-form-urlencoded',
        );

        $request = $this->messageFactory->createRequest('POST', $this->getIpnEndpoint(), $headers, http_build_query($fields));

        $response = $this->client->send($request);

        if (false == ($response->getStatusCode() >= 200 && $response->getStatusCode() < 300)) {
            throw HttpException::factory($request, $response);
        }

        $result = $response->getBody()->getContents();

        return self::NOTIFY_VERIFIED === $result ? self::NOTIFY_VERIFIED : self::NOTIFY_INVALID;
    }

    /**
     * @return string
     */
    public function getIpnEndpoint()
    {
        return $this->options['sandbox'] ?
            'https://www.sandbox.paypal.com/cgi-bin/webscr' :
            'https://www.paypal.com/cgi-bin/webscr'
        ;
    }
}


<?php
namespace Payum\Paypal\ExpressCheckout\Nvp\Action;

use Payum\Core\Action\ActionInterface;
use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Core\Request\GetStatusInterface;
use Payum\Paypal\ExpressCheckout\Nvp\Api;

class RecurringPaymentDetailsStatusAction implements ActionInterface
{
    /**
     * {@inheritDoc}
     *
     * @param GetStatusInterface $request
     */
    public function execute($request)
    {
        RequestNotSupportedException::assertSupports($this, $request);

        $model = ArrayObject::ensureArrayObject($request->getModel());

        foreach (range(0, 9) as $index) {
            if ($model['L_ERRORCODE'.$index]) {
                $request->markFailed();

                return;
            }
        }

        if (false == $model['PROFILESTATUS'] && false == $model['STATUS']) {
            $request->markNew();

            return;
        }

        if (Api::RECURRINGPAYMENTSTATUS_ACTIVE == $model['STATUS']) {
            $request->markCaptured();

            return;
        }

        if (Api::RECURRINGPAYMENTSTATUS_CANCELLED == $model['STATUS']) {
            $request->markCanceled();

            return;
        }

        if (Api::RECURRINGPAYMENTSTATUS_PENDING == $model['STATUS']) {
            $request->markPending();

            return;
        }

        if (Api::RECURRINGPAYMENTSTATUS_EXPIRED == $model['STATUS']) {
            $request->markExpired();

            return;
        }

        if (Api::RECURRINGPAYMENTSTATUS_SUSPENDED == $model['STATUS']) {
            $request->markSuspended();

            return;
        }

        if (Api::PROFILESTATUS_PENDINGPROFILE == $model['PROFILESTATUS']) {
            $request->markPending();

            return;
        }

        if (Api::PROFILESTATUS_ACTIVEPROFILE == $model['PROFILESTATUS']) {
            $request->markCaptured();

            return;
        }

        $request->markUnknown();
    }

    /**
     * {@inheritDoc}
     */
    public function supports($request)
    {
        if (false == $request instanceof GetStatusInterface) {
            return false;
        }

        $model = $request->getModel();
        if (false == $model instanceof \ArrayAccess) {
            return false;
        }

        return isset($model['BILLINGPERIOD']) && null !== $model['BILLINGPERIOD'];
    }
}


<?php
namespace Payum\Core\Extension;

class ExtensionCollection implements ExtensionInterface
{
    /**
     * @var ExtensionInterface[]
     */
    protected $extensions = array();

    /**
     * @param ExtensionInterface $extension
     * @param bool               $forcePrepend
     *
     * @return void
     */
    public function addExtension(ExtensionInterface $extension, $forcePrepend = false)
    {
        $forcePrepend ?
            array_unshift($this->extensions, $extension) :
            array_push($this->extensions, $extension)
        ;
    }

    /**
     * {@inheritDoc}
     */
    public function onPreExecute(Context $context)
    {
        foreach ($this->extensions as $extension) {
            $extension->onPreExecute($context);
        }
    }

    /**
     * {@inheritDoc}
     */
    public function onExecute(Context $context)
    {
        foreach ($this->extensions as $extension) {
            $extension->onExecute($context);
        }
    }

    /**
     * {@inheritDoc}
     */
    public function onPostExecute(Context $context)
    {
        foreach ($this->extensions as $extension) {
            $extension->onPostExecute($context);
        }
    }
}


<?php
namespace Payum\Paypal\Masspay\Nvp\Action;

use Payum\Core\Action\ActionInterface;
use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Core\Request\GetStatusInterface;
use Payum\Paypal\Masspay\Nvp\Api;

class GetPayoutStatusAction implements ActionInterface
{
    /**
     * {@inheritdoc}
     *
     * @param GetStatusInterface $request
     */
    public function execute($request)
    {
        RequestNotSupportedException::assertSupports($this, $request);

        $model = ArrayObject::ensureArrayObject($request->getModel());

        if (false == $model['ACK']) {
            $request->markNew();

            return;
        }

        if (in_array($model['ACK'], [Api::ACK_SUCCESS, Api::ACK_SUCCESS_WITH_WARNING])) {
            $request->markPayedout();

            return;
        }

        if (in_array($model['ACK'], [Api::ACK_FAILURE, Api::ACK_FAILURE_WITH_WARNING])) {
            $request->markFailed();

            return;
        }

        $request->markUnknown();
    }

    /**
     * {@inheritdoc}
     */
    public function supports($request)
    {
        return
            $request instanceof GetStatusInterface &&
            $request->getModel() instanceof \ArrayAccess
        ;
    }
}


<?php
namespace Payum\Klarna\Invoice\Action;

use Payum\Core\Action\ActionInterface;
use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Core\Request\GetStatusInterface;

class StatusAction implements ActionInterface
{
    /**
     * {@inheritDoc}
     *
     * @param GetStatusInterface $request
     */
    public function execute($request)
    {
        RequestNotSupportedException::assertSupports($this, $request);

        $details = ArrayObject::ensureArrayObject($request->getModel());

        if ($details['error_code']) {
            $request->markFailed();

            return;
        }

        if ($details['canceled']) {
            $request->markCanceled();

            return;
        }

        if ($details['refund_invoice_number']) {
            $request->markRefunded();

            return;
        }

        if ($details['invoice_number']) {
            $request->markCaptured();

            return;
        }

        if (false == $details['status']) {
            $request->markNew();

            return;
        }

        if (\KlarnaFlags::ACCEPTED == $details['status']) {
            $request->markAuthorized();

            return;
        }

        if (\KlarnaFlags::PENDING == $details['status']) {
            $request->markPending();

            return;
        }

        if (\KlarnaFlags::DENIED == $details['status']) {
            $request->markFailed();

            return;
        }

        $request->markUnknown();
    }

    /**
     * {@inheritDoc}
     */
    public function supports($request)
    {
        return
            $request instanceof GetStatusInterface &&
            $request->getModel() instanceof \ArrayAccess
        ;
    }
}


<?php
namespace Payum\Payex\Action;

use Payum\Core\Action\ActionInterface;
use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Core\Request\GetStatusInterface;
use Payum\Payex\Api\RecurringApi;
use Payum\Payex\Api\OrderApi;

class PaymentDetailsStatusAction implements ActionInterface
{
    /**
     * {@inheritDoc}
     *
     * @param GetStatusInterface $request
     */
    public function execute($request)
    {
        RequestNotSupportedException::assertSupports($this, $request);

        $model = ArrayObject::ensureArrayObject($request->getModel());

        //TODO: It may be not correct for all cases. This does NOT indicate wether the transaction requested was successful, only wether the request was carried out successfully.
        if ($model['errorCode'] && OrderApi::ERRORCODE_OK != $model['errorCode']) {
            $request->markFailed();

            return;
        }

        $recurringCanceledStatuses = array(
            RecurringApi::RECURRINGSTATUS_STOPPEDBYADMIN,
            RecurringApi::RECURRINGSTATUS_STOPPEDBYCLIENT,
            RecurringApi::RECURRINGSTATUS_STOPPEDBYMERCHANT,
            RecurringApi::RECURRINGSTATUS_STOPPEDBYSYSTEM,
        );
        if (
            is_numeric($model['recurringStatus']) &&
            in_array($model['recurringStatus'], $recurringCanceledStatuses)
        ) {
            $request->markCanceled();

            return;
        }

        if (
            is_numeric($model['recurringStatus']) &&
            RecurringApi::RECURRINGSTATUS_FAILED == $model['recurringStatus']
        ) {
            $request->markFailed();

            return;
        }

        if (count(iterator_to_array($model)) == 0) {
            $request->markNew();

            return;
        }

        if (null === $model['orderStatus']) {
            $request->markNew();

            return;
        }

        //A purchase has been done, but check the transactionStatus to see the result
        if (OrderApi::ORDERSTATUS_COMPLETED == $model['orderStatus']) {
            if (OrderApi::TRANSACTIONSTATUS_CANCEL == $model['transactionStatus']) {
                $request->markCanceled();

                return;
            }

            if (OrderApi::TRANSACTIONSTATUS_FAILURE == $model['transactionStatus']) {
                $errorDetails = $model['errorDetails'];
                if (
                    isset($errorDetails['transactionErrorCode']) &&
                    $errorDetails['transactionErrorCode'] == OrderApi::TRANSACTIONERRORCODE_OPERATIONCANCELLEDBYCUSTOMER
                ) {
                    $request->markCanceled();

                    return;
                }

                $request->markFailed();

                return;
            }

            //If you are running 2-phase transactions, you should check that the node transactionStatus contains 3 (authorize)
            if (OrderApi::PURCHASEOPERATION_AUTHORIZATION == $model['purchaseOperation']) {
                if (OrderApi::TRANSACTIONSTATUS_AUTHORIZE == $model['transactionStatus']) {
                    $request->markCaptured();

                    return;
                }

                //Anything else indicates that the transaction has failed or is still processing
                $request->markFailed();

                return;
            }

            //If you are running 1-phase transactions, you should check that the node transactionStatus contains 0 (sale)
            if (OrderApi::PURCHASEOPERATION_SALE == $model['purchaseOperation']) {
                if (is_numeric($model['transactionStatus']) && OrderApi::TRANSACTIONSTATUS_SALE == $model['transactionStatus']) {
                    $request->markCaptured();

                    return;
                }

                //Anything else indicates that the transaction has failed or is still processing
                $request->markFailed();

                return;
            }

            $request->markUnknown();

            return;
        }

        if (OrderApi::ORDERSTATUS_PROCESSING == $model['orderStatus']) {
            $request->markPending();

            return;
        }

        //PxOrder.Complete can return orderStatus 1 for 2 weeks after PxOrder.Initialize is called. Afterwards the orderStatus will be set to 2
        if (OrderApi::ORDERSTATUS_NOT_FOUND == $model['orderStatus']) {
            $request->markExpired();

            return;
        }

        $request->markUnknown();
    }

    /**
     * {@inheritDoc}
     */
    public function supports($request)
    {
        if (false == (
            $request instanceof GetStatusInterface &&
            $request->getModel() instanceof \ArrayAccess
        )) {
            return false;
        }

        $model = ArrayObject::ensureArrayObject($request->getModel());

        if (count(iterator_to_array($model)) == 0) {
            return true;
        }

        if ($model['recurring']) {
            return true;
        }

        //Make sure it is not auto pay payment. There is an other capture action for auto pay payments;
        if (isset($model['autoPay']) && false == $model['autoPay']) {
            return true;
        }

        return false;
    }
}


<?php
namespace Payum\Core\Security;

use League\Uri\Http as HttpUri;
use League\Uri\Components\Query;
use League\Uri\QueryParser;
use Payum\Core\Registry\StorageRegistryInterface;
use Payum\Core\Security\Util\Random;
use Payum\Core\Storage\IdentityInterface;
use Payum\Core\Storage\StorageInterface;

abstract class AbstractTokenFactory implements TokenFactoryInterface
{
    /**
     * @var StorageInterface
     */
    protected $tokenStorage;

    /**
     * @var StorageRegistryInterface
     */
    protected $storageRegistry;

    /**
     * @param StorageInterface         $tokenStorage
     * @param StorageRegistryInterface $storageRegistry
     */
    public function __construct(StorageInterface $tokenStorage, StorageRegistryInterface $storageRegistry)
    {
        $this->tokenStorage = $tokenStorage;
        $this->storageRegistry = $storageRegistry;
    }

    /**
     * {@inheritDoc}
     */
    public function createToken($gatewayName, $model, $targetPath, array $targetParameters = [], $afterPath = null, array $afterParameters = [])
    {
        /** @var TokenInterface $token */
        $token = $this->tokenStorage->create();
        $token->setHash($token->getHash() ?: Random::generateToken());

        $targetParameters = array_replace(['payum_token' => $token->getHash()], $targetParameters);

        $token->setGatewayName($gatewayName);

        if ($model instanceof IdentityInterface) {
            $token->setDetails($model);
        } elseif (null !== $model) {
            $token->setDetails($this->storageRegistry->getStorage($model)->identify($model));
        }

        if (0 === strpos($targetPath, 'http')) {
            $targetUri = HttpUri::createFromString($targetPath);
            $targetUri = $this->addQueryToUri($targetUri, $targetParameters);

            $token->setTargetUrl((string) $targetUri);
        } else {
            $token->setTargetUrl($this->generateUrl($targetPath, $targetParameters));
        }

        if ($afterPath && 0 === strpos($afterPath, 'http')) {
            $afterUri = HttpUri::createFromString($afterPath);
            $afterUri = $this->addQueryToUri($afterUri, $afterParameters);

            $token->setAfterUrl((string) $afterUri);
        } elseif ($afterPath) {
            $token->setAfterUrl($this->generateUrl($afterPath, $afterParameters));
        }

        $this->tokenStorage->update($token);

        return $token;
    }

    /**
     * @param HttpUri $uri
     * @param array $query
     *
     * @return HttpUri
     */
    protected function addQueryToUri(HttpUri $uri, array $query)
    {
        $query = array_replace((new QueryParser())->parse($uri->getQuery()), $query);
        $query = array_filter($query, function ($value) {
            return null !== $value;
        });

        return $uri->withQuery((string) Query::createFromPairs($query));
    }

    /**
     * @param string $path
     * @param array  $parameters
     *
     * @return string
     */
    abstract protected function generateUrl($path, array $parameters = array());
}


<?php
namespace Payum\Core\Exception\Http;

use Payum\Core\Exception\RuntimeException;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

class HttpException extends RuntimeException implements HttpExceptionInterface
{
    /**
     * @var RequestInterface
     */
    protected $request;

    /**
     * @var ResponseInterface
     */
    protected $response;

    /**
     * {@inheritDoc}
     */
    public function setRequest(RequestInterface $request)
    {
        $this->request = $request;
    }

    /**
     * {@inheritDoc}
     */
    public function getRequest()
    {
        return $this->request;
    }

    /**
     * {@inheritDoc}
     */
    public function setResponse(ResponseInterface $response)
    {
        $this->response = $response;
    }

    /**
     * {@inheritDoc}
     */
    public function getResponse()
    {
        return $this->response;
    }

    /**
     * @param RequestInterface  $request
     * @param ResponseInterface $response
     *
     * @return HttpException
     */
    public static function factory(RequestInterface $request, ResponseInterface $response)
    {
        if ($response->getStatusCode() >= 400 && $response->getStatusCode() < 500) {
            $label = 'Client error response';
        } elseif ($response->getStatusCode() >= 500 && $response->getStatusCode() < 600) {
            $label = 'Server error response';
        } else {
            $label = 'Unsuccessful response';
        }

        $message = implode(PHP_EOL, array(
            $label,
            '[status code] '.$response->getStatusCode(),
            '[reason phrase] '.$response->getReasonPhrase(),
            '[url] '.$request->getUri(),
        ));

        $e = new static($message, $response->getStatusCode());
        $e->setResponse($response);
        $e->setRequest($request);

        return $e;
    }
}


<?php
namespace Payum\Paypal\Masspay\Nvp;

use Http\Message\MessageFactory;
use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\Exception\Http\HttpException;
use Payum\Core\Exception\InvalidArgumentException;
use Payum\Core\HttpClientInterface;

/**
 */
class Api
{
    const VERSION = '2.3';

    const ACK_SUCCESS = 'Success';

    const ACK_SUCCESS_WITH_WARNING = 'SuccessWithWarning';

    const ACK_FAILURE = 'Failure';

    const ACK_FAILURE_WITH_WARNING = 'FailureWithWarning';

    /**
     * @var HttpClientInterface
     */
    protected $client;

    /**
     * @var MessageFactory
     */
    protected $messageFactory;

    /**
     * @var array
     */
    protected $options = [
        'username' => null,
        'password' => null,
        'signature' => null,
    ];

    /**
     * @param array                    $options
     * @param HttpClientInterface|null $client
     * @param MessageFactory|null      $messageFactory
     */
    public function __construct(array $options, HttpClientInterface $client, MessageFactory $messageFactory)
    {
        $options = ArrayObject::ensureArrayObject($options);
        $options->defaults($this->options);
        $options->validateNotEmpty(array(
            'username',
            'password',
            'signature',
        ));

        if (false == is_bool($options['sandbox'])) {
            throw new InvalidArgumentException('The boolean sandbox option must be set.');
        }

        $this->options = $options;
        $this->client = $client;
        $this->messageFactory = $messageFactory;
    }

    /**
     * @param array $fields
     *
     * @return array
     */
    public function massPay(array $fields)
    {
        $fields['METHOD'] = 'MassPay';

        $this->addVersionField($fields);
        $this->addAuthorizeFields($fields);

        return $this->doRequest($fields);
    }

    /**
     * @param array $fields
     *
     * @throws HttpException
     *
     * @return array
     */
    protected function doRequest(array $fields)
    {
        $headers = array(
            'Content-Type' => 'application/x-www-form-urlencoded',
        );

        $request = $this->messageFactory->createRequest('POST', $this->getApiEndpoint(), $headers, http_build_query($fields));

        $response = $this->client->send($request);

        if (false == ($response->getStatusCode() >= 200 && $response->getStatusCode() < 300)) {
            throw HttpException::factory($request, $response);
        }

        $result = [];
        parse_str($response->getBody()->getContents(), $result);
        foreach ($result as &$value) {
            $value = urldecode($value);
        }

        return $result;
    }

    /**
     * @return string
     */
    protected function getApiEndpoint()
    {
        return $this->options['sandbox'] ?
            'https://api-3t.sandbox.paypal.com/nvp' :
            'https://api-3t.paypal.com/nvp'
        ;
    }

    /**
     * @param array $fields
     */
    protected function addAuthorizeFields(array &$fields)
    {
        $fields['PWD'] = $this->options['password'];
        $fields['USER'] = $this->options['username'];
        $fields['SIGNATURE'] = $this->options['signature'];
    }

    /**
     * @param array $fields
     */
    protected function addVersionField(array &$fields)
    {
        $fields['VERSION'] = self::VERSION;
    }
}


<?php
namespace Payum\Core\Storage;

use Payum\Core\Exception\LogicException;
use Payum\Core\Model\Identity;

class FilesystemStorage extends AbstractStorage
{
    /**
     * @var string
     */
    protected $storageDir;

    /**
     * @var string
     */
    protected $idProperty;

    /**
     * @var array
     */
    protected $identityMap;

    /**
     * @param string $storageDir
     * @param string $modelClass
     * @param string $idProperty
     */
    public function __construct($storageDir, $modelClass, $idProperty = 'payum_id')
    {
        parent::__construct($modelClass);

        $this->storageDir = $storageDir;
        $this->idProperty = $idProperty;
    }

    /**
     * {@inheritDoc}
     */
    public function findBy(array $criteria)
    {
        throw new LogicException('Method is not supported by the storage.');
    }

    /**
     * {@inheritDoc}
     */
    protected function doFind($id)
    {
        if (isset($this->identityMap[$id])) {
            return $this->identityMap[$id];
        }

        if (file_exists($this->storageDir.'/payum-model-'.$id)) {
            return $this->identityMap[$id] = unserialize(file_get_contents($this->storageDir.'/payum-model-'.$id));
        }
    }

    /**
     * {@inheritDoc}
     */
    protected function doUpdateModel($model)
    {
        $ro = new \ReflectionObject($model);

        if (false == $ro->hasProperty($this->idProperty)) {
            $model->{$this->idProperty} = null;
        }

        $rp = new \ReflectionProperty($model, $this->idProperty);
        $rp->setAccessible(true);

        $id = $rp->getValue($model);
        if (false == $id) {
            $rp->setValue($model, $id = uniqid());
        }

        $rp->setAccessible(false);

        $this->identityMap[$id] = $model;
        file_put_contents($this->storageDir.'/payum-model-'.$id, serialize($model));
    }

    /**
     * {@inheritDoc}
     */
    protected function doDeleteModel($model)
    {
        $rp = new \ReflectionProperty($model, $this->idProperty);
        $rp->setAccessible(true);

        if ($id = $rp->getValue($model)) {
            unlink($this->storageDir.'/payum-model-'.$id);
            unset($this->identityMap[$id]);
        }
    }

    /**
     * {@inheritDoc}
     */
    protected function doGetIdentity($model)
    {
        $rp = new \ReflectionProperty($model, $this->idProperty);
        $rp->setAccessible(true);

        if (false == $id = $rp->getValue($model)) {
            throw new LogicException('The model must be persisted before usage of this method');
        }

        return new Identity($id, $model);
    }
}


<?php
namespace Payum\Core\Exception;

use Payum\Core\Action\ActionInterface;
use Payum\Core\Debug\Humanify;
use Payum\Core\Request\Generic;
use Payum\Core\Storage\IdentityInterface;

class RequestNotSupportedException extends InvalidArgumentException
{
    /**
     * @var mixed
     */
    protected $request;

    /**
     * @var ActionInterface|null
     */
    protected $action;

    /**
     * @return mixed
     */
    public function getRequest()
    {
        return $this->request;
    }

    /**
     * @return ActionInterface|null
     */
    public function getAction()
    {
        return $this->action;
    }

    /**
     * @param \Payum\Core\Action\ActionInterface $action
     * @param mixed                              $request
     *
     * @throws RequestNotSupportedException
     */
    public static function assertSupports(ActionInterface $action, $request)
    {
        if (false == $action->supports($request)) {
            throw static::createActionNotSupported($action, $request);
        }
    }

    /**
     * @param mixed $request
     *
     * @return RequestNotSupportedException
     */
    public static function create($request)
    {
        $exception = new self(sprintf(
            'Request %s is not supported. %s',
            Humanify::request($request),
            implode(" ", static::suggestions($request))
        ));

        $exception->request = $request;

        return $exception;
    }

    /**
     * @param \Payum\Core\Action\ActionInterface $action
     * @param mixed                              $request
     *
     * @return RequestNotSupportedException
     */
    public static function createActionNotSupported(ActionInterface $action, $request)
    {
        $exception = new self(sprintf("Action %s is not supported the request %s. %s",
            Humanify::value($action),
            Humanify::request($request),
            implode(" ", static::suggestions($request))
        ));

        $exception->request = $request;
        $exception->action = $action;

        return $exception;
    }

    /**
     * @param $request
     *
     * @return string[]
     */
    protected static function suggestions($request)
    {
        $suggestions = [];

        if ($request instanceof Generic && $request->getModel() instanceof IdentityInterface) {
            $suggestions[] = sprintf(
                'Make sure the storage extension for "%s" is registered to the gateway.',
                $request->getModel()->getClass()
            );

            $suggestions[] = sprintf(
                'Make sure the storage find method returns an instance by id "%s".',
                $request->getModel()->getId()
            );
        }

        $suggestions[] = 'Make sure the gateway supports the requests and there is an action which supports this request (The method returns true).';
        $suggestions[] = 'There may be a bug, so look for a related issue on the issue tracker.';

        return $suggestions;
    }
}


<?php
namespace Payum\Paypal\Rest;

use PayPal\Auth\OAuthTokenCredential;
use PayPal\Rest\ApiContext;
use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\GatewayFactory;
use Payum\Paypal\Rest\Action\CaptureAction;
use Payum\Paypal\Rest\Action\StatusAction;
use Payum\Paypal\Rest\Action\SyncAction;
use Payum\Core\Exception\InvalidArgumentException;

class PaypalRestGatewayFactory extends GatewayFactory
{
    /**
     * {@inheritDoc}
     */
    protected function populateConfig(ArrayObject $config)
    {
        if (false == class_exists(ApiContext::class)) {
            throw new \LogicException('You must install "paypal/rest-api-sdk-php" library.');
        }

        $config->defaults([
            'payum.factory_name' => 'paypal_rest',
            'payum.factory_title' => 'PayPal Rest',

            'payum.action.capture' => new CaptureAction(),
            'payum.action.sync' => new SyncAction(),
            'payum.action.status' => new StatusAction(),
        ]);

        if (false == $config['payum.api']) {
            $config['payum.default_options'] = [
                'client_id' => '',
                'client_secret' => '',
                'config_path' => '',
            ];
            $config->defaults($config['payum.default_options']);

            $config['payum.required_options'] = ['client_id', 'client_secret', 'config_path'];
            $config['payum.api'] = function (ArrayObject $config) {
                $config->validateNotEmpty($config['payum.required_options']);

                if (false == defined('PP_CONFIG_PATH')) {
                    define('PP_CONFIG_PATH', $config['config_path']);
                } elseif (PP_CONFIG_PATH !== $config['config_path']) {
                    throw new InvalidArgumentException(sprintf('Given "config_path" is invalid. Should be equal to the defined "PP_CONFIG_PATH": %s.', PP_CONFIG_PATH));
                }

                $credential = new OAuthTokenCredential($config['client_id'], $config['client_secret']);
                return new ApiContext($credential);
            };
        }
    }
}


<?php
namespace Payum\Klarna\Invoice\Action;

use Payum\Core\Action\ActionInterface;
use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Core\GatewayAwareInterface;
use Payum\Core\GatewayAwareTrait;
use Payum\Core\Request\Refund;
use Payum\Klarna\Invoice\Request\Api\CreditPart;

class RefundAction implements ActionInterface, GatewayAwareInterface
{
    use GatewayAwareTrait;

    /**
     * {@inheritDoc}
     *
     * @param Refund $request
     */
    public function execute($request)
    {
        RequestNotSupportedException::assertSupports($this, $request);

        $details = ArrayObject::ensureArrayObject($request->getModel());

        if ($details['refund_invoice_number']) {
            return;
        }

        $details->validateNotEmpty(array('invoice_number'));

        $this->gateway->execute(new CreditPart($details));
    }

    /**
     * {@inheritDoc}
     */
    public function supports($request)
    {
        return
            $request instanceof Refund &&
            $request->getModel() instanceof \ArrayAccess
        ;
    }
}


<?php
namespace Payum\Payex\Action;

use Payum\Core\Action\ActionInterface;
use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Payex\Api\AgreementApi;
use Payum\Core\Request\GetStatusInterface;
use Payum\Payex\Api\OrderApi;

class AutoPayPaymentDetailsStatusAction implements ActionInterface
{
    /**
     * {@inheritDoc}
     *
     * @param GetStatusInterface $request
     */
    public function execute($request)
    {
        RequestNotSupportedException::assertSupports($this, $request);

        $model = ArrayObject::ensureArrayObject($request->getModel());

        //TODO: It may be not correct for all cases. This does NOT indicate wether the transaction requested was successful, only wether the request was carried out successfully.
        if ($model['errorCode'] && OrderApi::ERRORCODE_OK != $model['errorCode']) {
            $request->markFailed();

            return;
        }

        if (null === $model['transactionStatus']) {
            $request->markNew();

            return;
        }

        if (
            AgreementApi::PURCHASEOPERATION_SALE == $model['purchaseOperation'] &&
            OrderApi::TRANSACTIONSTATUS_SALE == $model['transactionStatus']
        ) {
            $request->markCaptured();

            return;
        }

        if (
            AgreementApi::PURCHASEOPERATION_AUTHORIZATION == $model['purchaseOperation'] &&
            OrderApi::TRANSACTIONSTATUS_AUTHORIZE == $model['transactionStatus']
        ) {
            $request->markCaptured();

            return;
        }

        $request->markFailed();
    }

    /**
     * {@inheritDoc}
     */
    public function supports($request)
    {
        if (false == (
            $request instanceof GetStatusInterface &&
            $request->getModel() instanceof \ArrayAccess
        )) {
            return false;
        }

        $model = ArrayObject::ensureArrayObject($request->getModel());

        //Make sure it is not recurring payment. There is an status action for recurring payments;
        if (true == $model['recurring']) {
            return false;
        }

        if ($model['autoPay']) {
            return true;
        }

        return false;
    }
}


<?php
namespace Payum\Klarna\Checkout\Action;

use Payum\Core\Action\ActionInterface;
use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Core\GatewayAwareInterface;
use Payum\Core\GatewayAwareTrait;
use Payum\Core\Request\Notify;
use Payum\Core\Request\Sync;
use Payum\Klarna\Checkout\Constants;
use Payum\Klarna\Checkout\Request\Api\UpdateOrder;

class NotifyAction implements ActionInterface, GatewayAwareInterface
{
    use GatewayAwareTrait;

    /**
     * {@inheritDoc}
     *
     * @param Notify $request
     */
    public function execute($request)
    {
        RequestNotSupportedException::assertSupports($this, $request);

        $details = ArrayObject::ensureArrayObject($request->getModel());

        $this->gateway->execute(new Sync($details));

        if (Constants::STATUS_CHECKOUT_COMPLETE == $details['status']) {
            $this->gateway->execute(new UpdateOrder(array(
                'location' => $details['location'],
                'status' => Constants::STATUS_CREATED,
                'merchant_reference' => array(
                    'orderid1' => $details['merchant_reference']['orderid1'],
                ),
            )));

            $this->gateway->execute(new Sync($details));
        }
    }

    /**
     * {@inheritDoc}
     */
    public function supports($request)
    {
        return
            $request instanceof Notify &&
            $request->getModel() instanceof \ArrayAccess
        ;
    }
}


<?php
namespace Payum\Stripe\Action\Api;

use Payum\Core\Action\ActionInterface;
use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Core\GatewayAwareInterface;
use Payum\Core\GatewayAwareTrait;
use Payum\Core\Model\CreditCardInterface;
use Payum\Core\Security\SensitiveValue;
use Payum\Stripe\Request\Api\CreateToken;
use Payum\Stripe\Request\Api\CreateTokenForCreditCard;

class CreateTokenForCreditCardAction implements ActionInterface, GatewayAwareInterface
{
    use GatewayAwareTrait;

    /**
     * {@inheritDoc}
     */
    public function execute($request)
    {
        /** @var $request CreateTokenForCreditCard */
        RequestNotSupportedException::assertSupports($this, $request);

        /** @var CreditCardInterface $card */
        $card = $request->getModel();

        $token = ArrayObject::ensureArrayObject($request->getToken());
        $token['object'] = 'card';
        $token['number'] = SensitiveValue::ensureSensitive($card->getNumber());
        $token['exp_month'] = SensitiveValue::ensureSensitive($card->getExpireAt()->format('m'));
        $token['exp_year'] = SensitiveValue::ensureSensitive($card->getExpireAt()->format('Y'));

        if ($card->getSecurityCode()) {
            $token['cvc'] = SensitiveValue::ensureSensitive($card->getSecurityCode());
        }

        $this->gateway->execute(new CreateToken($token));

        $request->setToken($token->toUnsafeArray());
    }

    /**
     * {@inheritDoc}
     */
    public function supports($request)
    {
        return
            $request instanceof CreateTokenForCreditCard &&
            $request->getModel() instanceof CreditCardInterface
        ;
    }
}


<?php
namespace Payum\Stripe\Action\Api;

use Payum\Core\Action\ActionInterface;
use Payum\Core\ApiAwareInterface;
use Payum\Core\ApiAwareTrait;
use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\Exception\LogicException;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Core\GatewayAwareInterface;
use Payum\Core\GatewayAwareTrait;
use Payum\Core\Request\ObtainCreditCard;
use Payum\Stripe\Constants;
use Payum\Stripe\Keys;
use Payum\Stripe\Request\Api\CreateTokenForCreditCard;
use Payum\Stripe\Request\Api\ObtainToken;

/**
 * @param Keys $keys
 * @param Keys $api
 */
class ObtainTokenForCreditCardAction implements ActionInterface, GatewayAwareInterface, ApiAwareInterface
{
    use ApiAwareTrait {
        setApi as _setApi;
    }
    use GatewayAwareTrait;

    /**
     * @deprecated BC will be removed in 2.x. Use $this->api
     *
     * @var Keys
     */
    protected $keys;

    public function __construct()
    {
        $this->apiClass = Keys::class;
    }

    /**
     * {@inheritDoc}
     */
    public function setApi($api)
    {
        $this->_setApi($api);

        // BC. will be removed in 2.x
        $this->keys = $this->api;
    }

    /**
     * {@inheritDoc}
     */
    public function execute($request)
    {
        /** @var $request ObtainToken */
        RequestNotSupportedException::assertSupports($this, $request);

        $model = ArrayObject::ensureArrayObject($request->getModel());
        if ($model['card']) {
            throw new LogicException('Payment already has token set');
        }

        $obtainCreditCard = new ObtainCreditCard($request->getToken());
        $obtainCreditCard->setModel($request->getFirstModel());
        $obtainCreditCard->setModel($request->getModel());
        $this->gateway->execute($obtainCreditCard);
        $card = $obtainCreditCard->obtain();

        $local = $model->getArray('local');

        $createTokenForCreditCard = new CreateTokenForCreditCard($card);
        $createTokenForCreditCard->setToken((array) $local->getArray('token'));

        $this->gateway->execute($createTokenForCreditCard);
        $token = ArrayObject::ensureArrayObject($createTokenForCreditCard->getToken());

        $local['token'] = $token->toUnsafeArray();
        $model['local'] = (array) $local;

        if ($token['id']) {
            $model['card'] = $token['id'];
        } else {
            $model['status'] = Constants::STATUS_FAILED;
        }
    }

    /**
     * {@inheritDoc}
     */
    public function supports($request)
    {
        return
            $request instanceof ObtainToken &&
            $request->getModel() instanceof \ArrayAccess
        ;
    }
}


<?php
namespace Payum\Core\Bridge\Spl;

use Payum\Core\Exception\InvalidArgumentException;
use Payum\Core\Exception\LogicException;
use Payum\Core\Security\SensitiveValue;

class ArrayObject extends \ArrayObject
{
    protected $input;

    /**
     * {@inheritDoc}
     */
    public function __construct($input = array(), $flags = 0, $iterator_class = "ArrayIterator")
    {
        if ($input instanceof \ArrayAccess && false == $input instanceof \ArrayObject) {
            $this->input = $input;

            if (false == $input instanceof \Traversable) {
                throw new LogicException('Traversable interface must be implemented in case custom ArrayAccess instance given. It is because some php limitations.');
            }

            $input = iterator_to_array($input);
        }

        parent::__construct($input, $flags, $iterator_class);
    }

    /**
     * @param string $key
     * @param mixed $default
     *
     * @return mixed
     */
    public function get($key, $default = null)
    {
        return isset($this[$key]) ? $this[$key] : $default;
    }

    /**
     * @param string $key
     * @param mixed $default
     *
     * @return static
     */
    public function getArray($key, $default = [])
    {
        return static::ensureArrayObject($this->get($key, $default));
    }

    /**
     * @param array|\Traversable $input
     *
     * @throws \Payum\Core\Exception\InvalidArgumentException
     *
     * @return void
     */
    public function replace($input)
    {
        if (false == (is_array($input) || $input instanceof \Traversable)) {
            throw new InvalidArgumentException('Invalid input given. Should be an array or instance of \Traversable');
        }

        foreach ($input as $index => $value) {
            $this[$index] = $value;
        }
    }

    /**
     * @param array|\Traversable $input
     *
     * @throws \Payum\Core\Exception\InvalidArgumentException
     *
     * @return void
     */
    public function defaults($input)
    {
        if (false == (is_array($input) || $input instanceof \Traversable)) {
            throw new InvalidArgumentException('Invalid input given. Should be an array or instance of \Traversable');
        }

        foreach ($input as $index => $value) {
            if (null === $this[$index]) {
                $this[$index] = $value;
            }
        }
    }

    /**
     * @param array   $required
     * @param boolean $throwOnInvalid
     *
     * @throws LogicException when one of the required fields is empty
     *
     * @return bool
     */
    public function validateNotEmpty($required, $throwOnInvalid = true)
    {
        $required = is_array($required) ? $required : array($required);

        $empty = array();

        foreach ($required as $r) {
            $value = $this[$r];

            if (empty($value)) {
                $empty[] = $r;
            }
        }

        if ($empty && $throwOnInvalid) {
            throw new LogicException(sprintf('The %s fields are required.', implode(', ', $empty)));
        }

        if ($empty) {
            return false;
        }

        return true;
    }

    /**
     * @param array   $required
     * @param boolean $throwOnInvalid
     *
     * @throws \Payum\Core\Exception\LogicException when one of the required fields present
     *
     * @return bool
     */
    public function validatedKeysSet($required, $throwOnInvalid = true)
    {
        $required = is_array($required) ? $required : array($required);

        foreach ($required as $required) {
            if (false == $this->offsetExists($required)) {
                if ($throwOnInvalid) {
                    throw new LogicException(sprintf('The %s fields is not set.', $required));
                }

                return false;
            }
        }

        return true;
    }

    /**
     * {@inheritDoc}
     */
    public function offsetSet($index, $value)
    {
        if ($this->input) {
            $this->input[$index] = $value;
        }

        return parent::offsetSet($index, $value);
    }

    /**
     * {@inheritDoc}
     */
    public function offsetUnset($index)
    {
        if ($this->input) {
            unset($this->input[$index]);
        }

        return parent::offsetUnset($index);
    }

    /**
     * This simply returns NULL when an array does not have this index.
     * It allows us not to do isset all the time we want to access something.
     *
     * {@inheritDoc}
     */
    public function offsetGet($index)
    {
        if ($this->offsetExists($index)) {
            return parent::offsetGet($index);
        }

        return;
    }

    /**
     * @experimental
     *
     * @return array
     */
    public function toUnsafeArray()
    {
        $array = [];
        foreach ($this as $name => $value) {
            if ($value instanceof SensitiveValue) {
                $array[$name] = $value->get();

                continue;
            }

            $array[$name] = $value;
        }

        return $array;
    }

    /**
     * @experimental
     *
     * @return array
     */
    public function toUnsafeArrayWithoutLocal()
    {
        $array = $this->toUnsafeArray();
        unset($array['local']);

        return $array;
    }

    /**
     * @param mixed $input
     *
     * @return ArrayObject
     */
    public static function ensureArrayObject($input)
    {
        return $input instanceof static ? $input : new static($input);
    }
}


<?php
namespace Payum\Core;

use Payum\Core\Exception\LogicException;
use Payum\Core\Exception\UnsupportedApiException;

trait ApiAwareTrait
{
    /**
     * @var mixed
     */
    protected $api;

    /**
     * @var string
     */
    protected $apiClass;

    /**
     * {@inheritDoc}
     */
    public function setApi($api)
    {
        if (empty($this->apiClass)) {
            throw new LogicException(sprintf('You must configure apiClass in __constructor method of the class the trait is applied to.'));
        }
        if (false == (class_exists($this->apiClass) || interface_exists($this->apiClass))) {
            throw new LogicException(sprintf('Api class not found or invalid class. "%s", $this->apiClass', $this->apiClass));
        }

        if (false == $api instanceof $this->apiClass) {
            throw new UnsupportedApiException(sprintf('Not supported api given. It must be an instance of %s', $this->apiClass));
        }

        $this->api = $api;
    }
}


<?php
namespace Payum\Core\Security;

use Payum\Core\Exception\LogicException;

class GenericTokenFactory implements GenericTokenFactoryInterface
{
    /**
     * @var TokenFactoryInterface
     */
    protected $tokenFactory;

    /**
     * @var string[]
     */
    protected $paths;

    /**
     * @param TokenFactoryInterface $tokenFactory
     * @param string[] $paths
     */
    public function __construct(TokenFactoryInterface $tokenFactory, array $paths)
    {
        $this->tokenFactory = $tokenFactory;
        $this->paths = $paths;
    }

    /**
     * {@inheritDoc}
     */
    public function createToken($gatewayName, $model, $targetPath, array $targetParameters = [], $afterPath = null, array $afterParameters = [])
    {
        return $this->tokenFactory->createToken(
            $gatewayName,
            $model,
            $targetPath,
            $targetParameters,
            $afterPath,
            $afterParameters
        );
    }

    /**
     * {@inheritDoc}
     */
    public function createCaptureToken($gatewayName, $model, $afterPath, array $afterParameters = [])
    {
        $capturePath = $this->getPath('capture');

        $afterToken = $this->createToken($gatewayName, $model, $afterPath, $afterParameters);

        return $this->createToken(
            $gatewayName,
            $model,
            $capturePath,
            [],
            $afterToken->getTargetUrl()
        );
    }

    /**
     * {@inheritDoc}
     */
    public function createAuthorizeToken($gatewayName, $model, $afterPath, array $afterParameters = [])
    {
        $authorizePath = $this->getPath('authorize');

        $afterToken = $this->createToken($gatewayName, $model, $afterPath, $afterParameters);

        return $this->createToken($gatewayName, $model, $authorizePath, [], $afterToken->getTargetUrl());
    }

    /**
     * {@inheritDoc}
     */
    public function createRefundToken($gatewayName, $model, $afterPath = null, array $afterParameters = [])
    {
        $refundPath = $this->getPath('refund');

        $afterUrl = null;
        if ($afterPath) {
            $afterUrl = $this->createToken($gatewayName, $model, $afterPath, $afterParameters)->getTargetUrl();
        }

        return $this->createToken($gatewayName, $model, $refundPath, [], $afterUrl);
    }

    /**
     * {@inheritDoc}
     */
    public function createCancelToken($gatewayName, $model, $afterPath = null, array $afterParameters = [])
    {
        $cancelPath = $this->getPath('cancel');

        $afterUrl = null;
        if ($afterPath) {
            $afterUrl = $this->createToken($gatewayName, $model, $afterPath, $afterParameters)->getTargetUrl();
        }

        return $this->createToken($gatewayName, $model, $cancelPath, [], $afterUrl);
    }

    /**
     * {@inheritDoc}
     */
    public function createPayoutToken($gatewayName, $model, $afterPath, array $afterParameters = [])
    {
        $capturePath = $this->getPath('payout');

        $afterToken = $this->createToken($gatewayName, $model, $afterPath, $afterParameters);

        return $this->createToken(
            $gatewayName,
            $model,
            $capturePath,
            [],
            $afterToken->getTargetUrl()
        );
    }

    /**
     * {@inheritDoc}
     */
    public function createNotifyToken($gatewayName, $model = null)
    {
        return $this->createToken($gatewayName, $model, $this->getPath('notify'));
    }

    /**
     * @param string $name
     *
     * @return string
     */
    protected function getPath($name)
    {
        if (empty($this->paths[$name])) {
            throw new LogicException(sprintf(
                'The path "%s" is not found. Possible paths are %s',
                $name,
                implode(', ', array_keys($this->paths))
            ));
        }

        return $this->paths[$name];
    }
}


<?php
namespace Payum\Core\Bridge\Symfony\Builder;

use Payum\Core\GatewayFactoryInterface;

class GatewayFactoryBuilder
{
    /**
     * @var string
     */
    private $gatewayFactoryClass;

    /**
     * @param string $gatewayFactoryClass
     */
    public function __construct($gatewayFactoryClass)
    {
        $this->gatewayFactoryClass = $gatewayFactoryClass;
    }

    /**
     * @param array $defaultConfig
     * @param GatewayFactoryInterface $coreGatewayFactory
     *
     * @return GatewayFactoryInterface
     */
    public function build(array $defaultConfig, GatewayFactoryInterface $coreGatewayFactory)
    {
        $gatewayFactoryClass = $this->gatewayFactoryClass;

        return new $gatewayFactoryClass($defaultConfig, $coreGatewayFactory);
    }

    public function __invoke()
    {
        return call_user_func_array([$this, 'build'], func_get_args());
    }
}


<?php
namespace Payum\Klarna\Checkout\Action\Api;

use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Klarna\Checkout\Request\Api\CreateOrder;

class CreateOrderAction extends BaseApiAwareAction
{
    /**
     * {@inheritDoc}
     *
     * @param CreateOrder $request
     */
    public function execute($request)
    {
        RequestNotSupportedException::assertSupports($this, $request);

        $model = ArrayObject::ensureArrayObject($request->getModel());

        $this->addMerchantId($model);

        $this->callWithRetry(function () use ($model, $request) {
            $order = new \Klarna_Checkout_Order($this->getConnector());
            $order->create($model->toUnsafeArray());

            $request->setOrder($order);
        });
    }

    /**
     * {@inheritDoc}
     */
    public function supports($request)
    {
        return $request instanceof CreateOrder;
    }
}


<?php
namespace Payum\Core\Bridge\Twig\Action;

use Payum\Core\Action\ActionInterface;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Core\Request\RenderTemplate;

class RenderTemplateAction implements ActionInterface
{
    /**
     * @var \Twig_Environment
     */
    protected $twig;

    /**
     * @var string
     */
    protected $layout;

    /**
     * @param \Twig_Environment $twig
     * @param string            $layout
     */
    public function __construct(\Twig_Environment $twig, $layout)
    {
        $this->twig = $twig;
        $this->layout = $layout;
    }

    /**
     * {@inheritDoc}
     */
    public function execute($request)
    {
        /** @var $request RenderTemplate */
        RequestNotSupportedException::assertSupports($this, $request);

        $request->setResult($this->twig->render($request->getTemplateName(), array_replace(
            array('layout' => $this->layout),
            $request->getParameters()
        )));
    }

    /**
     * {@inheritDoc}
     */
    public function supports($request)
    {
        return $request instanceof RenderTemplate;
    }
}


<?php
namespace Payum\Paypal\ProCheckout\Nvp\Action;

use Payum\Core\Action\ActionInterface;
use Payum\Core\ApiAwareInterface;
use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\Exception\LogicException;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Core\Exception\UnsupportedApiException;
use Payum\Paypal\ProCheckout\Nvp\Api;
use Payum\Core\Request\Refund;

class RefundAction implements ActionInterface, ApiAwareInterface
{
    /**
     * @var Api
     */
    protected $api;

    /**
     * {@inheritDoc}
     */
    public function setApi($api)
    {
        if (false == $api instanceof Api) {
            throw new UnsupportedApiException('Not supported.');
        }

        $this->api = $api;
    }

    /**
     * {@inheritDoc}
     */
    public function execute($request)
    {
        /** @var $request Refund */
        RequestNotSupportedException::assertSupports($this, $request);

        $details = ArrayObject::ensureArrayObject($request->getModel());

        if (null === $details['RESULT']) {
            return;
        }

        $refundableTrxTypes = array(Api::TRXTYPE_SALE, Api::TRXTYPE_DELAYED_CAPUTER, Api::TRXTYPE_VOICE_AUTHORIZATION);
        if (false == in_array($details['TRXTYPE'], $refundableTrxTypes)) {
            throw new LogicException(sprintf(
                'You cannot refund transaction with type %s. Only these types could be refunded: %s',
                $details['TRXTYPE'],
                implode(', ', $refundableTrxTypes)
            ));
        }

        $details->validateNotEmpty(array('PNREF'), true);

        $details['PURCHASE_TRXTYPE'] = $details['TRXTYPE'];
        $details['TRXTYPE'] = null;

        $details['PURCHASE_RESULT'] = $details['RESULT'];
        $details['RESULT'] = null;

        $details['ORIGID'] = $details['PNREF'];

        $details->replace($this->api->doCredit($details->toUnsafeArray()));
    }

    /**
     * {@inheritDoc}
     */
    public function supports($request)
    {
        return
            $request instanceof Refund &&
            $request->getModel() instanceof \ArrayAccess
        ;
    }
}


<?php
namespace Payum\AuthorizeNet\Aim\Action;

use Payum\Core\Action\ActionInterface;
use Payum\Core\Request\GetStatusInterface;
use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\Exception\RequestNotSupportedException;

class StatusAction implements ActionInterface
{
    /**
     * {@inheritDoc}
     *
     * @param GetStatusInterface $request
     */
    public function execute($request)
    {
        RequestNotSupportedException::assertSupports($this, $request);

        $model = ArrayObject::ensureArrayObject($request->getModel());

        if (null === $model['response_code']) {
            $request->markNew();

            return;
        }

        if (\AuthorizeNetAIM_Response::APPROVED == $model['response_code']) {
            $request->markCaptured();

            return;
        }

        if (\AuthorizeNetAIM_Response::DECLINED == $model['response_code']) {
            $request->markCanceled();

            return;
        }

        if (\AuthorizeNetAIM_Response::ERROR == $model['response_code']) {
            $request->markFailed();

            return;
        }

        if (\AuthorizeNetAIM_Response::HELD == $model['response_code']) {
            $request->markPending();

            return;
        }

        $request->markUnknown();
    }

    /**
     * {@inheritDoc}
     */
    public function supports($request)
    {
        return
            $request instanceof GetStatusInterface &&
            $request->getModel() instanceof \ArrayAccess
        ;
    }
}


<?php
namespace Payum\Core\Bridge\Psr\Log;

use Payum\Core\Extension\Context;
use Payum\Core\Extension\ExtensionInterface;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;

class LoggerExtension implements ExtensionInterface, LoggerAwareInterface
{
    /**
     * @var LoggerInterface
     */
    protected $logger;

    /**
     * @var NullLogger
     */
    protected $nullLogger;

    /**
     * @param LoggerInterface $logger
     */
    public function __construct(LoggerInterface $logger = null)
    {
        $this->nullLogger = new NullLogger();
        $this->logger = $logger ?: $this->nullLogger;
    }

    /**
     * {@inheritDoc}
     */
    public function setLogger(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }

    /**
     * {@inheritDoc}
     */
    public function onPreExecute(Context $context)
    {
    }

    /**
     * {@inheritDoc}
     */
    public function onExecute(Context $context)
    {
        $action = $context->getAction();
        if ($action instanceof LoggerAwareInterface) {
            $action->setLogger($this->logger);
        }
    }

    /**
     * {@inheritDoc}
     */
    public function onPostExecute(Context $context)
    {
        $action = $context->getAction();
        if ($action instanceof LoggerAwareInterface) {
            $action->setLogger($this->nullLogger);
        }
    }
}


<?php
namespace Payum\Paypal\ProCheckout\Nvp;

use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\GatewayFactory;
use Payum\Paypal\ProCheckout\Nvp\Action\CaptureAction;
use Payum\Paypal\ProCheckout\Nvp\Action\RefundAction;
use Payum\Paypal\ProCheckout\Nvp\Action\ConvertPaymentAction;
use Payum\Paypal\ProCheckout\Nvp\Action\StatusAction;

class PaypalProCheckoutGatewayFactory extends GatewayFactory
{
    /**
     * {@inheritDoc}
     */
    protected function populateConfig(ArrayObject $config)
    {
        $config->defaults(array(
            'payum.factory_name' => 'paypal_pro_checkout_nvp',
            'payum.factory_title' => 'PayPal ProCheckout',

            'payum.action.capture' => new CaptureAction(),
            'payum.action.refund' => new RefundAction(),
            'payum.action.convert_payment' => new ConvertPaymentAction(),
            'payum.action.status' => new StatusAction(),
        ));

        if (false == $config['payum.api']) {
            $config['payum.default_options'] = array(
                'username' => '',
                'password' => '',
                'partner' => '',
                'vendor' => '',
                'tender' => '',
                'sandbox' => true,
            );
            $config->defaults($config['payum.default_options']);
            $config['payum.required_options'] = array('username', 'password', 'partner', 'vendor', 'tender');

            $config['payum.api'] = function (ArrayObject $config) {
                $config->validateNotEmpty($config['payum.required_options']);

                $paypalConfig = array(
                    'username' => $config['username'],
                    'password' => $config['password'],
                    'partner' => $config['partner'],
                    'vendor' => $config['vendor'],
                    'tender' => $config['tender'],
                    'sandbox' => $config['sandbox'],
                );

                return new Api($paypalConfig, $config['payum.http_client'], $config['httplug.message_factory']);
            };
        }
    }
}


<?php
namespace Payum\Core\Bridge\PlainPhp\Security;

use League\Uri\Components\Path;
use League\Uri\Http as HttpUri;
use Payum\Core\Registry\StorageRegistryInterface;
use Payum\Core\Security\AbstractTokenFactory;
use Payum\Core\Storage\StorageInterface;

class TokenFactory extends AbstractTokenFactory
{
    /**
     * @var HttpUri
     */
    protected $baseUrl;

    /**
     * @param StorageInterface         $tokenStorage
     * @param StorageRegistryInterface $storageRegistry
     * @param string                   $baseUrl
     */
    public function __construct(StorageInterface $tokenStorage, StorageRegistryInterface $storageRegistry, $baseUrl = null)
    {
        parent::__construct($tokenStorage, $storageRegistry);

        $this->baseUrl = $baseUrl ? HttpUri::createFromString($baseUrl) : HttpUri::createFromServer($_SERVER);
    }

    /**
     * {@inheritDoc}
     */
    protected function generateUrl($path, array $parameters = [])
    {
        $newPath = (new Path($this->baseUrl->getPath()))->withTrailingSlash().$path;

        $uri = $this->baseUrl->withPath($newPath);
        $uri = $this->addQueryToUri($uri, $parameters);

        return (string) $uri;
    }
}


<?php
namespace Payum\Klarna\Checkout\Action\Api;

use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Klarna\Checkout\Request\Api\CreateOrder;
use Payum\Klarna\Checkout\Request\Api\UpdateOrder;

class UpdateOrderAction extends BaseApiAwareAction
{
    /**
     * {@inheritDoc}
     *
     * @param CreateOrder $request
     */
    public function execute($request)
    {
        RequestNotSupportedException::assertSupports($this, $request);

        $model = ArrayObject::ensureArrayObject($request->getModel());

        $this->callWithRetry(function () use ($model, $request) {
            $order = new \Klarna_Checkout_Order($this->getConnector(), $model['location']);

            $data = $model->toUnsafeArray();
            unset($data['location']);

            $order->update($data);

            $request->setOrder($order);
        });
    }

    /**
     * {@inheritDoc}
     */
    public function supports($request)
    {
        return $request instanceof UpdateOrder;
    }
}


<?php
namespace Payum\Klarna\Checkout;

use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\GatewayFactory;
use Payum\Klarna\Checkout\Action\Api\CreateOrderAction;
use Payum\Klarna\Checkout\Action\Api\FetchOrderAction;
use Payum\Klarna\Checkout\Action\Api\UpdateOrderAction;
use Payum\Klarna\Checkout\Action\AuthorizeAction;
use Payum\Klarna\Checkout\Action\AuthorizeRecurringAction;
use Payum\Klarna\Checkout\Action\ConvertPaymentAction;
use Payum\Klarna\Checkout\Action\NotifyAction;
use Payum\Klarna\Checkout\Action\StatusAction;
use Payum\Klarna\Checkout\Action\SyncAction;

class KlarnaCheckoutGatewayFactory extends GatewayFactory
{
    /**
     * {@inheritDoc}
     */
    protected function populateConfig(ArrayObject $config)
    {
        if (!class_exists('Klarna_Checkout_Order')) {
            throw new \LogicException('You must install "klarna/checkout" library.');
        }

        $config->defaults(array(
            'payum.factory_name' => 'klarna_checkout',
            'payum.factory_title' => 'Klarna Checkout',
            'payum.template.authorize' => '@PayumKlarnaCheckout/Action/capture.html.twig',
            'contentType' => Constants::CONTENT_TYPE_AGGREGATED_ORDER_V2,
            'sandbox' => true,
        ));

        $config->defaults(array(
            'payum.action.authorize_recurring' => new AuthorizeRecurringAction(),

            // must be before authorize.
            'payum.action.authorize' => new AuthorizeAction($config['payum.template.authorize']),

            'payum.action.notify' => new NotifyAction(),
            'payum.action.status' => new StatusAction(),
            'payum.action.sync' => new SyncAction(),
            'payum.action.convert_payment' => new ConvertPaymentAction(),

            'payum.action.api.create_order' => new CreateOrderAction(),
            'payum.action.api.update_order' => new UpdateOrderAction(),
            'payum.action.api.fetch_order' => new FetchOrderAction(),
        ));

        if (false == $config['payum.api']) {
            $config['payum.default_options'] = array(
                'merchant_id' => '',
                'secret' => '',
                'terms_uri' => '',
                'checkout_uri' => '',
                'sandbox' => true,
            );
            $config->defaults($config['payum.default_options']);
            $config['payum.required_options'] = array('merchant_id', 'secret');

            $config['payum.api'] = function (ArrayObject $config) {
                $config->validateNotEmpty($config['payum.required_options']);

                $klarnaConfig = new Config();
                $klarnaConfig->merchantId = $config['merchant_id'];
                $klarnaConfig->secret = $config['secret'];
                $klarnaConfig->contentType = $config['contentType'];
                $klarnaConfig->termsUri = $config['termsUri'] ?: $config['terms_uri'];
                $klarnaConfig->checkoutUri = $config['checkoutUri'] ?: $config['checkout_uri'];
                $klarnaConfig->baseUri = $config['sandbox'] ?
                    Constants::BASE_URI_SANDBOX :
                    Constants::BASE_URI_LIVE
                ;

                return $klarnaConfig;
            };
        }

        $config['payum.paths'] = array_replace([
            'PayumKlarnaCheckout' => __DIR__.'/Resources/views',
        ], $config['payum.paths'] ?: []);
    }
}


<?php
namespace Payum\Paypal\Masspay\Nvp;

use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\GatewayFactory;
use Payum\Paypal\Masspay\Nvp\Action\Api\MasspayAction;
use Payum\Paypal\Masspay\Nvp\Action\ConvertPayoutAction;
use Payum\Paypal\Masspay\Nvp\Action\GetPayoutStatusAction;
use Payum\Paypal\Masspay\Nvp\Action\PayoutAction;

class PaypalMasspayGatewayFactory extends GatewayFactory
{
    /**
     * {@inheritDoc}
     */
    protected function populateConfig(ArrayObject $config)
    {
        $config->defaults(array(
            'payum.factory_name' => 'paypal_masspay_nvp',
            'payum.factory_title' => 'PayPal Masspay',

            'payum.action.payout' => new PayoutAction(),
            'payum.action.api.masspay' => new MasspayAction(),
            'payum.action.convert_payout' => new ConvertPayoutAction(),
            'payum.action.get_payout_status' => new GetPayoutStatusAction(),
        ));

        if (false == $config['payum.api']) {
            $config['payum.default_options'] = array(
                'username' => '',
                'password' => '',
                'signature' => '',
                'sandbox' => true,
            );
            $config->defaults($config['payum.default_options']);
            $config['payum.required_options'] = array('username', 'password', 'signature');

            $config['payum.api'] = function (ArrayObject $config) {
                $config->validateNotEmpty($config['payum.required_options']);

                $paypalConfig = [
                    'username' => $config['username'],
                    'password' => $config['password'],
                    'signature' => $config['signature'],
                    'sandbox' => $config['sandbox'],
                ];

                return new Api($paypalConfig, $config['payum.http_client'], $config['httplug.message_factory']);
            };
        }
    }
}


<?php
namespace Payum\Payex\Api;

use Payum\Core\Exception\InvalidArgumentException;

abstract class BaseApi
{
    /**
     * Returns OK if request is successful.
     */
    const ERRORCODE_OK = 'OK';

    const TRANSACTIONERRORCODE_OPERATIONCANCELLEDBYCUSTOMER = 'OperationCancelledbyCustomer';

    const PURCHASEOPERATION_SALE = 'SALE';

    /**
     * If AUTHORIZATION is submitted, this indicates that the order will be a 2-phased transaction if the payment method supports it.
     */
    const PURCHASEOPERATION_AUTHORIZATION = 'AUTHORIZATION';

    /**
     * @var SoapClientFactory
     */
    protected $clientFactory;

    /**
     * @var array
     */
    protected $options;

    /**
     * @param SoapClientFactory $clientFactory
     * @param array             $options
     *
     * @throws \Payum\Core\Exception\InvalidArgumentException if an option is invalid
     */
    public function __construct(SoapClientFactory $clientFactory, array $options)
    {
        $this->clientFactory = $clientFactory;
        $this->options = $options;

        if (true == empty($this->options['account_number'])) {
            throw new InvalidArgumentException('The account_number option must be set.');
        }

        if (true == empty($this->options['encryption_key'])) {
            throw new InvalidArgumentException('The encryption_key option must be set.');
        }

        if (false == is_bool($this->options['sandbox'])) {
            throw new InvalidArgumentException('The boolean sandbox option must be set.');
        }
    }

    /**
     * @param string $operation
     * @param array  $parameters
     * @param string $serviceWsdl
     *
     * @return array
     */
    protected function call($operation, array $parameters, $serviceWsdl)
    {
        $client = $this->clientFactory->createWsdlClient($serviceWsdl);

        $response = @$client->$operation($parameters);

        $result = $this->convertSimpleXmlToArray(new \SimpleXMLElement($response->{$operation.'Result'}));

        $result = $this->normalizeStatusFields($result);
        $result = $this->removeHeader($result);
        $result = $this->removeObsolete($result);

        return $result;
    }

    /**
     * @param array $parameters
     * @param array $parametersKeys
     *
     * @return string
     */
    protected function calculateHash(array $parameters, array $parametersKeys)
    {
        $orderedParameters = array();
        foreach ($parametersKeys as $parametersKey) {
            if (false == isset($parameters[$parametersKey])) {
                //TODO exception?
                continue;
            }

            $orderedParameters[$parametersKey] = $parameters[$parametersKey];
        }

        return md5(trim(implode("", $orderedParameters)).$this->options['encryption_key']);
    }

    /**
     * @param \SimpleXMLElement $element
     *
     * @return array
     */
    protected function convertSimpleXmlToArray(\SimpleXMLElement $element)
    {
        return json_decode(
            json_encode((array) $element),
            $assoc = true
        );
    }

    /**
     * @param array $inputResult
     *
     * @return array
     */
    protected function normalizeStatusFields(array $inputResult)
    {
        $result = $inputResult;

        unset($result['status']);
        if (array_key_exists('status', $inputResult) && is_array($inputResult['status'])) {
            $statuses = $inputResult['status'];

            //agreement.autoPay seems has a bug. it returns two sub arrays inside status. Lets take the first as status.
            if (is_array(current($statuses))) {
                $statuses = array_shift($statuses);
            }

            foreach ($statuses as $name => $value) {
                if ('description' == $name) {
                    $name = 'errorDescription';
                }

                $result[$name] = $value;
            }
        }

        return $result;
    }

    /**
     * @param array $inputResult
     *
     * @return array
     */
    protected function removeHeader(array $inputResult)
    {
        $result = $inputResult;

        unset($result['header']);

        return $result;
    }

    /**
     * @param  array $inputResult
     * @return array
     */
    protected function removeObsolete(array $inputResult)
    {
        $result = $inputResult;

        unset($result['code']);
        unset($result['sessionRef']);

        return $result;
    }
}


<?php
namespace Payum\Klarna\Invoice\Action\Api;

use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Klarna\Invoice\Request\Api\GetAddresses;

class GetAddressesAction extends BaseApiAwareAction
{
    /**
     * {@inheritDoc}
     *
     * @param GetAddresses $request
     */
    public function execute($request)
    {
        RequestNotSupportedException::assertSupports($this, $request);

        $klarna = $this->getKlarna();

        foreach ($klarna->getAddresses($request->getPno()) as $address) {
            /** @var \KlarnaAddr $address */
            $address->setEmail(utf8_encode($address->getEmail()));
            $address->setTelno(utf8_encode($address->getTelno()));
            $address->setCellno(utf8_encode($address->getCellno()));
            $address->setFirstName(utf8_encode($address->getFirstName()));
            $address->setLastName(utf8_encode($address->getLastName()));
            $address->setCompanyName(utf8_encode($address->getCompanyName()));
            $address->setCareof(utf8_encode($address->getCareof()));
            $address->setStreet(utf8_encode($address->getStreet()));
            $address->setHouseNumber(utf8_encode($address->getHouseNumber()));
            $address->setHouseExt(utf8_encode($address->getHouseExt()));
            $address->setZipCode(utf8_encode($address->getZipCode()));
            $address->setCity(utf8_encode($address->getCity()));
            $address->setCountry(utf8_encode($address->getCountry()));

            $request->addAddress($address);
        }
    }

    /**
     * {@inheritDoc}
     */
    public function supports($request)
    {
        return $request instanceof GetAddresses;
    }
}