62 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
f60aa3e1bc Merge branch 'development'
PHPstan config updates, test file update
2021-11-25 17:45:13 +09:00
b041720a18 phpstan config updates, test code update 2021-11-25 17:44:04 +09:00
be6b2be55d Merge pull request #1 from arif-rh/minor-fix
Minor fix for README type on composer command and trailing , in config class
2021-11-25 17:42:33 +09:00
Arif RH
6163d26f50 minor fix Config 2021-11-25 14:55:55 +07:00
Arif RH
a4c06b9a36 minor fix README 2021-11-25 14:55:09 +07:00
24dc8426de Remove space from client interface 2021-10-27 17:55:29 +09:00
7ef4133537 Update composer json file to exclude test and static test files 2021-10-21 15:51:05 +09:00
4f90f2d916 Update test with simpl .env variables and include example file 2021-10-21 15:00:45 +09:00
192e4e54e1 Add psalm to level 3 and clean up more code 2021-10-21 14:57:29 +09:00
d0b676f8ce Add phan checks at max level 2021-10-21 13:37:59 +09:00
c2721cb8d4 Add PHPstan and fix all checks for max level 2021-10-21 13:31:18 +09:00
18cf3330df Change license to MIT 2021-10-21 10:31:08 +09:00
3b7f644ae9 Update readme file, Update composer.json file, Exception updates
Alle exceptions thrown in Response are now general AmazonError with json
return string for full compability
2021-10-21 10:22:17 +09:00
6f9bf781f2 Rewrite to composer package
composer require gullevek/amazon-incentives
2021-10-21 09:55:32 +09:00
94ea118731 Documentation update 2021-10-20 16:18:18 +09:00
a56f33c81c Debug log updates, error message updates on curl failure
Config setup works with null data if no data is set.

Debugger is now launched in the AamzonIncentive class at the beginning
Debugger id/debug flag set is now one static call
Debugger methods all have proper PHPdoc documentationn added

Fixed Client/Curl error messages to include the original error message
too. Also made sure that a possible null endpoint is not throwing errors
2021-10-20 09:39:26 +09:00
f3b17bbf4c Update tests script with logging, etc 2021-10-19 11:46:52 +09:00
42 changed files with 3342 additions and 1507 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/

102
.phan/config.php Normal file
View File

@@ -0,0 +1,102 @@
<?php
/**
* This configuration will be read and overlaid on top of the
* default configuration. Command line arguments will be applied
* after this file is read.
*
* @see src/Phan/Config.php
* See Config for all configurable options.
*
* A Note About Paths
* ==================
*
* Files referenced from this file should be defined as
*
* ```
* Config::projectPath('relative_path/to/file')
* ```
*
* where the relative path is relative to the root of the
* project which is defined as either the working directory
* of the phan executable or a path passed in via the CLI
* '-d' flag.
*/
// 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.
"allow_missing_properties" => false,
// Allow null to be cast as any type and for any
// type to be cast to null.
"null_casts_as_any_type" => false,
// Backwards Compatibility Checking
'backward_compatibility_checks' => true,
// Run a quick version of checks that takes less
// time
"quick_mode" => false,
// Only emit critical issues to start with
// (0 is low severity, 5 is normal severity, 10 is critical)
"minimum_severity" => 10,
// default false for include path check
"enable_include_path_checks" => true,
"include_paths" => [
],
'ignore_undeclared_variables_in_global_scope' => true,
"file_list" => [
],
// A list of directories that should be parsed for class and
// method information. After excluding the directories
// defined in exclude_analysis_directory_list, the remaining
// files will be statically analyzed for errors.
//
// Thus, both first-party and third-party code being used by
// your application should be included in this list.
'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
// project depends on.
// `phan --init` will generate a list of folders for you
//'www/vendor',
],
// A list of directories holding code that we want
// to parse, but not analyze
"exclude_analysis_directory_list" => [
'vendor',
'test',
],
'exclude_file_list' => [
],
// what not to show as problem
'suppress_issue_types' => [
// 'PhanUndeclaredMethod',
'PhanEmptyFile',
],
// Override to hardcode existence and types of (non-builtin) globals in the global scope.
// Class names should be prefixed with `\`.
//
// (E.g. `['_FOO' => '\FooClass', 'page' => '\PageClass', 'userId' => 'int']`)
'globals_type_map' => [],
];

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>

191
README.md Normal file
View File

@@ -0,0 +1,191 @@
# Amazon Incentives - Gift Codes on Demand stand alone class
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`
## _ENV variables needed
Uses .env file to load configuration data
The below keys are searched in the _ENV file for loading
* AWS_GIFT_CARD_KEY
* AWS_GIFT_CARD_SECRET
* AWS_GIFT_CARD_PARTNER_ID
* AWS_GIFT_CARD_ENDPOINT
* AWS_GIFT_CARD_CURRENCY
* AWS_DEBUG (1/0)
## How to use
The above _ENV variables must be set (Except AWS_DEBUG, defaults to off).
### create gift card
```php
use gullevek\AmazonIncentives\AmazonIncentives;
// buy a gift card with a value
$value = 500;
$aws_gc = AmazonIncentives::make()->buyGiftCard((float)$value);
// the two below are need if we want to cancel the card
// get gift card id (gcID)
$aws_gc->getId();
// get creation request id (creationRequestId)
$aws_gc->getCreationRequestId();
// the one below must be printed to the user
$aws_gc->getClaimCode();
// check status (SUCCESS/RESEND/FAILURE)
$aws_gc->getStatus();
// others:
// getAmount, getCurrency
```
#### Throttle Rates
Note that you can only send 10 requests per second. On a Throttle Excepion you need to wait about 10s to create another request.
Recommended to pool requests. Or check when last requests where sent and then process them.
#### On F400 errors
1) try again
2) if failed run cancel gift card
3) if cance ok try create again with different request id
4) if 2) failed, wait a view seconds and try again
5) if 10s elapse, we need to wait a full day
6) if >24h call Amazon
### cancel gift card
```php
// use getCreationRequestId() and getId() from request
$aws_gc = gullevek\AmazonIncentives\AmazonIncentives::make()->cancelGiftCard($creation_request_id, $gift_card_id);
// return is as above
```
### check balance
```php
$aws_gc = gullevek\AmazonIncentives\AmazonIncentives::make()->getAvailableFunds();
```
## Exceptions
If the HTTPS request does not return 220 OK it will throw an exception.
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`, `code` and `type` must be checked on a failure.
## Other Errors from exceptions
### T001
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.
### E999
if code is E999 some other critical error has happened
### E001
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
## Debugging
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
`AmazonDebug::writeLog(['foo' => 'bar']);`
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`

105
Readme.md
View File

@@ -1,105 +0,0 @@
# Amazon Incentives - Gift on Demand standa alone class
This is a abastract from (https://github.com/kamerk22/AmazonGiftCode) to be not dependend on Laravel base code.
Uses .env file to load configuration data
## _ENV variables needed
* AWS_GIFT_CARD_KEY
* AWS_GIFT_CARD_SECRET
* AWS_GIFT_CARD_PARTNER_ID
* AWS_GIFT_CARD_ENDPOINT
* AWS_GIFT_CARD_CURRENCY
* AWS_DEBUG (1/0)
## How to use
The class must be loaded with an autoloader (see test/autoloader.php for example).
The above _ENV variables must be set (Except AWS_DEBUG, defaults to off).
### create gift card
```php
$aws_gc = Amazon\AmazonIncentives::make()->buyGiftCard((float)$value);
// the two below are need if we want to cancel the card
// get gift card id (gcID)
$aws_gc->getId();
// get creation request id (creationRequestId)
$aws_gc->getCreationRequestId();
// the one below must be printed to the user
$aws_gc->getClaimCode();
// check status (SUCCESS/RESEND/FAILURE)
$aws_gc->getStatus();
// others:
// getAmount, getCurrency
```
#### Throttle Rates
Note that you can only send 10 requests per second. On a Throttle Excepion you need to wait about 10s to create another request.
Recommended to pool requests. Or check when last requests where sent and then process them.
#### On F400 errors
1) try again
2) if failed run cancel gift card
3) if cance ok try create again with different request id
4) if 2) failed, wait a view seconds and try again
5) if 10s elapse, we need to wait a full day
6) if >24h call Amazon
### cancel gift card
```php
// use getCreationRequestId() and getId() from request
$aws_gc = Amazon\AmazonIncentives::make()->cancelGiftCard($creation_request_id, $gift_card_id);
// return is as above
```
### check balance
```php
$aws_gc = Amazon\AmazonIncentives::make()->getAvailableFunds();
```
## Exceptions
If the HTTPS request does not return 220 OK it will throw an exception.
The error code is the curl handler error code.
The error message is json encoded array with the layout
```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`, `code` and `type` must be checked on a failure.
**NOTE**: if code is E999 then this is a request flood error:
In this case the request has to be resend after a certain waiting period.
## Debugging
If AWS_DEBUG is set to 1 and internal array will be written with debug info.
The Amazon\Debug\AmazonDebug class handles all this.
In the Amazon\AWS\AWS main class the debugger gets set
* setFlag that turns debugger on/off
* setId (to set unique id for each run)
New entries can be written with
`AmazonDebug::writeLog(['foo' => 'bar']);`
On sucessful run the log data is accessable with `$aws->getLog()`
On exception the log data is in the error message json (see exceptions)

34
composer.json Normal file
View File

@@ -0,0 +1,34 @@
{
"name": "gullevek/amazon-incentives",
"description": "Amazon Gift Codes, Gift on Demand, Incentives",
"keywords": ["AmazonGiftCode", "Amazon", "GiftCard", "AGCOD", "Incentives API", "Amazon Incentives API"],
"type": "library",
"license": "MIT",
"autoload": {
"psr-4": {
"gullevek\\AmazonIncentives\\": "src/"
}
},
"authors": [
{
"name": "Clemens Schwaighofer",
"email": "gullevek@gullevek.org",
"homepage": "http://gullevek.org"
}
],
"homepage": "https://github.com/gullevek/AmazonIncentives",
"minimum-stability": "dev",
"require": {
"php": ">=7.4.0"
},
"archive": {
"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>

11
phpstan.neon Normal file
View File

@@ -0,0 +1,11 @@
# PHP Stan Config
parameters:
tmpDir: %currentWorkingDirectory%/tmp/phpstan-codeblocks-amazon-incentives
level: 9
paths:
- %currentWorkingDirectory%/src
excludePaths:
# ignore composer
- vendor
- 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>

16
psalm.xml Normal file
View File

@@ -0,0 +1,16 @@
<?xml version="1.0"?>
<psalm
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="." />
<ignoreFiles>
<directory name="vendor" />
<directory name="test" />
</ignoreFiles>
</projectFiles>
</psalm>

View File

@@ -1,48 +1,70 @@
<?php
namespace Amazon\AWS;
declare(strict_types=1);
use Amazon\Client\Client;
use Amazon\Config\Config;
use Amazon\Exceptions\AmazonErrors;
use Amazon\Debug\AmazonDebug;
use Amazon\Response\CancelResponse;
use Amazon\Response\CreateBalanceResponse;
use Amazon\Response\CreateResponse;
namespace gullevek\AmazonIncentives\AWS;
use gullevek\AmazonIncentives\Client\Client;
use gullevek\AmazonIncentives\Config\Config;
use gullevek\AmazonIncentives\Exceptions\AmazonErrors;
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 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)
{
$this->config = $config;
AmazonDebug::setFlag($config->getDebug());
AmazonDebug::setId();
AmazonDebug::writeLog([__METHOD__ => date('Y-m-d H:m:s.u')]);
}
/**
* @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
*/
@@ -53,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
*/
@@ -76,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
*/
@@ -97,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,
@@ -155,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
* 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,
@@ -187,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
{
@@ -223,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
{
@@ -268,7 +328,31 @@ class AWS
}
/**
* @return string
* 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
* (US, CA, MX)
* 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
* (JP, AU, SG)
*
* CURRENCY
* USD for US
* EUR for EU (IT, ES, DE, FR, PL, NL, SE)
* JPY for JP
* CAD for CA
* AUD for AU
* TRY for TR
* AED for UAE
* MXN for MX
* GBP for UK
*
* @return string Region string depending on given endpoint url
*/
public function getRegion(): string
{
@@ -283,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':
@@ -295,56 +379,91 @@ class AWS
/**
* @param float $amount
* @param string $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
{
$amount = trim($amount);
$payload = [
'creationRequestId' => $creation_id ?: uniqid($this->config->getPartner() . '_'),
'partnerId' => $this->config->getPartner(),
'value' =>
[
'currencyCode' => $this->config->getCurrency(),
'amount' => (float)$amount
'amount' => $amount
]
];
return json_encode($payload);
return (json_encode($payload)) ?: '';
}
/**
* @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
{
$gift_card_response_id = trim($gift_card_id);
$payload = [
'creationRequestId' => $creation_request_id,
'partnerId' => $this->config->getPartner(),
'gcId' => $gift_card_response_id
'gcId' => $gift_card_id
];
return json_encode($payload);
return (json_encode($payload)) ?: '';
}
/**
* @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
{
$payload = [
'partnerId' => $this->config->getPartner(),
];
return json_encode($payload);
return (json_encode($payload)) ?: '';
}
/**
* @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
{
@@ -363,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
{
@@ -372,18 +493,13 @@ class AWS
}
/**
* @return false|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
{
@@ -391,7 +507,21 @@ class AWS
}
/**
* @return bool|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()
{
@@ -399,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

@@ -1,180 +0,0 @@
<?php
/*
* Amazon Incentive Code
*
* # settings:
* aws endpoint (also sets region)
* aws key
* aws secret
* aws partner id
* money type (set as default, override in call)
* money value (set as default, override in call)
*
* # checks
* endpoint + region must match
*
* # calls:
* create gift card: CreateGiftCard
* cancel gift card: CancelGiftCard
*
* activate gift card: ActivateGiftCard
* deactivate gift card: DeactivateGiftCard
*
* gift card status: ActivationStatusCheck
*
* check available funds: GetAvailablefunds
*
* api server health check
*
* # sub classes
* config reader/checker
* API v4 encrypter
* submitter/data getter
* error handler/retry
*/
namespace Amazon;
use Amazon\AWS\AWS;
use Amazon\Config\Config;
use Amazon\Exceptions\AmazonErrors;
class AmazonIncentives
{
private $config;
/**
* 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
*/
public function __construct(
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
$this->config = new Config($key, $secret, $partner, $endpoint, $currency, $debug);
}
// *********************************************************************
// PRIVATE HELPER METHODS
// *********************************************************************
// *********************************************************************
// PUBLIC DEBUG METHODS
// *********************************************************************
// like log collector (array) and returner
// *********************************************************************
// PUBLIC METHODS
// *********************************************************************
/**
* @param float $value
* @param string $creation_request_id AWS creationRequestId
* @return Response\CreateResponse
*
* @throws AmazonErrors
*/
public function buyGiftCard(float $value, string $creation_request_id = null): Response\CreateResponse
{
return (new AWS($this->config))->getCode($value, $creation_request_id);
}
/**
* @param string $creation_request_id AWS creationRequestId
* @param string $gift_card_id AWS gcId
* @return Response\CancelResponse
*/
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 Response\CreateBalanceResponse
*
* @throws AmazonErrors
*/
public function getAvailableFunds(): Response\CreateBalanceResponse
{
return (new AWS($this->config))->getBalance();
}
/**
* AmazonGiftCode make own client.
*
* @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 AmazonGiftCode
*/
public static function make(
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);
}
/**
* 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 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 = [
'status' => '',
'code' => '',
'type' => '',
'message' => $message,
'log_id' => '',
'log' => []
];
}
return $message_ar;
}
// *********************************************************************
// PUBLIC TEST METHODS
// *********************************************************************
public function checkMe(): array
{
$data = [];
$data['ENV'] = $_ENV;
$data['CONFIG'] = $this->config;
$data['KEY'] = $this->config->getAccessKey();
return $data;
}
}
// __END__

View File

@@ -1,18 +0,0 @@
<?php
namespace Amazon\Client;
interface ClientInterface
{
/**
* @param string $url The URL being requested, including domain and protocol
* @param array $headers Headers to be used in the request
* @param array|string $params Can be nested for arrays and hashes
*
* @return String
*/
public function request(string $url, array $headers, $params): string;
}
// __END__

View File

@@ -1,199 +0,0 @@
<?php
namespace Amazon\Config;
class Config implements ConfigInterface
{
/**
* @var string
*/
private $endpoint;
/**
* @var string
*/
private $access_key;
/**
* @var string
*/
private $secret_key;
/**
* @var string
*/
private $partner_id;
/**
* @var string
*/
private $currency;
/**
* @var bool
*/
private $debug;
/**
* @param string|null $key
* @param string|null $secret
* @param string|null $partner
* @param string|null $endpoint
* @param string|null $currency
*/
public function __construct(
?string $key,
?string $secret,
?string $partner,
?string $endpoint,
?string $currency,
?bool $debug,
) {
$this->setAccessKey($key ?: $this->parseEnv('AWS_GIFT_CARD_KEY'));
$this->setSecret($secret ?: $this->parseEnv('AWS_GIFT_CARD_SECRET'));
$this->setPartner($partner ?: $this->parseEnv('AWS_GIFT_CARD_PARTNER_ID'));
$this->setEndpoint($endpoint ?: $this->parseEnv('AWS_GIFT_CARD_ENDPOINT'));
$this->setCurrency($currency ?: $this->parseEnv('AWS_GIFT_CARD_CURRENCY'));
$this->setDebug($debug ?: $this->parseEnv('AWS_DEBUG'));
}
/**
* 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)
*/
private function parseEnv(string $key)
{
$return = '';
switch ($key) {
case 'AWS_DEBUG':
$return = !empty($_ENV['AWS_DEBUG']) ? true : false;
break;
case 'AWS_GIFT_CARD_KEY':
case 'AWS_GIFT_CARD_SECRET':
case 'AWS_GIFT_CARD_PARTNER_ID':
case 'AWS_GIFT_CARD_ENDPOINT':
case 'AWS_GIFT_CARD_CURRENCY':
$return = $_ENV[$key] ?? '';
break;
default:
break;
}
return $return;
}
/**
* @return string
*/
public function getEndpoint(): string
{
return $this->endpoint;
}
/**
* @param string $endpoint
* @return ConfigInterface
*/
public function setEndpoint(string $endpoint): ConfigInterface
{
// TODO: check valid endpoint + set region
$this->endpoint = parse_url($endpoint, PHP_URL_HOST);
return $this;
}
/**
* @return string
*/
public function getAccessKey(): string
{
return $this->access_key;
}
/**
* @param string $key
* @return ConfigInterface
*/
public function setAccessKey(string $key): ConfigInterface
{
$this->access_key = $key;
return $this;
}
/**
* @return string
*/
public function getSecret(): string
{
return $this->secret_key;
}
/**
* @param string $secret
* @return ConfigInterface
*/
public function setSecret(string $secret): ConfigInterface
{
$this->secret_key = $secret;
return $this;
}
/**
* @return string
*/
public function getCurrency(): string
{
return $this->currency;
}
/**
* @param string $currency
* @return ConfigInterface
*/
public function setCurrency(string $currency): ConfigInterface
{
// TODO: check currency valid + currenc valid for region
$this->currency = $currency;
return $this;
}
/**
* @return string
*/
public function getPartner(): string
{
return $this->partner_id;
}
/**
* @param string $partner
* @return ConfigInterface
*/
public function setPartner(string $partner): ConfigInterface
{
$this->partner_id = $partner;
return $this;
}
/**
* @return bool
*/
public function getDebug(): bool
{
return $this->debug;
}
/**
* @param bool $debug
* @return ConfigInterface
*/
public function setDebug(bool $debug): ConfigInterface
{
$this->debug = $debug;
return $this;
}
}
// __END__

View File

@@ -1,74 +0,0 @@
<?php
namespace Amazon\Config;
interface ConfigInterface
{
/**
* @return String
*/
public function getEndpoint(): string;
/**
* @param string $endpoint
* @return $this
*/
public function setEndpoint(string $endpoint): ConfigInterface;
/**
* @return String
*/
public function getAccessKey(): string;
/**
* @param string $key
* @return $this
*/
public function setAccessKey(string $key): ConfigInterface;
/**
* @return String
*/
public function getSecret(): string;
/**
* @param string $secret
* @return $this
*/
public function setSecret(string $secret): ConfigInterface;
/**
* @return String
*/
public function getCurrency(): string;
/**
* @param string $currency
* @return $this
*/
public function setCurrency(string $currency): ConfigInterface;
/**
* @return String
*/
public function getPartner(): string;
/**
* @param string $partner
* @return $this
*/
public function setPartner(string $partner): ConfigInterface;
/**
* @return bool
*/
public function getDebug(): bool;
/**
* @param bool $debug
* @return $this
*/
public function setDebug(bool $debug): ConfigInterface;
}
// __END__

View File

@@ -1,56 +0,0 @@
<?php
// simple write all into an array that we can poll in the return group
namespace Amazon\Debug;
class AmazonDebug
{
private static $log = [];
private static $debug = false;
private static $id = null;
public function __construct()
{
}
public static function setId(?string $id = null): void
{
if (self::$debug === false) {
return;
}
if ($id === null) {
$id = uniqid();
}
self::$id = $id;
}
public static function getId(): string
{
return self::$id;
}
public static function setFlag(bool $debug): void
{
self::$debug = $debug;
}
public static function writeLog(array $data): void
{
if (self::$debug === false) {
return;
}
self::$log[self::$id][] = $data;
}
public static function getLog(?string $id = null): array
{
if ($id === null) {
return self::$log;
} else {
return self::$log[$id] ?? [];
}
}
}
// __END__

View File

@@ -1,40 +0,0 @@
<?php
namespace Amazon\Exceptions;
use RuntimeException;
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 string $_error_code
* @return AmazonErrors
*/
public static function getError(
string $error_status,
string $error_code,
string $error_type,
string $message,
string $_error_code
): self {
// NOTE: if xdebug.show_exception_trace is set to 1 this will print ERRORS
return new static(
json_encode([
'status' => $error_status,
'code' => $error_code,
'type' => $error_type,
'message' => $message,
// atach log data if exists
'log_id' => \Amazon\Debug\AmazonDebug::getId(),
'log' => \Amazon\Debug\AmazonDebug::getLog(),
]),
$_error_code
);
}
}
// __END__

View File

@@ -1,111 +0,0 @@
<?php
namespace Amazon\Response;
class CancelResponse
{
/**
* Amazon Gift Card gcId.
*
* @var string
*/
protected $id;
/**
* Amazon Gift Card creationRequestId
*
* @var string
*/
protected $creation_request_id;
/**
* Amazon Gift Card status
*
* @var string
*/
protected $status;
/**
* Amazon Gift Card Raw JSON
*
* @var string
*/
protected $raw_json;
/**
* @var array
*/
protected $log;
/**
* Response constructor.
* @param array $json_response
*/
public function __construct(array $json_response)
{
$this->raw_json = $json_response;
$this->log = \Amazon\Debug\AmazonDebug::getLog(\Amazon\Debug\AmazonDebug::getId());
$this->parseJsonResponse($json_response);
}
/**
* @return array
*/
public function getLog(): array
{
return $this->log;
}
/**
* @return string
*/
public function getId(): string
{
return $this->id;
}
/**
* @return string
*/
public function getCreationRequestId(): string
{
return $this->creation_request_id;
}
/**
* @return string
*/
public function getStatus(): string
{
return $this->status;
}
/**
* @return string
*/
public function getRawJson(): string
{
return json_encode($this->raw_json);
}
/**
* @param array $json_response
* @return CancelResponse
*/
public function parseJsonResponse(array $json_response): self
{
if (!is_array($json_response)) {
throw new \RuntimeException('Response must be a scalar value');
}
if (array_key_exists('gcId', $json_response)) {
$this->id = $json_response['gcId'];
}
if (array_key_exists('creationRequestId', $json_response)) {
$this->creation_request_id = $json_response['creationRequestId'];
}
// SUCCESS, FAILURE, RESEND
if (array_key_exists('status', $json_response)) {
$this->status = $json_response['status'];
}
return $this;
}
}
// __END__

View File

@@ -1,129 +0,0 @@
<?php
namespace Amazon\Response;
class CreateBalanceResponse
{
/**
* Amazon Gift Card Balance Amount
*
* @var string
*/
protected $amount;
/**
* Amazon Gift Card Balance Currency
*
* @var string
*/
protected $currency;
/**
* Amazon Gift Card Balance Status
*
* @var string
*/
protected $status;
/**
* Amazon Gift Card Balance Timestamp
*
* @var string
*/
protected $timestamp;
/**
* Amazon Gift Card Raw JSON
*
* @var string
*/
protected $raw_json;
/**
* @var array
*/
protected $log;
/**
* Response constructor.
*
* @param array $json_response
*/
public function __construct(array $json_response)
{
$this->raw_json = $json_response;
$this->log = \Amazon\Debug\AmazonDebug::getLog(\Amazon\Debug\AmazonDebug::getId());
$this->parseJsonResponse($json_response);
}
/**
* @return array
*/
public function getLog(): array
{
return $this->log;
}
/**
* @return string
*/
public function getAmount(): string
{
return $this->amount;
}
/**
* @return string
*/
public function getCurrency(): string
{
return $this->currency;
}
/**
* @return string
*/
public function getStatus(): string
{
return $this->status;
}
/**
* @return string
*/
public function getTimestamp(): string
{
return $this->timestamp;
}
/**
* @return string
*/
public function getRawJson(): string
{
return json_encode($this->raw_json);
}
/**
* Undocumented function
*
* @param array $json_response
* @return CreateBalanceResponse
*/
public function parseJsonResponse(array $json_response): self
{
if (!is_array($json_response)) {
throw new \RuntimeException('Response must be a scalar value');
}
if (array_key_exists('amount', $json_response['availableFunds'])) {
$this->amount = $json_response['availableFunds']['amount'];
}
if (array_key_exists('currencyCode', $json_response['availableFunds'])) {
$this->currency = $json_response['availableFunds']['currencyCode'];
}
// SUCCESS, FAILURE, RESEND
if (array_key_exists('status', $json_response)) {
$this->status = $json_response['status'];
}
if (array_key_exists('timestamp', $json_response)) {
$this->timestamp = $json_response['timestamp'];
}
return $this;
}
}

View File

@@ -1,201 +0,0 @@
<?php
namespace Amazon\Response;
class CreateResponse
{
/**
* Amazon Gift Card gcId.
*
* @var string
*/
protected $id;
/**
* Amazon Gift Card creationRequestId
*
* @var string
*/
protected $creation_request_id;
/**
* Amazon Gift Card gcClaimCode
*
* @var string
*/
protected $claim_code;
/**
* Amazon Gift Card amount
*
* @var float
*/
protected $value;
/**
* Amazon Gift Card currency
*
* @var string
*/
protected $currency;
/**
* Amazon Gift Card status
*
* @var string
*/
protected $status;
/**
* Amazon Gift Card Expiration Date
*
* @var string
*/
protected $expiration_date;
/**
* Amazon Gift Card Expiration Date
*
* @var string
*/
protected $card_status;
/**
* Amazon Gift Card Raw JSON
*
* @var string
*/
protected $raw_json;
/**
* @var array
*/
protected $log;
/**
* Response constructor.
* @param array $json_response
*/
public function __construct(array $json_response)
{
$this->raw_json = $json_response;
$this->log = \Amazon\Debug\AmazonDebug::getLog(\Amazon\Debug\AmazonDebug::getId());
$this->parseJsonResponse($json_response);
}
/**
* @return array
*/
public function getLog(): array
{
return $this->log;
}
/**
* @return string
*/
public function getId(): string
{
return $this->id;
}
/**
* @return string
*/
public function getCreationRequestId(): string
{
return $this->creation_request_id;
}
/**
* @return string
*/
public function getClaimCode(): string
{
return $this->claim_code;
}
/**
* @return string
*/
public function getValue(): string
{
return $this->value;
}
/**
* @return string
*/
public function getCurrency(): string
{
return $this->currency;
}
/**
* @return string
*/
public function getStatus(): string
{
return $this->status;
}
/**
* @return string
*/
public function getExpirationDate(): string
{
return $this->expiration_date;
}
/**
* @return string
*/
public function getCardStatus(): string
{
return $this->card_status;
}
/**
* @return string
*/
public function getRawJson(): string
{
return json_encode($this->raw_json);
}
/**
* @param array $json_response
* @return CreateResponse
*/
public function parseJsonResponse(array $json_response): self
{
if (!is_array($json_response)) {
throw new \RuntimeException('Response must be a scalar value');
}
if (array_key_exists('gcId', $json_response)) {
$this->id = $json_response['gcId'];
}
if (array_key_exists('creationRequestId', $json_response)) {
$this->creation_request_id = $json_response['creationRequestId'];
}
if (array_key_exists('gcClaimCode', $json_response)) {
$this->claim_code = $json_response['gcClaimCode'];
}
if (array_key_exists('amount', $json_response['cardInfo']['value'])) {
$this->value = $json_response['cardInfo']['value']['amount'];
}
if (array_key_exists('currencyCode', $json_response['cardInfo']['value'])) {
$this->currency = $json_response['cardInfo']['value']['currencyCode'];
}
if (array_key_exists('gcExpirationDate', $json_response)) {
$this->expiration_date = $json_response['gcExpirationDate'];
}
if (array_key_exists('cardStatus', $json_response['cardInfo'])) {
$this->card_status = $json_response['cardInfo']['cardStatus'];
}
// SUCCESS, FAILURE, RESEND
if (array_key_exists('status', $json_response)) {
$this->status = $json_response['status'];
}
return $this;
}
}
// __END__

171
src/AmazonIncentives.php Normal file
View File

@@ -0,0 +1,171 @@
<?php
/*
* Amazon Incentive Code
* Amazon Gift Code on Demand
*/
namespace gullevek\AmazonIncentives;
use gullevek\AmazonIncentives\AWS\AWS;
use gullevek\AmazonIncentives\Config\Config;
use gullevek\AmazonIncentives\Exceptions\AmazonErrors;
use gullevek\AmazonIncentives\Debug\AmazonDebug;
final class AmazonIncentives
{
/**
* @var Config
*/
private $config;
/**
* AmazonGiftCode constructor.
*
* @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
) {
// load AWS settings
// fail here if settings missing
$this->config = new Config($key, $secret, $partner, $endpoint, $currency, $debug);
// init debug
AmazonDebug::setDebug($this->config->getDebug());
}
// *********************************************************************
// PRIVATE HELPER METHODS
// *********************************************************************
// *********************************************************************
// PUBLIC METHODS
// *********************************************************************
/**
* 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
{
return ($this->newAWS())->getCode($value, $creation_request_id);
}
/**
* 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 ($this->newAWS())->cancelCode($creation_request_id, $gift_card_id);
}
/**
* Gets the current funds in this account
*
* @return Response\CreateBalanceResponse Returns the account funds object
*
* @throws AmazonErrors
*/
public function getAvailableFunds(): Response\CreateBalanceResponse
{
return ($this->newAWS())->getBalance();
}
/**
* AmazonIncentives creates own client and returns it as static object
*
* @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
): 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)
* message (Amazon returned error message string)
*
* @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
{
return AmazonErrors::decodeExceptionMessage($message);
}
// *********************************************************************
// PUBLIC TEST METHODS
// *********************************************************************
/**
* 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
{
$data = [];
$data['ENV'] = $_ENV;
$data['CONFIG'] = $this->config;
$data['KEY'] = $this->config->getAccessKey();
return $data;
}
}
// __END__

View File

@@ -1,27 +1,41 @@
<?php
namespace Amazon\Client;
declare(strict_types=1);
use Amazon\Exceptions\AmazonErrors;
use Amazon\Debug\AmazonDebug;
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 $headers Headers to be used in the request
* @param array|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
{
$handle = curl_init($url);
if ($handle === false) {
// throw Error here with all codes
throw AmazonErrors::getError(
'FAILURE',
'C001',
'CurlInitError',
'Failed to init curl with url: ' . $url,
0
);
}
curl_setopt($handle, CURLOPT_POST, true);
curl_setopt($handle, CURLOPT_HTTPHEADER, $headers);
// curl_setopt($handle, CURLOPT_FAILONERROR, true);
@@ -39,13 +53,18 @@ 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($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';
$error_code = 'T001';
$error_type = 'RateExceeded';
$message = $result_ar['message'];
$message = $result_ar['message'] ?? 'Rate exceeded';
} else {
// for all other error messages
$error_status = $result_ar['agcodResponse']['status'] ?? 'FAILURE';
@@ -62,15 +81,16 @@ class Client implements ClientInterface
$err
);
}
return $result;
return (string)$result;
}
/**
* Undocumented function
* handles any CURL errors and throws an error with the correct
* error message
*
* @param string $url
* @param string $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
@@ -79,25 +99,24 @@ class Client implements ClientInterface
case CURLE_COULDNT_CONNECT:
case CURLE_COULDNT_RESOLVE_HOST:
case CURLE_OPERATION_TIMEOUTED:
$msg = 'Could not connect to AWS (' . $url . '). Please check your '
. 'internet connection and try again.';
$message = 'Could not connect to AWS (' . $url . '). Please check your '
. 'internet connection and try again. [' . $message . ']';
break;
case CURLE_SSL_CACERT:
case CURLE_SSL_PEER_CERTIFICATE:
$msg = 'Could not verify AWS SSL certificate. Please make sure '
. 'that your network is not intercepting certificates. '
. '(Try going to ' . $url . 'in your browser.) '
. 'If this problem persists,';
$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:
default:
$msg = 'Unexpected error communicating with AWS. ' . $message;
$message = 'Unexpected error communicating with AWS: ' . $message;
}
// throw an error like in the normal reqeust, but set to CURL error
throw AmazonErrors::getError(
'FAILURE',
'C001',
'C002',
'CurlError',
$message,
$errno

View File

@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace gullevek\AmazonIncentives\Client;
interface ClientInterface
{
/**
* @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
*/
public function request(string $url, array $headers, $params): string;
}
// __END__

216
src/Config/Config.php Normal file
View File

@@ -0,0 +1,216 @@
<?php
declare(strict_types=1);
namespace gullevek\AmazonIncentives\Config;
class Config implements ConfigInterface
{
/** @var string Endpoint URL without https:// */
private $endpoint = '';
/** @var string Access Key */
private $access_key = '';
/** @var string Secret Key */
private $secret_key = '';
/** @var string Partner ID */
private $partner_id = '';
/** @var string Currency type as USD, JPY, etc */
private $currency = '';
/** @var bool Debug flag on or off */
private $debug = false;
/**
* @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,
?string $secret,
?string $partner,
?string $endpoint,
?string $currency,
?bool $debug
) {
/**
* @psalm-suppress InvalidScalarArgument
* @phpstan-ignore-next-line
*/
$this->setAccessKey(($key) ?: $this->parseEnv('AWS_GIFT_CARD_KEY'));
/**
* @psalm-suppress InvalidScalarArgument
* @phpstan-ignore-next-line
*/
$this->setSecret(($secret) ?: $this->parseEnv('AWS_GIFT_CARD_SECRET'));
/**
* @psalm-suppress InvalidScalarArgument
* @phpstan-ignore-next-line
*/
$this->setPartner(($partner) ?: $this->parseEnv('AWS_GIFT_CARD_PARTNER_ID'));
/**
* @psalm-suppress InvalidScalarArgument
* @phpstan-ignore-next-line
*/
$this->setEndpoint(($endpoint) ?: $this->parseEnv('AWS_GIFT_CARD_ENDPOINT'));
/**
* @psalm-suppress InvalidScalarArgument
* @phpstan-ignore-next-line
*/
$this->setCurrency(($currency) ?: $this->parseEnv('AWS_GIFT_CARD_CURRENCY'));
/**
* @psalm-suppress InvalidScalarArgument
* @phpstan-ignore-next-line
*/
$this->setDebug(($debug) ?: $this->parseEnv('AWS_DEBUG'));
}
/**
* 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)
*/
private function parseEnv(string $key)
{
$return = '';
switch ($key) {
case 'AWS_DEBUG':
$return = !empty($_ENV['AWS_DEBUG']) ? true : false;
break;
case 'AWS_GIFT_CARD_KEY':
case 'AWS_GIFT_CARD_SECRET':
case 'AWS_GIFT_CARD_PARTNER_ID':
case 'AWS_GIFT_CARD_ENDPOINT':
case 'AWS_GIFT_CARD_CURRENCY':
$return = !empty($_ENV[$key]) && is_string($_ENV[$key]) ?
$_ENV[$key] : '';
break;
default:
break;
}
return $return;
}
/**
* @return string Returns current set endpoint, without https://
*/
public function getEndpoint(): string
{
return $this->endpoint;
}
/**
* @param string $endpoint Full endpoint url with https://
* @return ConfigInterface Class interface (self)
*/
public function setEndpoint(string $endpoint): ConfigInterface
{
// TODO: check valid endpoint + set region
$this->endpoint = (parse_url($endpoint, PHP_URL_HOST)) ?: '';
return $this;
}
/**
* @return string Current access key
*/
public function getAccessKey(): string
{
return $this->access_key;
}
/**
* @param string $key Access Key to set
* @return ConfigInterface Class interface (self)
*/
public function setAccessKey(string $key): ConfigInterface
{
$this->access_key = $key;
return $this;
}
/**
* @return string Current secret key
*/
public function getSecret(): string
{
return $this->secret_key;
}
/**
* @param string $secret Secret key to set
* @return ConfigInterface Class interface (self)
*/
public function setSecret(string $secret): ConfigInterface
{
$this->secret_key = $secret;
return $this;
}
/**
* @return string Current set currency
*/
public function getCurrency(): string
{
return $this->currency;
}
/**
* @param string $currency Currency to set (eg USD, JPY, etc)
* @return ConfigInterface Class interface (self)
*/
public function setCurrency(string $currency): ConfigInterface
{
// TODO: check currency valid + currenc valid for region
$this->currency = $currency;
return $this;
}
/**
* @return string Current set partner id
*/
public function getPartner(): string
{
return $this->partner_id;
}
/**
* @param string $partner Partner id to set
* @return ConfigInterface Class interface (self)
*/
public function setPartner(string $partner): ConfigInterface
{
$this->partner_id = $partner;
return $this;
}
/**
* @return bool Current set debug flag as bool
*/
public function getDebug(): bool
{
return $this->debug;
}
/**
* @param bool $debug Set debug flag as bool
* @return ConfigInterface Class interface (self)
*/
public function setDebug(bool $debug): ConfigInterface
{
$this->debug = $debug;
return $this;
}
}
// __END__

View File

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

111
src/Debug/AmazonDebug.php Normal file
View File

@@ -0,0 +1,111 @@
<?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
namespace gullevek\AmazonIncentives\Debug;
class AmazonDebug
{
/** @var array<string,array<array<mixed>>> Log data array log id -> array of log entries */
private static $log = [];
/** @var bool debug flag */
private static $debug = false;
/** @var string|null Last set internal log array id */
private static $id = null;
/**
* set the ID for current run
* if debug is off, nothing will be set and id is null
* This is run on setFlag, if debug is true
*
* @param string|null $id If not set, will default to uniqid() call
* @return void
*/
private static function setId(?string $id = null): void
{
if (self::$debug === false) {
return;
}
if ($id === null) {
$id = uniqid();
}
self::$id = $id;
}
/**
* set the debug flag.
* This is automatically run in gullevek\AmazonIncentives\AmazonIncentives::__construct
* No need to run manuall
*
* @param boolean $debug Can only be True or False
* @param string|null $id If not set, will default to uniqid() call
* @return void
*/
public static function setDebug(bool $debug, ?string $id = null): void
{
self::$debug = $debug;
if (self::$debug === false) {
return;
}
self::setId($id);
}
/**
* returns current debug flag status
*
* @return boolean True if debug is on, False if debug is off
*/
public static function getDebug(): bool
{
return self::$debug;
}
/**
* get the current set ID, can return null if debug is off
*
* @return string|null Current set ID for this log run
*/
public static function getId(): ?string
{
return self::$id;
}
/**
* write a log entry
* Data is as array key -> value
* Will be pushed as new array entry int log
* Main key is the set Id for this run
*
* @param array<mixed> $data Any array data to store in the log
* @return void
*/
public static function writeLog(array $data): void
{
if (self::$debug === false) {
return;
}
self::$log[self::getId() ?? ''][] = $data;
}
/**
* get all logs written since first class run
* or get all log entries for given ID
*
* @param string|null $id If set returns only this id logs
* or empty array if not found
* @return array<mixed> Always array, empty if not data or not found
*/
public static function getLog(?string $id = null): array
{
if ($id === null) {
return self::$log;
} else {
return self::$log[$id] ?? [];
}
}
}
// __END__

View File

@@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace gullevek\AmazonIncentives\Exceptions;
use RuntimeException;
use gullevek\AmazonIncentives\Debug\AmazonDebug;
final class AmazonErrors extends RuntimeException
{
/**
* 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,
string $error_code,
string $error_type,
string $message,
int $_error_code
): self {
// NOTE: if xdebug.show_exception_trace is set to 1 this will print ERRORS
return new static(
(json_encode([
'status' => $error_status,
'code' => $error_code,
'type' => $error_type,
'message' => $message,
// 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

@@ -0,0 +1,105 @@
<?php
declare(strict_types=1);
namespace gullevek\AmazonIncentives\Response;
use gullevek\AmazonIncentives\Debug\AmazonDebug;
class CancelResponse
{
/** @var string Amazon Gift Card gcId (gift card id). */
protected $id = '';
/** @var string Amazon Gift Card creationRequestId (creation request id) */
protected $creation_request_id = '';
/** @var string Amazon Gift Card status */
protected $status = '';
/** @var array<mixed> Amazon Gift Card Raw JSON */
protected $raw_json = [];
/**
* Response constructor for canceling gitf cards
*
* @param array<mixed> $json_response JSON response from web request to AWS
*/
public function __construct(array $json_response)
{
$this->raw_json = $json_response;
$this->parseJsonResponse($json_response);
}
/**
* Get log entry with current set log id
*
* @return array<mixed> Log array
*/
public function getLog(): array
{
return AmazonDebug::getLog(AmazonDebug::getId());
}
/**
* The gift card id as created by the previous get code call
*
* @return string Gift card id
*/
public function getId(): string
{
return $this->id;
}
/**
* Creation Request id from original get code call
*
* @return string Creation request id
*/
public function getCreationRequestId(): string
{
return $this->creation_request_id;
}
/**
* 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
{
return (json_encode($this->raw_json)) ?: '';
}
/**
* 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
{
if (array_key_exists('gcId', $json_response)) {
$this->id = $json_response['gcId'];
}
if (array_key_exists('creationRequestId', $json_response)) {
$this->creation_request_id = $json_response['creationRequestId'];
}
// SUCCESS, FAILURE, RESEND
if (array_key_exists('status', $json_response)) {
$this->status = $json_response['status'];
}
return $this;
}
}
// __END__

View File

@@ -0,0 +1,127 @@
<?php
declare(strict_types=1);
namespace gullevek\AmazonIncentives\Response;
use gullevek\AmazonIncentives\Debug\AmazonDebug;
class CreateBalanceResponse
{
/** @var float Amazon Gift Card Balance Amount */
protected $amount = 0;
/** @var string Amazon Gift Card Balance Currency */
protected $currency = '';
/** @var string Amazon Gift Card Balance Status */
protected $status = '';
/** @var string Amazon Gift Card Balance Timestamp */
protected $timestamp = '';
/** @var array<mixed> Amazon Gift Card Raw JSON */
protected $raw_json = [];
/**
* Response constructor for requesting account funds status
*
* @param array<mixed> $json_response JSON response from web request to AWS
*/
public function __construct(array $json_response)
{
$this->raw_json = $json_response;
$this->parseJsonResponse($json_response);
}
/**
* Get log entry with current set log id
*
* @return array<mixed> Log array
*/
public function getLog(): array
{
return AmazonDebug::getLog(AmazonDebug::getId());
}
/**
* Return the current available funds amount
*
* @return float Funds amount in set currency
*/
public function getAmount(): float
{
return $this->amount;
}
/**
* Get the set currency type
*
* @return string Currency type. Eg USD, JPY, etc
*/
public function getCurrency(): string
{
return $this->currency;
}
/**
* Get timestamp as set.
* eg 20220609T061446Z
*
* @return string Timestamp string. Ymd\THis\Z
*/
public function getTimestamp(): string
{
return $this->timestamp;
}
/**
* 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
{
return (json_encode($this->raw_json)) ?: '';
}
/**
* Set class variables with response data from makeRequest and return self
*
* @param array<mixed> $json_response JSON response as array
* @return CreateBalanceResponse Return self object
*/
public function parseJsonResponse(array $json_response): self
{
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 (
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
if (array_key_exists('status', $json_response)) {
$this->status = $json_response['status'];
}
if (array_key_exists('timestamp', $json_response)) {
$this->timestamp = $json_response['timestamp'];
}
return $this;
}
}

View File

@@ -0,0 +1,184 @@
<?php
declare(strict_types=1);
namespace gullevek\AmazonIncentives\Response;
use gullevek\AmazonIncentives\Debug\AmazonDebug;
class CreateResponse
{
/** @var string Amazon Gift Card gcId */
protected $id = '';
/** @var string Amazon Gift Card creationRequestId */
protected $creation_request_id = '';
/** @var string Amazon Gift Card gcClaimCode */
protected $claim_code = '';
/** @var float Amazon Gift Card amount */
protected $value = 0;
/** @var string Amazon Gift Card currency */
protected $currency = '';
/** @var string Amazon Gift Card status */
protected $status = '';
/** @var string Amazon Gift Card Expiration Date */
protected $expiration_date = '';
/** @var string Amazon Gift Card Expiration Date */
protected $card_status = '';
/** @var array<mixed> Amazon Gift Card Raw JSON as array */
protected $raw_json = [];
/**
* Response constructor for creating gift cards
*
* @param array<mixed> $json_response JSON response from web request to AWS
*/
public function __construct(array $json_response)
{
$this->raw_json = $json_response;
$this->parseJsonResponse($json_response);
}
/**
* Get log entry with current set log id
*
* @return array<mixed> Log array
*/
public function getLog(): array
{
return AmazonDebug::getLog(AmazonDebug::getId());
}
/**
* Gift Card ID returned from AWS. Can be used in the cancel request
*
* @return string Gift card id
*/
public function getId(): string
{
return $this->id;
}
/**
* Either the one set with the method parameter, or automatically created
* during get code request
*
* @return string Creation request id
*/
public function getCreationRequestId(): string
{
return $this->creation_request_id;
}
/**
* 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
{
return $this->claim_code;
}
/**
* The ordered gift code value in given currency
*
* @return float Gift order value in currency
*/
public function getValue(): float
{
return $this->value;
}
/**
* The currently set currency
*
* @return string Currency type. Eg USD, JPY, etc
*/
public function getCurrency(): string
{
return $this->currency;
}
/**
* 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
{
return $this->expiration_date;
}
/**
* 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;
}
/**
* @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
{
return (json_encode($this->raw_json)) ?: '';
}
/**
* 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
{
if (array_key_exists('gcId', $json_response)) {
$this->id = $json_response['gcId'];
}
if (array_key_exists('creationRequestId', $json_response)) {
$this->creation_request_id = $json_response['creationRequestId'];
}
if (array_key_exists('gcClaimCode', $json_response)) {
$this->claim_code = $json_response['gcClaimCode'];
}
if (array_key_exists('amount', $json_response['cardInfo']['value'])) {
$this->value = $json_response['cardInfo']['value']['amount'];
}
if (array_key_exists('currencyCode', $json_response['cardInfo']['value'])) {
$this->currency = $json_response['cardInfo']['value']['currencyCode'];
}
if (array_key_exists('gcExpirationDate', $json_response)) {
$this->expiration_date = $json_response['gcExpirationDate'];
}
if (array_key_exists('cardStatus', $json_response['cardInfo'])) {
$this->card_status = $json_response['cardInfo']['cardStatus'];
}
// SUCCESS, FAILURE, RESEND
if (array_key_exists('status', $json_response)) {
$this->status = $json_response['status'];
}
return $this;
}
}
// __END__

7
test/.env.example Normal file
View File

@@ -0,0 +1,7 @@
# for AWS gift card testing
AWS_GIFT_CARD_ENDPOINT=
AWS_GIFT_CARD_KEY=
AWS_GIFT_CARD_SECRET=
AWS_GIFT_CARD_PARTNER_ID=
AWS_GIFT_CARD_CURRENCY=
AWS_DEBUG=

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

@@ -1,63 +0,0 @@
<?php // phpcs:ignore PSR1.Files.SideEffects
declare(strict_types=1);
namespace Autoloader;
// shall implement an auto loader
if (class_exists('Autoload', false) === false) {
// define the auto loader class
class Autoload
{
// we do it simple here
// passes on the class to load and we search here in namespace
// to load that class
public static function load($class)
{
// print "(1) Class: $class / DIR: " . __DIR__ . "<br>";
// set directory seperator (we need to replace from namespace)
$DS = DIRECTORY_SEPARATOR;
// base lib
$LIB = defined('LIB') ? LIB : '../src' . $DS;
// if lib is in path at the end, do not add lib again
// note that $LIB can have a directory seperator at the end
// strip that out before we do a match
$_LIB = rtrim($LIB, $DS);
if (!preg_match("|$_LIB$|", __DIR__)) {
$LIB .= $DS;
} else {
$LIB = '';
}
// default path is unset
$path = false;
// set path on full dir
// if we have the namespace in the class, strip it out
$len = 0;
if (strpos($class, __NAMESPACE__) !== false) {
$len = strlen(__NAMESPACE__);
}
// set default extension
$extension = '.php';
// set full include path
$path = __DIR__ . $DS . $LIB . substr($class, $len);
// replace namespace \ with dir sepeator
$path = str_replace('\\', $DS, $path) . $extension;
// print "(2) Class clean: $path<br>";
// if path is set and a valid file
if ($path !== false && is_file($path)) {
// print "<b>(3)</b> Load Path: $path<br>";
// we should sub that
// self::loadFile($path);
include $path;
return true;
}
return false;
}
// end class define
}
spl_autoload_register('Autoloader\Autoload::load', true, true);
} // end check for already defined
// __END__

View File

@@ -1,14 +1,69 @@
<?php // phpcs:ignore PSR1.Files.SideEffects
// test for Amazon Gift Card Incentives
// Tests for Amazon Gift Card Incentives
// general auto loader
require 'autoloader.php';
// env file loader
require 'read_env_file.php';
/**
* write log as string from array data
* includes timestamp
*
* @param array<mixed> $data Debug log array data to add to the json string
* @return string
*/
function writeLog(array $data): string
{
return json_encode([
'date' => date('Y-m-d H:i:s'),
'log' => $data
]) . "\n";
}
/**
* translate the UTC amazon date string to Y-m-d H:i:s standard
*
* @param string $date A UTC string date from Amazon
* @return string
*/
function dateTr(string $date): string
{
return date('Y-m-d H:i:s', (strtotime($date)) ?: null);
}
/**
* print exception string
*
* @param string $call_request Call request, eg buyGiftCard
* @param integer $error_code $e Exception error code
* @param array<mixed> $error Array from the Exception message json string
* @param boolean $debug_print If we should show the debug log
* @return void
*/
function printException(
string $call_request,
int $error_code,
array $error,
bool $debug_print
): void {
print "AWS: " . $call_request . ": " . $error['status']
. " [" . $error_code . "]: "
. $error['code'] . " | " . $error['type']
. " | " . $error['message'];
if ($debug_print === true) {
print "<pre>" . print_r($error['log'][$error['log_id'] ?? ''] ?? [], true) . "</pre>";
}
}
// composer auto loader
$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\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>";
@@ -17,89 +72,64 @@ print "<h1>Amazon Gift Card Incentives</h1><br>";
// aws key: AWS_GIFT_CARD_KEY
// aws secret: AWS_GIFT_CARD_SECRET
// partner id: AWS_GIFT_CARD_PARTNER_ID
// optional
// currency: AWS_ICENTIVE_CURRENCY
// optional
// debug: AWS_DEBUG (if not set: off)
// as in .env
// AWS_GIFT_CARD_ENDPOINT.TEST
// AWS_GIFT_CARD_ENDPOINT.LIVE
define('LOCATION', 'test');
foreach (
[
'AWS_GIFT_CARD_KEY', 'AWS_GIFT_CARD_SECRET', 'AWS_GIFT_CARD_PARTNER_ID',
'AWS_GIFT_CARD_ENDPOINT', 'AWS_GIFT_CARD_CURRENCY', 'AWS_DEBUG'
] as $key
) {
//
$_ENV[$key] = $_ENV[$key . '.' . strtoupper((LOCATION))] ?? $_ENV[$key] ?? '';
}
/*
ENDPOINTS:
- remove '-gamma' for non sandox
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
(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
(JP, AU, SG)
CURRENCY
USD for US
EUR for EU
JPY for JP
CAD for CA
AUD for AU
TRY for TR
AED for UAE
*/
// run info test (prints ENV vars)
$run_info_test = !empty($_GET['info']) ? true : false;
// run test to get funds info
$run_fund_test = false;
$run_fund_test = !empty($_GET['fund']) ? true : false;
// run the normal get/cancel gift card tests
$run_gift_tests = true;
$run_gift_tests = !empty($_GET['gift']) ? true : false;
// run mock error check tests
$run_mocks = false;
$run_mocks = !empty($_GET['mocks']) ? true : false;
// should we print debug info
$debug_print = false;
$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 = false;
$mock_debug = !empty($_GET['debug_mock']) ? true : false;
// wait in seconds between mock tests
$mock_wait = 2;
$aws = new Amazon\AmazonIncentives();
// $aws->createGiftCard(100);
print "checkMe: <pre>" . print_r($aws->checkMe(), true) . "</pre>";
print "<hr>";
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");
}
// we should open log file to collect all creationRequestId/gcId
// so we can test and cancel
if ($run_info_test === true) {
$aws = new AmazonIncentives();
$aws_check_me = $aws->checkMe();
print "checkMe: <pre>" . print_r($aws_check_me, true) . "</pre>";
fwrite($fp, writeLog($aws_check_me));
print "<hr>";
}
// check balance
if ($run_fund_test === true) {
try {
$aws_test = Amazon\AmazonIncentives::make()->getAvailableFunds();
print "AWS: getAvailableFunds: <pre>" . print_r($aws_test, true) . "</pre><br>";
} catch (Exception $e) {
print "AWS: getAvailableFunds: " . $error['status']
. " [" . $e->getCode() . "]: "
. $error['code'] . " | " . $error['type']
. " | " . $error['message'] . ": ";
$aws_test = AmazonIncentives::make()->getAvailableFunds();
print "AWS: getAvailableFunds: " . $aws_test->getStatus() . ": "
. "Amount: " . $aws_test->getAmount() . ", "
. "Currency: " . $aws_test->getCurrency() . ", "
. "Timestamp: " . $aws_test->getTimestamp();
if ($debug_print === true) {
print "/<pre>" . print_r($error['log'][$error['log_id'] ?? ''] ?? [], true) . "</pre>";
print "<pre>" . print_r($aws_test, true) . "</pre>";
}
fwrite($fp, writeLog((array)$aws_test));
} catch (Exception $e) {
$error = AmazonErrors::decodeExceptionMessage($e->getMessage());
printException('getAvailableFunds', $e->getCode(), $error, $debug_print);
fwrite($fp, writeLog($error));
};
print "<br>";
sleep($debug_wait);
// print "LOG: <pre>" . print_r($aws_test->getLog(), true) . "</pre><br>";
print "<hr>";
@@ -108,62 +138,133 @@ if ($run_fund_test === true) {
if ($run_gift_tests === true) {
// create card
$value = 1000;
// we must be sure we pass FLOAT there
$aws_test = Amazon\AmazonIncentives::make()->buyGiftCard((float)$value);
$creation_request_id = $aws_test->getCreationRequestId();
$gift_card_id = $aws_test->getId();
$claim_code = $aws_test->getClaimCode();
$request_status = $aws_test->getStatus();
print "AWS: buyGiftCard: " . $request_status . ": "
. "creationRequestId: " . $creation_request_id . ", gcId: " . $gift_card_id . ", "
. "CLAIM CODE: <b>" . $claim_code . "</b><br>";
if ($debug_print === true) {
print "<pre>" . print_r($aws_test, true) . "</pre><br>";
$creation_request_id = '';
$gift_card_id = '';
try {
// we must be sure we pass FLOAT there
$aws_test = AmazonIncentives::make()->buyGiftCard((float)$value);
$creation_request_id = $aws_test->getCreationRequestId();
$gift_card_id = $aws_test->getId();
$claim_code = $aws_test->getClaimCode();
$expiration_date = $aws_test->getExpirationDate();
$request_status = $aws_test->getStatus();
print "AWS: buyGiftCard: " . $request_status . ": "
. "creationRequestId: " . $creation_request_id . ", gcId: " . $gift_card_id . ", "
. "EXPIRE DATE: <b>" . dateTr($expiration_date) . "</b>, "
. "CLAIM CODE: <b>" . $claim_code . "</b>";
if ($debug_print === true) {
print "<pre>" . print_r($aws_test, true) . "</pre>";
}
fwrite($fp, writeLog((array)$aws_test));
} catch (\Exception $e) {
$error = AmazonErrors::decodeExceptionMessage($e->getMessage());
printException('buyGiftCard', $e->getCode(), $error, $debug_print);
fwrite($fp, writeLog($error));
}
print "<br>";
sleep($debug_wait);
// cancel above created card card
$aws_test = Amazon\AmazonIncentives::make()->cancelGiftCard($creation_request_id, $gift_card_id);
$request_status = $aws_test->getStatus();
print "AWS: cancelGiftCard: " . $request_status . ": "
. "creationRequestId: " . $creation_request_id . ", gcId: " . $gift_card_id
. "<br>";
if ($debug_print === true) {
print "<pre>" . print_r($aws_test, true) . "</pre><br>";
try {
// cancel above created card card
$aws_test = AmazonIncentives::make()->cancelGiftCard($creation_request_id, $gift_card_id);
$request_status = $aws_test->getStatus();
print "AWS: cancelGiftCard: " . $request_status . ": "
. "creationRequestId: " . $creation_request_id . ", gcId: " . $gift_card_id;
if ($debug_print === true) {
print "<pre>" . print_r($aws_test, true) . "</pre>";
}
fwrite($fp, writeLog((array)$aws_test));
} catch (\Exception $e) {
$error = AmazonErrors::decodeExceptionMessage($e->getMessage());
print "AWS: cancelGiftCard: " . $error['status']
. " [" . $e->getCode() . "]: "
. $error['code'] . " | " . $error['type']
. " | " . $error['message'];
if ($debug_print === true) {
print "<pre>" . print_r($error['log'][$error['log_id'] ?? ''] ?? [], true) . "</pre>";
}
fwrite($fp, writeLog($error));
}
print "<br>";
sleep($debug_wait);
// request same card again and get error
try {
$aws_test = AmazonIncentives::make()->buyGiftCard((float)$value, $creation_request_id);
$request_status = $aws_test->getStatus();
// same?
$claim_code = $aws_test->getClaimCode();
$expiration_date = $aws_test->getExpirationDate();
print "AWS: buyGiftCard: CANCLED SAME CODE AGAIN: " . $request_status . ": "
. "creationRequestId: " . $creation_request_id . ", gcId: " . $gift_card_id . ", "
. "EXPIRE DATE: <b>" . dateTr($expiration_date) . "</b>, "
. "CLAIM CODE: <b>" . $claim_code . "</b>";
if ($debug_print === true) {
print "<pre>" . print_r($aws_test, true) . "</pre>";
}
fwrite($fp, writeLog((array)$aws_test));
} catch (\Exception $e) {
$error = AmazonErrors::decodeExceptionMessage($e->getMessage());
printException('buyGiftCard', $e->getCode(), $error, $debug_print);
fwrite($fp, writeLog($error));
}
print "<br>";
sleep($debug_wait);
// set same request ID twice to get same response test
$aws_test = Amazon\AmazonIncentives::make()->buyGiftCard((float)$value);
$creation_request_id = $aws_test->getCreationRequestId();
$gift_card_id = $aws_test->getId();
$claim_code = $aws_test->getClaimCode();
$request_status = $aws_test->getStatus();
print "AWS: buyGiftCard: CODE A: " . $request_status . ": "
. "creationRequestId: " . $creation_request_id . ", gcId: " . $gift_card_id . ", "
. "CLAIM CODE: <b>" . $claim_code . "</b><br>";
if ($debug_print === true) {
print "<pre>" . print_r($aws_test, true) . "</pre><br>";
try {
$aws_test = AmazonIncentives::make()->buyGiftCard((float)$value);
$creation_request_id = $aws_test->getCreationRequestId();
$gift_card_id = $aws_test->getId();
$claim_code = $aws_test->getClaimCode();
$expiration_date = $aws_test->getExpirationDate();
$request_status = $aws_test->getStatus();
print "AWS: buyGiftCard: CODE A: " . $request_status . ": "
. "creationRequestId: " . $creation_request_id . ", gcId: " . $gift_card_id . ", "
. "EXPIRE DATE: <b>" . dateTr($expiration_date) . "</b>, "
. "CLAIM CODE: <b>" . $claim_code . "</b>";
if ($debug_print === true) {
print "<pre>" . print_r($aws_test, true) . "</pre>";
}
fwrite($fp, writeLog((array)$aws_test));
} catch (\Exception $e) {
$error = AmazonErrors::decodeExceptionMessage($e->getMessage());
printException('cancelGiftCard', $e->getCode(), $error, $debug_print);
fwrite($fp, writeLog($error));
}
print "<br>";
sleep($debug_wait);
$aws_test = Amazon\AmazonIncentives::make()->buyGiftCard((float)$value, $creation_request_id);
$request_status = $aws_test->getStatus();
print "AWS: buyGiftCard: SAME CODE A AGAIN: " . $request_status . ": "
. "creationRequestId: " . $creation_request_id . ", gcId: " . $gift_card_id . ", "
. "CLAIM CODE: <b>" . $claim_code . "</b><br>";
if ($debug_print === true) {
print "<pre>" . print_r($aws_test, true) . "</pre><br>";
try {
$aws_test = AmazonIncentives::make()->buyGiftCard((float)$value, $creation_request_id);
$request_status = $aws_test->getStatus();
// same?
$claim_code = $aws_test->getClaimCode();
$expiration_date = $aws_test->getExpirationDate();
print "AWS: buyGiftCard: SAME CODE A AGAIN: " . $request_status . ": "
. "creationRequestId: " . $creation_request_id . ", gcId: " . $gift_card_id . ", "
. "EXPIRE DATE: <b>" . dateTr($expiration_date) . "</b>, "
. "CLAIM CODE: <b>" . $claim_code . "</b>";
if ($debug_print === true) {
print "<pre>" . print_r($aws_test, true) . "</pre>";
}
fwrite($fp, writeLog((array)$aws_test));
} catch (\Exception $e) {
$error = AmazonErrors::decodeExceptionMessage($e->getMessage());
printException('buyGiftCard', $e->getCode(), $error, $debug_print);
fwrite($fp, writeLog($error));
}
print "<br>";
print "<hr>";
sleep($debug_wait);
}
// MOCK TEST
if ($mock_debug === true) {
if ($run_mocks === true) {
$mock_ok = '<span style="color:green;">MOCK OK</span>';
$mock_failure = '<span style="color:red;">MOCK FAILURE</span>';
$mock_value = 500;
$mock = [];
$mock['F0000'] = [ 'ret' => '', 'st' => 'SUCCESS']; // success mock
$mock['F1000'] = [ 'ret' => 'F100', 'st' => 'FAILURE']; // SimpleAmountIsNull, etc
$mock['F2003'] = [ 'ret' => 'F200', 'st' => 'FAILURE']; // InvalidAmountInput
$mock['F2004'] = [ 'ret' => 'F200', 'st' => 'FAILURE']; // InvalidAmountValue
$mock['F2005'] = [ 'ret' => 'F200', 'st' => 'FAILURE']; // InvalidCurrencyCodeInput
@@ -181,12 +282,12 @@ if ($mock_debug === true) {
foreach ($mock as $creation_id => $mock_return) {
print "<b>TS: " . microtime() . "</b>: ";
try {
$aws_test = Amazon\AmazonIncentives::make()->buyGiftCard((float)$mock_value, $creation_id);
$aws_test = AmazonIncentives::make()->buyGiftCard((float)$mock_value, $creation_id);
$creation_request_id = $aws_test->getCreationRequestId();
$gift_card_id = $aws_test->getId();
$claim_code = $aws_test->getClaimCode();
$request_status = $aws_test->getStatus();
print "AWS: MOCK: " . $creation_id . ": buyGiftCard: " . $request_status . ": "
print "AWS: MOCK: " . $creation_id . ": buyGiftCard: <b>" . $request_status . "</b>: "
. "creationRequestId: " . $creation_request_id . ", gcId: " . $gift_card_id . ", "
. "CLAIM CODE: <b>" . $claim_code . "</b>: ";
if ($mock_return['st'] == $request_status) {
@@ -197,8 +298,9 @@ if ($mock_debug === true) {
if ($mock_debug === true) {
print "<pre>" . print_r($aws_test, true) . "</pre>";
}
fwrite($fp, writeLog((array)$aws_test));
} catch (Exception $e) {
$error = Amazon\AmazonIncentives::decodeExceptionMessage($e->getMessage());
$error = AmazonErrors::decodeExceptionMessage($e->getMessage());
print "AWS: MOCK: " . $creation_id . ": buyGiftCard: " . $error['status']
. " [" . $e->getCode() . "]: "
. $error['code'] . " | " . $error['type']
@@ -212,8 +314,9 @@ if ($mock_debug === true) {
print $mock_failure;
}
if ($mock_debug === true) {
print "/<pre>" . print_r($error['log'][$error['log_id'] ?? ''] ?? [], true) . "</pre>";
print "<pre>" . print_r($error['log'][$error['log_id'] ?? ''] ?? [], true) . "</pre>";
}
fwrite($fp, writeLog($error));
}
print "<br>";
// Waiting a moment, so we don't flood
@@ -222,4 +325,6 @@ if ($mock_debug === true) {
print "<hr>";
}
fclose($fp);
// __END__

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__

2
test/log/.gitignore vendored Normal file
View File

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

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