Closed SunnyFlail closed 2 years ago
I searched for it too and I deceided to patch the AmazonPHP\SellingPartner\Configuration
class. I added am member sandbox
and changed the method apiURL
. I just prefixed the host with sandbox
:
public function apiURL(string $awsRegion) : string
{
if (!Regions::isValid($awsRegion)) {
throw new InvalidArgumentException("Invalid region {$awsRegion}");
}
$apiUrl = null;
switch ($awsRegion) {
case Regions::EUROPE:
$apiUrl = Regions::EUROPE_URL;
break;
case Regions::FAR_EAST:
$apiUrl = Regions::FAR_EAST_URL;
break;
case Regions::NORTH_AMERICA:
$apiUrl = Regions::NORTH_AMERICA_URL;
break;
default:
throw new \RuntimeException('unknown region');
}
if ($this->useSandBox) {
$apiUrl = substr_replace($apiUrl, 'sandbox.', 8, 0);
}
return $apiUrl;
}
Unfortunately there is no way to modify the request object not even with plugins yet, because they are passed to the plugin method, but the reqeust object is immutable as the specification PSR-18 requires it.
When I'm bulding the Configuration I do it this way:
$configuration = Configuration::forIAMUser(
'clientId',
'clientSecret',
'accessKey',
'secretKey'
);
$configuration->setSandbox(true);
This is the smallest workaround for me and doesn't affect production at all. It's maybe not the right way, but it works for me.
Regards
hey, I'm not entirely familiar with the sandbox, so @sw-Oldeu you are saying that it should be enough just to adjust the URL in order to make sandox available? If that's the case I don't see a reason for not making it part of the config just like you did
Unfortunately there is no way to modify the request object not even with plugins yet, because they are passed to the > plugin method, but the reqeust object is immutable as the specification PSR-18 requires it.
Could you please elaborate on this one? What changes are needed on the request object? Or maybe you are saying that modification of the request object would be an alternative approach to the one you showed above?
@norberttech here the doc — https://developer-docs.amazon.com/sp-api/docs/the-selling-partner-api-sandbox
First I tried to add middleware to the Guzzle HTTP Client and prefix the URL like @SunnyFlail did. I tried it with the example retrieving a Catalog Item (this call: $item = $sdk->catalogItem()->getCatalogItem()
) and this wasn't working. It's because in the method AmazonPHP\SellingPartner\Api\CatalogApi\CatalogItemSDK::getCatalogItemRequest
a request Object is built and used for the header signature (in the return statement). And after the request is built preRequest extensions are applied here.
Yes, changing the request object could be an alternative approach but I think an ugly one. Even if you could modify the request, you would have to re-calculate the header signature. But this is not possible because calling the methods withHeader
or withUri
will return a new instance of this request object and won't modify this object (It's required from PSR Interfaces). To be able to modify the request Object (don't know if this would be a good idea) this call
$this->configuration->extensions()->preRequest('CatalogItem', 'getCatalogItem', $request);
should be modified to
$request = $this->configuration->extensions()->preRequest('CatalogItem', 'getCatalogItem', $request);
Another option could be applying a plugin in AmazonPHP\SellingPartner\HttpSignatureHeaders
to modify the request before calculate the signature. But telling the Configuration to use sandbox mode was more obvious to me. Because this was just one method to change.
To be honest: I changed this and tried to make successful calls to the API. I logged the called URLs and that was enough for me to approve this works because I didn't get any errors. Because my change won't hurt my production environment even if my modification will be removed with an update and it's not having any BC I think this is a good idea.
@norberttech what do you think about that? I'm not very experienced with this, but I could post the changes here or I could make a PR for this. Or would you say this would lead to some code smells?
hey @sw-Oldeu if you say that simply adding sandbox to each url solves the problem I would say lets adjust the config and make it possible. Changing requests on the fly through extensions sounds to me a bit over-complicated
As I said, I have not really tested it out. Maybe I can make some calls tomorrow and compare with the result in the docs.
Oh and I see now, that not all Regions have sandboxes available as it is described here. I don't know which way should be preferred: Throwing an exception using sandbos mode for unsupported regions or just pass through the expectable 404 from amazon?
There are only 3 regions, North America, Europe and Far East, all seem to be supported, what I am missing?
@norberttech sorry for not replying. I tested it out but it seems there are limitations. I was not able to create a restricted data token (RDT) in sandbox environment. But I can create one in production environment. If I use my RDT to fetch an order in sandbox it works. Maybe the sandbox cannot create an RDT? I think since the sandbox just gives you the prepared data I could determine if I'm using sandbox or not. And when in sandbox, I must not use the RDT but a normal access token. With this my sandbox testcalls are working.
Maybe I'm doing it wrong? The sdk is throwing an AmazonPHP\SellingPartner\Exception\ApiException
with message [400] Error connecting to the API (https://sandbox.sellingpartnerapi-eu.amazon.com/tokens/2021-03-01/restrictedDataToken)
with the error response:
[
{
"code": "InvalidInput",
"message": "Could not match input arguments"
}
]
The Body I send is
{
"restrictedResources": [
{
"method": "GET",
"path": "/orders/v0/orders",
"dataElements": [
"buyerInfo",
"shippingAddress"
]
}
]
}
Now my Problem is, that I currently do not have any more time for at least 4 Weeks to dig deeper into this. Maybe others can. All I did was prepending sandbox
to the apiURL and the apiHost as well.
Regards
Hey @sw-Oldeu, to successfully call for RDT you need to provide exactly same data like defined in the schema.
There are some exceptions to this - looks like it depends on the API, but in the case of sandbox tokens API:
GET
/orders/v0/orders/{orderId}/address
or /orders/v0/orders/943-12-123434/address
Everything else will result in Could not match input arguments
exception.
I've tested this request:
{
"restrictedResources": [
{
"method": "GET",
"path": "/orders/v0/orders/943-12-123434/address",
"dataElements": [
"buyerInfo",
"shippingAddress"
]
}
]
}
And it works fine(although looks like returned token can't be used in sandboxed orders API - it have to be a regular access token).
I've also checked sandboxed orders & inbound APIs. As long as we stick with request parameters that Amazon expects, we're good - what needs to be send is defined in each API's schema in x-amzn-api-sandbox
object.
Static sandbox is veeery limited here.
When it comes to dynamic sandbox - it would be awesome to have it, but looks like only couple of vendor APIs endpoints supports this. The rest is using static sandbox, but I think there are no difference between them - both are using same URLs and same SDKs, so no additional code is needed here.
I've testes those requests: Request uri for fetching orders:
https://sandbox.sellingpartnerapi-na.amazon.com/orders/v0/orders?CreatedAfter=TEST_CASE_200&MarketplaceIds=ATVPDKIKX0DER
Request parameters for creating inbound shipment:
{
"InboundShipmentHeader": {
"ShipmentName": "43545345",
"ShipFromAddress": {
"Name": "35435345",
"AddressLine1": "123 any st",
"DistrictOrCounty": "Washtenaw",
"City": "Ann Arbor",
"StateOrProvinceCode": "Test",
"CountryCode": "US",
"PostalCode": "48103"
},
"DestinationFulfillmentCenterId": "AEB2",
"AreCasesRequired": true,
"ShipmentStatus": "WORKING",
"LabelPrepPreference": "SELLER_LABEL",
"IntendedBoxContentsSource": "NONE"
},
"InboundShipmentItems": [
{
"ShipmentId": "345453",
"SellerSKU": "34534545",
"FulfillmentNetworkSKU": "435435435",
"QuantityShipped": 0,
"QuantityReceived": 0,
"QuantityInCase": 0,
"ReleaseDate": "2020-04-23",
"PrepDetailsList": [
{
"PrepInstruction": "Polybagging",
"PrepOwner": "AMAZON"
}
]
}
],
"MarketplaceId": "MarketplaceId"
}
Implemented by #212 and #213
Hi, we have a problem with integrating the sandbox API. We're using Guzzle as Client, using middlewares I managed to call the sandbox endpoints manually, but got
The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.\n\nThe Canonical String for this request should have been...
as an response, so I figured out there was some kind of problem with singing the request. I think it is a pretty much necessary feature, so I wondered whether you will implement it in your sdk , or is there some kind of battle-tested workaround of this problem.Thanks, Patrick