Payum
Source🩷 Sponsor
  • Payum docs
  • Get started
  • Instant payment notification
  • ISO4217 or Currency Details
  • The Architecture
  • Your order integration
  • Supported Gateways
  • Storages
  • Debugging
  • Logging
  • Event Dispatcher
  • Configure gateways in backend
  • Working with sensitive information
  • Encrypt gateway configs stored in database
  • Mask credit card number
  • Develop a custom Payum gateway
  • Troubleshooting
  • Frameworks and e-commerce integration.
  • Payum vs Omnipay
  • How to contribute from sub repository
  • Examples
    • Paypal: Create Gateway
    • Paypal: Redirects
    • Handle redirect
    • Get Status
    • Stripe Js: Create gateway
    • Capture Payment
    • Get Http Reponse
    • Capture Credit Card
    • Authorise script
    • Capture Script
    • Done Script
    • index
    • Notify script
    • Payout Script
    • Refund script
  • Authorize-NET
    • AIM
      • Authorize.Net AIM: Get started
  • Be2Bill
    • Be2Bill Direct
    • Be2Bill Offsite
  • jms-payment-bridge
    • Get Started
  • Klarna
    • Checkout
      • Klarna Checkout: Get Started
    • Invoice
      • Klarna Invoice: Get Started
  • Laravel
    • Get Started
    • Blade Templating
    • Eloquent Storage
    • Payment done controller
    • Store gateway config in database
    • Examples
  • Offline
    • Get Started
  • Payex
    • Get Started
  • Paypal
    • Express Checkout
      • Get Started
      • Authorize order
      • Authorize token custom query parameters
      • Cancel recurring payment
      • Confirm order step
      • Recurring Payments Basics
    • IPN
      • Get Started
    • Masspay
      • Get Started
    • Pro Checkout
      • Get Started
    • Pro Hosted
      • Get Started
    • REST
      • Get Started
      • Credit Card Purchase
  • Silex
    • Get Started
    • Payment Done Controller
  • Sofort
    • Get Started
    • Disable Notifications
  • Stripe
    • Checkout
    • Direct
    • Stripe.js
    • Raw Capture
    • Store card and use later
    • Subscription Billing
  • Symfony
    • Get started
    • Authorize Payment
    • Configure payment in backend
    • Console commands
    • Container tags
    • Custom Action
    • Custom API usage
    • Creating custom view for payment page
    • Custom purchase examples
    • Debugging
    • Encrypt gateway configs stored in database
    • ISO4217 or Currency Details
    • Purchase done action
    • Refund Payment
    • Sandbox
    • Storages
    • Configuration Reference
    • Custom Purchase Examples
      • Authorize.NET AIM
      • Be2Bill onsite
      • Be2Bill Credit Card
      • Klarna Checkout
      • Klarna Invoice
      • Payex
      • Paypal Express Checkout
      • Paypal Pro Checkout
      • Paypal via Omnipay
      • Stripe checkout
      • Stripe.js
      • Stripe via Omnipay
Powered by GitBook
On this page
  • Sub Requests
  • Replys
  • Managing status
  • Extensions
  • Persisting models
  • All about API
  • Conclusion
  • Supporting Payum
Edit on GitHub

The Architecture

PreviousISO4217 or Currency DetailsNextYour order integration

Last updated 1 year ago

The code snippets presented below are only for demonstration purposes (pseudo code). Their goal is to illustrate the general approach to various tasks. To see real life examples please follow the links provided when appropriate. In general, you have to create a , implement in order to know what to do with such request. And use gateway that implements . This is where things get processed. This interface forces us to specify route to possible actions and can execute the request. So, gateway is the place where a request and an action meet together.

Note: If you'd like to see real world examples we have provided you with a sandbox: , .

<?php
use Payum\Core\Gateway;
use Payum\Core\Request\Capture;

$gateway = new Gateway;
$gateway->addAction(new CaptureAction);

//CaptureAction does its job.
$gateway->execute($capture = new Capture(array(
    'amount' => 100,
    'currency' => 'USD'
));

var_export($capture->getModel());
<?php
use Payum\Core\Action\ActionInterface;
use Payum\Core\Request\Capture;

class CaptureAction implements ActionInterface
{
    public function execute($request)
    {
       $model = $request->getModel();

       //capture payment logic here

       $model['status'] = 'success';
       $model['transaction_id'] = 'an_id';
    }

    public function supports($request)
    {
        return $request instanceof Capture;
    }
}

That's the big picture. Now let's talk about the details:

Sub Requests

An action does not want to do all the job alone, so it delegates some responsibilities to other actions. In order to achieve this the action must be a gateway aware action. Only then, it can create a sub request and pass it to the gateway.

<?php
use Payum\Core\Action\ActionInterface;
use Payum\Core\GatewayAwareInterface;
use Payum\Core\GatewayAwareTrait;

class FooAction implements ActionInterface, GatewayAwareInterface
{
    use GatewayAwareTrait;
    
    public function execute($request)
    {
        //do its jobs

        // delegate some job to bar action.
        $this->gateway->execute(new BarRequest);
    }
}

Replys

<?php
use Payum\Core\Action\ActionInterface;
use Payum\Core\Reply\HttpRedirect;

class FooAction implements ActionInterface
{
    public function execute($request)
    {
        throw new HttpRedirect('http://example.com/auth');
    }
}

Above we see an action which throws a reply. The reply is about redirecting a user to another url. Next code example demonstrate how you catch and process it.

<?php

use Payum\Core\Reply\HttpRedirect;

try {
    /** @var \Payum\Core\Gateway $gateway */
    $gateway->addAction(new FooAction);

    $gateway->execute(new FooRequest);
} catch (HttpRedirect $reply) {
    header( 'Location: '.$reply->getUrl());
    exit;
}

Managing status

<?php
use Payum\Core\Action\ActionInterface;
use Payum\Core\Request\GetStatusInterface;

class FooAction implements ActionInterface
{
    public function execute($request)
    {
        if ('success condition') {
           $request->markCaptured();
        } else if ('pending condition') {
           $request->markPending();
        } else {
           $request->markUnknown();
        }
    }

    public function supports($request)
    {
        return $request instanceof GetStatusInterface;
    }
}
<?php

use Payum\Core\Request\GetHumanStatus;

/** @var \Payum\Core\Gateway $gateway */
$gateway->addAction(new FooAction);

$gateway->execute($status = new GetHumanStatus);

$status->isCaptured();
$status->isPending();

// or

$status->getValue();

Extensions

<?php
use Payum\Core\Extension\ExtensionInterface;
use Payum\Core\Extension\Context;

class PermissionExtension implements ExtensionInterface
{
    public function onPreExecute(Context $context)
    {
        $request = $context->getRequest();
        
        if (! in_array('ROLE_CUSTOMER', $request->getModel()->getRoles())) {
            throw new Exception('The user does not have the required roles.');
        }

        // congrats, user has enough rights.
    }
}
<?php

/** @var \Payum\Core\Gateway $gateway */
$gateway->addExtension(new PermissionExtension);

// here is the place where the exception may be thrown.
$gateway->execute(new FooRequest);

Persisting models

<?php
use Payum\Core\Gateway;
use Payum\Core\Extension\StorageExtension;

/** @var \Payum\Core\Storage\StorageInterface $storage */
$storage = new FooStorage;

$gateway = new Gateway;
$gateway->addExtension(new StorageExtension($storage));

All about API

The gateway API has different versions? Or, a gateway provide official sdk? We already thought about these problems and you know what?

Let's say gateway have different versions: first and second. And in the FooAction we want to use first api and BarAction second one. To solve this problem we have to implement API aware action to the actions. When such api aware action is added to a gateway it tries to set an API, one by one, to the action until the action accepts one.

<?php
use Payum\Core\ApiAwareInterface;
use Payum\Core\ApiAwareTrait;
use Payum\Core\Action\ActionInterface;
use Payum\Core\Exception\UnsupportedApiException;

class FooAction implements ActionInterface, ApiAwareInterface
{
    use ApiAwareTrait;
    
    public function __construct() 
    {
        $this->apiClass = Api::class;    
    }    
    
    
    public function execute($request) 
    {
        $this->api; // Api::class 
    }
}

class BarAction implements ActionInterface, ApiAwareInterface
{
    use ApiAwareTrait;
    
    public function __construct() 
    {
        $this->apiClass = AnotherApi::class;    
    }    
    
    
    public function execute($request) 
    {
        $this->api; // AnotherApi::class 
    }
}
<?php
use Payum\Core\Gateway;

$gateway = new Gateway;
$gateway->addApi(new FirstApi);
$gateway->addApi(new SecondApi);

// here the ApiVersionOne will be injected to FooAction
$gateway->addAction(new FooAction);

// here the ApiVersionTwo will be injected to BarAction
$gateway->addAction(new BarAction);

Conclusion

As a result of the architecture described above we end up with a well decoupled, easy to extend and reusable library. For example, you can add your domain specific actions or a logger extension. Thanks to its flexibility any task could be achieved.


Supporting Payum

Payum is an MIT-licensed open source project with its ongoing development made possible entirely by the support of community and our customers. If you'd like to join them, please consider:

Link: See a real world example: .

Link: See paypal .

What about redirects or a credit card form? Some gateways, like Paypal ExpressCheckout for instance, require authorization on their side. Payum can handle such cases and for that we use something called . It is a special object which extends an exception hence could be thrown. You can throw a http redirect reply for example at any time and catch it at a top level.

Link: See real world example: .

Good status handling is very important. Statuses must not be hard coded and should be easy to reuse, hence we use the to handle this. The is provided by default by our library, however you are free to use your own and you can do so by implementing the status interface.

Link: The status logic could be a bit complicated or pretty simple as .

There must be a way to extend the gateway with custom logic. to the rescue. Let's look at the example below. Imagine you want to check permissions before a user can capture the payment:

Link: The is a built-in extension.

Before you are redirected to the gateway side, you may want to store data somewhere, right? We take care of that too. This is handled by and its for gateway. The extension can solve two tasks. First it can save a model after the request is processed. Second, it can find a model by its id before the request is processed. Currently and (use it for tests only!) storages are supported.

Link: See authorize.net .

Next .

request
action
gateway interface
online
code
CaptureController
CaptureAction
replys
AuthorizeTokenAction
interface
Status request
as paypal one
authorize.net one
Extension
storage extension
storage
storage extension
Doctrine
Laminas Table Gateway
filesystem
capture action
Your order integration
Become a sponsor