Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Authorizenet Laravel Package

omnipay/authorizenet

Authorize.Net payment gateway driver for the Omnipay PHP library. Process card payments with authorize, capture, purchase, refund and void operations through a consistent, framework-agnostic API, with support for Authorize.Net's AIM and SIM integration methods.

Deep Wiki
Context7

Getting Started

Install the version that matches your Omnipay core. The package ships two incompatible majors:

# Omnipay v3 (PHP 7.1+, PSR-4) — use this for new work
composer require omnipay/common:^3.0 omnipay/authorizenet:^3.0

# Omnipay v2 (legacy Laravel 5.x apps)
composer require omnipay/common:^2.0 omnipay/authorizenet:^2.0

Know which gateway you actually need — this package ships five, and they do not share a feature set:

Short name Class Use for
AuthorizeNet_AIM AIMGateway Simple server-to-server charge/auth. Card data hits your server (PCI scope).
AuthorizeNet_CIM CIMGateway Stored customer profiles, saved cards, repeat/subscription charges.
AuthorizeNet_Api ApiGateway The modern JSON API + Accept.js tokenisation + webhooks (acceptNotification).
AuthorizeNet_DPM / AuthorizeNet_SIM DPMGateway / SIMGateway Hosted/relay payment forms.

Where to look first. The README is thin; the request classes under src/Message/<Gateway>/ are the real documentation — they enumerate every supported parameter. Start with Message/AIM/AIMPurchaseRequest.php, then Message/Api/ApiAbstractRequest.php.

First use case — a sandbox AIM purchase:

$gateway = Omnipay\Omnipay::create('AuthorizeNet_AIM');
$gateway->setApiLoginId(config('services.authorizenet.login_id'));
$gateway->setTransactionKey(config('services.authorizenet.transaction_key'));
$gateway->setTestMode(true);            // switches endpoint only
$gateway->setDeveloperMode(true);      // AIM: verbose/test responses in sandbox

$response = $gateway->purchase([
    'amount'   => '10.00',
    'currency' => 'USD',
    'card'     => [
        'number'      => '4111111111111111',
        'expiryMonth' => '12',
        'expiryYear'  => '2030',
        'cvv'         => '123',
        'billingFirstName' => 'Jane',
        'billingLastName'  => 'Doe',
    ],
])->send();

Credentials come from the Authorize.Net merchant interface (Account → API Login ID and Transaction Key). Sandbox credentials are separate — create them at sandbox.authorize.net; production keys will not authenticate against the test endpoint.

Implementation Patterns

Bind one gateway in the container, inject the interface. Never call Omnipay::create() in controllers; build it once in a service provider and type-hint Omnipay\Common\GatewayInterface so tests can swap a stub.

$this->app->singleton(GatewayInterface::class, fn () => tap(
    Omnipay::create('AuthorizeNet_Api'),
    function ($g) {
        $g->setApiLoginId(config('services.authorizenet.login_id'));
        $g->setTransactionKey(config('services.authorizenet.transaction_key'));
        $g->setTestMode(app()->isLocal());
    }
));

Persist the whole response, not just success/failure. Every attempt should write getTransactionReference(), getCode(), getMessage(), and getData() to your payments table. You will need the transaction reference weeks later for capture/void/refund and for reconciliation against the merchant interface.

Two-step authorise → capture is the standard pattern when you ship goods later:

$auth = $gateway->authorize([...])->send();
if ($auth->isSuccessful()) {
    Order::find($id)->update([
        'auth_txn' => $auth->getTransactionReference(),
        'auth_code' => $auth->getAuthorizationCode(),
    ]);
}

// ... later, at shipment
$capture = $gateway->capture([
    'amount'               => '10.00',
    'transactionReference' => $order->auth_txn,
])->send();

Void vs refund is a timing decision, not a preference. Before the daily batch settles (roughly 16:00–17:00 Pacific), use void() with the original transaction reference. After settlement, void() fails and you must use refund(). Build a small helper that tries void, checks the response code, and falls back to refund — or simply decide based on age of the transaction.

Saved cards belong in CIM (or Accept.js), never in purchase() loops. CIM splits the flow:

// 1. Store the customer (returns a profile id)
$profile = $gateway->createCustomerProfile([
    'email' => $user->email,
    'card'  => [...],
    'billingAddress1' => ...,
])->send();

// 2. Charge the stored profile — no card data on the wire
$charge = $gateway->purchase([
    'amount'                   => '25.00',
    'customerProfileId'        => $user->cim_profile_id,
    'customerPaymentProfileId' => $user->cim_payment_profile_id,
])->send();

A CIM purchase will ignore a card array — it needs both profile IDs, or it errors. Use createCustomerProfileFromTransaction to retro-fit a profile onto an existing AIM sale.

Hosted forms follow a different lifecycle. DPM/SIM purchase() returns isRedirect() === true; the amount is pre-set and the card is entered on Authorize.Net's page. You render $response->getRedirectData() as hidden inputs POSTed to $response->getRedirectUrl(), then handle the callback with completePurchase() (DPM) / completeAuthorize() (SIM). Use these when PCI scope matters more than UX.

Accept.js is the low-friction PCI path. Let the browser tokenise the card, then send only the opaque token to the Api gateway — a purchase(['opaqueData' => [...]]) call. Inspect ApiAbstractRequest in your installed version for the exact key names, and use the Api gateway's acceptNotification() to consume webhooks for async settlement states.

Testing. Don't mock HTTP; either run against the sandbox in CI (with a PaymentAttempt guard so re-runs don't duplicate-charge within the 2-minute window) or construct responses directly: new Omnipay\AuthorizeNet\Message\AIM\Response($request, ['responseCode' => 1, ...]). Test cards: 4111111111111111 (approved), 4222222222222 (declined), and vary amount last-digits to trigger error code 3.

Gotchas and Tips

  • setTestMode(true) only changes the endpoint URI. It does not make bad cards pass, and it does not enable verbose diagnostics — that's setDeveloperMode(true) on AIM. Both must be env-driven, and neither should ever be true in production.
  • Duplicate transaction error (code 11). Identical amount + card inside a ~2-minute window returns "A duplicate transaction has been submitted." This is your only idempotency protection — Authorize.Net has no idempotency key. Keep a local attempt record keyed by your own payment_uuid and reconcile rather than retrying.
  • A timeout is ambiguous, not a failure. send() can throw InvalidResponseException on transport/parse failure (WAFs and proxies returning HTML bite here). The charge may still have gone through. Always catch around send() and mark the attempt unknown for reconciliation; never blindly retry.
  • Read getCode(), not just isSuccessful(). Codes: 1 approved, 2 declined, 3 error, 4 held for review. Declines and gateway errors are both "not successful" but have completely different retry semantics — and code 4 needs a deliberate hold, not a refund.
  • Missing parameters throw InvalidRequestException before the network call. Useful: wrap construction in the same try/catch and log the input keys (card number excluded).
  • Amounts must be decimal strings. '10.00', not 10.0 or a float cast from a calculation. Format with number_format($cents / 100, 2, '.', ''). Currency is USD-only for Authorize.Net.
  • Never log request parameters wholesale. $parameters['card'] contains the PAN and CVV. Log getTransactionReference(), the response getData(), and the last-4 from the response payload instead.
  • Inject Guzzle for visibility. $gateway->setHttpClient($client) accepts a Guzzle client, so you can attach logging middleware, set timeouts, and enable retries on connection errors only (not on 5xx from the gateway).
  • Extension points: subclass ApiAbstractRequest and register a custom request via AbstractGateway::createRequest() when you need an endpoint the package doesn't wrap (e.g. transaction detail lookups), and use $gateway->supportsPurchase() / supportsRefund() for feature detection rather than instanceof checks.
  • Maintenance reality: last release was April 2019. The code still runs on PHP 8.x in practice, but expect deprecation noise and Guzzle constraint conflicts, and expect no support for newer Authorize.Net capabilities. For anything long-lived, either pin the dependency tightly and fork it internally, or move new features to the official authorizenet/authorizenet SDK.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
cgv/localized-resource