OmniPay/Authorize.Net DPM Sequence Chart

In part 1 I introduced some of the Authorize.Net gateways, in particular the Direct Post Method (DPM) gateway. I also introduced the OmniPay package that handles the DPM gateway. The code samples that follow will focus on DPM.

To provide some context, I’ll bring in a sequence chart for this gateway used through OmniPay.

What is a Sequence Chart?

When payments are authorised through payment gateways, there is a complicated flow of data between the user, the merchant application (e.g. the shop) and the payment gateway. This is mediated by OmniPay, which handles the messaging between all these systems. Understanding what those flows are, helps in putting the application together.

A sequence chart shows the flow of data between the disparate systems. In the sequence diagram below, the systems are the Authorize.Net gateway, the merchant application front end (what the user interacts with), the merchant application back-end storage, and the merchant application back-channel handler (where Authorize.Net contacts the application directly). I have tried to show where that data flows through the OmniPay gateway driver.

OmniPay/Authorize.Net Sequence Diagram

OmniPay/Authorize.Net Sequence Diagram

A PDF version may be easier to read: OmniPay-AuthorizeNet-DPM

So starting from the top, we first collect what information we have about the user, in the “glue”. The glue code is what we create to join the merchant application to OmniPay. The information we may have about the user could include their shipping and billing address, name, email address, or we may know nothing about them at all. If the user is logged in, then it makes sense to pre-fill any payment forms with details that we do know, to save them typing out those details again.

In addition we need to collect the details of what the user is authorising payment for – the total cost of their shop basket or cart, or the due amount on the invoice they are paying.

Then we go through a few steps to generate the POST form the user will see. Start by creating the gateway object:

use Omnipay\Omnipay;
...
$gateway = Omnipay::create('AuthorizeNet_DPM');

And set your account credentials for using the gateway:

$gateway->setApiLoginId($your_login_id);
$gateway->setTransactionKey($your_transaction_key);

Next we create a transaction ID. The transaction ID in OmniPay is the unique ID that a merchant application will give to the transaction being authorised. The function generateTransactionId() here will generate a new unique ID for the merchant site.

$transactionId = generateTransactionId();

Next we need to create a CreditCard object. This object is a bit of a misnomer in OmniPay version 2. It is used to log all details we have about the customer – name, addresses, emails, and of course, the credit card details. In this case, using DPM, we leave the credit card details empty, since we are not (and have not) captured them.

use Omnipay\Common\CreditCard;

$card = new CreditCard([
'firstName' => $firstName,
'lastName' => $lastName,

'billingAddress1' => $billingAddress1,
'billingAddress2' => $billingAddress2,
'billingState' => $billingState,
'billingCity' => $billingCity,
'billingPostcode' => $billingPostcode,
'billingCountry' => $billingCountry,
'billingPhone' => $billingPhone,

'email' =>  $email,

'shippingAddress1' => $shippingAddress1,
'shippingAddress2' => $shippingAddress2,
'shippingState' => $shippingState,
'shippingCity' => $shippingCity,
'shippingPostcode' => $shippingPostcode,
'shippingCountry' => $shippingCountry,
'shippingPhone' => $shippingPhone,

'number' => null,
'expiryMonth' => null,
'expiryYear' => null,
'cvv' => null,
]);

Any of those details can be left out if they are not known. The user will complete them later on.

So we have a gateway object and a credit card object. Now we need the request. We will generate an authorize request, giving it the credit card details and details of the transaction.

$request = $gateway->authorize([
'amount' => $amount,
'currency' => 'USD', // Example
'transactionId' => $transactionId,
'card' => $card,

'description' => "Description for this transaction",
'customerId' => $customerId,

'shippingAmount' => $shippingAmount,
'taxAmount' => $taxAmount,

// The returnUrl is the callback for notifying the merchant application of the results.
'returnUrl' => $callback_url,
'cancelUrl' => $cancel_url,
]);

There are other details that can be supplied, but these make up a sensible minimum.

Two important fields are the returnUrl and the cancelUrl. The cancelUrl is where the user is sent if they hit any of the cancel links when making or authorising a payment.

The returnUrl, for this gateway driver, is the URL in the merchant application that Authorize.Net will use to notify the application of the result – commonly called the notification URL where the notification handler will be found. Note that there is no “completion URL” in this data. That destination is supplied by the notification handler.

Different gateways that use a notification handler work in different ways. Some require the final completion URL to be set right at the start, some expect it to be supplied by the notification handler, and some may have it defined the gateway account rather then anywhere in the code.

So this gives as a request object, containing all the data needed to make a request. This is “sent” to get the result like this:

$response = $request->send();

Now, this can be a little unintuitive. We are asking OmniPay to “send” this request in order to get a response. However, we are not sending anything to the remote gateway at this stage. Some gateway drivers will send send a message to the remote gateway at this point, and that is where the language comes from, and the idea is to keep it the use of different gateway drivers as consistent as possible but it can result in some steps feeling a little strange.

So we “send” the authorize message and get a response in return. That response is there to tell us what to do next. With some gateways there may not be a following action – the transaction may be complete right here. Some gateways may require the user to be redirected to the remote gateway site to supply their credit card details. This gateway driver is different – it expects the user to present a payment form to the user, and the response object provides some tools to help this process.

Before we present the payment form to the user, there are two things to save. First the transactionId needs to be saved in the session. We will need that when we return from Authorisze.Net. Here we save it using the Laravel Session façade, but you use whatever your framework provides:

Session::put('transactionId', $transactionId)

Secondly we need to save the details of the transaction authorisation response. The transaction will be indexed by the transactionId, so we can fetch it again later. Its initial purpose is to provide details for the notification handler to check up against, so it needs to have a status indicating that it is waiting for the notification, and the amount needs to be stored for a further check in the notification handler.

A minimal Laravel eloquent model may store the details like this:

$transaction = new Transaction([

'transactionId' => $transactionId,

'amount' => $amount,

'status' => 'PENDING',

]);

$transaction->save();

In reality you will likely want to store the complete transaction result so far here, to help fix problems as they arrise.

The next part of this series will explain how the form is generated and what happens when it is POSTed.

Series Navigation<< OmniPay Authorize.Net DPM Gateway

7 Responses to OmniPay/Authorize.Net DPM Sequence Chart

  1. Dmitry 2015-07-28 at 13:35 #

    Hi! When the continuation? I have a problem with response from merchant.

  2. Dmitry 2015-07-28 at 13:40 #

    My controller:

    const authorizenet_callback_url = ‘{my_domen}/pay/callback’;
    const authorizenet_cancel_url = ‘{my_domen}/pay/cancel’;

    public function indexAction(Request $request)
    {
    /* Authorize.net */
    $gateway = Omnipay::create(‘AuthorizeNet_SIM’);

    $gateway->setTestMode(true);
    $gateway->setDeveloperMode(true);
    $gateway->setApiLoginId(self::authorizenet_login_id);
    $gateway->setTransactionKey(self::authorizenet_transaction_key);

    $transactionId = $this->generateTransactionId();

    $card = new CreditCard([
    ‘number’ => null,
    ‘expiryMonth’ => null,
    ‘expiryYear’ => null,
    ‘cvv’ => null,
    ]);

    $request = $gateway->purchase([
    ‘amount’ => ‘200.00’,
    ‘currency’ => ‘USD’,

    ‘transactionId’ => ‘my_unique_transaction_id’,
    ‘card’ => $card,

    ‘returnUrl’ => self::authorizenet_callback_url,
    ‘cancelUrl’ => self::authorizenet_cancel_url,
    ]);

    $response = $request->send();

    try
    {
    if ($response->isSuccessful())
    {
    /* actions */
    }
    elseif ($response->isRedirect())
    {
    $response->redirect();
    }
    else
    {
    $response->getMessage();
    }
    }
    catch (\Exception $e)
    {
    exit(‘Sorry, there was an error processing your payment. Please try again later.’);
    }

    return $this->render(‘PayBundle:Default:index.html.twig’);
    }

    public function callbackAction(Request $request)
    {
    /* actions */
    return Response();
    }

    public function receiptAction(Request $request)
    {
    /* actions */
    return Response();
    }

    • Jason Judge 2015-07-29 at 17:07 #

      Sorry – been away for most of a week. I’ll try to finish this series over the coming couple of weeks, and will take a look at your code tomorrow.

      Just a few notes:

      * Your $response = $request->send(); needs to be in the try section of the code.
      * Your callback (better called “notify”) handler needs to check the response from Authorize.Net (using OmniPay), store the result, then returned a page to enable the browser to redirect. For now, just do a dump of $_REQUEST and you should see that appear in the browser. If that works, then you are a step forward.

  3. Dmitry 2015-07-28 at 13:44 #

    I would be grateful for your help!

  4. Skip 2016-03-04 at 21:51 #

    Hi,

    I am trying to build a payment page using Omnipay with Authorize.net in DPM mode. I have carefully read the DPM flow diagram above. It seems the tutorial was never finished. You can click on the website link to see the error I am receiving.

    My code:

    CC Test Page

    setTestMode(true);

    $gateway->setDeveloperMode(true);

    $gateway->setApiLoginId($apiLoginId);

    $gateway->setTransactionKey($transactionKey);

    $settings = $gateway->getParameters();

    $transactionId = time();

    $transactionId = $transactionId *2;

    $formInputData = array(
    ‘firstName’ => $_POST[‘firstname’],
    ‘lastName’ => $_POST[‘lastname’],
    ‘billingAddress1’=>$_POST[‘address’],
    ‘billingCity’=> $_POST[‘city’],
    ‘billingState’=> $_POST[‘state’],
    ‘billingPostcode’=> $_POST[‘zip’],
    ‘billingCountry’=> ‘USA’,
    ‘billingPhone’=> $_POST[‘phoneprime’],
    ’email’=> $_POST[’email’],
    ‘shippingAddress1’ => ”,
    ‘shippingAddress2’ => ”,
    ‘shippingState’ => ”,
    ‘shippingCity’ => ”,
    ‘shippingPostcode’ => ”,
    ‘shippingCountry’ => ”,
    ‘shippingPhone’ => ”,
    ‘number’ => null,
    ‘expiryMonth’=> null,
    ‘expiryYear’=> null,
    ‘cvv’=> null

    );

    $card = new CreditCard($formInputData);

    $amount=’10.00’;

    $request = $gateway->purchase(
    array(
    ‘transactionId’=> $transactionId,
    ‘description’=> ‘Test Description’,
    ‘currency’ => ‘USD’,
    ‘amount’ => $amount,
    ‘card’ => $card,
    ‘returnUrl’ =>’https://174.51.210.249/test_project/relay.php’,
    ‘cancelUrl’ =>’https://174.51.210.249/test_project/cancel.php’
    )
    );

    try{

    $response = $request->send();

    if ($response->isSuccessful()){

    // Temporary Storage – will eventually be stored in DB
    session_start();

    $_SESSION[‘transaction’][‘transactionId’]= $transactionId;

    $_SESSION[‘transaction’][‘amount’]= $amount;

    $_SESSION[‘transaction’][‘status’]= ‘PENDING’;

    // actions
    }elseif ($response->isRedirect()){
    /*
    echo(”);

    print_r($response);

    echo(”);
    */
    $response->redirect();

    }else{

    $response->getMessage();
    }

    }catch (Exception $e){

    exit(‘Sorry, there was an error processing your payment. Please try again later.’);

    }

    }else{

    ?>

     

    Purchase Information
    * Indicates Required

    * First Name

    * Last Name

    * Address
    1234 Test St.

    * City

    * State

    – select –
    Alabama
    Alaska
    Arizona
    Arkansas
    California
    Colorado
    Connecticut
    Delaware
    District of Columbia
    Florida
    Georgia
    Hawaii
    Idaho
    Illinois
    Indiana
    Iowa
    Kansas
    Kentucky
    Louisiana
    Maine
    Maryland
    Massachusetts
    Michigan
    Minnesota
    Mississippi
    Missouri
    Montana
    Nebraska
    Nevada
    New Hampshire
    New Jersey
    New Mexico
    New York
    North Carolina
    North Dakota
    Ohio
    Oklahoma
    Oregon
    Pennsylvania
    Rhode Island
    South Carolina
    South Dakota
    Tennessee
    Texas
    Utah
    Vermont
    Virginia
    Washington
    West Virginia
    Wisconsin
    Wyoming

    * Zip

    * Phone

    * Email

    Amount: $

    Any help would be much appreciated.

    Thanks

    Skip

  5. Nick 2017-04-21 at 21:32 #

    If you finish this tutorial, even today, in 2017 you would have the best tutorial out there. Let down you hadn’t finished it.

    • Jason Judge 2017-04-24 at 10:27 #

      Yes, Nick. I hang my head in shame 🙁 Let’s see what I can do over the next few weeks. Come and kick me now and then.

Leave a Reply