Authorize.Net Payment Gateway Integration in Laravel

Authorize.Net is one of the most trusted and popular payment solutions on the Internet. If you are running an online store and wish to receive payments using credit and debit cards then Authorize.Net would be a good choice. In this article, I show you how to integrate the Authorize.Net payment gateway into your Laravel application.

Authorize.Net provides APIs to collect payments. Using APIs you can charge the cards in 2 ways and I’ll write a code for both approaches.

  • API Authorize/Purchase (Credit Card): In this case, you will send credit card details on the server side for authorization.
  • API Authorize/Purchase (Opaque Data): Here, instead of sending card details you send tokenized card references. Much safer as card details are not sent on the server.

For getting started you need to have an account on Authorize.Net. As a general practice, you should first test your payments in sandbox mode. If everything works as expected on sandbox then go for live mode. To integrate the Authorize.Net, you require the API credentials. Login to your sandbox account and get these API keys from Account->API Credentials & Keys.

Authorize.Net API Keys

On the next page, you will find the ‘API Login ID’. Create a new transaction key from the ‘Create New Key(s)’ section.

Authorize.Net API Login ID and Transaction Key

Under Account->Manage Public Client Key, you will get the public client key. This key will be required during the integration of “Opaque Data”.

Basic Setup

When someone purchases products from your store, you should store their payment details in the database. Let’s create a table payments for storing transaction details.

php artisan make:migration create_payments_table

In the generated migration file add the below code to the up method.

<?php
...
...
public function up()
{
    Schema::create('payments', function (Blueprint $table) {
        $table->id();
        $table->string('transaction_id');
        $table->float('amount', 10, 2);
        $table->string('currency');
        $table->string('payment_status');
        $table->timestamps();
    });
}

Next, run the migrate command to create this table in your database.

php artisan migrate

To interact with the payments table create a Payment model using the command:

php artisan make:model Payment

You have copied Authorize.Net API credentials. Let’s put it in your .env file as follows.

ANET_API_LOGIN_ID=
ANET_TRANSACTION_KEY=
ANET_PUBLIC_CLIENT_KEY=

After this, clear your configuration cache.

php artisan config:clear

Now to run the payment process define the following routes.

Route::get('payment', 'PaymentController@index');
Route::post('charge', 'PaymentController@charge');

Authorize.Net Payment Gateway Integration in Laravel

Gone are the days when developers needed to read the API documentation of payment gateway providers to integrate their services. Omnipay is a payment processing library for PHP and they do all the hard work to make our developer’s life easy. They provide support for multi-payment gateway services. I’m also going to use Omnipay for the integration of Authorize.Net in Laravel. Along with Omnipay we also use their other library for the implementation of Authorize.Net API.

Having said that, install these 2 libraries using the Composer command:

composer require league/omnipay "academe/omnipay-authorizenetapi: ~3.0"

API Authorize/Purchase (Credit Card)

As I mentioned, we will see 2 options for charging cards. Let’s first see how to charge a credit card directly.

Create a controller that will call the view file, process the payment, and store transaction details in the database.

php artisan make:controller PaymentController

In this PaymentController, add the code below to handle all payment-related stuff.

<?php
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use Omnipay\Omnipay;
use App\Models\Payment;
 
class PaymentController extends Controller
{
    public $gateway;
 
    public function __construct()
    {
        $this->gateway = Omnipay::create('AuthorizeNetApi_Api');
        $this->gateway->setAuthName(env('ANET_API_LOGIN_ID'));
        $this->gateway->setTransactionKey(env('ANET_TRANSACTION_KEY'));
        $this->gateway->setTestMode(true); //comment this line when move to 'live'
    }
 
    public function index()
    {
        return view('payment');
    }
 
    public function charge(Request $request)
    {
        try {
            $creditCard = new \Omnipay\Common\CreditCard([
                'number' => $request->input('cc_number'),
                'expiryMonth' => $request->input('expiry_month'),
                'expiryYear' => $request->input('expiry_year'),
                'cvv' => $request->input('cvv'),
            ]);
 
            // Generate a unique merchant site transaction ID.
            $transactionId = rand(100000000, 999999999);
 
            $response = $this->gateway->authorize([
                'amount' => $request->input('amount'),
                'currency' => 'USD',
                'transactionId' => $transactionId,
                'card' => $creditCard,
            ])->send();
 
            if($response->isSuccessful()) {
 
                // Captured from the authorization response.
                $transactionReference = $response->getTransactionReference();
 
                $response = $this->gateway->capture([
                    'amount' => $request->input('amount'),
                    'currency' => 'USD',
                    'transactionReference' => $transactionReference,
                    ])->send();
 
                $transaction_id = $response->getTransactionReference();
                $amount = $request->input('amount');
 
                // Insert transaction data into the database
                $isPaymentExist = Payment::where('transaction_id', $transaction_id)->first();
 
                if(!$isPaymentExist)
                {
                    $payment = new Payment;
                    $payment->transaction_id = $transaction_id;
                    $payment->amount = $request->input('amount');
                    $payment->currency = 'USD';
                    $payment->payment_status = 'Captured';
                    $payment->save();
                }
 
                return "Payment is successful. Your transaction id is: ". $transaction_id;
            } else {
                // not successful
                return $response->getMessage();
            }
        } catch(Exception $e) {
            return $e->getMessage();
        }
    }
}

This controller grabs the amount, and card details from the HTML form and processes the payment. On successful payment, it stores all the transaction details in the ‘payments’ table.

Create a view payment.blade.php and it will contain the following code.

<form action="{{ url('charge') }}" method="post">
    @csrf
    <p><input type="text" name="amount" placeholder="Enter Amount" /></p>
    <p><input type="text" name="cc_number" placeholder="Card Number" /></p>
    <p><input type="text" name="expiry_month" placeholder="Month" /></p>
    <p><input type="text" name="expiry_year" placeholder="Year" /></p>
    <p><input type="text" name="cvv" placeholder="CVV" /></p>
    <input type="submit" name="submit" value="Submit" />
</form>

When you submit this form, control goes to the charge function in the PaymentController to perform the rest of the payment process. Click here for dummy credit card numbers to test the sandbox payment. The user can also see their transaction on the Authorize.Net dashboard. Once the transaction is done, it will be listed as ‘Unsettled transactions’. Authorize.Net later settles the transaction on its own.

Authorize.Net Transaction

API Authorize/Purchase (Opaque Data)

The recommended way of processing a card payment is using “Opaque Data”. For this, you need to change the input elements of your form. We will add 2 hidden fields to the HTML form. Authorize.Net generates values for these hidden fields. These tokenized values of a card then send to the server side instead of actual credit card details. The Omnipay library will authorize these values against the APIs and complete the transaction.

payment.blade.php

<form id="paymentForm" method="post" action="{{ url('charge') }}">
    @csrf
    <p><input type="text" name="amount" placeholder="Enter Amount" /></p>
    <p><input type="text" id="cardNumber" placeholder="cardNumber"/></p>
    <p><input type="text" id="expMonth" placeholder="expMonth"/></p>
    <p><input type="text" id="expYear" placeholder="expYear"/></p>
    <p><input type="text" id="cardCode" placeholder="cardCode"/></p>
    <input type="hidden" name="opaqueDataValue" id="opaqueDataValue" />
    <input type="hidden" name="opaqueDataDescriptor" id="opaqueDataDescriptor" />
    <button type="button" onclick="sendPaymentDataToAnet()">Pay Now</button>
</form>

Notice, I have not added name attributes to the card information elements. As a result, these values will not be submitted to the server side. Now to generate values for opaque data(hidden fields), include accept.js after the payment form. This JS file is different for both sandbox and production.

  • Sandbox: https://jstest.authorize.net/v1/Accept.js
  • Production: https://js.authorize.net/v1/Accept.js

As we are testing the sandbox payment, include the sandbox path. With the click of a button, you have to grab card details and generate the opaque data as follows.

<script type="text/javascript" src="https://jstest.authorize.net/v1/Accept.js" charset="utf-8"></script>
<script type="text/javascript">
function sendPaymentDataToAnet() {
    // Set up authorisation to access the gateway.
    var authData = {};
        authData.clientKey = "{!! env('ANET_PUBLIC_CLIENT_KEY') !!}";
        authData.apiLoginID = "{!! env('ANET_API_LOGIN_ID') !!}";

    var cardData = {};
        cardData.cardNumber = document.getElementById("cardNumber").value;
        cardData.month = document.getElementById("expMonth").value;
        cardData.year = document.getElementById("expYear").value;
        cardData.cardCode = document.getElementById("cardCode").value;
 
    // Now send the card data to the gateway for tokenisation.
    // The responseHandler function will handle the response.
    var secureData = {};
        secureData.authData = authData;
        secureData.cardData = cardData;
        Accept.dispatchData(secureData, responseHandler);
}

function responseHandler(response) {
    if (response.messages.resultCode === "Error") {
        var i = 0;
        while (i < response.messages.message.length) {
            i = i + 1;
        }
    } else {
        paymentFormUpdate(response.opaqueData);
    }
}
 
function paymentFormUpdate(opaqueData) {
    document.getElementById("opaqueDataDescriptor").value = opaqueData.dataDescriptor;
    document.getElementById("opaqueDataValue").value = opaqueData.dataValue;
    document.getElementById("paymentForm").submit();
}
</script>

This JavaScript code populates the values in the hidden items used for opaque data and finally submits the form. On the server side, you have to capture these hidden fields – opaqueDataDescriptor and opaqueDataValue.

Note: If your form is not submitted, check out the errors in the browser console.

Finally, charge method of the PaymentController will have the following code.

public function charge(Request $request)
{
    if ($request->input('opaqueDataDescriptor') && $request->input('opaqueDataValue')) {

        try {
            // Generate a unique merchant site transaction ID.
            $transactionId = rand(100000000, 999999999);
    
            $response = $this->gateway->authorize([
                'amount' => $request->input('amount'),
                'currency' => 'USD',
                'transactionId' => $transactionId,
                'opaqueDataDescriptor' => $request->input('opaqueDataDescriptor'),
                'opaqueDataValue' => $request->input('opaqueDataValue'),
            ])->send();
    
            if($response->isSuccessful()) {
    
                // Captured from the authorization response.
                $transactionReference = $response->getTransactionReference();
    
                $response = $this->gateway->capture([
                    'amount' => $request->input('amount'),
                    'currency' => 'USD',
                    'transactionReference' => $transactionReference,
                    ])->send();
    
                $transaction_id = $response->getTransactionReference();
                $amount = $request->input('amount');

                // Insert transaction data into the database
                $isPaymentExist = Payment::where('transaction_id', $transaction_id)->first();

                if(!$isPaymentExist)
                {
                    $payment = new Payment;
                    $payment->transaction_id = $transaction_id;
                    $payment->amount = $request->input('amount');
                    $payment->currency = 'USD';
                    $payment->payment_status = 'Captured';
                    $payment->save();
                }

                echo "Your payment transaction id: ". $transaction_id;
            } else {
                // not successful
                echo $response->getMessage();
            }
        } catch(Exception $e) {
            echo $e->getMessage();
        }
    }
}

Make Payment Live

Once you have successfully tested your sandbox payments, you can switch to production mode. All you need to do is replace sandbox credentials with the production details in the .env file. Comment or remove the below line from the __construct method of PaymentController.

$this->gateway->setTestMode(true); //comment this line when move to 'live'

Also in case of opaque data, don’t forget to include a production-ready JS file.

That’s all! I hope you understand Authorize.Net Payment Gateway Integration in Laravel. I would like to hear your thoughts or suggestions in the comment section below.

Related Articles

If you liked this article, then please subscribe to our YouTube Channel for video tutorials.

8 thoughts on “Authorize.Net Payment Gateway Integration in Laravel

    1. Hi Erica,

      You can choose from PayPal, Stripe, or Authorize.Net. The charges vary per payment gateways. Pick a service that is reasonable for you.

  1. Followed the steps, It’s working however when I checked the authorize.net transaction type is Authorization Only. It should be Authorization and Captured right?

    1. We are capturing payment on the successful authorization. It may be taking time to reflect in the Authorize.net dashboard.

  2. Hello, I am Serena Martin and i liked your article alot you really did a great job. The article is Very Interesting and Informative at the same time, thanks for sharing this with us.

Leave a Reply

Your email address will not be published. Required fields are marked *