Implementation

We start off by creating a new module called FoggylinePaymentBundle. We do so with the help of the console by running the following command:

php bin/console generate:bundle --namespace=Foggyline/PaymentBundle

The command triggers an interactive process which asks us several questions along the way, shown as follows:

Implementation

Once done, files app/AppKernel.php and app/config/routing.yml are modified automatically. The registerBundles method of an AppKernel class has been added to the following line under the $bundles array:

new FoggylinePaymentBundleFoggylinePaymentBundle(),

The routing.yml has been updated with the following entry:

foggyline_payment:
  resource: "@FoggylinePaymentBundle/Resources/config/routing.xml"
  prefix:   /

In order to avoid colliding with the core application code, we need to change the prefix: / to prefix: /payment/.

Creating a card entity

Even though we won't be storing any credit cards in our database as part of this chapter, we want to reuse the Symfony auto-generate CRUD feature in order for it to provide us with a credit card model and form. Let's go ahead and create a Card entity. We will do so by using the console, shown as follows:

php bin/console generate:doctrine:entity

The command triggers the interactive generator, providing it with FoggylinePaymentBundle:Card for an entity shortcut, where we also need to provide entity properties. We want to model our Card entity with the following fields:

  • card_type: string
  • card_number: string
  • expiry_date: date
  • security_code: string

Once done, the generator creates Entity/Card.php and Repository/CardRepository.php within the src/Foggyline/PaymentBundle/ directory. We can now update the database so it pulls in the Card entity, shown as follows:

php bin/console doctrine:schema:update --force

With the entity in place, we are ready to generate its CRUD. We will do so by using the following command:

php bin/console generate:doctrine:crud

This results in a src/Foggyline/PaymentBundle/Controller/CardController.php file being created. It also adds an entry to our app/config/routing.yml file, as follows:

foggyline_payment_card:
  resource: "@FoggylinePaymentBundle/Controller/CardController.php"
  type:    annotation

Again, the view files were created under the app/Resources/views/card/ directory. Since we won't actually be doing any CRUD related actions around cards as such, we can go ahead and delete all of the generated view files, as well as the entire body of the CardController class. At this point, we should have our Card entity, CardType form, and empty CardController class.

Creating a card payment service

The card payment service is going to provide the relevant information our future sales module will need for its checkout process. Its role is to provide the payment method label, code, and processing URLs of an order, such as authorize, capture, and cancel.

We will start by defining the following service under the services element of the src/Foggyline/PaymentBundle/Resources/config/services.xml file:

<service id="foggyline_payment.card_payment"class="FoggylinePaymentBundleServiceCardPayment">
  <argument type="service" id="form.factory"/>
  <argument type="service" id="router"/>
  <tag name="payment_method"/>
</service>

This service accepts two arguments: one being form.factory and the other being router. form.factory that will be used within service to create a form view for the CardType form. The tag is a crucial element here, as our SalesBundle module will be looking for payment methods based on the payment_method tag assigned to the service.

We now need to create the actual service class within the src/Foggyline/PaymentBundle/Service/CardPayment.php file as follows:

namespace FoggylinePaymentBundleService;

use FoggylinePaymentBundleEntityCard;

class CardPayment
{
  private $formFactory;
  private $router;

  public function __construct(
    $formFactory,
    SymfonyBundleFrameworkBundleRoutingRouter $router
  )
  {
    $this->formFactory = $formFactory;
    $this->router = $router;
  }

  public function getInfo()
  {
    $card = new Card();
    $form = $this->formFactory->create('FoggylinePaymentBundleFormCardType', $card);

    return array(
      'payment' => array(
      'title' =>'Foggyline Card Payment',
      'code' =>'card_payment',
      'url_authorize' => $this->router->generate('foggyline_payment_card_authorize'),
      'url_capture' => $this->router->generate('foggyline_payment_card_capture'),
      'url_cancel' => $this->router->generate('foggyline_payment_card_cancel'),
      'form' => $form->createView()
      )
    );
  }
}

The getInfo method is what's going to provide the necessary information to our future SalesBundle module in order for it to construct the payment step of the checkout process. We are passing on three different types of URLs here: authorize, capture, and cancel. These routes do not exist just yet, as we will create them soon. The idea is that we will shift the payment actions and process to the actual payment method. Our future SalesBundle module will merely be doing an AJAX POST to these payment URLs, and will expect either a success or error JSON response. A success response should yield some sort of transaction ID and an error response should yield a label message to show to the user.

Creating a card payment controller and routes

We will edit the src/Foggyline/PaymentBundle/Resources/config/routing.xml file by adding the following route definitions to it:

<route id="foggyline_payment_card_authorize" path="/card/authorize">
  <default key="_controller">FoggylinePaymentBundle:Card:authorize</default>
</route>

<route id="foggyline_payment_card_capture" path="/card/capture">
  <default key="_controller">FoggylinePaymentBundle:Card:capture</default>
</route>

<route id="foggyline_payment_card_cancel" path="/card/cancel">
  <default key="_controller">FoggylinePaymentBundle:Card:cancel</default>
</route>

We will then edit the body of the CardController class by adding the following to it:

public function authorizeAction(Request $request)
{
  $transaction = md5(time() . uniqid()); // Just a dummy string, simulating some transaction id, if any

  if ($transaction) {
    return new JsonResponse(array(
      'success' => $transaction
    ));
  }

  return new JsonResponse(array(
    'error' =>'Error occurred while processing Card payment.'
  ));
}

public function captureAction(Request $request)
{
  $transaction = md5(time() . uniqid()); // Just a dummy string, simulating some transaction id, if any

  if ($transaction) {
    return new JsonResponse(array(
      'success' => $transaction
    ));
  }

  return new JsonResponse(array(
    'error' =>'Error occurred while processing Card payment.'
  ));
}

public function cancelAction(Request $request)
{
  $transaction = md5(time() . uniqid()); // Just a dummy string, simulating some transaction id, if any

  if ($transaction) {
    return new JsonResponse(array(
      'success' => $transaction
    ));
  }

  return new JsonResponse(array(
    'error' =>'Error occurred while processing Card payment.'
  ));
}

We should now be able to access URLs like /app_dev.php/payment/card/authorize and see the output of authorizeAction. Implementations given here are dummy ones. For the purpose of this chapter ,we are not going to connect to a real payment processing API. What is important for us to know is that the sales module will, during its checkout process, render any possible form view pushed through the ['payment']['form'] key of the getInfo method of a payment_method tagged service. Meaning, the checkout process should show a credit card form under card payment. The behavior of checking out will be coded such that if payment with a form is selected and the Place Order button is clicked, that payment form will prevent the checkout process from proceeding until the payment form is submitted to either authorize or capture the URL defined in the payment itself. We will touch upon this some more when we get to the SalesBundle module.

Creating a check money payment service

Aside from the credit card payment method, let's go ahead and define one more static payment, called Check Money.

We will start by defining the following service under the services element of the src/Foggyline/PaymentBundle/Resources/config/services.xml file:

<service id="foggyline_payment.check_money"class="FoggylinePaymentBundleServiceCheckMoneyPayment">
  <argument type="service" id="router"/>
  <tag name="payment_method"/>
</service>

The service defined here accepts only one router argument. The tag name is the same as with the card payment service.

We will then create the src/Foggyline/PaymentBundle/Service/CheckMoneyPayment.php file, with content as follows:

namespace FoggylinePaymentBundleService;

class CheckMoneyPayment
{
  private $router;

  public function __construct(
    SymfonyBundleFrameworkBundleRoutingRouter $router
  )
  {
    $this->router = $router;
  }

  public function getInfo()
  {
    return array(
      'payment' => array(
        'title' =>'Foggyline Check Money Payment',
        'code' =>'check_money',
        'url_authorize' => $this->router->generate('foggyline_payment_check_money_authorize'),
        'url_capture' => $this->router->generate('foggyline_payment_check_money_capture'),
        'url_cancel' => $this->router->generate('foggyline_payment_check_money_cancel'),
        //'form' =>''
      )
    );
  }
}

Unlike a card payment, the check money payment has no form key defined under the getInfo method. This is because there are no credit card entries for it to define. It is just going to be a static payment method. However, we still need to define the authorize, capture, and cancel URLs, even though their implementation might be nothing more than just a simple JSON response with success or error keys.

Creating a check money payment controller and routes

Once the check money payment service is in place, we can go ahead and create the necessary routes for it. We will start by adding the following route definitions to the src/Foggyline/PaymentBundle/Resources/config/routing.xml file:

<route id="foggyline_payment_check_money_authorize"path="/check_money/authorize">
  <default key="_controller">FoggylinePaymentBundle:CheckMoney:authorize</default>
</route>

<route id="foggyline_payment_check_money_capture"path="/check_money/capture">
  <default key="_controller">FoggylinePaymentBundle:CheckMoney:capture</default>
</route>

<route id="foggyline_payment_check_money_cancel"path="/check_money/cancel">
  <default key="_controller">FoggylinePaymentBundle:CheckMoney:cancel</default>
</route>

We will then create the src/Foggyline/PaymentBundle/Controller/CheckMoneyController.php file, with content as follows:

namespace FoggylinePaymentBundleController;

use SymfonyComponentHttpFoundationJsonResponse;
use SymfonyComponentHttpFoundationRequest;
use SymfonyBundleFrameworkBundleControllerController;

class CheckMoneyController extends Controller
{
  public function authorizeAction(Request $request)
  {
    $transaction = md5(time() . uniqid());
    return new JsonResponse(array(
      'success' => $transaction
    ));
  }

  public function captureAction(Request $request)
  {
    $transaction = md5(time() . uniqid());
    return new JsonResponse(array(
      'success' => $transaction
    ));
  }

  public function cancelAction(Request $request)
  {
    $transaction = md5(time() . uniqid());
    return new JsonResponse(array(
      'success' => $transaction
    ));
  }
}

Similar to a card payment, here we added a simple dummy implementation of the authorize, capture, and cancel methods. The method responses will feed into the SalesBundle module later on. We can easily implement more robust functionality from within these methods, but that is out of the scope of this chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.18.220.243