Authorize.Net Payment Gateway Integration in Laravel

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

Using Authorize.Net APIs you can charge the cards in 2 ways. We are going to 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 we send tokenized card references.

For getting started you should have an account on Authorize.Net. You should first test your payments in sandbox mode and if everything works as expected then go for live mode. In order to accept payment using Authorize.Net, you need to grab your 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 get your ‘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 in Laravel to Accept Payment using Authorize.Net

When someone purchases products from your store, you should store their payment details in the database. That being said, 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 into your database.

php artisan migrate

To interact with the ‘payments’ table you will need to have a model ‘Payment’. Create it using the command:

php artisan make:model Payment

In the previous steps, 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=

Make sure to pass the actual values. After this, clear your configuration cache.

php artisan config:clear

Now to process payment, you are required to define routes.

routes/web.php

<?php
...
...
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 developers’ lives easy. They provide support for multi-payment gateway services. We are 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:^3 "academe/omnipay-authorizenetapi: ~3.0"

API Authorize/Purchase (Credit Card)

As 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 to the database.

php artisan make:controller PaymentController

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

paymentController.php

<?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, 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.

payment.blade.php

<form action="{{ url('charge') }}" method="post">
    {{ csrf_field() }}
    <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 and the controller does the rest of the payment process. Click here to get dummy credit card numbers to test the sandbox payment. The user can also see their transaction on the Authorize.Net dashboard. At first, transactions were listed as ‘Unsettled transactions’. Authorize.Net later settles the transaction on its own.

Authorize.Net Transaction

API Authorize/Purchase (Opaque Data)

Actually, the recommended way of processing a card payment is using “Opaque Data”. For this, we need to change the input elements of our 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_field() }}
    <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>

In the above form, we have not added names 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 <code>accept.js</code> 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

We are testing the sandbox payment, so include the sandbox file path. With the click of a button, we 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) {
            console.log(
                response.messages.message[i].code + ": " +
                response.messages.message[i].text
            );
            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 opaque data hidden form items and finally submits the form. On the server-side, we have to capture 2 fields – opaqueDataDescriptor and opaqueDataValue.

Note: If your form is not submitting then 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 payments in the sandbox mode, you can switch to production mode. All you need to do is replace sandbox credentials with the production details in the .env file. Comment 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.

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

  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 *