Paysera Symfony2 Integration

It's a common need to integrate a payment gateway into a project. Even though there are many well known systems and aggregators like PayPal, RBKMoney, Paymentwall, Robokassa and so on, I want to talk about Paysera. It's another quite new payment system. They claim to have low commission fees for a convenient and wide range of services. Paysera allows your users to pay via cards and SMS. Integration is quite easy, but has some unclear moments during the process that I want to point out.

First of all, you need to register at Paysera.

Enable Required Services

Then you will need to open the Service management Enable/Disable page and enable some additional services. As we want to get payments through the Internet, let's enable the following ones:

- Collection of payments online via e-banking and other systems

- Collection of payments via the Internet with payment cards

- Collection of payments via operators (SMS)

Paysera - service management

Now we have an additional section in the left menu - Payment Gateway.

In order to create a project (even a demo one) you need to have at least the Basic account level. To get the desired level you need, confirm your email, phone and provide a personal document (passport ID). I dislike this approach. Even if I just want to test their system in a test mode, I still must provide all those things. They can block the cash-out feature or just not approve projects if you don't provide them legal documents.

From here, I suppose that you have already confirmed your identity and have at least a Basic account.

Create a Demo Project

Now we can create a demo project. Just open Payment gateway / Manage projects. You will have to fill in some general information about your project. When you're done, you will have it listed on the page:

Paysera - add project

Please note the project number, we will need it later. You also need to set up the project URL in the General project information tab:

Paysera - project URL

We will also need the project password. Just open the General project settings tab and generate one if you don't have it: 

Paysera - project general settings

Enable Test Mode

As always, we need to test integration before we go live. That's why we need to enable the test mode. Just open the Settings of the payment collection service tab and check the following options:

- Allow test payments (to pay without real money)

- Accept inflows from any informational system (because we will query from our local machine)

Paysera - test mode

OK, we have just finished the setup on the Paysera side. Let's move to the programming.

Paysera PHP Library

If you don't use Symfony, you can find a plugin for your framework, CMS or install the PHP library via Composer:

composer require webtopay/libwebtopay

Paysera Symfony2 Bundle

I'm going to show you how to integrate Paysera into your Symfony project.

Installation

First of all, we need to install the bundle:

composer require webtopay/webtopay-bundle

Configuration

Then you need to add the bundle to your kernel:

<?php

// app/AppKernel.php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{    
    public function registerBundles()
    {
        $bundles = array(
            //...
            new Evp\Bundle\WebToPayBundle\EvpWebToPayBundle(),
        );

//...

And finally, you need to configure it:

# app/config/config.yml

# PaySera
evp_web_to_pay:
   credentials:
       project_id: %paysera_project_id%
       sign_password: %paysera_project_password%

That's it.

High-level Architecture of Payment Gateways

If you want to understand how payment gateways work in general, please read my previous blog post - high-level architecutre of payment gateways. From this point I assume that you have some basic knowledge and will show only specific parts realted to Paysera directly.

Simplified Controller

<?php

// ...

class OrderController extends Controller
{
    /**
    * @Route("/pay/{hash}", name="OrderView") 
    * @Method({"GET"}) 
    */
    public function payOrderAction($hash)
    {
        $order = // get order by hash or ID 

        $url = $this->get('evp_web_to_pay.request_builder')->buildRequestUrlFromData([
            'orderid' => // ID in your system
            'amount' => // amount in CENTS
            'currency' => // USD, EUR and so on
            'paytext' => // will be visible to the user in some cases
            'accepturl' => $this->generateUrl('OrderThankYou', ['id' => $order->getId()]),
            'cancelurl' => $this->generateUrl('Home'),
            'callbackurl' => $this->generateUrl('PaymentCallback'),
            'p_email' => // user email
            'test' => // test mode, boolean
        ]);

        return new RedirectResponse($url);
    }    

    /**
    * @Route("/thank-you/{id}", name="OrderThankYou", requirements={"id": "\d+"})
    * @Method({"GET"})
    * @Template()
    */
    public function thankYouAction($id)
    {
        return [
            'id' => $id,
        ];
    }

    /**
     * @Route("/paymentcallback/custom-super-secret-endpoint", name="PaymentCallback")
     */
    public function callbackAction(Request $request)
    {
        // log callback

        $data = $this->get('evp_web_to_pay.callback_validator')->validateAndParseData($request->query->all());
        if ($data['status'] == 1) {
            // give the user his service
        }

        return new Response('OK');
    }
}

So, we have three main actions:

1. Pay Order.

Here we form the URL for the user. At this URL, the user will see the payment methods to choose from. A few pieces of code require your attention:

- amount is in cents. If you store amount in your own system in another way, don't forget to multiply this value.

- paytext - this text will be shown to the user in some payment methods. You must use two placeholders in this text:

[order_nr] - means order Id in your system
[site_name] - your site name.

For example: 

$paytext = $orderTitle . ' Order: [order_nr] on the website [site_name]';

accepturl - we set up here a URL to the Thank You page. I also put there the order id to be able to show more information to the user.

cancelurl - just points to the home page in my case

callbackurl - URL for payment gateway callbacks

test - to use test mode or not. Allows the user to make payments without real money.

By design EvpWebToPayBundle  provides a better way to test payments - the sandbox mode. If you configure it, all requests will be targeted to the sandbox.paysera.com instead of paysera.com. This is a good idea, but, unfortunatelly their sandbox server doesn't work properly. It's not possible to confirm the account on it because their confirmation email is broken:

2. Thank You

As we have troubles with sandbox, we need to modify our ThankYou action a little:

<?php

// ...
/**
 * @Route("/thank-you/{id}", name="OrderThankYou", requirements={"id": "\d+"})
 * @Method({"GET"})
 * @Template()
 */
public function thankYouAction($id)
{
    if ($this->container->getParameter('kernel.debug')) {
        $data = $this->get('evp_web_to_pay.callback_validator')->validateAndParseData($request->query->all());
        if ($data['status'] == 1) {
            // give the user his service 
        }
    }

    return [
        'id' => $id,
    ];
}

This way you will be able to test payments locally in the dev environment via Paysera's test mode. 

3. Callback

Here you process the result of a real payment. If the status is 1 - success. You must provide the service to the user and return the 200 OK response. You can also dispatch an event and attach any useful listeners to it. For example, you may send an email to the user, update payment stats and so on.

Pros

  • documentation is up to date with the API. This is important. Why? Because I had bad experiences with some payment gateways. Their owners were too lazy to update their docs, and I had to talk to their tech guys directly to figure out why things didn't work as they were described in the documentation.
  • support seems to work 7 days / week

Cons

  • they have sandbox, but it doesn’t work. 
  • you will need to approve personal data before you can use projects, even in the test mode
  • doesn’t support recurring payments so far
  • isn’t integrated with http://payum.org
  • tech support works only on business days

Conclusions

If you don't need recurring payments, then Paysera may look attractive to you because of its low fees. If you do need recurring payments - Paysera is not the right choice.

See also:

How to Use a Custom Storage Layer in FOSUserBundle

Almost each Symfony project uses FOSUserBundle because it speeds up the development and provides useful features to manage users. It has a few built in storage layer implementations: one for Propel and a few for Doctrine (both ORM and ODM). It's great, but there are some cases when you have to use another storage. Luckily, FOSUserBundle is flexible enough and provides a way to implement a custom storage layer.  You just need to implement your own user manager to use all features of FOSUserBundle.

YAML as a data format for application's configuration

It seems obvious that almost every application has its configuration. We can use a lot of data formats for configuration files, for example YAML or XML, JSON or plain PHP (replace with any language you use) files. Personally I like and prefer to use YAML to other formats and here I just want to describe a little one of Symfony components called YAML. Also It's quite interesting for me which format you use, what pros and cons you see, so don't hesitate to leave a comment with your thoughts.

Travis - Beginning

 - it's a service for automated code testing. It's integrated with GitHub, supports many languages and libraries. This time, we're interested in PHP and the PHPUnit tests. It's very convenient to have such an instrument while developing an open source library, because other developers don't need to run tests locally, everything will be available on Travis. I'm going to show how we can add a library to travis-ci.org, and for this purpose I'm going to use 4devs/blog.