# Selling Partner API for PHP
A PHP library for connecting to Amazon's [Selling Partner API](https://github.com/amzn/selling-partner-api-docs/). This library is *not* for connecting to Amazon's older MWS API---if that's what you need, check out [glassfrogbooks/php-amazon-mws](https://github.com/glassfrogbooks/phpAmazonMWS).

This package is partially generated by the [OpenAPITools generator](https://github.com/OpenAPITools/openapi-generator).

## Powering companies like...

* [Wizard Industries](https://wizard-industries.com)
* [Mendota Pet](https://mendotapet.com)

---

If you've found this library useful, please consider [becoming a Sponsor](https://github.com/sponsors/jlevers), or making a one-time donation via the button below. I appreciate any and all support you can provide!

[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/donate?business=EL4PRLAEMGXNQ&currency_code=USD)

## Requirements

* PHP 7.2 or later
* Composer

## Installation

To install the bindings via [Composer](http://getcomposer.org/), run `composer require jlevers/selling-partner-api` inside your project directory.

## Getting Started

### Prerequisites

You need a few things to get started:
* A Selling Partner API developer account
* An AWS IAM user or role configured for use with the Selling Partner API
* A Selling Partner API application

If you're looking for more information on how to set those things up, check out [this blog post](https://jesseevers.com/selling-partner-api-access/). It provides a detailed walkthrough of the whole setup process.


### Setup

#### Using the Configuration and ConfigurationOptions classes

TL;DR: pass a `ConfigurationOptions` instance to the `Configuration` constructor, and pass the `Configuration` instance to the `SellingPartnerApi\Api\*Api` constructor.

The [`ConfigurationOptions`](https://github.com/jlevers/selling-partner-api/blob/main/lib/ConfigurationOptions.php) constructor takes all the configuration information that's needed to connect to the Selling Partner API:

```php
$configurationOptions = new SellingPartnerApi\ConfigurationOptions(
    "<LWA client ID>",
    "<LWA client secret>",
    "<LWA refresh token>",
    "<AWS access key ID>",
    "<AWS secret access key>",
    "<SP API AWS region>",
    "<SP API endpoint>",
);
```

If you created your Selling Partner API application using an IAM role ARN instead of a user ARN, pass that role ARN to the `ConfigurationOptions` constructor:

```php
    $configurationOptions = new SellingPartnerApi\ConfigurationOptions(
    "<LWA client ID>",
    "<LWA client secret>",
    "<LWA refresh token>",
    "<AWS access key ID>",
    "<AWS secret access key>",
    "<SP API AWS region>",
    "<SP API endpoint>",
    null,  //
    null,  // More about these parameters in the `ConfigurationOptions` section below
    null,  //
    "<Role ARN>"
);
```

That object gets passed to the [`Configuration`](https://github.com/jlevers/selling-partner-api/blob/main/lib/ConfigurationOptions.php) constructor:

```php
$config = new SellingPartnerApi\Configuration($configurationOptions);
```

`$config` can then be passed into the constructor of any `SellingPartnerApi\Api\*Api` class. See the `Example` section for a complete example.

##### ConfigurationOptions

The `ConfigurationOptions` constructor takes the following parameters:

* `lwaClientId (string)`: The LWA client ID of the SP API application to use to execute API requests.
* `lwaClientSecret (string)`: The LWA client secret of the SP API application to use to execute API requests.
* `lwaRefreshToken (string)`: The LWA refresh token of the SP API application to use to execute API requests.
* `awsAccessKey (string)`: AWS IAM user Access Key ID with SP API ExecuteAPI permissions.
* `awsAccessSecret (string)`: AWS IAM user Secret Access Key with SP API ExecuteAPI permissions.
* `spapiAwsRegion (string)`: The AWS region associated with the SP API endpoint you're using. See [here](https://github.com/amzn/selling-partner-api-docs/blob/main/guides/en-US/developer-guide/SellingPartnerApiDeveloperGuide.md#selling-partner-api-endpoints) for more details.
* `spapiEndpoint (string)`: The SP API endpoint you want to make calls to (i.e. https://sellingpartnerapi-na.amazon.com).
* `accessToken (string|null)`: An access token generated from the refresh token.
* `accessTokenExpiration (int|null)`: A Unix timestamp corresponding to the time when the `accessToken` expires. If `accessToken` is given, `accessTokenExpiration` is required (and vice versa).
* `$onUpdateCredentials (callable|Closure|null)`: A callback function to call when a new access token is generated. The function should accept a single argument of type [`SellingPartnerApi\Credentials`](https://github.com/jlevers/selling-partner-api/blob/main/lib/Credentials.php).
* `$roleArn (string|null)`: If you set up your SP API application with an AWS IAM role ARN instead of a user ARN, pass that ARN here.

##### Configuration

The `Configuration` constructor takes the following parameters:
* `configInfo (ConfigurationOptions|array)`: A `ConfigurationOptions` instance, or an array of configuration options for backwards compatibility.
* `spapiEndpoint (string|null)`: A Selling Partner endpoint. This parameter exists for backwards compatibility, but is deprecated and will be removed in the next major version.

(I know this configuration system is somewhat clunky, but it's necessary for backwards compatibility. In v3.0.0, the `ConfigurationOptions` object will be removed, and all configuration parameters will be passed directly to `Configuration`).

#### Using .env (deprecated)

**Note:** `.env`-based configuration is deprecated and will be removed in the next major release (v3.0.0).

Copy the sample configuration file to the root of your project: `cp vendor/jlevers/selling-partner-api/.env.example .env`

Then, fill in the environment variables in `.env` with your IAM user credentials and the LWA credentials from your application. For more information on where to get those credentials, check out [this blog post](https://jesseevers.com/spapi-php-library/#installation-and-configuration).


### Example

This example assumes you have access to the `Seller Insights` Selling Partner API role, but the general format applies to any Selling Partner API request.


```php
<?php
require_once(__DIR__ . '/vendor/autoload.php');

use SellingPartnerApi\ConfigurationOptions;
use SellingPartnerApi\Configuration;
use SellingPartnerApi\Api;

$configurationOptions = new ConfigurationOptions(
    "amzn1.application-oa2-client.....",
    "abcd....",
    "Aztr|IwEBI....",
    "AKIA....",
    "ABCD....",
    "us-east-1",
    "https://sellingpartnerapi-na.amazon.com",
);
$config = new Configuration($configurationOptions);

$api = new Api\SellersApi();
try {
    $result = $api->getMarketplaceParticipations();
    print_r($result);
} catch (Exception $e) {
    echo 'Exception when calling SellersApi->getMarketplaceParticipations: ', $e->getMessage(), PHP_EOL;
}

?>
```

## Endpoints

All endpoint URIs are relative to the `SPAPI_ENDPOINT` value you specify in your `.env` file (the default is https://sellingpartnerapi-na.amazon.com). For instance, the `SellersApi::getMarketplaceParticipations()` endpoint, `/sellers/v1/marketplaceParticipations`, is expanded to `https://sellingpartnerapi-na.amazon.com/sellers/v1/marketplaceParticipations`.

The [`docs/Api/`](https://github.com/jlevers/selling-partner-api/tree/main/docs/Api) directory contains the documentation for interacting each distinct section of the Selling Partner API. Those sections are referred to as **APIs** throughout the documentation---you can think of the Selling Partner API as having many sub-APIs, where each sub-API has a number of endpoints that provide closely related functionality.

Endpoint methods that perform `POST`, `PUT`, and `DELETE` requests typically take some model as a parameter, and nearly all endpoint methods return a model with result information. For instance, [`ShippingApi::createShipment()`](https://github.com/jlevers/selling-partner-api/blob/main/docs/Api/ShippingApi.md#createShipment) takes an instance of the [`CreateShipmentRequest`](https://github.com/jlevers/selling-partner-api/blob/main/docs/Model/Shipping/CreateShipmentRequest.md) model as its only argument, and returns an instance of the [`CreateShipmentResponse`](https://github.com/jlevers/selling-partner-api/blob/main/docs/Model/Shipping/CreateShipmentResponse.md) model.

See the `Models` section below for more information about models.

## Models

Each endpoint has one or more models associated with it. These models are classes that contain the data needed to make a certain kind of request to the API, or contain the data returned by a given request type. All of the models share the same general interface: you can either specify all the model's attributes during initialization, or use setter methods to set each attribute after the fact. Here's an example using the Service API's `Buyer` model ([docs](https://github.com/jlevers/selling-partner-api/blob/main/docs/Model/Service/Buyer.md), ([source](https://github.com/jlevers/selling-partner-api/blob/main/lib/Model/Service/Buyer.php)).

The `Buyer` model has four attributes: `buyer_id`, `name`, `phone`, and `is_prime_member`. (If you're wondering how you would figure out which attributes the model has on your own, check out the `docs` link above.) To create an instance of the `Buyer` model with all those attributes set:

```php
$buyer = new SellingPartnerApi\Model\Service\Buyer([
    "buyer_id" => "ABCDEFGHIJKLMNOPQRSTU0123456",
    "name" => "Jane Doe",
    "phone" => "+12345678901",
    "is_prime_member" => true
]);
```

Alternatively, you can create an instance of the `Buyer` model and then populate its fields:

``` php
$buyer = new SellingPartnerApi\Model\Service\Buyer();
$buyer->setBuyerId("ABCDEFGHIJKLMNOPQRSTU0123456");
$buyer->setName("Jane Doe");
$buyer->setPhone("+12345678901");
$buyer->setIsPrimeMember(true);
```

Each model also has the getter methods you might expect:

``` php
$buyer->getBuyerId();        // -> "ABCDEFGHIJKLMNOPQRSTU0123456"
$buyer->getName();           // -> "Jane Doe"
$buyer->getPhone();          // -> "+12345678901"
$buyer->getIsPrimeMember();  // -> true
```

Models can (and usually do) have other models as attributes:

``` php
$serviceJob = new SellingPartnerApi\Model\Service\Buyer([
    // ...
    "buyer" => $buyer,
    // ...
]);

$serviceJob->getBuyer();             // -> [Buyer instance]
$serviceJob->getBuyer()->getName();  // -> "Jane Doe"
```

## Uploading and downloading documents

The Feeds and Reports APIs include operations that involve uploading and downloading documents to and from Amazon. Amazon encrypts all documents they generate, and requires that all uploaded documents be encrypted. The `SellingPartnerApi\Document` class handles all the encryption/decryption, given an instance of one of the `Model\Reports\ReportDocument`, `Model\Feeds\FeedDocument`, or `Model\Feeds\CreateFeedDocumentResponse` classes. Instances of those classes are in the response returned by Amazon when you make a call to the [`getReportDocument`](https://github.com/jlevers/selling-partner-api/blob/main/docs/Api/ReportsApi.md#getReportDocument), [`getFeedDocument`](https://github.com/jlevers/selling-partner-api/blob/main/docs/Api/FeedsApi.md#getFeedDocument), and [`createFeedDocument`](https://github.com/jlevers/selling-partner-api/blob/main/docs/Api/FeedsApi.md#createFeedDocument) endpoints, respectively.

### Downloading a report document

``` php
use SellingPartnerApi\Api\ReportsApi;

// Assume we've already fetched a report document ID
$documentId = "foo.1234";
$reportsApi = new ReportsApi();
$reportDocumentInfo = $reportsApi->getReportDocument($documentId);

// Pass the content type of the report you're fetching
$docToDownload = new SellingPartnerApi\Document($reportDocumentInfo->getPayload(), "text/tab-separated-values");
$contents = $docToDownload->download();  // The raw report text
// A SimpleXML object if the content type is text/xml, or an array of associative arrays, each
// sub array corresponding to a row of the report
$data = $docToDownload->getData();
// ... do something with report contents
```

### Uploading a feed document

``` php
use SellingPartnerApi\Api\FeedsApi;
use SellingPartnerApi\Model\Feeds;

const CONTENT_TYPE = "text/xml";  // This will be different depending on your feed type
$feedsApi = new FeedsApi();
$createFeedDocSpec = new Feeds\CreateFeedDocumentSpecification(["content_type" => CONTENT_TYPE]);
$feedDocumentInfo = $feedsApi->createFeedDocument($createFeedDocSpec);

$documentContents = file_get_contents("<your/feed/file.xml>");

$docToUpload = new SellingPartnerApi\Document($feedDocumentInfo->getPayload(), CONTENT_TYPE);
$docToUpload->upload($documentContents);
```
