facebook pixel
Published on

Integrating with the Selling Partner API using Laravel

If you need help building custom e-commerce solutions, get in touch. We spend every day working on tools to speed up and improve e-commerce business processes.

We have built a lot of Laravel applications with our Selling Partner API library for PHP. After the umpteenth time re-integrating it, we did what any self-respecting developers would do and turned that integration into a library – and highsidelabs/laravel-spapi is that library.

Our library allows you to type-hint Selling Partner API classes into Laravel methods, which saves you from needing to build SP API configuration infrastructure. It supports both single-account setups (if you're a solo seller who needs to access the SP API) and more complex multi-account systems (if you own multiple seller accounts, or build tools that are authorized by multiple sellers).

Let's get started.

Installation

This is the easiest part: assuming you already have a Laravel project set up, install the package:

$ composer require highsidelabs/laravel-spapi

Then publish the library's Laravel configuration file:

$ php artisan vendor:publish --provider="HighsideLabs\LaravelSpApi\SellingPartnerApiServiceProvider" --tag="config"

That command places the library's config file in your config/ folder, so that you can edit it if you want. Later on we'll discuss some of the changes you might want to make.

Single-seller installations

Setup

Add some variables to your .env:

SPAPI_AWS_ACCESS_KEY_ID=
SPAPI_AWS_SECRET_ACCESS_KEY=
SPAPI_LWA_CLIENT_ID=
SPAPI_LWA_CLIENT_SECRET=
SPAPI_LWA_REFRESH_TOKEN=

# Optional
# SPAPI_AWS_ROLE_ARN=
# SPAPI_ENDPOINT_REGION=

Fill in the AWS- and LWA-related variables with the relevant credentials. You only need to use the SPAPI_AWS_ROLE_ARN environment variable if the IAM ARN you used to create your Selling Partner API application is a role ARN (rather than a user ARN). If you're not sure if this is you, it probably isn't – role ARNs look like arn:aws:iam::012345678901:role/<role name>. For a detailed explanation of how to get the SP API credentials you need, check out our post on getting access to the Selling Partner API.

If the Seller account you're working with is in Amazon's North America region, you can leave the SPAPI_ENDPOINT_REGION blank. If you're in one of the other regions, you'll want to populate it – valid values are NA, EU, and FE. That will ensure you're making calls to the correct regional SP API endpoint.

Usage

When you're only using the Selling Partner API with a single set of seller credentials, usage is super simple. All you need to do is type-hint Selling Partner API classes into your methods, and you'll be able to make calls to the API! The example below assumes you have access to the Selling Partner Insights role (more info on that here) in your SP API app configuration so that you can call the SellersV1Api::getMarketplaceParticipations() endpoint, but the same principle applies to type-hinting any other Selling Partner API class.

Put this in app/Http/Controllers/SingleSellerController.php:

<?php
namespace App\Http\Controllers;

use Illuminate\Http\JsonResponse;
use SellingPartnerApi\Api\SellersV1Api as SellersApi;
use SellingPartnerApi\ApiException;

class SingleSellerController extends Controller
{
    public function participations(SellersApi $api): JsonResponse
    {
        try {
            $result = $api->getMarketplaceParticipations();
            return response()->json($result);
        } catch (ApiException $e) {
            $jsonBody = json_decode($e->getResponseBody());
            return response()->json($jsonBody, $e->getCode());
        }
    }
}

Then add this to routes/api.php:

use App\Http\Controllers\SingleSellerController;

// ...

Route::get('/seller/participations', [SingleSellerController::class, 'participations'])
    ->name('seller.participations');

If you start your Laravel application with php artisan serve and then go to http://localhost:8000/api/seller/participations, you should see a JSON response that contains the Selling Partner API response data. If you get a 403 Unauthorized error, you might not have access to the Selling Partner Insights role mentioned earlier.

For single-seller setups, that's basically all there is to know! If you want to use this library to interface with the Selling Partner API using multiple sets of Seller credentials, keep on reading.

Multi-seller installations

If you work with more than one seller (people who build tools for Sellers fall into this category, for instance), or if you only have one Seller account but you sell in multiple regions (and therefore have multiple sets of credentials for the same Seller), this package supports that use case too.

Setup

To get started, change the installation_type key in config/spapi.php from single to multi. If the different sets of seller credentials you plan to use will have different AWS credentials, change aws.dynamic to true. If you operate multiple seller accounts, each of which has its own SP API application configured in Seller Central, and the different SP API applications were configured with separate AWS accounts, this applies to you...but if you're building an application that sellers authorize via OAuth, it probably doesn't. Most OAuth-based SP API applications will have the same AWS credentials associated with all authorized seller accounts.1

There are some database migrations associated with multi-seller installations, because we're going to store the different sets of credentials in the database. Publish those migrations:

$ php artisan vendor:publish --provider="HighsideLabs\LaravelSpApi\SellingPartnerApiServiceProvider" --tag="multi"

If you want to change anything about those tables, now is the time to do so. You can edit the two migration files that were just added to your database/migrations/ folder. The create_spapi_sellers_table migration is intended to be extended with any fields you need to save business-specific data about a particular seller.

Run the migrations:

$ php artisan migrate

You will now have two new tables: spapi_sellers and spapi_credentials. spapi_sellers is very simple, and mostly exists to link multiple sets of credentials for the same seller (which is a common need, since one seller will have a different set of credentials for each Amazon region they are active in). spapi_credentials stores the actual tokens needed to make calls to the SP API, and links back to the spapi_sellers table. There are corresponding Seller and Credentials models linked to those tables, which you can import from the HighsideLabs\LaravelSpApi\Models namespace.2

If you changed the aws.dynamic flag to true in config/spapi.php, you can leave your .env alone. If you didn't change that configuration value, you will need to update your .env to include your AWS credentials:

SPAPI_AWS_ACCESS_KEY_ID=
SPAPI_AWS_SECRET_ACCESS_KEY=
# SPAPI_AWS_ROLE_ARN=

As mentioned in the single-seller setup instructions, you only need to use the SPAPI_AWS_ROLE_ARN environment variable if the IAM ARN you used to create your Selling Partner API application is a role ARN (rather than a user ARN).

Usage

Since we're working with multiple sets of Selling Partner API credentials, we need a way to specify which SP API credentials to use with a particular SP API class instance before we start actually making calls to the API with that instance. There are two ways to do that.

Auto-injected API classes with placeholder credentials

As with the single-seller setup, we can auto-inject SP API classes into methods in classes that are in the Laravel service container. The injected instance will have non-functional SP API credentials associated with it, so we need to retrieve some legitimate credentials and pass them to the API class instance:

<?php

namespace App\Http\Controllers;

use HighsideLabs\LaravelSpApi\Models\Credentials;
use Illuminate\Http\JsonResponse;
use SellingPartnerApi\Api\SellersV1Api as SellersApi;
use SellingPartnerApi\ApiException;


class MultiSellerController extends Controller
{
    public function injected(SellersApi $api): JsonResponse
    {
        // Retrieve a set of credentials
        $creds = Credentials::first();
        // Inject the credentials into the API class instance
        $creds->useOn($api);

        try {
            // Now we can make SP API calls!
            $result = $api->getMarketplaceParticipations();
            return response()->json($result);
        } catch (ApiException $e) {
            $jsonBody = json_decode($e->getResponseBody());
            return response()->json($jsonBody, $e->getCode());
        }
    }
}

If we try to make a request with the SP API class instance without first injecting real credentials, we will get an error:

<?php

// ...
class MultiSellerController extends Controller
{
    // ...
    public function goingToFail(SellersApi $api): void
    {
        $api->getMarketplaceParticipations();
        // Exception: You must call Credentials::useOn($apiInstance) before calling any API methods.
    }
}

Create SP API classes yourself and inject credentials into them

We can also make an SP API class instance manually (without having Laravel auto-inject it) with the SellingPartnerApi::makeApi() method:

<?php

// ...
use HighsideLabs\LaravelSpApi\SellingPartnerApi;

class MultiSellerController extends Controller
{
    // ...

    public function manual(): JsonResponse
    {
        // Retrieve the credentials we want to use to make the API call
        $creds = Credentials::first();
        // Generate an instance of a particular API class, already populated with the creds we retrieved above
        $api = SellingPartnerApi::makeApi(SellersApi::class, $creds);

        try {
            // Then make an SP API call!
            $result = $api->getMarketplaceParticipations();
            return response()->json($result);
        } catch (ApiException $e) {
            $jsonBody = json_decode($e->getResponseBody());
            return response()->json($jsonBody, $e->getCode());
        }
    }
}

Let's put this all together into one controller. Put this in your Laravel project, in app/Http/Controllers/MultiSellerController.php:

<?php

namespace App\Http\Controllers;

use HighsideLabs\LaravelSpApi\Models\Credentials;
use HighsideLabs\LaravelSpApi\SellingPartnerApi;
use Illuminate\Http\JsonResponse;
use SellingPartnerApi\Api\SellersV1Api as SellersApi;
use SellingPartnerApi\ApiException;

class MultiSellerController extends Controller
{
    public function injected(SellersApi $api): JsonResponse
    {
        // Retrieve a set of credentials
        $creds = Credentials::first();
        // Inject the credentials into the API class instance
        $creds->useOn($api);

        try {
            // Now we can make SP API calls!
            $result = $api->getMarketplaceParticipations();
            return response()->json($result);
        } catch (ApiException $e) {
            $jsonBody = json_decode($e->getResponseBody());
            return response()->json($jsonBody, $e->getCode());
        }
    }

    public function manual(): JsonResponse
    {
        // Retrieve the credentials we want to use to make the API call
        $creds = Credentials::first();
        // Generate an instance of a particular API class, already populated with the creds we retrieved above
        $api = SellingPartnerApi::makeApi(SellersApi::class, $creds);

        try {
            // Then make an SP API call!
            $result = $api->getMarketplaceParticipations();
            return response()->json($result);
        } catch (ApiException $e) {
            $jsonBody = json_decode($e->getResponseBody());
            return response()->json($jsonBody, $e->getCode());
        }
    }
}

And then make those methods accessible by adding this to your routes/api.php:

use App\Http\Controllers\MultiSellerController;

// ...

Route::controller(MultiSellerController::class)->group(function () {
    Route::get('/multi/injected', 'injected')
        ->name('multiSeller.injected');

    Route::get('/multi/manual', 'manual')
        ->name('multiSeller.manual');
});

To test this, we're going to need some actual SP API credentials in the database. Find a set of functional SP API credentials and save them to the database:

$ php artisan tinker
> use HighsideLabs\LaravelSpApi\Models;
> $seller = Models\Seller::create(['name' => 'My Seller']);
> $credentials = Models\Credentials::create([
      'selling_partner_id' => 'ABCDEFGHIJK1234',
      'region' => 'NA',  # One of NA, EU, FE
      'client_id' => 'amzn1.application-oa2-client......',
      'client_secret' => 'e2c7......',
      'refresh_token' => 'Aztr|IWeBI......',
      'seller_id' => $seller->id,
      # The following attributes are only relevant if you set `aws.dynamic` to true in config/spapi.php.
      # If you have that value set to `false`, you can omit these attributes entirely
      'access_key_id' => 'AKIA......',
      'secret_access_key' => 'frhn12/......',
      # As mentioned throughout this article, this is only needed if you configured your SP API app with an IAM role ARN
      'role_arn' => 'arn::aws::iam::123456789012:role/myrolename',
  ]);
> ^D

Now we have a Seller and some Credentials, so the code in our controller will actually work! Try making GET requests to localhost:8000/api/multi/injected and localhost:8000/api/multi/manual endpoints. If you set everything up right, and the credentials you added to the database are valid, you should get back a JSON response containing Selling Partner API seller participations data!

Wrapup

If you find any bugs in the library, please open a GitHub issue. If you have any questions or comments on this post, please feel free to email us! We'd love to hear from you.

We specialize in developing custom e-commerce API solutions, so if you need help with something related to this article, or with any other e-commerce integration, get in touch and we'll figure out how we can help.

Footnotes

  1. If you ran the multi-seller migrations before realizing you needed dynamic AWS credentials, there's a secret migration you can use to fix that. First, make sure that the aws.dynamic config key is set to true. Here's how to use it:

    $ composer require doctrine/dbal  # This is likely already installed, but just in case
    $ php artisan vendor:publish --provider="HighsideLabs\LaravelSpApi\SellingPartnerApiServiceProvider" --tag="add-aws"
    $ php artisan migrate
    
  2. To modify the models used by this library, extend them in your Laravel project. For instance, if in the migrations you changed the name of the table that stores Selling Partner API credentials from spapi_credentials to credentials, you can make a model that reflects that like so:

    <?php
    
    namespace App\Models;
    
    use HighsideLabs\LaravelSpApi\Models\Credentials as SpApiCredentials;
    
    class Credentials extends SpApiCredentials
    {
        protected $table = 'credentials';
    }
    

    Then, everywhere that the Credentials model is referenced in the highsidelabs/laravel-spapi documentation, use your Credentials model from App\Models\Credentials instead of the library's model (HighsideLabs\LaravelSpApi\Models\Credentials).

Stay updated on our work