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.
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:
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:
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.