- OmniPay Authorize.Net DPM Gateway
- 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.
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.
Hi! When the continuation? I have a problem with response from merchant.
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();
}
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.
I would be grateful for your help!
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
If you finish this tutorial, even today, in 2017 you would have the best tutorial out there. Let down you hadn’t finished it.
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.