kbart/amazon-ads-sdk

Amazon Ads API SDK for Laravel

Maintainers

Package info

github.com/Braindea7/amazon-ads-sdk

pkg:composer/kbart/amazon-ads-sdk

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-main 2026-04-02 22:46 UTC

This package is auto-updated.

Last update: 2026-04-02 22:53:55 UTC


README

Run Tests

A developer-friendly (DX) Laravel package for integrating the Amazon Ads API.

This package combines the type safety of a generated OpenAPI SDK with the convenience of dynamic Laravel proxy routes. It handles the complete authentication (LWA Token Refresh), rate limiting, and DTO hydration for you.

💎 Features

  • Magic LWA Token Manager: Fully automatically fetches and refreshes Access Tokens via Amazon "Login with Amazon" and caches them securely in Redis/Cache.
  • Smart Retries: Catches rate limits (429) and server errors (5xx) and automatically retries requests using exponential backoff.
  • Multi-Seller Ready: Authentication is handled dynamically via the {seller} route parameter.
  • Debugging & Agent: Easy control over Guzzle debug output and User-Agent headers.
  • Dual Architecture:
    • Direct SDK Usage (via Facade): Full autocompletion and type safety for backend jobs (cron jobs, queues).
    • Dynamic SDK Proxy: A universal interface (/api/amazon-ads/sdk/{seller}/...) for the frontend (Vue/React) with fully automatic DTO routing.
  • Self-Documenting API: An info route (/api/amazon-ads/sdk/{seller}/info/...) that resolves the entire Amazon Ads API, including Enums and Schemas, using PHP Reflection.

⚡ Installation

  1. Install the package via Composer
composer require kbart/amazon-ads-sdk
  1. Publish the configuration file
php artisan vendor:publish --tag="amazon-ads-config"
  1. Add your default credentials to your .env file
AMAZON_ADS_DEFAULT_CLIENT_ID="amzn1.application-oa2-client.xxxx"
AMAZON_ADS_DEFAULT_CLIENT_SECRET="amzn1.oa2-client.v1.xxxx"
AMAZON_ADS_DEFAULT_REFRESH_TOKEN="Atzr|xxxx"
AMAZON_ADS_DEFAULT_REGION="EU" # EU, NA, or FE
AMAZON_ADS_DEFAULT_PROFILE_ID="xxx"
  1. Routes & Middleware

By default, the package registers the SDK routes under the prefix api/amazon-ads/sdk/{seller}. To use the SDK routes, you must manually load them in your routes/api.php. You can also assign your own middleware:

use KBart\AmazonAds\AmazonAds;
use App\Http\Middleware\Authenticate;

// Registers the routes under api/amazon-ads/sdk/{seller}/...
AmazonAds::routes([
    'middleware' => [Authenticate::class, 'your-custom-access-check'],
]);

(If you register the routes in your web.php, you must either include the 'api' middleware or exclude the routes from the CSRF token check in your bootstrap/app.php!)

Usage

1. The SdkProxy (Recommended)

The SdkProxyController receives HTTP requests, forwards them to the corresponding classes of the generated SDK, and returns the result as clean JSON.

Example Request (POST): Request to your local Laravel endpoint to fetch keywords.

POST /api/amazon-ads/sdk/{seller}/SponsoredProduct/Keywords/listSponsoredProductsKeywords
Content-Type: application/json
{
    "sponsoredProductsListSponsoredProductsKeywordsRequestContent": {
        "maxResults": 100,
        "stateFilter": {
            "include": [
                "ENABLED",
                "PAUSED"
            ]
        }
    }
}

(The AmazonAdsExecutor recognizes that the sponsoredProductsListSponsoredProductsKeywordsRequestContent parameter requires a complex SDK object and safely casts your JSON array into this DTO before calling the SDK method.)

A. Docs / Info Route (Self-Documenting API)

Want to know which APIs are available and what the payload should look like? Send a GET request to the info routes:

  • All Verticals: GET /api/amazon-ads/sdk/{seller}/info
  • All Resources (e.g., SponsoredProducts): GET /api/amazon-ads/sdk/{seller}/info/sponsored-products
  • All Methods (e.g., Campaigns): GET /api/amazon-ads/sdk/{seller}/info/sponsored-products/campaigns

The response will show you exactly which parameters are expected, whether a complex DTO needs to be sent, and which Enum values are allowed!

2. The Native Proxy

If you want to bypass the SDK completely and instead call the Amazon endpoints directly using Guzzle/Laravel HTTP, use the ProxyController.

Example Request (POST):

POST /api/amazon-ads/proxy/sp/keywords/list?seller={seller}
Content-Type: application/json
X-Amazon-Profile-Id: 1234567890
{
    "maxResults": 100,
    "stateFilter": {
        "include":[
            "ENABLED"
        ]
    }
}

3. Direct Usage in PHP (Facades)

If you are building complex calculations or nightly synchronizations, you should use the generated SDK classes directly. You can comfortably use the AmazonAdsFactory facade for this. The package handles token injection and rate-limit retries in the background!

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use KBart\AmazonAds\Facades\AmazonAdsSdk;
use KBart\AmazonAds\Auth\AmazonAdsCredentials;
use KBart\AmazonAds\Generated\SponsoredProducts\Api\CampaignsApi;

// The facade takes care of EVERYTHING:
// Token Refresh, Retries, Rate Limits AND the automatic 
// hydration of the array into complex Amazon DTOs!
class CustomAmazonController extends Controller
{
    // Via Request: Pass the Request, as well as the Namespace, desired Api Class and Method.
    public function createCampaignWithRequest(Request $request)
    {
        return $this->handleApiCall(function () use ($request) {
            return AmazonAdsSdk::execute(
                request: $request,
                namespace: 'SponsoredProduct',
                apiClass: CampaignsApi::class,
                method: 'listSponsoredProductsCampaigns'
            );
        });
    }

    // Via Payload: Pass the Payload, as well as the Namespace, desired Api Class and Method.
    public function createCampaignWithArray(array $payload)
    {
        return $this->handleApiCall(function () use ($payload) {
            $credentials = AmazonAdsCredentials::fromArray($payload);

            return AmazonAdsSdk::executeWithCredentials(
                credentials: $credentials,
                namespace: 'SponsoredProduct',
                apiClass: CampaignsApi::class,
                method: 'createCampaigns',
                payload: $payload
            );
        });
    }
}

👨‍💼 Advanced

1. Custom Credentials Resolver

By default, the package uses the .env variables. If you want to store your seller data in a database, for example, you can define your own credentials resolver in the AppServiceProvider of your Laravel application.

From now on, the package will always call this logic when executing API calls!

// in app/Providers/AppServiceProvider.php

use Illuminate\Support\Facades\Log;
use Illuminate\Support\Carbon;
use Illuminate\Validation\Rule;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use KBart\AmazonAds\AmazonAds;
use KBart\AmazonAds\Auth\AmazonAdsCredentials;
use App\Models\Seller;

public function boot(): void
{
    AmazonAds::resolveCredentialsUsing(function (Request $request) {
        $request->mergeIfMissing([
            'seller' => $request->route('seller', $request->query('seller', $request->input('seller', null))),
            'endpoint' => 'EU',
            'countryCode' => 'DE',
        ]);

        $validated = $request->validate([
            'seller'     => [
                'required',
                'string',
                Rule::exists(Seller::class, 'seller'),
            ],
            'endpoint' => ['required', 'string'],
            'countryCode' => ['required', 'string'],
        ]);

        $seller = Seller::where('seller', $validated['seller'])
            ->whereHas('logins', function ($query) use ($validated) {
                $query->where('endpoint', $validated['endpoint'])
                    ->whereHas('profiles', function ($q) use ($validated) {
                        $q->where('countryCode', $validated['countryCode']);
                    });
            })
            ->with([
                'logins' => function ($query) use ($validated) {
                    $query->where('endpoint', $validated['endpoint'])
                        ->limit(1)
                        ->with([
                            'profiles' => function ($q) use ($validated) {
                                $q->where('countryCode', $validated['countryCode'])
                                    ->limit(1);
                            },
                        ]);
                },
            ])
            ->first();

        if (empty($seller)) {
            Log::error("Doesn't find Seller {$validated['seller']} with Login for endpoint {$validated['endpoint']} or profile for {$validated['countryCode']}, in Database.");
            throw new \InvalidArgumentException("Can't find Sellerprofile {$validated['seller']}");
        }

        $login = $seller->logins->first();
        $profile = $login->profiles->first();

        if (! empty($login->clientSecretExpiry)) {
            if (Carbon::parse($login->clientSecretExpiry)->subDays(3)->lessThanOrEqualTo(now())) {
                Log::error("clientSecret of {$seller->seller} is very close to expire!", [
                    'expiresAt' => $login->clientSecretExpiry,
                    'hoursLeft' => Carbon::parse($login->clientSecretExpiry)->diffInHours(now()),
                ]);
            } elseif (Carbon::parse($login->clientSecretExpiry)->subDays(14)->lessThanOrEqualTo(now())) {
                Log::info("clientSecret of {$seller->seller} is about to expire!", [
                    'expiresAt' => $login->clientSecretExpiry,
                    'daysLeft' => Carbon::parse($login->clientSecretExpiry)->diffInDays(now()),
                ]);
            }
        }

        return new AmazonAdsCredentials(
            clientId: $login->clientId,
            clientSecret: $login->clientSecret,
            refreshToken: $login->refreshToken,
            region: $login->endpoint,
            profileId: $profile->profileId,
        );
    });
}

2. Set a custom user-agent

Amazon does not strictly require a specific User-Agent; it can be determined by you and adjusted in the config if necessary.

'settings' => [
    'user-agent' => 'Your-User-Agent/1.0.0/PHP',
]

3. Caching & TTL Configuration

In config/amazon-ads.php, specific TTL values for caching endpoints can be defined.

'cache-ttl' => [
    'catalogItemsV20220401' => [
        'getCatalogItem'     => 3600, // cache for 1 hour
        'searchCatalogItems' => 600,  // cache for 10 minutes
    ],
    'productPricingV0' => [
        'getPricing'         => 300,  // cache for 5 minutes
    ],
]

Enable Debugging

If you want to enable debugging for the SDK, adjust the corresponding parameters in the config.

'settings' => [
    'debug' => true,
    'debugFile' => base_path('storage/logs/amazon-ads-sdk.log'),
]

🛠️ SDK Generation (Build Process)

This SDK uses the official OpenAPI Specifications from Amazon to always remain 100% type-safe and up to date. The required .json and .yaml specification files are now located directly within your package in the ./resources/openapi/ directory. An automated pipeline based on Node.js and PHP has been built, generating ready-to-use classes for you at the push of a button. This utilizes the official openapi-generator-cli tool. The build script also automatically fixes known Amazon OpenAPI bugs and cleans up the endpoints.

Prerequisites

Ensure that you have Node.js and npm installed on your system. Before running for the first time, install the necessary dependencies in the root directory of the package:

npm install

1. Regenerate all endpoints

If you want to bring the entire SDK up to date, run this command:

npm run build:all

(This is equivalent to npm run clean && npm run generate)

What happens in the background?

  1. The src/Generated folder is completely emptied.
  2. The script loads the OpenAPI files directly from the local ./resources/openapi/ directory.
  3. A pre-processor script cleans the OpenAPI spec files in .json format and removes problematic Content-Types like application/json if application/vnd... is present.
  4. The openapi-generator-cli generates all Models (DTOs) and APIs.
  5. All redundant boilerplate files are cleaned up (cleanup.js). (Uses rimraf under the hood).
  6. A PHP script (generate_api_map.php) scans the new classes and fully automatically creates the src/Laravel/amazon_api_map.php file, which acts as a lightning-fast registry.
  7. A post-processor script fixes duck-typing and import issues in the generated classes ObjectSerializer.php and FormDataProcessor.php.

2. Update a single endpoint

If Amazon has only updated one area (e.g., SponsoredProduct), you can specifically target individual endpoints without rebuilding everything:

npm run generate -- SponsoredProduct

(This only updates the SponsoredProduct classes and automatically regenerates the API map at the end, without deleting other endpoints).

3. Add a new endpoint

Has Amazon released a new API? No problem:

  1. Download the specification file from Amazon and save it in your ./resources/openapi/ folder (e.g., as NewApi.json).
  2. Open the generate-apis.js file in the root directory.
  3. Add a new entry to the apis array, referencing the local file path: { name: 'GreatNewApi', input: './resources/openapi/NewApi.json' }
  4. Run npm run generate -- GreatNewApi.
  5. Done! The API is now immediately available as a PHP class and is automatically registered in the Proxy Controller.

📄 License

This project is licensed under the Apache License 2.0.