45 Commits

Author SHA1 Message Date
59d4612a0a Documentation update for development part 2026-01-13 18:42:12 +09:00
aeb84dcb60 Switch to PHPunit 12 and adjust code accordingly 2026-01-13 18:26:16 +09:00
b0d8c55536 Add phive tools folder, Switch to tabs for indent and add phpcs.xml 2026-01-13 11:06:11 +09:00
4adbf4e30a Run CI only on pull requests 2025-03-10 19:53:43 +09:00
09df49009a Fix .gitignore 2025-02-28 12:57:54 +09:00
33a48f47de Update unit tests to allow skip if AWS API flood
on Github a lot of API tests will fail with "T001" flooding, so we just skip them
2025-01-21 11:11:56 +09:00
cf5ece0b5f github flow yml had tabs instead of spaces 2025-01-21 11:07:10 +09:00
db8df612b4 Remove phpunit from github actions 2025-01-21 11:05:58 +09:00
75f4d0b10a Add phpstan to compoer dev install 2025-01-21 11:02:55 +09:00
7fed1c2a85 Readme update 2025-01-21 10:55:11 +09:00
28a9e390cc Code clean up with declare strict and php stan level 9 checks
add "declare(strict_types=1);" to all pages

Add a json handler class to handle that json_decode always returns array<mixed> and throws error otherwise
On failure these will throw the normal JSON encoded error was FAILURE with code "J-" and the number is the json error
If 0 then the return was null or some other problem that did not return an array

amount is float and not string and checks are done that the returned value is a float

various updates for phpdoc array delcarations
- curl header is array<int, string>
- the log array is proper declared as array with string key and a list of mixed arrays
2025-01-21 10:48:41 +09:00
a565d2899b Comment typo fix 2024-08-21 11:22:33 +09:00
58b126ab83 Rename the github action job 2024-05-22 18:42:48 +09:00
e9a6332ad0 Add skip on error for funds test 2024-05-22 18:26:46 +09:00
a79fd519ed Gitbub action cache 2024-05-22 18:02:01 +09:00
04aa9fa019 Github actions add 2024-05-22 17:27:20 +09:00
d86ad8c051 Switch to PSR12 with spaces instead of tabs 2023-01-19 12:51:12 +09:00
6f5cd028b9 Minor fix for return type alignment
make sure string returned is set to string in config getter
2023-01-06 10:36:24 +09:00
3634c5bdfa Merge pull request #2 from kapierstau/fix/eu-region-for-eu-endpoint 2022-08-18 10:22:22 +09:00
kapierstau.io
37b4cbce40 Update AWS.php
Fixed region for EU endpoint
2022-08-17 13:54:10 +02:00
54a4d9e4c3 Remove composer.lock file 2022-06-14 09:16:14 +09:00
bd2a328956 Merge split git repositories into one agin 2022-06-14 09:15:37 +09:00
bcbf97b406 phpUnit tests updates and fixes
All gift card id checks are against string and not numeric.

Add missing checks for card status, card amount value, card amount
currency in any buy card checks.

Add epiration date regex check for buy card

Add request cancled card check.
2022-06-14 06:56:19 +09:00
422fc66137 phpUnit tests updates and fixes
All gift card id checks are against string and not numeric.

Add missing checks for card status, card amount value, card amount
currency in any buy card checks.

Add epiration date regex check for buy card

Add request cancled card check.
2022-06-14 06:55:38 +09:00
462d00cb42 Readme file update 2022-06-13 20:00:04 +09:00
308794488d Update Readme file with various fixes 2022-06-13 19:59:07 +09:00
96b24f9424 Add phpUnit testing and update test file.
Test file does not run any tests automatically but they need to be
triggered with the _GET parameters.

A full phpunit test has been added for the full functionality
2022-06-13 19:53:00 +09:00
d69781a709 Move decodeExceptionMessage to Exceptions class
The method "decodeExceptionMessage" previous located in the
AmazonIncentives\AmazonIncentives main class has been moved to the
AmazonIncentives\Exceptions\AmazonErrors where it logically belongs.

A deprecation phpdoc message has been added for the current version.

Update error messages in curl error part and remove double spaces.
2022-06-13 19:52:34 +09:00
2bcd42f4a5 Add phpUnit testing for Amazon Incentives class 2022-06-13 18:09:52 +09:00
05b33ac157 Move decode error method, minor text update for error strings
the method "decodeExceptionMessage" has moved from
AmazonIncentives\AmazonIncentives to
AmazonIncentives\Exceptions\AmazonErrors

The old method is currently tagged deprecated internal via @deprecated
PHPdoc entry
2022-06-13 18:06:16 +09:00
4f072d2226 Merge branch 'development' Wrapper method additions 2022-06-10 14:51:22 +09:00
774e553d90 Add new class create methods for proper phpunit testing
Instead of direct calling Client class and AWS class add a wrapper
method to call those so we can quicker replace under elements and
also do proper testing

Turn on mock tests on AWS side in normal test file
2022-06-10 14:50:32 +09:00
fd5477269b Turn on mock tests on AWS side in normal test file 2022-06-10 14:49:32 +09:00
afb11a6797 Add new class create methods for proper phpunit testing
instead of direct calling Client class and AWS class add a wrapper
method to call those so we can quicker replace under elements and also
do proper testing
2022-06-10 14:40:40 +09:00
d6382960bf Merge branch 'development' with phpDoc changes 2022-06-10 14:38:15 +09:00
b26cc82055 Add PHPdoc to all methods, minor code reorder
Move some methods around to have them together by call order logic.

Also update current test methods with composer based .env reader.
2022-06-10 14:37:07 +09:00
6f177986f9 Update PHPdoc in AmazonIncentive and Config class
Missing documentation for methods/class constructors
2022-06-10 14:30:20 +09:00
e1b5dc1bab Add PHPdoc to all methods, minor code reorder
Move some methods around to have them together by call order logic
2022-06-09 16:02:25 +09:00
7cb5e00a5f Add base phpUnit file 2022-06-09 09:26:42 +09:00
a51f4df53e Update core git config files, vendor installs for unit testing
Update .gitattributes with more files to ignore
Add .gitignore for php unit test cache
Install dotenv file parser for unit testing
Update current test pages with dotenv file parser (replaces function
call)

Documentation PHPdoc update for checkMe method call
2022-06-09 09:25:08 +09:00
42087a64fa Add .gitignore file for not needed files 2022-06-08 16:45:43 +09:00
ab95e1c21b Remove not needed files and folders for composer publish 2022-06-08 15:21:58 +09:00
1e836e9a2b Update to static checker file, add phpunit xml, update composer json 2022-06-08 15:04:59 +09:00
b1079e1d24 Install composer phpunit v9 for development testing 2022-06-08 06:34:47 +09:00
5b99da616b Just rename the readEnvFile function for testing
Until I find out how I can exclude folders from beeing deployed
Update composer ingore list
Add .gitattributes for ignore list
2022-03-18 09:24:27 +09:00
37 changed files with 2057 additions and 1251 deletions

6
.gitattributes vendored Normal file
View File

@@ -0,0 +1,6 @@
test/ export-ignore
phpstan.neon export-ignore
phpunit.xml export-ignore
psalm.xml export-ignore
.phan/ export-ignore
.* export-ignore

55
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,55 @@
name: CI
run-name: ${{ github.actor}} runs CI
on:
pull_request:
branches:
- main
- master
- staging
- development
jobs:
ci-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: php-actions/composer@v6
env:
COMPOSER_ROOT_VERSION: dev-master
- name: "Restore result cache"
uses: actions/cache/restore@v4
with:
path: ./tmp
key: "result-cache-v1-${{ matrix.php-version }}-${{ github.run_id }}"
restore-keys: |
result-cache-v1-${{ matrix.php-version }}-
- name: PHPStan Static Analysis
uses: php-actions/phpstan@v3
with:
path: src/
- name: "Save result cache"
uses: actions/cache/save@v4
if: always()
with:
path: ./tmp
key: "result-cache-v1-${{ matrix.php-version }}-${{ github.run_id }}"
# We need to use phpunit from the self install to get the class paths
# Skip auto tests here, there are too many problems with flooding of the AWS API from github
- name: PHPunit Tests Prepare
env:
AWS_GIFT_CARD_ENDPOINT: "${{ secrets.AWS_GIFT_CARD_ENDPOINT }}"
AWS_GIFT_CARD_KEY: "${{ secrets.AWS_GIFT_CARD_KEY }}"
AWS_GIFT_CARD_SECRET: "${{ secrets.AWS_GIFT_CARD_SECRET }}"
AWS_GIFT_CARD_PARTNER_ID: "${{ secrets.AWS_GIFT_CARD_PARTNER_ID }}"
AWS_GIFT_CARD_CURRENCY: "${{ secrets.AWS_GIFT_CARD_CURRENCY }}"
run: |
echo "AWS_GIFT_CARD_ENDPOINT=${AWS_GIFT_CARD_ENDPOINT}" > test/.env;
echo "AWS_GIFT_CARD_KEY=${AWS_GIFT_CARD_KEY}" >> test/.env;
echo "AWS_GIFT_CARD_SECRET=${AWS_GIFT_CARD_SECRET}" >> test/.env;
echo "AWS_GIFT_CARD_PARTNER_ID=${AWS_GIFT_CARD_PARTNER_ID}" >> test/.env;
echo "AWS_GIFT_CARD_CURRENCY=${AWS_GIFT_CARD_CURRENCY}" >> test/.env;
echo "AWS_DEBUG=1" >> test/.env;
- name: PHPunit Tests
run: |
vendor/bin/phpunit

7
.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
vendor
.phpunit.result.cache
composer.lock
**/.env
**/.target
.phpunit.cache/
tools/

View File

@@ -26,6 +26,10 @@
// use Phan\Config;
return [
// turn color on (-C)
"color_issue_messages_if_supported" => true,
// set minimum version
"minimum_target_php_version" => "7.4",
// If true, missing properties will be created when
// they are first seen. If false, we'll report an
// error message.
@@ -65,7 +69,7 @@ return [
'directory_list' => [
// Change this to include the folders you wish to analyze
// (and the folders of their dependencies)
'.'
'src/'
// 'www',
// To speed up analysis, we recommend going back later and
// limiting this to only the vendor/ subdirectories your
@@ -79,6 +83,7 @@ return [
// to parse, but not analyze
"exclude_analysis_directory_list" => [
'vendor',
'test',
],
'exclude_file_list' => [
],

8
.phive/phars.xml Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<phive xmlns="https://phar.io/phive">
<phar name="phpcs" version="^4.0.1" installed="4.0.1" location="./tools/phpcs" copy="false"/>
<phar name="phpcbf" version="^4.0.1" installed="4.0.1" location="./tools/phpcbf" copy="false"/>
<phar name="phan" version="^5.5.2" installed="5.5.2" location="./tools/phan" copy="false"/>
<phar name="phpstan" version="^2.1.33" installed="2.1.33" location="./tools/phpstan" copy="false"/>
<phar name="phpunit" version="^12.5.4" installed="12.5.4" location="./tools/phpunit" copy="false"/>
</phive>

View File

@@ -1,9 +1,11 @@
# Amazon Incentives - Gift Codes on Demand stand alone class
This is a abastract from (https://github.com/kamerk22/AmazonGiftCode) to be not dependend on Laravel base code.
This is a abastract from [https://github.com/kamerk22/AmazonGiftCod](https://github.com/kamerk22/AmazonGiftCode) to be not dependend on Laravel base code.
Amazon Gift Codes On Demand (AGCOD). Integration for Amazon Incentive API.
[General Amazon Incentives Documentation](https://developer.amazon.com/docs/incentives-api/digital-gift-cards.html)
## How to install
`composer require gullevek/amazon-incentives`
@@ -82,18 +84,21 @@ The error code is the curl handler error code.
The error message is json encoded array with the layout
Use
```php
$exception_array = gullevek\AmazonIncentives\AmazonIncentives::decodeExceptionMessage($exception_message);
```
to extract the below array from the thrown exception
```php
[
'status' => 'AWS Status FAILURE or RESEND',
'code' => 'AWS Error Code Fnnn',
'type' => 'AWS Error info',
'message' => 'AWS long error message',
'log_id' => 'If logging is on the current log id',
'log' => 'The complete log collected over all calls',
'status' => 'AWS Status FAILURE or RESEND',
'code' => 'AWS Error Code Fnnn',
'type' => 'AWS Error info',
'message' => 'AWS long error message',
'log_id' => 'If logging is on the current log id',
'log' => 'The complete log collected over all calls',
]
```
@@ -106,7 +111,7 @@ to extract the below array from the thrown exception
if code is T001 then this is a request flood error:
In this case the request has to be resend after a certain waiting period.
### E9999
### E999
if code is E999 some other critical error has happened
@@ -117,10 +122,16 @@ if code is E001 if the return create/cancel/check calls is not an array
### C001
fif code is C001 curl failed to init
### C002
if code is C002 a curl error has happened
### J-number
if a JSON error was encountered during some encoding this error will be found.
The number is the json error code.
### empty error code
any other NON amazon error will have only 'message' set if run through decode
@@ -132,6 +143,7 @@ If AWS_DEBUG is set to 1 and internal array will be written with debug info.
The gulleek\AmazonIncentives\Debug\AmazonDebug class handles all this.
In the gulleek\AmazonIncentives\AmazonIncentives main class the debugger gets set
* setDebug that turns debugger on/off and if on sets unique id (getId to check)
New entries can be written with
@@ -140,3 +152,40 @@ New entries can be written with
On sucessful run the log data is accessable with `$aws->getLog()`
On exception the log data is in the error message json (see exceptions)
## Development
Run `composer install` or `phive install` to setup the tools
phpcs config is stored in `phpcs.xml` and should be picked up by the standard tools
* we use tabs instead of spaces for indents, the rest is standard PSR1+PSR12
### Direct tests
These direct run tests exist:
* `tests/aws_read_env_tests.php`: Equal to running `tests/aws_gift_card_tests.php?info=true`, will output env test data
* `test/aws_gift_card_tests.php`: The following parameters exists
* info: set to print out info
* fund: set to run funds get tests
* gift: run all the gift card get/set/remove tests
* mocks: run the error checks
* debug: print out debug information
* debug_mock: print out all the debug logs too
### Phan
`vendor/bin/phan --analyze-twice` or `tools/phan --analyze-twice`
### PHPstan
`vendor/bin/phpstan` or `tools/phpstan`
> [!notice] Level 9 is set as we have various mixed values that cannot be easily changed into a direct value type
### PHPUnit
Unit tests have to be run from base folder with
`vendor/bin/phpunit` or `tools/phpunit`

View File

@@ -22,6 +22,13 @@
"php": ">=7.4.0"
},
"archive": {
"exclude": ["/test/", "/phpstan.neon", "/psalm.xml", "/.phan/", "/.vscode/"]
"exclude": ["/test/", "/test/*", "/phpstan.neon", "/psalm.xml", "/.phan/", "/.vscode/", "/phpunit.xml"]
},
"require-dev": {
"gullevek/dotenv": "dev-master",
"dg/bypass-finals": "dev-master",
"phpstan/phpstan": "2.1.x-dev",
"phpstan/phpstan-deprecation-rules": "2.0.x-dev",
"phpunit/phpunit": "^12"
}
}

18
phpcs.xml Normal file
View File

@@ -0,0 +1,18 @@
<?xml version="1.0"?>
<ruleset name="MyStandard">
<description>PSR12 override rules (strict, standard). Switch spaces indent to tab.</description>
<arg name="tab-width" value="4"/>
<rule ref="PSR1"/>
<rule ref="PSR12">
<!-- turn off white space check for tab -->
<exclude name="Generic.WhiteSpace.DisallowTabIndent"/>
</rule>
<!-- no space indent, must be tab, 4 is tab iwdth -->
<rule ref="Generic.WhiteSpace.DisallowSpaceIndent"/>
<rule ref="Generic.WhiteSpace.ScopeIndent">
<properties>
<property name="indent" value="4"/>
<property name="tabIndent" value="true"/>
</properties>
</rule>
</ruleset>

View File

@@ -1,15 +1,11 @@
# PHP Stan Config
parameters:
tmpDir: /tmp/phpstan-codeblocks-amazon-incentives
level: 8
tmpDir: %currentWorkingDirectory%/tmp/phpstan-codeblocks-amazon-incentives
level: 9
paths:
- %currentWorkingDirectory%
- %currentWorkingDirectory%/src
excludePaths:
# ignore composer
- vendor
# ignore errores with
ignoreErrors:
-
message: '#Strict comparison using === between false and true will always evaluate to false.#'
path: %currentWorkingDirectory%/test/aws_gift_card_tests.php
- test

12
phpunit.xml Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0"?>
<phpunit colors="true" cacheDirectory=".phpunit.cache" bootstrap="vendor/autoload.php" >
<!-- Below removes final from classes for mock tests -->
<extensions>
<bootstrap class="DG\BypassFinals\PHPUnitExtension"/>
</extensions>
<testsuites>
<testsuite name="unit">
<directory>test/phpUnit/</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@@ -1,15 +1,16 @@
<?xml version="1.0"?>
<psalm
errorLevel="3"
errorLevel="8"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
<projectFiles>
<directory name="src" />
<directory name="." />
<ignoreFiles>
<directory name="vendor" />
<directory name="test" />
</ignoreFiles>
</projectFiles>
</psalm>

View File

@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace gullevek\AmazonIncentives\AWS;
use gullevek\AmazonIncentives\Client\Client;
@@ -9,29 +11,44 @@ use gullevek\AmazonIncentives\Debug\AmazonDebug;
use gullevek\AmazonIncentives\Response\CancelResponse;
use gullevek\AmazonIncentives\Response\CreateBalanceResponse;
use gullevek\AmazonIncentives\Response\CreateResponse;
use gullevek\AmazonIncentives\Handle\Json;
class AWS
{
/** @var string What AWS Service to use: Gift Card on Demand (GCOD) */
public const SERVICE_NAME = 'AGCODService';
/** @var string */
public const ACCEPT_HEADER = 'accept';
/** @var string content-type */
public const CONTENT_HEADER = 'content-type';
/** @var string */
public const HOST_HEADER = 'host';
/** @var string */
public const X_AMZ_DATE_HEADER = 'x-amz-date';
/** @var string */
public const X_AMZ_TARGET_HEADER = 'x-amz-target';
/** @var string */
public const AUTHORIZATION_HEADER = 'Authorization';
/** @var string type of encryption type */
public const AWS_SHA256_ALGORITHM = 'AWS4-HMAC-SHA256';
/** @var string key type to use */
public const KEY_QUALIFIER = 'AWS4';
/** @var string */
public const TERMINATION_STRING = 'aws4_request';
/** @var string Service to use: Create Gift Card */
public const CREATE_GIFT_CARD_SERVICE = 'CreateGiftCard';
/** @var string Service to use: Cancle Gift Card */
public const CANCEL_GIFT_CARD_SERVICE = 'CancelGiftCard';
/** @var string Service to use: Get Available Funds */
public const GET_AVAILABLE_FUNDS_SERVICE = 'GetAvailableFunds';
/**
* @var Config
*/
/** @var Config Configuration class with all settings */
private $config;
/**
* Initialize the main AWS class. This class prepares and sends all the actions
* and returns reponses as defined in in the CreateResponse class
*
* @param Config $config
*/
public function __construct(Config $config)
@@ -41,9 +58,13 @@ class AWS
}
/**
* @param float $amount
* @param string|null $creation_id
* @return CreateResponse
* Bug a gift card
*
* @param float $amount Amount to buy a gifr card in set currencty
* @param string|null $creation_id Override creation id, if not set will
* be created automatically. If not valid error
* will be thrown
* @return CreateResponse Object with AWS response data
*
* @throws AmazonErrors
*/
@@ -54,19 +75,21 @@ class AWS
$canonical_request = $this->getCanonicalRequest($service_operation, $payload);
$date_time_string = $this->getTimestamp();
AmazonDebug::writeLog(['call' => __METHOD__]);
$result = json_decode($this->makeRequest(
$result = Json::jsonDecode($this->makeRequest(
$payload,
$canonical_request,
$service_operation,
$date_time_string
), true);
));
return new CreateResponse($result);
}
/**
* @param string $creation_request_id
* @param string $gift_card_id
* @return CancelResponse
* Cancle an ordered gift card, only possible within the the time limit
*
* @param string $creation_request_id Previously created creation request id
* @param string $gift_card_id Previously created gift card id
* @return CancelResponse Object with AWS response data
*
* @throws AmazonErrors
*/
@@ -77,17 +100,19 @@ class AWS
$canonical_request = $this->getCanonicalRequest($service_operation, $payload);
$date_time_string = $this->getTimestamp();
AmazonDebug::writeLog(['call' => __METHOD__]);
$result = json_decode($this->makeRequest(
$result = Json::jsonDecode($this->makeRequest(
$payload,
$canonical_request,
$service_operation,
$date_time_string
), true);
));
return new CancelResponse($result);
}
/**
* @return CreateBalanceResponse
* Get current account funds
*
* @return CreateBalanceResponse Object with AWS response data
*
* @throws AmazonErrors
*/
@@ -98,21 +123,37 @@ class AWS
$canonical_request = $this->getCanonicalRequest($service_operation, $payload);
$date_time_string = $this->getTimestamp();
AmazonDebug::writeLog(['call' => __METHOD__]);
$result = json_decode($this->makeRequest(
$result = Json::jsonDecode($this->makeRequest(
$payload,
$canonical_request,
$service_operation,
$date_time_string
), true);
));
return new CreateBalanceResponse($result);
}
/**
* @param string $payload
* @param string $canonical_request
* @param string $service_operation
* @param string $date_time_string
* @return string
* return a new curl connection client class
*
* @return Client Curl connection Client
*/
public function newClient(): Client
{
return new Client();
}
/**
* General request method for all actions
* Calls the Client class that actually runs the json request
* For service_operation valid data see AWS GCOD documentation
*
* @param string $payload The data needed for this request
* @param string $canonical_request Header data to send for this request
* @param string $service_operation Service operation. CREATE_GIFT_CARD_SERVICE,
* CANCEL_GIFT_CARD_SERVICE or
* GET_AVAILABLE_FUNDS_SERVICE constant values
* @param string $date_time_string Ymd\THis\Z encoded timestamp, getTimestamp()
* @return string Request result as string, json data
*/
public function makeRequest(
string $payload,
@@ -156,16 +197,25 @@ class AWS
]]);
$url = 'https://' . $endpoint . '/' . $service_operation;
$headers = $this->buildHeaders($payload, $authorization_value, $date_time_string, $service_target);
return (new Client())->request($url, $headers, $payload);
$headers = $this->buildHeaders(
$payload,
$authorization_value,
$date_time_string,
$service_target
);
return ($this->newClient())->request($url, $headers, $payload);
}
/**
* @param string $payload
* @param string $authorization_value
* @param string $date_time_string
* @param string $service_target
* @return array<mixed>
* Build the headers used in the makeRequest method.
* These are the HTML headers used with curl
*
* @param string $payload Paylout to create this header for
* @param string $authorization_value Auth string
* @param string $date_time_string Ymd\THis\Z encoded timestamp, getTimestamp()
* @param string $service_target Target service in the agcod string:
* Value like com.amazonaws.agcod.<sn>.<so>
* @return array<int,string> Header data as array for curl request
*/
public function buildHeaders(
string $payload,
@@ -188,8 +238,33 @@ class AWS
}
/**
* @param string $string_to_sign
* @return string
* The request string build with the actauly request data created by
* getCanonicalRequest(). This string is used in the auth signature call
*
* @param string $canonical_request_hash sha256 hash to build from
* @return string String to send to buildAuthSignature()
*/
public function buildStringToSign($canonical_request_hash): string
{
$AWS_SHA256_ALGORITHM = self::AWS_SHA256_ALGORITHM;
$TERMINATION_STRING = self::TERMINATION_STRING;
$SERVICE_NAME = self::SERVICE_NAME;
$region_name = $this->getRegion();
$date_time_string = $this->getTimestamp();
$date_string = $this->getDateString();
$string_to_sign = "$AWS_SHA256_ALGORITHM\n"
. "$date_time_string\n"
. "$date_string/$region_name/$SERVICE_NAME/$TERMINATION_STRING\n"
. "$canonical_request_hash";
return $string_to_sign;
}
/**
* Build the authentication signature used in the buildHeaders method
*
* @param string $string_to_sign Data to sign, buildStringToSign()
* @return string Authorized value as string
*/
public function buildAuthSignature(string $string_to_sign): string
{
@@ -224,28 +299,12 @@ class AWS
}
/**
* @param string $canonical_request_hash
* @return string
*/
public function buildStringToSign($canonical_request_hash): string
{
$AWS_SHA256_ALGORITHM = self::AWS_SHA256_ALGORITHM;
$TERMINATION_STRING = self::TERMINATION_STRING;
$SERVICE_NAME = self::SERVICE_NAME;
$region_name = $this->getRegion();
$date_time_string = $this->getTimestamp();
$date_string = $this->getDateString();
$string_to_sign = "$AWS_SHA256_ALGORITHM\n"
. "$date_time_string\n"
. "$date_string/$region_name/$SERVICE_NAME/$TERMINATION_STRING\n"
. "$canonical_request_hash";
return $string_to_sign;
}
/**
* @param bool $rawOutput
* @return string
* Build the derived key to build the final hmac signature string
*
* @param bool $rawOutput Set to true to create the hash based message
* authenticator string as normal text string or
* lowercase hexbits
* @return string Derived key (hmac type)
*/
public function buildDerivedKey(bool $rawOutput = true): string
{
@@ -271,15 +330,15 @@ class AWS
/**
* get the region based on endpoint
* list as of 2021/10/20
* WHERE URL REGION
* North America https://agcod-v2-gamma.amazon.com us-east-1
* https://agcod-v2.amazon.com
* WHERE URL REGION
* North America https://agcod-v2-gamma.amazon.com us-east-1
* https://agcod-v2.amazon.com
* (US, CA, MX)
* Europe and Asia https://agcod-v2-eu-gamma.amazon.com eu-west-1
* https://agcod-v2-eu.amazon.com
* Europe and Asia https://agcod-v2-eu-gamma.amazon.com eu-west-1
* https://agcod-v2-eu.amazon.com
* (IT, ES, DE, FR, UK, TR, UAE, KSA, PL, NL, SE)
* Far East https://agcod-v2-fe-gamma.amazon.com us-west-2
* https://agcod-v2-fe.amazon.com
* Far East https://agcod-v2-fe-gamma.amazon.com us-west-2
* https://agcod-v2-fe.amazon.com
* (JP, AU, SG)
*
* CURRENCY
@@ -293,7 +352,7 @@ class AWS
* MXN for MX
* GBP for UK
*
* @return string
* @return string Region string depending on given endpoint url
*/
public function getRegion(): string
{
@@ -308,7 +367,7 @@ class AWS
break;
case 'agcod-v2-eu.amazon.com':
case 'agcod-v2-eu-gamma.amazon.com':
$region_name = 'us-west-1';
$region_name = 'eu-west-1';
break;
case 'agcod-v2-fe.amazon.com':
case 'agcod-v2-fe-gamma.amazon.com':
@@ -320,9 +379,15 @@ class AWS
/**
* @param float $amount
* @param string|null $creation_id
* @return string
* The actual data to send as json encoded string for creating a gift card.
* The creation request id must be in the format:
* <partner_id>_<unique id 13 characters>
*
* @param float $amount Amount of currencty to create the gift card
* request for
* @param string|null $creation_id The creation id, if not set will be created here
* @return string JSON encoded array to be used as payload
* in get gift card call
*/
public function getGiftCardPayload(float $amount, ?string $creation_id = null): string
{
@@ -339,9 +404,12 @@ class AWS
}
/**
* @param string $creation_request_id
* @param string $gift_card_id
* @return string
* The actual data to send as json encoded string to cancel a created gift card
*
* @param string $creation_request_id Creation request id from previous get gift card
* @param string $gift_card_id Gift card id from previous get gift card
* @return string JSON encoded array to be used as payload
* in cancle gift card call
*/
public function getCancelGiftCardPayload(string $creation_request_id, string $gift_card_id): string
{
@@ -354,7 +422,10 @@ class AWS
}
/**
* @return string
* The actualy data to send as json encoded string for getting the current
* account funds
*
* @return string JSON encoded array to be used as payload in funds call
*/
public function getAvailableFundsPayload(): string
{
@@ -365,9 +436,34 @@ class AWS
}
/**
* @param string $service_operation
* @param string $payload
* @return string
* Heeders used in the getCanonicalRequest()
*
* @param string $service_operation Service operation code in the service string request
* Value is: com.amazonaws.agcod.AGCODService.<so>
* @return string Header string to be used
*/
public function buildCanonicalHeaders(string $service_operation): string
{
$ACCEPT_HEADER = self::ACCEPT_HEADER;
$HOST_HEADER = self::HOST_HEADER;
$X_AMZ_DATE_HEADER = self::X_AMZ_DATE_HEADER;
$X_AMZ_TARGET_HEADER = self::X_AMZ_TARGET_HEADER;
$date_time_string = $this->getTimestamp();
$endpoint = $this->config->getEndpoint();
$content_type = $this->getContentType();
return "$ACCEPT_HEADER:$content_type\n"
. "$HOST_HEADER:$endpoint\n"
. "$X_AMZ_DATE_HEADER:$date_time_string\n"
. "$X_AMZ_TARGET_HEADER:com.amazonaws.agcod.AGCODService.$service_operation";
}
/**
* Headers used in the get/cancel/funds requests
*
* @param string $service_operation Service operation code to be used in header request
* and main request call
* @param string $payload Payload from get/cancle Code or funds call
* @return string Full POST service request code
*/
public function getCanonicalRequest(string $service_operation, string $payload): string
{
@@ -386,8 +482,10 @@ class AWS
}
/**
* @param string $data
* @return string
* Build sha256 hash from given data
*
* @param string $data Data to be hashed with sha256
* @return string sha256 hash
*/
public function buildHash(string $data): string
{
@@ -395,18 +493,13 @@ class AWS
}
/**
* @return string
*/
public function getTimestamp()
{
return gmdate('Ymd\THis\Z');
}
/**
* @param string $data
* @param string $key
* @param bool $raw
* @return string
* Create a sha256 based Hash-Based Message Authentication Code
* with the given key and data
*
* @param string $data Data to be hashed with key below
* @param string $key Key to be used for creating the hash
* @param bool $raw Returning data as ascii string or hexibits
* @return string Hash-Based Message Authentication Code
*/
public function hmac(string $data, string $key, bool $raw = true): string
{
@@ -414,7 +507,21 @@ class AWS
}
/**
* @return string
* Build timestamp in the format used by AWS services
* eg 20211009\T102030\Z
*
* @return string date string based on current time. Ymd\THis\Z
*/
public function getTimestamp()
{
return gmdate('Ymd\THis\Z');
}
/**
* Get only the date string from the getTimestamp
* eg 20211009
*
* @return string Date string YYYYmmdd extracted from getTimestamp()
*/
public function getDateString()
{
@@ -422,31 +529,14 @@ class AWS
}
/**
* @return string
* Fixed content type for submission, is json
*
* @return string 'application/json' string
*/
public function getContentType(): string
{
return 'application/json';
}
/**
* @param string $service_operation
* @return string
*/
public function buildCanonicalHeaders(string $service_operation): string
{
$ACCEPT_HEADER = self::ACCEPT_HEADER;
$HOST_HEADER = self::HOST_HEADER;
$X_AMZ_DATE_HEADER = self::X_AMZ_DATE_HEADER;
$X_AMZ_TARGET_HEADER = self::X_AMZ_TARGET_HEADER;
$date_time_string = $this->getTimestamp();
$endpoint = $this->config->getEndpoint();
$content_type = $this->getContentType();
return "$ACCEPT_HEADER:$content_type\n"
. "$HOST_HEADER:$endpoint\n"
. "$X_AMZ_DATE_HEADER:$date_time_string\n"
. "$X_AMZ_TARGET_HEADER:com.amazonaws.agcod.AGCODService.$service_operation";
}
}
// __END__

View File

@@ -22,20 +22,20 @@ final class AmazonIncentives
/**
* AmazonGiftCode constructor.
*
* @param string|null $key
* @param string|null $secret
* @param string|null $partner
* @param string|null $endpoint
* @param string|null $currency
* @param bool|null $debug
* @param string|null $key Account key
* @param string|null $secret Secret key
* @param string|null $partner Partner ID
* @param string|null $endpoint Endpoint URL including https://
* @param string|null $currency Currency type. Eg USD, JPY, etc
* @param bool|null $debug Debug flag
*/
public function __construct(
string $key = null,
string $secret = null,
string $partner = null,
string $endpoint = null,
string $currency = null,
bool $debug = null
?string $key = null,
?string $secret = null,
?string $partner = null,
?string $endpoint = null,
?string $currency = null,
?bool $debug = null
) {
// load AWS settings
// fail here if settings missing
@@ -53,60 +53,83 @@ final class AmazonIncentives
// *********************************************************************
/**
* @param float $value
* @param string|null $creation_request_id AWS creationRequestId
* @return Response\CreateResponse
* Buy a gift card
*
* @param float $value Amount to purchase a gift card
* in currency value
* @param string|null $creation_request_id Override automatically created request id
* If not set will create a new one, or
* return data for created one
* @return Response\CreateResponse Returns new created response object or
* previous created if creation_request_id was used
*
* @throws AmazonErrors
*/
public function buyGiftCard(float $value, string $creation_request_id = null): Response\CreateResponse
public function buyGiftCard(float $value, ?string $creation_request_id = null): Response\CreateResponse
{
return (new AWS($this->config))->getCode($value, $creation_request_id);
return ($this->newAWS())->getCode($value, $creation_request_id);
}
/**
* @param string $creation_request_id AWS creationRequestId
* @param string $gift_card_id AWS gcId
* @return Response\CancelResponse
* Cancel a previous created gift card, if within the time frame
*
* @param string $creation_request_id Previous created request id from buyGiftCard
* @param string $gift_card_id Previous gift card id from buyGiftCard (gcId)
* @return Response\CancelResponse Returns the cancled request object
*
* @throws AmazonErrors
*/
public function cancelGiftCard(string $creation_request_id, string $gift_card_id): Response\CancelResponse
{
return (new AWS($this->config))->cancelCode($creation_request_id, $gift_card_id);
return ($this->newAWS())->cancelCode($creation_request_id, $gift_card_id);
}
/**
* @return Response\CreateBalanceResponse
* Gets the current funds in this account
*
* @return Response\CreateBalanceResponse Returns the account funds object
*
* @throws AmazonErrors
*/
public function getAvailableFunds(): Response\CreateBalanceResponse
{
return (new AWS($this->config))->getBalance();
return ($this->newAWS())->getBalance();
}
/**
* AmazonIncentives make own client.
* AmazonIncentives creates own client and returns it as static object
*
* @param string|null $key
* @param string|null $secret
* @param string|null $partner
* @param string|null $endpoint
* @param string|null $currency
* @param bool|null $debug
* @return AmazonIncentives
* @param string|null $key Account key
* @param string|null $secret Secret key
* @param string|null $partner Partner ID
* @param string|null $endpoint Endpoint URL including https://
* @param string|null $currency Currency type. Eg USD, JPY, etc
* @param bool|null $debug Debug flag
* @return AmazonIncentives self class
*/
public static function make(
string $key = null,
string $secret = null,
string $partner = null,
string $endpoint = null,
string $currency = null,
bool $debug = null
?string $key = null,
?string $secret = null,
?string $partner = null,
?string $endpoint = null,
?string $currency = null,
?bool $debug = null
): AmazonIncentives {
return new static($key, $secret, $partner, $endpoint, $currency, $debug);
}
/**
* wrapper to create new AWS class.
* used in all buy/cancel/get calss
*
* @return AWS Main AWS worker class
*/
public function newAWS(): AWS
{
return new AWS($this->config);
}
/**
* Decodes the Exception message body
* Returns an array with code (Amazon error codes), type (Amazon error info)
@@ -114,22 +137,12 @@ final class AmazonIncentives
*
* @param string $message Exception message json string
* @return array<mixed> Decoded with code, type, message fields
*
* @deprecated use \gullevek\AmazonIncentives\Exceptions\AmazonErrors::decodeExceptionMessage()
*/
public static function decodeExceptionMessage(string $message): array
{
$message_ar = json_decode($message, true);
// if we have an error, build empty block and only fill message
if (json_last_error()) {
$message_ar = [
'status' => '',
'code' => '',
'type' => '',
'message' => $message,
'log_id' => '',
'log' => []
];
}
return $message_ar;
return AmazonErrors::decodeExceptionMessage($message);
}
// *********************************************************************
@@ -137,6 +150,10 @@ final class AmazonIncentives
// *********************************************************************
/**
* Prints out ENV, CONFIG and KEY data
* This is for debug only, this will print out secrets.
* Use with care
*
* @return array<mixed>
*/
public function checkMe(): array

View File

@@ -1,23 +1,27 @@
<?php
declare(strict_types=1);
namespace gullevek\AmazonIncentives\Client;
use gullevek\AmazonIncentives\Exceptions\AmazonErrors;
use gullevek\AmazonIncentives\Debug\AmazonDebug;
use gullevek\AmazonIncentives\Handle\Json;
class Client implements ClientInterface
{
// instead of JsonResponse::HTTP_OK
/** @var int instead of JsonResponse::HTTP_OK */
private const HTTP_OK = 200;
/**
* Makes an request to the target url via curl
* Returns result as string (json)
*
* @param string $url The URL being requested, including domain and protocol
* @param array<mixed> $headers Headers to be used in the request
* @param array<mixed>|string $params Can be nested for arrays and hashes
*
*
* @return string
* @param string $url The URL being requested,
* including domain and protocol
* @param array<int,string> $headers Headers to be used in the request
* @param array<mixed>|string $params Can be nested for arrays and hashes
* @return string Result as json string
*/
public function request(string $url, array $headers, $params): string
{
@@ -49,7 +53,12 @@ class Client implements ClientInterface
$err = curl_errno($handle);
AmazonDebug::writeLog(['CURL_REQUEST_RESULT' => $result]);
// extract all the error codes from Amazon
$result_ar = json_decode((string)$result, true);
// note we do not care about result errors here, if json decode fails, just set to empty
try {
$result_ar = Json::jsonDecode((string)$result);
} catch (AmazonErrors $e) {
$result_ar = [];
}
// if message is 'Rate exceeded', set different error
if (($result_ar['message'] ?? '') == 'Rate exceeded') {
$error_status = 'RESEND';
@@ -76,11 +85,12 @@ class Client implements ClientInterface
}
/**
* Undocumented function
* handles any CURL errors and throws an error with the correct
* error message
*
* @param string $url
* @param int $errno
* @param string $message
* @param string $url The url that was originaly used
* @param int $errno Error number from curl handler
* @param string $message The error message string from curl
* @return void
*/
private function handleCurlError(string $url, int $errno, string $message): void
@@ -89,13 +99,13 @@ class Client implements ClientInterface
case CURLE_COULDNT_CONNECT:
case CURLE_COULDNT_RESOLVE_HOST:
case CURLE_OPERATION_TIMEOUTED:
$message = 'Could not connect to AWS (' . $url . '). Please check your '
$message = 'Could not connect to AWS (' . $url . '). Please check your '
. 'internet connection and try again. [' . $message . ']';
break;
case CURLE_SSL_PEER_CERTIFICATE:
$message = 'Could not verify AWS SSL certificate. Please make sure '
. 'that your network is not intercepting certificates. '
. '(Try going to ' . $url . 'in your browser.) '
$message = 'Could not verify AWS SSL certificate. Please make sure '
. 'that your network is not intercepting certificates. '
. '(Try going to ' . $url . 'in your browser.) '
. '[' . $message . ']';
break;
case 0:

View File

@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace gullevek\AmazonIncentives\Client;
interface ClientInterface

View File

@@ -1,40 +1,32 @@
<?php
declare(strict_types=1);
namespace gullevek\AmazonIncentives\Config;
class Config implements ConfigInterface
{
/**
* @var string
*/
/** @var string Endpoint URL without https:// */
private $endpoint = '';
/**
* @var string
*/
/** @var string Access Key */
private $access_key = '';
/**
* @var string
*/
/** @var string Secret Key */
private $secret_key = '';
/**
* @var string
*/
/** @var string Partner ID */
private $partner_id = '';
/**
* @var string
*/
/** @var string Currency type as USD, JPY, etc */
private $currency = '';
/**
* @var bool
*/
/** @var bool Debug flag on or off */
private $debug = false;
/**
* @param string|null $key
* @param string|null $secret
* @param string|null $partner
* @param string|null $endpoint
* @param string|null $currency
* @param string|null $key Access key
* @param string|null $secret Secret Key
* @param string|null $partner Partner ID
* @param string|null $endpoint Endpoing URL including https://
* @param string|null $currency Currency to use, see valid list on AWS documentation.
* valid names are like USD, JPY, etc
* @param bool|null $debug Debug flag
*/
public function __construct(
?string $key,
@@ -80,8 +72,8 @@ class Config implements ConfigInterface
* string key to search, returns entry from _ENV
* if not matchin key, returns empty
*
* @param string $key To search in _ENV array
* @return string|bool Returns either string or true/false (DEBUG flag)
* @param string $key To search in _ENV array
* @return string|bool Returns either string or true/false (DEBUG flag)
*/
private function parseEnv(string $key)
{
@@ -95,7 +87,8 @@ class Config implements ConfigInterface
case 'AWS_GIFT_CARD_PARTNER_ID':
case 'AWS_GIFT_CARD_ENDPOINT':
case 'AWS_GIFT_CARD_CURRENCY':
$return = $_ENV[$key] ?? '';
$return = !empty($_ENV[$key]) && is_string($_ENV[$key]) ?
$_ENV[$key] : '';
break;
default:
break;
@@ -104,7 +97,7 @@ class Config implements ConfigInterface
}
/**
* @return string
* @return string Returns current set endpoint, without https://
*/
public function getEndpoint(): string
{
@@ -112,8 +105,8 @@ class Config implements ConfigInterface
}
/**
* @param string $endpoint
* @return ConfigInterface
* @param string $endpoint Full endpoint url with https://
* @return ConfigInterface Class interface (self)
*/
public function setEndpoint(string $endpoint): ConfigInterface
{
@@ -124,7 +117,7 @@ class Config implements ConfigInterface
}
/**
* @return string
* @return string Current access key
*/
public function getAccessKey(): string
{
@@ -132,8 +125,8 @@ class Config implements ConfigInterface
}
/**
* @param string $key
* @return ConfigInterface
* @param string $key Access Key to set
* @return ConfigInterface Class interface (self)
*/
public function setAccessKey(string $key): ConfigInterface
{
@@ -143,7 +136,7 @@ class Config implements ConfigInterface
}
/**
* @return string
* @return string Current secret key
*/
public function getSecret(): string
{
@@ -151,8 +144,8 @@ class Config implements ConfigInterface
}
/**
* @param string $secret
* @return ConfigInterface
* @param string $secret Secret key to set
* @return ConfigInterface Class interface (self)
*/
public function setSecret(string $secret): ConfigInterface
{
@@ -162,7 +155,7 @@ class Config implements ConfigInterface
}
/**
* @return string
* @return string Current set currency
*/
public function getCurrency(): string
{
@@ -170,8 +163,8 @@ class Config implements ConfigInterface
}
/**
* @param string $currency
* @return ConfigInterface
* @param string $currency Currency to set (eg USD, JPY, etc)
* @return ConfigInterface Class interface (self)
*/
public function setCurrency(string $currency): ConfigInterface
{
@@ -182,7 +175,7 @@ class Config implements ConfigInterface
}
/**
* @return string
* @return string Current set partner id
*/
public function getPartner(): string
{
@@ -190,8 +183,8 @@ class Config implements ConfigInterface
}
/**
* @param string $partner
* @return ConfigInterface
* @param string $partner Partner id to set
* @return ConfigInterface Class interface (self)
*/
public function setPartner(string $partner): ConfigInterface
{
@@ -201,7 +194,7 @@ class Config implements ConfigInterface
}
/**
* @return bool
* @return bool Current set debug flag as bool
*/
public function getDebug(): bool
{
@@ -209,8 +202,8 @@ class Config implements ConfigInterface
}
/**
* @param bool $debug
* @return ConfigInterface
* @param bool $debug Set debug flag as bool
* @return ConfigInterface Class interface (self)
*/
public function setDebug(bool $debug): ConfigInterface
{

View File

@@ -1,72 +1,74 @@
<?php
declare(strict_types=1);
namespace gullevek\AmazonIncentives\Config;
interface ConfigInterface
{
/**
* @return string
* @return string Returns current set endpoint, without https://
*/
public function getEndpoint(): string;
/**
* @param string $endpoint
* @return ConfigInterface
* @param string $endpoint Full endpoint url with https://
* @return ConfigInterface Class interface (self)
*/
public function setEndpoint(string $endpoint): ConfigInterface;
/**
* @return string
* @return string Current access key
*/
public function getAccessKey(): string;
/**
* @param string $key
* @return ConfigInterface
* @param string $key Access Key to set
* @return ConfigInterface Class interface (self)
*/
public function setAccessKey(string $key): ConfigInterface;
/**
* @return string
* @return string Current secret key
*/
public function getSecret(): string;
/**
* @param string $secret
* @return ConfigInterface
* @param string $secret Secret key to set
* @return ConfigInterface Class interface (self)
*/
public function setSecret(string $secret): ConfigInterface;
/**
* @return string
* @return string Current set currency
*/
public function getCurrency(): string;
/**
* @param string $currency
* @return ConfigInterface
* @param string $currency Currency to set (eg USD, JPY, etc)
* @return ConfigInterface Class interface (self)
*/
public function setCurrency(string $currency): ConfigInterface;
/**
* @return string
* @return string Current set partner id
*/
public function getPartner(): string;
/**
* @param string $partner
* @return ConfigInterface
* @param string $partner Partner id to set
* @return ConfigInterface Class interface (self)
*/
public function setPartner(string $partner): ConfigInterface;
/**
* @return bool
* @return bool Current set debug flag as bool
*/
public function getDebug(): bool;
/**
* @param bool $debug
* @return ConfigInterface
* @param bool $debug Set debug flag as bool
* @return ConfigInterface Class interface (self)
*/
public function setDebug(bool $debug): ConfigInterface;
}

View File

@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
// simple write all into an array that we can poll in the return group
// to activate AmazonDebug::setDebug(true) must be called once
@@ -7,17 +9,11 @@ namespace gullevek\AmazonIncentives\Debug;
class AmazonDebug
{
/**
* @var array<mixed>
*/
/** @var array<string,array<array<mixed>>> Log data array log id -> array of log entries */
private static $log = [];
/**
* @var bool
*/
/** @var bool debug flag */
private static $debug = false;
/**
* @var string|null
*/
/** @var string|null Last set internal log array id */
private static $id = null;
/**

View File

@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace gullevek\AmazonIncentives\Exceptions;
use RuntimeException;
@@ -8,12 +10,15 @@ use gullevek\AmazonIncentives\Debug\AmazonDebug;
final class AmazonErrors extends RuntimeException
{
/**
* @param string $error_status agcodResponse->status from Amazon
* @param string $error_code errorCode from Amazon
* @param string $error_type errorType from Amazon
* @param string $message
* @param int $_error_code
* @return AmazonErrors
* Returns an Runtime exception including a json encoded string with all
* parameters including last log id and log
*
* @param string $error_status agcodResponse->status from Amazon
* @param string $error_code errorCode from Amazon
* @param string $error_type errorType from Amazon
* @param string $message Message string to ad
* @param int $_error_code Error code to set
* @return AmazonErrors Exception Class
*/
public static function getError(
string $error_status,
@@ -29,13 +34,38 @@ final class AmazonErrors extends RuntimeException
'code' => $error_code,
'type' => $error_type,
'message' => $message,
// atach log data if exists
// attach log data if exists
'log_id' => AmazonDebug::getId(),
'log' => AmazonDebug::getLog(),
])) ?: 'AmazonErrors: json encode problem: ' . $message,
$_error_code
);
}
/**
* Decodes the Exception message body
* Returns an array with code (Amazon error codes), type (Amazon error info)
* message (Amazon returned error message string)
*
* @param string $message Exception message json string
* @return array<mixed> Decoded with code, type, message fields
*/
public static function decodeExceptionMessage(string $message): array
{
$message_ar = json_decode($message, true);
// if we have an error, build empty block and only fill message
if (json_last_error() || $message_ar === null || !is_array($message_ar)) {
$message_ar = [
'status' => '',
'code' => '',
'type' => '',
'message' => $message,
'log_id' => '',
'log' => []
];
}
return $message_ar;
}
}
// __END__

42
src/Handle/Json.php Normal file
View File

@@ -0,0 +1,42 @@
<?php
/**
* Handle json conversions
*/
declare(strict_types=1);
namespace gullevek\AmazonIncentives\Handle;
use gullevek\AmazonIncentives\Exceptions\AmazonErrors;
class Json
{
/**
* decode json string
*
* @param string $json
* @return array<mixed>
* @throws AmazonErrors
*/
public static function jsonDecode(string $json): array
{
$result = json_decode($json, true);
if (
($json_last_error = json_last_error()) ||
$result === null ||
!is_array($result)
) {
throw AmazonErrors::getError(
'FAILURE',
'J-' . $json_last_error,
'jsonDecoreError',
'Failed to decode json data',
0
);
}
return $result;
}
}
// __END__

View File

@@ -1,39 +1,26 @@
<?php
declare(strict_types=1);
namespace gullevek\AmazonIncentives\Response;
use gullevek\AmazonIncentives\Debug\AmazonDebug;
class CancelResponse
{
/**
* Amazon Gift Card gcId.
*
* @var string
*/
/** @var string Amazon Gift Card gcId (gift card id). */
protected $id = '';
/**
* Amazon Gift Card creationRequestId
*
* @var string
*/
/** @var string Amazon Gift Card creationRequestId (creation request id) */
protected $creation_request_id = '';
/**
* Amazon Gift Card status
*
* @var string
*/
/** @var string Amazon Gift Card status */
protected $status = '';
/**
* Amazon Gift Card Raw JSON
*
* @var array<mixed>
*/
/** @var array<mixed> Amazon Gift Card Raw JSON */
protected $raw_json = [];
/**
* Response constructor.
* @param array<mixed> $json_response
* Response constructor for canceling gitf cards
*
* @param array<mixed> $json_response JSON response from web request to AWS
*/
public function __construct(array $json_response)
{
@@ -42,7 +29,9 @@ class CancelResponse
}
/**
* @return array<mixed>
* Get log entry with current set log id
*
* @return array<mixed> Log array
*/
public function getLog(): array
{
@@ -50,7 +39,9 @@ class CancelResponse
}
/**
* @return string
* The gift card id as created by the previous get code call
*
* @return string Gift card id
*/
public function getId(): string
{
@@ -58,7 +49,9 @@ class CancelResponse
}
/**
* @return string
* Creation Request id from original get code call
*
* @return string Creation request id
*/
public function getCreationRequestId(): string
{
@@ -66,7 +59,9 @@ class CancelResponse
}
/**
* @return string
* Request status
*
* @return string Request status as string: SUCCESS, FAILURE, RESEND
*/
public function getStatus(): string
{
@@ -74,7 +69,10 @@ class CancelResponse
}
/**
* @return string
* Returns the request data as json string. This is a re-encode from decoded
* makeRequest call
*
* @return string JSON encoded string from the return values
*/
public function getRawJson(): string
{
@@ -82,8 +80,10 @@ class CancelResponse
}
/**
* @param array<mixed> $json_response
* @return CancelResponse
* Set class variables with response data from makeRequest and return self
*
* @param array<mixed> $json_response JSON response as array
* @return CancelResponse Return self object
*/
public function parseJsonResponse(array $json_response): self
{

View File

@@ -1,46 +1,28 @@
<?php
declare(strict_types=1);
namespace gullevek\AmazonIncentives\Response;
use gullevek\AmazonIncentives\Debug\AmazonDebug;
class CreateBalanceResponse
{
/**
* Amazon Gift Card Balance Amount
*
* @var string
*/
protected $amount = '';
/**
* Amazon Gift Card Balance Currency
*
* @var string
*/
/** @var float Amazon Gift Card Balance Amount */
protected $amount = 0;
/** @var string Amazon Gift Card Balance Currency */
protected $currency = '';
/**
* Amazon Gift Card Balance Status
*
* @var string
*/
/** @var string Amazon Gift Card Balance Status */
protected $status = '';
/**
* Amazon Gift Card Balance Timestamp
*
* @var string
*/
/** @var string Amazon Gift Card Balance Timestamp */
protected $timestamp = '';
/**
* Amazon Gift Card Raw JSON
*
* @var array<mixed>
*/
/** @var array<mixed> Amazon Gift Card Raw JSON */
protected $raw_json = [];
/**
* Response constructor.
* Response constructor for requesting account funds status
*
* @param array<mixed> $json_response
* @param array<mixed> $json_response JSON response from web request to AWS
*/
public function __construct(array $json_response)
{
@@ -49,7 +31,9 @@ class CreateBalanceResponse
}
/**
* @return array<mixed>
* Get log entry with current set log id
*
* @return array<mixed> Log array
*/
public function getLog(): array
{
@@ -57,15 +41,19 @@ class CreateBalanceResponse
}
/**
* @return string
* Return the current available funds amount
*
* @return float Funds amount in set currency
*/
public function getAmount(): string
public function getAmount(): float
{
return $this->amount;
}
/**
* @return string
* Get the set currency type
*
* @return string Currency type. Eg USD, JPY, etc
*/
public function getCurrency(): string
{
@@ -73,15 +61,10 @@ class CreateBalanceResponse
}
/**
* @return string
*/
public function getStatus(): string
{
return $this->status;
}
/**
* @return string
* Get timestamp as set.
* eg 20220609T061446Z
*
* @return string Timestamp string. Ymd\THis\Z
*/
public function getTimestamp(): string
{
@@ -89,7 +72,20 @@ class CreateBalanceResponse
}
/**
* @return string
* Request status
*
* @return string Request status as string: SUCCESS, FAILURE, RESEND
*/
public function getStatus(): string
{
return $this->status;
}
/**
* Returns the request data as json string. This is a re-encode from decoded
* makeRequest call
*
* @return string JSON encoded string from the return values
*/
public function getRawJson(): string
{
@@ -97,17 +93,25 @@ class CreateBalanceResponse
}
/**
* Undocumented function
* Set class variables with response data from makeRequest and return self
*
* @param array<mixed> $json_response
* @return CreateBalanceResponse
* @param array<mixed> $json_response JSON response as array
* @return CreateBalanceResponse Return self object
*/
public function parseJsonResponse(array $json_response): self
{
if (array_key_exists('amount', $json_response['availableFunds'])) {
$this->amount = $json_response['availableFunds']['amount'];
if (
is_array($json_response['availableFunds']) &&
array_key_exists('amount', $json_response['availableFunds']) &&
is_numeric($json_response['availableFunds']['amount'])
) {
$this->amount = (float)$json_response['availableFunds']['amount'];
}
if (array_key_exists('currencyCode', $json_response['availableFunds'])) {
if (
is_array($json_response['availableFunds']) &&
array_key_exists('currencyCode', $json_response['availableFunds']) &&
is_string($json_response['availableFunds']['currencyCode'])
) {
$this->currency = $json_response['availableFunds']['currencyCode'];
}
// SUCCESS, FAILURE, RESEND

View File

@@ -1,73 +1,36 @@
<?php
declare(strict_types=1);
namespace gullevek\AmazonIncentives\Response;
use gullevek\AmazonIncentives\Debug\AmazonDebug;
class CreateResponse
{
/**
* Amazon Gift Card gcId.
*
* @var string
*/
/** @var string Amazon Gift Card gcId */
protected $id = '';
/**
* Amazon Gift Card creationRequestId
*
* @var string
*/
/** @var string Amazon Gift Card creationRequestId */
protected $creation_request_id = '';
/**
* Amazon Gift Card gcClaimCode
*
* @var string
*/
/** @var string Amazon Gift Card gcClaimCode */
protected $claim_code = '';
/**
* Amazon Gift Card amount
*
* @var float
*/
/** @var float Amazon Gift Card amount */
protected $value = 0;
/**
* Amazon Gift Card currency
*
* @var string
*/
/** @var string Amazon Gift Card currency */
protected $currency = '';
/**
* Amazon Gift Card status
*
* @var string
*/
/** @var string Amazon Gift Card status */
protected $status = '';
/**
* Amazon Gift Card Expiration Date
*
* @var string
*/
/** @var string Amazon Gift Card Expiration Date */
protected $expiration_date = '';
/**
* Amazon Gift Card Expiration Date
*
* @var string
*/
/** @var string Amazon Gift Card Expiration Date */
protected $card_status = '';
/**
* Amazon Gift Card Raw JSON
*
* @var array<mixed>
*/
/** @var array<mixed> Amazon Gift Card Raw JSON as array */
protected $raw_json = [];
/**
* Response constructor.
* @param array<mixed> $json_response
* Response constructor for creating gift cards
*
* @param array<mixed> $json_response JSON response from web request to AWS
*/
public function __construct(array $json_response)
{
@@ -76,7 +39,9 @@ class CreateResponse
}
/**
* @return array<mixed>
* Get log entry with current set log id
*
* @return array<mixed> Log array
*/
public function getLog(): array
{
@@ -84,7 +49,9 @@ class CreateResponse
}
/**
* @return string
* Gift Card ID returned from AWS. Can be used in the cancel request
*
* @return string Gift card id
*/
public function getId(): string
{
@@ -92,7 +59,10 @@ class CreateResponse
}
/**
* @return string
* Either the one set with the method parameter, or automatically created
* during get code request
*
* @return string Creation request id
*/
public function getCreationRequestId(): string
{
@@ -100,7 +70,10 @@ class CreateResponse
}
/**
* @return string
* The actual gift code, recommended not to be stored anywhere and only shown
* to user
*
* @return string Gift order claim code on AWS
*/
public function getClaimCode(): string
{
@@ -108,7 +81,9 @@ class CreateResponse
}
/**
* @return float
* The ordered gift code value in given currency
*
* @return float Gift order value in currency
*/
public function getValue(): float
{
@@ -116,7 +91,9 @@ class CreateResponse
}
/**
* @return string
* The currently set currency
*
* @return string Currency type. Eg USD, JPY, etc
*/
public function getCurrency(): string
{
@@ -124,15 +101,10 @@ class CreateResponse
}
/**
* @return string
*/
public function getStatus(): string
{
return $this->status;
}
/**
* @return string
* Expiration date for the ordered gift code.
* eg 20220609T061446Z
*
* @return string Timestamp until when the gift code is valid. Ymd\THis\Z
*/
public function getExpirationDate(): string
{
@@ -140,16 +112,31 @@ class CreateResponse
}
/**
* @return string
* Gift card status. If the same creation request id is sent again and the
* gift card got cancled, this is reflected here
*
* @return string Gift card status
*/
public function getCardStatus(): string
{
return $this->card_status;
}
/**
* Request status
*
* @return string Request status as string: SUCCESS, FAILURE, RESEND
*/
public function getStatus(): string
{
return $this->status;
}
/**
* @return string
* @Returns the request data as json string. This is a re-encode from decoded
* makeRequest call
*
* @return string JSON encoded string from the return values
*/
public function getRawJson(): string
{
@@ -157,8 +144,10 @@ class CreateResponse
}
/**
* @param array<array-key,mixed|array> $json_response
* @return CreateResponse
* Set class variables with response data from makeRequest and return self
*
* @param array<mixed> $json_response JSON response as array
* @return CreateResponse Return self object
*/
public function parseJsonResponse(array $json_response): self
{

View File

@@ -0,0 +1,140 @@
# Return data from AWS as json string
## Mock tests self
Tests run in local mock tests without connection to AWS
### funds
```json
{"availableFunds":{"amount":0.0,"currencyCode":"JPY"},"status":"SUCCESS","timestamp":"20220610T085450Z"}
```
### buy
```json
{"cardInfo":{"cardNumber":null,"cardStatus":"Fulfilled","expirationDate":null,"value":{"amount":1000.0,"currencyCode":"JPY"}},"creationRequestId":"PartnerId_62a309167e7a4","gcClaimCode":"LJ49-AKDUV6-UYCP","gcExpirationDate":"Thu Jun 10 14:59:59 UTC 2032","gcId":"5535125272070255","status":"SUCCESS"}
```
### cancel
```json
{"creationRequestId":"EG3bd_62a309167e7a4","gcId":"5535125272070255","status":"SUCCESS"}
```
### buy other 1
```json
{"cardInfo":{"cardNumber":null,"cardStatus":"RefundedToPurchaser","expirationDate":null,"value":{"amount":1000.0,"currencyCode":"JPY"}},"creationRequestId":"PartnerId_62a309167e7a4","gcClaimCode":"LJ49-AKDUV6-UYCP","gcExpirationDate":"Thu Jun 10 14:59:59 UTC 2032","gcId":"5535125272070255","status":"SUCCESS"}
```
### buy aother 2
```json
{"cardInfo":{"cardNumber":null,"cardStatus":"Fulfilled","expirationDate":null,"value":{"amount":1000.0,"currencyCode":"JPY"}},"creationRequestId":"PartnerId_62a30923e9705","gcClaimCode":"UM97-FD5QKK-WKCT","gcExpirationDate":"Thu Jun 10 14:59:59 UTC 2032","gcId":"5540334324324221","status":"SUCCESS"}
```
### buy other 2 (same create request id)
```json
{"cardInfo":{"cardNumber":null,"cardStatus":"Fulfilled","expirationDate":null,"value":{"amount":1000.0,"currencyCode":"JPY"}},"creationRequestId":"PartnerId_62a30923e9705","gcClaimCode":"UM97-FD5QKK-WKCT","gcExpirationDate":"Thu Jun 10 14:59:59 UTC 2032","gcId":"5540334324324221","status":"SUCCESS"}
```
## AWS GCOD Mocks
Mocking on AWS side, these will be returned if given request ID codes are sent.
A working test account must exist for this
## success
### buy gift card F0000
```json
{"cardInfo":{"cardNumber":null,"cardStatus":"Fulfilled","expirationDate":null,"value":{"amount":500.0,"currencyCode":"JPY"}},"creationRequestId":"F0000","gcClaimCode":"ZYXW-VUTS-RQPO","gcExpirationDate":null,"gcId":"ABC123ZYX987","status":"SUCCESS"}
```
## errors
### F1000 -> F100
```json
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F100","errorType":"GeneralError","message":"General Error"}
```
### F2003 -> F200
```json
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"InvalidAmountInput","message":"Amount can't be null"}
```
### F2004 -> F200
```json
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"InvalidAmountValue","message":"Amount must be larger than 0"}
```
### F2005 -> F200
```json
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"InvalidCurrencyCodeInput","message":"Currency Code can't be null or empty"}
```
### F2010 -> F200
```json
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"CardAlreadyActivatedWithDifferentRequestId","message":"The card was already activated with a different request id"}
```
### F2015 -> F200
```json
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"MaxAmountExceeded","message":"Max Amount Exceeded"}
```
### F2016 -> F200
```json
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"CurrencyCodeMismatch","message":"Currency Code Mismatch"}
```
### F2017 -> F200
```json
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"FractionalAmountNotAllowed","message":"Fractional Amount Not Allowed"}
```
### F2047 -> F200
```json
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"CancelRequestArrivedAfterTimeLimit","message":"Cancellation cannot be processed as too much time has elapsed since creation"}
```
### F3003 -> F300
```json
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F300","errorType":"InsufficientFunds","message":"Insufficient Funds"}
```
### F3005 -> F300
```json
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F300","errorType":"GeneralError","message":"General Error"}
```
### F3010 -> F300
```json
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F300","errorType":"CustomerSurpassedDailyVelocityLimit","message":"Customer has exceeded daily velocity limit"}
```
### F4000 -> F400
```json
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"RESEND"},"errorCode":"F400","errorType":"SystemTemporarilyUnavailable","message":"System Temporarily Unavailable"}
```
### F5000 -> F500
```json
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F500","errorType":"GeneralError","message":"General Error"}
```

View File

@@ -57,13 +57,13 @@ $loader = require '../vendor/autoload.php';
// need to add this or it will not load here
$loader->addPsr4('gullevek\\', __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'src');
// print "LOADER: <pre>" . print_r($loader, true) . "</pre>";
// env file loader (simple)
require 'read_env_file.php';
use gullevek\AmazonIncentives\AmazonIncentives;
use gullevek\AmazonIncentives\Exceptions\AmazonErrors;
use gullevek\dotEnv\DotEnv;
// load env data with dotenv
readEnvFile(__DIR__);
DotEnv::readEnvFile(__DIR__);
print "<h1>Amazon Gift Card Incentives</h1><br>";
@@ -76,34 +76,39 @@ print "<h1>Amazon Gift Card Incentives</h1><br>";
// optional
// debug: AWS_DEBUG (if not set: off)
// run info test (prints ENV vars)
$run_info_test = !empty($_GET['info']) ? true : false;
// run test to get funds info
$run_fund_test = !empty($_GET['fund']) ? true : false;
// run the normal get/cancel gift card tests
$run_gift_tests = !empty($_GET['gift']) ? true : false;
// run mock error check tests
$run_mocks = !empty($_GET['mocks']) ? true : false;
// should we print debug info
$debug_print = !empty($_GET['debug']) ? true : false;
// how long to wait between each call
$debug_wait = 2;
// if set to true will print all the debug logs too
$mock_debug = !empty($_GET['debug_mock']) ? true : false;
// wait in seconds between mock tests
$mock_wait = 2;
if (empty($_GET)) {
print "<b>Use _GET parameters to start tests</b>";
}
// open debug file output
$fp = fopen('log/debug.' . date('YmdHis') . '.log', 'w');
if (!is_resource($fp)) {
die("Cannot open log debug file");
}
// run info test (prints ENV vars)
$run_info_test = false;
// run test to get funds info
$run_fund_test = true;
// run the normal get/cancel gift card tests
$run_gift_tests = true;
// run mock error check tests
$run_mocks = false;
// should we print debug info
$debug_print = false;
// how long to wait between each call
$debug_wait = 2;
// if set to true will print all the debug logs too
$mock_debug = false;
// wait in seconds between mock tests
$mock_wait = 2;
if ($run_info_test === true) {
$aws = new AmazonIncentives();
print "checkMe: <pre>" . print_r($aws->checkMe(), true) . "</pre>";
fwrite($fp, writeLog($aws->checkMe()));
$aws_check_me = $aws->checkMe();
print "checkMe: <pre>" . print_r($aws_check_me, true) . "</pre>";
fwrite($fp, writeLog($aws_check_me));
print "<hr>";
}
@@ -120,7 +125,7 @@ if ($run_fund_test === true) {
}
fwrite($fp, writeLog((array)$aws_test));
} catch (Exception $e) {
$error = AmazonIncentives::decodeExceptionMessage($e->getMessage());
$error = AmazonErrors::decodeExceptionMessage($e->getMessage());
printException('getAvailableFunds', $e->getCode(), $error, $debug_print);
fwrite($fp, writeLog($error));
};
@@ -152,7 +157,7 @@ if ($run_gift_tests === true) {
}
fwrite($fp, writeLog((array)$aws_test));
} catch (\Exception $e) {
$error = AmazonIncentives::decodeExceptionMessage($e->getMessage());
$error = AmazonErrors::decodeExceptionMessage($e->getMessage());
printException('buyGiftCard', $e->getCode(), $error, $debug_print);
fwrite($fp, writeLog($error));
}
@@ -169,7 +174,7 @@ if ($run_gift_tests === true) {
}
fwrite($fp, writeLog((array)$aws_test));
} catch (\Exception $e) {
$error = AmazonIncentives::decodeExceptionMessage($e->getMessage());
$error = AmazonErrors::decodeExceptionMessage($e->getMessage());
print "AWS: cancelGiftCard: " . $error['status']
. " [" . $e->getCode() . "]: "
. $error['code'] . " | " . $error['type']
@@ -197,7 +202,7 @@ if ($run_gift_tests === true) {
}
fwrite($fp, writeLog((array)$aws_test));
} catch (\Exception $e) {
$error = AmazonIncentives::decodeExceptionMessage($e->getMessage());
$error = AmazonErrors::decodeExceptionMessage($e->getMessage());
printException('buyGiftCard', $e->getCode(), $error, $debug_print);
fwrite($fp, writeLog($error));
}
@@ -221,7 +226,7 @@ if ($run_gift_tests === true) {
}
fwrite($fp, writeLog((array)$aws_test));
} catch (\Exception $e) {
$error = AmazonIncentives::decodeExceptionMessage($e->getMessage());
$error = AmazonErrors::decodeExceptionMessage($e->getMessage());
printException('cancelGiftCard', $e->getCode(), $error, $debug_print);
fwrite($fp, writeLog($error));
}
@@ -242,7 +247,7 @@ if ($run_gift_tests === true) {
}
fwrite($fp, writeLog((array)$aws_test));
} catch (\Exception $e) {
$error = AmazonIncentives::decodeExceptionMessage($e->getMessage());
$error = AmazonErrors::decodeExceptionMessage($e->getMessage());
printException('buyGiftCard', $e->getCode(), $error, $debug_print);
fwrite($fp, writeLog($error));
}
@@ -295,7 +300,7 @@ if ($run_mocks === true) {
}
fwrite($fp, writeLog((array)$aws_test));
} catch (Exception $e) {
$error = AmazonIncentives::decodeExceptionMessage($e->getMessage());
$error = AmazonErrors::decodeExceptionMessage($e->getMessage());
print "AWS: MOCK: " . $creation_id . ": buyGiftCard: " . $error['status']
. " [" . $e->getCode() . "]: "
. $error['code'] . " | " . $error['type']

View File

@@ -0,0 +1,23 @@
<?php
// just print out env data nd connect data
// checkMe from AmazonIntentives call is requal to
// run_info_test === true in aws_gift_card_tests.php
$loader = require '../vendor/autoload.php';
// need to add this or it will not load here
$loader->addPsr4('gullevek\\', __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'src');
// print "LOADER: <pre>" . print_r($loader, true) . "</pre>";
use gullevek\AmazonIncentives\AmazonIncentives;
use gullevek\dotEnv\DotEnv;
// load env data with dotenv
DotEnv::readEnvFile(__DIR__);
print "_ENV: <pre>" . print_r($_ENV, true) . "</pre>";
$aws = new AmazonIncentives();
print "checkMe: <pre>" . print_r($aws->checkMe(), true) . "</pre>";
// __END__

File diff suppressed because it is too large Load Diff

View File

@@ -1,84 +0,0 @@
<?php
/**
* parses .env file
*
* Rules for .env file
* variable is any alphanumeric string followed by = on the same line
* content starts with the first non space part
* strings can be contained in "
* strings MUST be contained in " if they are multiline
* if string starts with " it will match until another " is found
* anything AFTER " is ignored
* if there are two variables with the same name only the first is used
* variables are case sensitive
*
* @param string $path Folder to file, default is __DIR__
* @param string $env_file What file to load, default is .env
* @return int -1 other error
* 0 for success full load
* 1 for file loadable, but no data inside
* 2 for file not readable
* 3 for file not found
*/
function readEnvFile(string $path = __DIR__, string $env_file = '.env'): int
{
// default -1;
$status = -1;
$env_file_target = $path . DIRECTORY_SEPARATOR . $env_file;
// this is not a file -> abort
if (!is_file($env_file_target)) {
$status = 3;
return $status;
}
// cannot open file -> abort
if (($fp = fopen($env_file_target, 'r')) === false) {
$status = 2;
return $status;
}
// set to readable but not yet any data loaded
$status = 1;
$block = false;
$var = '';
while ($line = fgets($fp)) {
// main match for variable = value part
if (preg_match("/^\s*([\w_.]+)\s*=\s*((\"?).*)/", $line, $matches)) {
$var = $matches[1];
$value = $matches[2];
$quotes = $matches[3];
// wirte only if env is not set yet, and write only the first time
if (empty($_ENV[$var])) {
if (!empty($quotes)) {
// match greedy for first to last so we move any " if there are
if (preg_match('/^"(.*[^\\\])"/U', $value, $matches)) {
$value = $matches[1];
} else {
// this is a multi line
$block = true;
// first " in string remove
// add removed new line back because this is a multi line
$value = ltrim($value, '"') . PHP_EOL;
}
}
// if block is set, we strip line of slashes
$_ENV[$var] = $block === true ? stripslashes($value) : $value;
// set successful load
$status = 0;
}
} elseif ($block === true) {
// read line until there is a unescaped "
// this also strips everything after the last "
if (preg_match("/(.*[^\\\])\"/", $line, $matches)) {
$block = false;
// strip ending " and EVERYTHING that follows after that
$line = $matches[1];
}
// strip line of slashes
$_ENV[$var] .= stripslashes($line);
}
}
fclose($fp);
return $status;
}
// __END__

2
tmp/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*
!.gitignore

7
vendor/autoload.php vendored
View File

@@ -1,7 +0,0 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit0c8f6bec90a6d60040a922f19a1f0e64::getLoader();

View File

@@ -1,572 +0,0 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
/** @var ?string */
private $vendorDir;
// PSR-4
/**
* @var array[]
* @psalm-var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, array<int, string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* @var array[]
* @psalm-var array<string, array<string, string[]>>
*/
private $prefixesPsr0 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false;
/**
* @var string[]
* @psalm-var array<string, string>
*/
private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false;
/**
* @var bool[]
* @psalm-var array<string, bool>
*/
private $missingClasses = array();
/** @var ?string */
private $apcuPrefix;
/**
* @var self[]
*/
private static $registeredLoaders = array();
/**
* @param ?string $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
}
/**
* @return string[]
*/
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
}
/**
* @return array[]
* @psalm-return array<string, array<int, string>>
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
/**
* @return string[] Array of classname => path
* @psalm-var array<string, string>
*/
public function getClassMap()
{
return $this->classMap;
}
/**
* @param string[] $classMap Class to filename map
* @psalm-param array<string, string> $classMap
*
* @return void
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 base directories
*
* @return void
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*
* @return void
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*
* @return void
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*
* @return void
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}
/**
* Unregisters this instance as an autoloader.
*
* @return void
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return true|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
return null;
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
/**
* Returns the currently registered loaders indexed by their corresponding vendor directories.
*
* @return self[]
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
/**
* @param string $class
* @param string $ext
* @return string|false
*/
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
* @private
*/
function includeFile($file)
{
include $file;
}

View File

@@ -1,21 +0,0 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -1,10 +0,0 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
);

View File

@@ -1,9 +0,0 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View File

@@ -1,10 +0,0 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'gullevek\\AmazonIncentives\\' => array($baseDir . '/src'),
);

View File

@@ -1,55 +0,0 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit0c8f6bec90a6d60040a922f19a1f0e64
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit0c8f6bec90a6d60040a922f19a1f0e64', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
spl_autoload_unregister(array('ComposerAutoloaderInit0c8f6bec90a6d60040a922f19a1f0e64', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit0c8f6bec90a6d60040a922f19a1f0e64::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
return $loader;
}
}

View File

@@ -1,36 +0,0 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInit0c8f6bec90a6d60040a922f19a1f0e64
{
public static $prefixLengthsPsr4 = array (
'G' =>
array (
'gullevek\\AmazonIncentives\\' => 26,
),
);
public static $prefixDirsPsr4 = array (
'gullevek\\AmazonIncentives\\' =>
array (
0 => __DIR__ . '/../..' . '/src',
),
);
public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit0c8f6bec90a6d60040a922f19a1f0e64::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit0c8f6bec90a6d60040a922f19a1f0e64::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit0c8f6bec90a6d60040a922f19a1f0e64::$classMap;
}, null, ClassLoader::class);
}
}