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

Authorize Net Aim Laravel Package

payum/authorize-net-aim

Payum gateway for Authorize.Net AIM (Advanced Integration Method), enabling direct payment processing through Authorize.Net's API within Payum. Install it to add Authorize.Net support to your Payum-based payment workflow, though the package is small and lightly maintained.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check first. This repo is a read-only subtree split of Payum\AuthorizeNet\Aim, last released in September 2016, and it ships a thin gateway (a factory, an API wrapper, and a couple of actions) around the legacy Authorize.Net AIM direct-POST API. Before writing any application code, spend five minutes proving it installs on your stack:

composer require payum/authorize-net-aim payum/payum --dry-run -W

Then verify what the transitive authorizenet/authorizenet SDK version resolved to. If that package won't cooperate with your PHP version, or you're on PHP 8.x, this is where the assessment ends — the cost of forking beats the cost of fighting it.

Where to look first, in this order:

  1. src/Payum/AuthorizeNet/Aim/AuthorizeNetAimGatewayFactory.php — the createConfig() method is the authoritative list of supported options (api_login_id, transaction_key, sandbox, and whatever else it declares). Do not trust README snippets over this file.
  2. AuthorizeNetAimApi.php — shows exactly which keys it pulls off the payment/details model (card_num, exp_date, amount, invoice_num, etc.) and which AuthorizeNetAIM request fields it maps them to.
  3. Action/ — tells you whether Capture, Status, Refund, Cancel, or Void are actually wired up. Anything absent is a RequestNotSupportedException at runtime.

Minimal bootstrap (no Symfony bundle — you're in Laravel, so you own the wiring):

use Payum\Core\PayumBuilder;
use Payum\Core\GatewayFactoryInterface;
use Payum\AuthorizeNet\Aim\AuthorizeNetAimGatewayFactory;

$this->app->singleton('payum', function ($app) {
    return (new PayumBuilder())
        ->addGatewayFactory('authorize-net-aim', function (array $config, GatewayFactoryInterface $core) {
            return new AuthorizeNetAimGatewayFactory($core);
        })
        ->addGateway('authorize-net-aim', [
            'factory'         => 'authorize-net-aim',
            'api_login_id'    => config('services.authorize.login_id'),
            'transaction_key' => config('services.authorize.transaction_key'),
            'sandbox'         => ! $app->isProduction(),
        ])
        ->setTokenStorage(new EloquentTokenStorage())      // you implement this
        ->setGatewayConfigStorage(new EloquentConfigStorage()) // you implement this
        ->getPayum();
});

The first use case worth building is a single sandbox sale against a fixed amount, executed synchronously and asserted on Status. AIM is a server-to-server API — there is no redirect, no callback URL, no pending state. If you can charge 10.00 in sandbox and see captured or failed, the integration works and everything else is refinement.

Implementation Patterns

Put credentials in config/services.php, never in a hardcoded array. Gateway config gets persisted by GatewayConfigStorage; if you build the config array with config() calls and later rotate the transaction key, stale configs in storage will keep using the old one. Prefer storing identifiers only and resolving secrets at request time, or make config storage re-seed on deploy.

Synchronous capture is the normal path. Unlike PayPal Express or Stripe Checkout, you do not mint a Capture token and redirect. You build the payment, execute Capture, and read the result in the same request:

use Payum\Core\Request\Capture;
use Payum\Core\Request\Status;

$storage = $payum->getStorage(\App\Models\PayumPayment::class);
$payment = $storage->create();
$payment['amount']    = '10.00';         // string, 2 decimals, never a float
$payment['currency']  = 'USD';
$payment['invoice_num'] = $order->id;    // your idempotency handle
$storage->update($payment);

$gateway->execute(new Capture($payment));

$gateway->execute($status = new Status($payment));
if ($status->getValue() === 'captured') {
    $order->markPaid();
}

Keep the token variant (new Capture($token)) for when you want Payum's token persistence and afterUrl auditing, but understand it buys you little here — there's no off-site leg to resume.

Wrap the charge in a DB transaction and a lock. This is a payment, not a query. DB::transaction() around create → capture → persist status, plus a row lock or cache lock keyed on the order so a double-submitted form doesn't produce two Authorize.Net transactions. Authorize.Net's duplicate-transaction window will reject identical submissions within a few minutes, but that's a safety net, not a concurrency strategy.

Implement StorageInterface over Eloquent rather than using the filesystem storage. Filesystem storage (payum/filesystem) is fine for a spike, but it writes serialized payment objects to disk with card data in them. An Eloquent-backed storage lets you control the columns, encrypt what needs encrypting, and join tokens back to your orders table for support tooling.

Treat status mapping as your own business logic. The package's Status action maps Authorize.Net response codes to Payum statuses. Don't trust that mapping blindly — surface response_reason_code, response_reason_text, and transaction_id into your own columns so support can read the real reason a card declined instead of Payum's abstraction of it.

Use Convert when your domain model isn't the payment model. If you already have an Order or Invoice Eloquent model, execute Convert (or hand-roll the mapping) to produce a Payum payment, rather than trying to make Order implement Payum\Core\Model\PaymentInterface. Keeps your domain schema clean.

Test with a fake API, not the sandbox. Point AuthorizeNetAimApi at a stub (or mock the AuthorizeNetAIM client) in unit tests so your suite doesn't depend on network access to Authorize.Net. Reserve the sandbox for a single happy-path integration test in CI, marked as a separate group and skipped when credentials are absent.

Gotchas and Tips

Do not send a PR to this repo. It's a read-only split; changes go to payum/payum and get mirrored down. If you patch a vendored copy you will lose it on the next composer install.

PCI scope is the real decision here. AIM means raw card numbers POST through your server, which puts you in SAQ D territory — the heaviest self-assessment questionnaire. If you have any alternative (Accept.js tokenization, Accept Hosted, or a different gateway entirely), that's a materially cheaper compliance posture than wiring this package up. Choose AIM deliberately, not by default.

Never let a PAN into storage, logs, or a serialized token. Payum serializes payment details and tokens into whatever storage you configure. Use Payum\Core\Security\SensitiveValue for card number and CVV fields so they don't round-trip through your database, and double-check your StorageInterface implementation doesn't json_encode the whole model. Also audit your Laravel logging: one Log::debug($payment) can put a card number on disk forever.

Amounts and currency. Send amount as a two-decimal string. Floats will produce rounding failures that show up as declined or mismatched-amount transactions. AIM is effectively USD for US merchant accounts — confirm your merchant account's settlement currency before assuming currency does anything; it may be silently ignored.

Retries create duplicates. Network timeout on a POST means you don't know whether the charge landed. Don't blind-retry. Re-query by invoice_num / transaction_id first, or lean on Authorize.Net's duplicate window and treat the duplicate response code as "already charged."

RequestNotSupportedException is a wiring signal, not a bug in your code. It means the factory didn't register an action for that request type — refunds and voids are the usual suspects on a thin package like this. Check Action/ before assuming you can call Refund.

Sandbox credentials are separate from production (different login ID and transaction key, and the endpoint is controlled by the sandbox flag). Standard sandbox cards — Visa 4007000000027, Mastercard 5424000000000015 — are the fastest way to distinguish "my config is wrong" from "my card data is wrong." Sandbox transactions never settle, so don't build reconciliation logic against them.

Pin the dependency and set a review reminder. With zero dependents, no releases in years, and an SDK dependency that's aged alongside it, treat this as vendored infrastructure. composer.json with an exact constraint, a note in the README explaining why it exists, and a calendar item to re-evaluate against a direct Authorize.Net client or a maintained abstraction on your next major upgrade.

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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager