Initial commit
This commit is contained in:
		
						commit
						7f4b49d98d
					
				
							
								
								
									
										15
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | |||||||
|  | ; This file is for unifying the coding style for different editors and IDEs. | ||||||
|  | ; More information at http://editorconfig.org | ||||||
|  | 
 | ||||||
|  | root = true | ||||||
|  | 
 | ||||||
|  | [*] | ||||||
|  | charset = utf-8 | ||||||
|  | indent_size = 4 | ||||||
|  | indent_style = space | ||||||
|  | end_of_line = lf | ||||||
|  | insert_final_newline = true | ||||||
|  | trim_trailing_whitespace = true | ||||||
|  | 
 | ||||||
|  | [*.md] | ||||||
|  | trim_trailing_whitespace = false | ||||||
							
								
								
									
										11
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | # Path-based git attributes | ||||||
|  | # https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html | ||||||
|  | 
 | ||||||
|  | # Ignore all test and documentation with "export-ignore". | ||||||
|  | /.gitattributes     export-ignore | ||||||
|  | /.gitignore         export-ignore | ||||||
|  | /.travis.yml        export-ignore | ||||||
|  | /phpunit.xml.dist   export-ignore | ||||||
|  | /.scrutinizer.yml   export-ignore | ||||||
|  | /tests              export-ignore | ||||||
|  | /.editorconfig      export-ignore | ||||||
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | build | ||||||
|  | composer.lock | ||||||
|  | vendor | ||||||
|  | coverage.clover | ||||||
|  | .phpunit.result.cache | ||||||
							
								
								
									
										18
									
								
								.scrutinizer.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								.scrutinizer.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | |||||||
|  | filter: | ||||||
|  |     excluded_paths: [tests/*] | ||||||
|  | 
 | ||||||
|  | checks: | ||||||
|  |     php: | ||||||
|  |         remove_extra_empty_lines: true | ||||||
|  |         remove_php_closing_tag: true | ||||||
|  |         remove_trailing_whitespace: true | ||||||
|  |         fix_use_statements: | ||||||
|  |             remove_unused: true | ||||||
|  |             preserve_multiple: false | ||||||
|  |             preserve_blanklines: true | ||||||
|  |             order_alphabetically: true | ||||||
|  |         fix_php_opening_tag: true | ||||||
|  |         fix_linefeed: true | ||||||
|  |         fix_line_ending: true | ||||||
|  |         fix_identation_4spaces: true | ||||||
|  |         fix_doc_comments: true | ||||||
							
								
								
									
										4
									
								
								.styleci.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.styleci.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | |||||||
|  | preset: laravel | ||||||
|  | 
 | ||||||
|  | disabled: | ||||||
|  |   - single_class_element_per_statement | ||||||
							
								
								
									
										35
									
								
								.travis.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								.travis.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | |||||||
|  | language: php | ||||||
|  | 
 | ||||||
|  | cache: | ||||||
|  |     directories: | ||||||
|  |         - $HOME/.composer/cache | ||||||
|  | 
 | ||||||
|  | matrix: | ||||||
|  |     fast_finish: true | ||||||
|  |     include: | ||||||
|  |         - php: 7.2 | ||||||
|  |           env: LARAVEL='5.8.*' TESTBENCH='3.8.*' COMPOSER_FLAGS='--prefer-lowest' | ||||||
|  |         - php: 7.2 | ||||||
|  |           env: LARAVEL='5.8.*' TESTBENCH='3.8.*' COMPOSER_FLAGS='--prefer-stable' | ||||||
|  |         - php: 7.3 | ||||||
|  |           env: LARAVEL='5.8.*' TESTBENCH='3.8.*' COMPOSER_FLAGS='--prefer-lowest' | ||||||
|  |         - php: 7.3 | ||||||
|  |           env: LARAVEL='5.8.*' TESTBENCH='3.8.*' COMPOSER_FLAGS='--prefer-stable' | ||||||
|  |         - php: 7.2 | ||||||
|  |           env: LARAVEL='6.*' TESTBENCH='4.*' COMPOSER_FLAGS='--prefer-lowest' | ||||||
|  |         - php: 7.2 | ||||||
|  |           env: LARAVEL='6.*' TESTBENCH='4.*' COMPOSER_FLAGS='--prefer-stable' | ||||||
|  |         - php: 7.3 | ||||||
|  |           env: LARAVEL='6.*' TESTBENCH='4.*' COMPOSER_FLAGS='--prefer-lowest' | ||||||
|  |         - php: 7.3 | ||||||
|  |           env: LARAVEL='6.*' TESTBENCH='4.*' COMPOSER_FLAGS='--prefer-stable' | ||||||
|  | 
 | ||||||
|  | before_install: | ||||||
|  |     - travis_retry composer self-update | ||||||
|  |     - travis_retry composer require --no-update --no-interaction "illuminate/support:${LARAVEL}" "orchestra/testbench:${TESTBENCH}" | ||||||
|  | 
 | ||||||
|  | install: | ||||||
|  |     - travis_retry composer update ${COMPOSER_FLAGS} --prefer-dist --no-interaction --no-suggest | ||||||
|  | 
 | ||||||
|  | script: | ||||||
|  |     - vendor/bin/phpunit | ||||||
							
								
								
									
										0
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								CONTRIBUTING.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								CONTRIBUTING.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										21
									
								
								LICENSE.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								LICENSE.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | |||||||
|  | The MIT License (MIT) | ||||||
|  | 
 | ||||||
|  | Copyright (c) App-vise V.O.F. <info@app-vise.nl> | ||||||
|  | 
 | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  | of this software and associated documentation files (the "Software"), to deal | ||||||
|  | in the Software without restriction, including without limitation the rights | ||||||
|  | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  | copies of the Software, and to permit persons to whom the Software is | ||||||
|  | furnished to do so, subject to the following conditions: | ||||||
|  | 
 | ||||||
|  | The above copyright notice and this permission notice shall be included in | ||||||
|  | all copies or substantial portions of the Software. | ||||||
|  | 
 | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  | THE SOFTWARE. | ||||||
							
								
								
									
										47
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | |||||||
|  | # Handle Appstore server-to-server notifications for auto-renewable subscriptions | ||||||
|  | 
 | ||||||
|  | [](https://packagist.org/packages/app-vise/laravel-appstore-notifications) | ||||||
|  | [](https://travis-ci.org/app-vise/laravel-appstore-notifications) | ||||||
|  | [](https://styleci.io/repos/105920179) | ||||||
|  | [](https://scrutinizer-ci.com/g/app-vise/laravel-appstore-notifications) | ||||||
|  | [](https://packagist.org/packages/spatie/laravel-appstore-notifications) | ||||||
|  | 
 | ||||||
|  | ## Installation | ||||||
|  | You can install this package via composer | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | composer require app-vise/laravel-appstore-server-notifications  | ||||||
|  |  ``` | ||||||
|  | 
 | ||||||
|  | The service provider will register itself. | ||||||
|  | You have to publish the config file with: | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | php artisan vendor:publish --provider="Appvise\AppStoreNotifications\NotificationsServiceProvider" --tag="config"  | ||||||
|  |  ``` | ||||||
|  | 
 | ||||||
|  | ## Usage | ||||||
|  | 
 | ||||||
|  | ## Changelog | ||||||
|  | 
 | ||||||
|  | Please see CHANGELOG for more information about what has changed recently. | ||||||
|  | 
 | ||||||
|  | ## Testing | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | composer test | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Security | ||||||
|  | 
 | ||||||
|  | If you discover any security related issues, please email daan@app-vise.nl instead of using the issue tracker. | ||||||
|  | 
 | ||||||
|  | ## Credits | ||||||
|  | 
 | ||||||
|  | - [Daan Geurts](https://github.com/DaanGeurts) | ||||||
|  | - [All Contributors](../../contributors) | ||||||
|  | 
 | ||||||
|  | A big thanks to [Spatie's](https://spatie.be) laravel-stripe-webhooks which was a huge inspiration and starting point for this package | ||||||
|  | ## License | ||||||
|  | 
 | ||||||
|  | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. | ||||||
							
								
								
									
										0
									
								
								UPGRADING.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								UPGRADING.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										53
									
								
								composer.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								composer.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | |||||||
|  | { | ||||||
|  |     "name": "app-vise/laravel-appstore-server-notifications", | ||||||
|  |     "description": "Handling Appstore server to server notifications", | ||||||
|  |     "keywords": [ | ||||||
|  |         "app-vise", | ||||||
|  |         "appvise", | ||||||
|  |         "laravel-appstore-server-to-server-notifications", | ||||||
|  |         "laravel-appstore-server-notifications", | ||||||
|  |         "laravel in app subscriptions", | ||||||
|  |         "laravel-appstore-server-notifications" | ||||||
|  |     ], | ||||||
|  |     "license": "MIT", | ||||||
|  |     "authors": [ | ||||||
|  |         { | ||||||
|  |             "name": "Daan Geurts", | ||||||
|  |             "email": "daan@app-vise.nl", | ||||||
|  |             "homepage": "https://www.app-vise.nl", | ||||||
|  |             "role": "Developer" | ||||||
|  |         } | ||||||
|  |     ], | ||||||
|  |     "require": { | ||||||
|  |         "php": "^7.2", | ||||||
|  |         "illuminate/support": "~5.7.0|^6.0", | ||||||
|  |         "bensampo/laravel-enum": "^1.0" | ||||||
|  |     }, | ||||||
|  |     "require-dev": { | ||||||
|  |         "orchestra/testbench": "~3.8.0|^4.0", | ||||||
|  |         "phpunit/phpunit": "^8.2" | ||||||
|  |     }, | ||||||
|  |     "autoload": { | ||||||
|  |         "psr-4": { | ||||||
|  |             "Appvise\\AppStoreNotifications\\": "src" | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     "autoload-dev": { | ||||||
|  |         "psr-4": { | ||||||
|  |             "Appvise\\AppStoreNotifications\\Tests\\": "tests" | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     "scripts": { | ||||||
|  |         "test": "vendor/bin/phpunit --verbose" | ||||||
|  |     }, | ||||||
|  |     "config": { | ||||||
|  |         "sort-packages": true | ||||||
|  |     }, | ||||||
|  |     "extra": { | ||||||
|  |         "laravel": { | ||||||
|  |             "providers": [ | ||||||
|  |                 "Appvise\\AppStoreNotifications\\NotificationsServiceProvider" | ||||||
|  |             ] | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										24
									
								
								config/appstore-server-notifications.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								config/appstore-server-notifications.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | |||||||
|  | <?php | ||||||
|  | return [ | ||||||
|  |     /* | ||||||
|  |      * Apple will send the shared secret with the request that should match | ||||||
|  |      * the one you use when validating receipts. | ||||||
|  |      * https://developer.apple.com/documentation/storekit/in-app_purchase/enabling_server-to-server_notifications?language=objc#overview
 | ||||||
|  |      */ | ||||||
|  |     'shared_secret' => env('APPLE_SHARED_SECRET'), | ||||||
|  |     /* | ||||||
|  |      * All the events that should be handeled by your application. | ||||||
|  |      * Typically you should uncomment all jobs | ||||||
|  |      * | ||||||
|  |      * You can find a list of all notification types here: | ||||||
|  |      * https://developer.apple.com/documentation/storekit/in-app_purchase/enabling_server-to-server_notifications?language=objc#3162176
 | ||||||
|  |      */ | ||||||
|  |     'jobs' => [ | ||||||
|  |         // 'initial_buy' => \App\Jobs\AppstoreNotifications\HandleInitialBuy::class,
 | ||||||
|  |         // 'cancel' => \App\Jobs\AppstoreNotifications\HandleCancellation::class,
 | ||||||
|  |         // 'renewal' => \App\Jobs\AppstoreNotifications\HandleRenewal::class,
 | ||||||
|  |         // 'interactive_renewal' => \App\Jobs\AppstoreNotifications\HandleInteractiveRenewal::class,
 | ||||||
|  |         // 'did_change_renewal_pref' => \App\Jobs\AppstoreNotifications\HandleDidChangeRenewalPreferences::class,
 | ||||||
|  |         // 'did_change_renewal_status' => \App\Jobs\AppstoreNotifications\HandleDidChangeRenewalStatus::class,
 | ||||||
|  |     ], | ||||||
|  | ]; | ||||||
| @ -0,0 +1,25 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | use Illuminate\Database\Schema\Blueprint; | ||||||
|  | use Illuminate\Database\Migrations\Migration; | ||||||
|  | 
 | ||||||
|  | class CreateAppleNotificationsTable extends Migration | ||||||
|  | { | ||||||
|  |     public function up() | ||||||
|  |     { | ||||||
|  |         Schema::create('apple_notifications', function (Blueprint $table) { | ||||||
|  |             $table->bigIncrements('id'); | ||||||
|  | 
 | ||||||
|  |             $table->string('type'); | ||||||
|  |             $table->text('payload')->nullable(); | ||||||
|  |             $table->text('exception')->nullable(); | ||||||
|  | 
 | ||||||
|  |             $table->timestamps(); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function down() | ||||||
|  |     { | ||||||
|  |         Schema::dropIfExists('apple_notifications'); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										29
									
								
								phpunit.xml.dist
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								phpunit.xml.dist
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <phpunit bootstrap="vendor/autoload.php" | ||||||
|  |          backupGlobals="false" | ||||||
|  |          backupStaticAttributes="false" | ||||||
|  |          colors="true" | ||||||
|  |          verbose="true" | ||||||
|  |          convertErrorsToExceptions="true" | ||||||
|  |          convertNoticesToExceptions="true" | ||||||
|  |          convertWarningsToExceptions="true" | ||||||
|  |          processIsolation="true" | ||||||
|  |          stopOnFailure="false"> | ||||||
|  |     <testsuites> | ||||||
|  |         <testsuite name="App-vise Test Suite"> | ||||||
|  |             <directory>tests</directory> | ||||||
|  |         </testsuite> | ||||||
|  |     </testsuites> | ||||||
|  |     <filter> | ||||||
|  |         <whitelist> | ||||||
|  |             <directory suffix=".php">src/</directory> | ||||||
|  |         </whitelist> | ||||||
|  |     </filter> | ||||||
|  |     <logging> | ||||||
|  |         <log type="tap" target="build/report.tap"/> | ||||||
|  |         <log type="junit" target="build/report.junit.xml"/> | ||||||
|  |         <log type="coverage-html" target="build/coverage"/> | ||||||
|  |         <log type="coverage-text" target="build/coverage.txt"/> | ||||||
|  |         <log type="coverage-clover" target="build/logs/clover.xml"/> | ||||||
|  |     </logging> | ||||||
|  | </phpunit> | ||||||
							
								
								
									
										30
									
								
								src/NotificationsServiceProvider.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/NotificationsServiceProvider.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | <?php | ||||||
|  | namespace Appvise\AppStoreNotifications; | ||||||
|  | 
 | ||||||
|  | use Illuminate\Support\ServiceProvider; | ||||||
|  | 
 | ||||||
|  | class NotificationsServiceProvider extends ServiceProvider | ||||||
|  | { | ||||||
|  |     public function boot() | ||||||
|  |     { | ||||||
|  |         if ($this->app->runningInConsole()) { | ||||||
|  |             $this->publishes([ | ||||||
|  |                 __DIR__.'/../config/appstore-server-notifications.php' => config_path('appstore-server-notifications.php'), | ||||||
|  |             ], 'config'); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (! class_exists('CreateAppleNotificationsTable')) { | ||||||
|  |             $timestamp = date('Y_m_d_His', time()); | ||||||
|  |             $this->publishes([ | ||||||
|  |                 __DIR__.'/../database/migrations/create_apple_notifications_table.php.stub' => database_path("migrations/{$timestamp}_create_apple_notifications_table.php"), | ||||||
|  |             ], 'migrations'); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $this->loadRoutesFrom(__DIR__.'/routes.php'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function register() | ||||||
|  |     { | ||||||
|  |         $this->mergeConfigFrom(__DIR__.'/../config/appstore-server-notifications.php', 'appstore-server-notifications'); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										43
									
								
								src/WebhooksController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/WebhooksController.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace Appvise\AppStoreNotifications; | ||||||
|  | 
 | ||||||
|  | use Appvise\AppStoreNotifications\Exceptions\WebhookFailed; | ||||||
|  | use Appvise\AppStoreNotifications\Model\AppleNotification; | ||||||
|  | use Appvise\AppStoreNotifications\Model\NotificationPayload; | ||||||
|  | use Appvise\AppStoreNotifications\Model\NotificationType; | ||||||
|  | use Illuminate\Http\Request; | ||||||
|  | 
 | ||||||
|  | class WebhooksController | ||||||
|  | { | ||||||
|  |     public function __invoke(Request $request) | ||||||
|  |     { | ||||||
|  |         $jobConfigKey = NotificationType::{$request->input('notification_type')}(); | ||||||
|  |         $payload = NotificationPayload::createFromRequest($request); | ||||||
|  | 
 | ||||||
|  |         $this->determineValidRequest($payload); | ||||||
|  | 
 | ||||||
|  |         AppleNotification::storeNotification($jobConfigKey, $payload); | ||||||
|  | 
 | ||||||
|  |         $jobClass = config("appstore-server-notifications.jobs.{$jobConfigKey}", null); | ||||||
|  | 
 | ||||||
|  |         if (is_null($jobClass)) { | ||||||
|  |             throw WebhookFailed::jobClassDoesNotExist($jobConfigKey); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         $job = new $jobClass($payload); | ||||||
|  |         dispatch($job); | ||||||
|  | 
 | ||||||
|  |         return response()->json(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private function determineValidRequest(NotificationPayload $notificationPayload): bool | ||||||
|  |     { | ||||||
|  |         if ($notificationPayload->getPassword() !== config('appstore-server-notifications.shared_secret')) { | ||||||
|  |             throw WebhookFailed::nonValidRequest(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										23
									
								
								src/exceptions/WebhookFailed.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/exceptions/WebhookFailed.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace Appvise\AppStoreNotifications\Exceptions; | ||||||
|  | 
 | ||||||
|  | use Exception; | ||||||
|  | 
 | ||||||
|  | class WebhookFailed extends Exception | ||||||
|  | { | ||||||
|  |     public static function nonValidRequest() | ||||||
|  |     { | ||||||
|  |         return new static("Your shared secret does not match password in Apple's request", 400); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static function jobClassDoesNotExist(string $jobClass) | ||||||
|  |     { | ||||||
|  |         return new static("Could not process webhook because the configured job `$jobClass` does not exist.", 501); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function render($request) | ||||||
|  |     { | ||||||
|  |         return response(['error' => $this->getMessage()], 400); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										19
									
								
								src/model/AppleNotification.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/model/AppleNotification.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace Appvise\AppStoreNotifications\Model; | ||||||
|  | 
 | ||||||
|  | use Illuminate\Database\Eloquent\Model; | ||||||
|  | 
 | ||||||
|  | class AppleNotification extends Model | ||||||
|  | { | ||||||
|  |     public $guarded = []; | ||||||
|  | 
 | ||||||
|  |     public static function storeNotification(String $notificationType, NotificationPayload $notificationPayload): AppleNotification | ||||||
|  |     { | ||||||
|  |         return self::create([ | ||||||
|  |             'type' => $notificationType, | ||||||
|  |             'payload' => serialize($notificationPayload), | ||||||
|  |         ]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										193
									
								
								src/model/NotificationPayload.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								src/model/NotificationPayload.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,193 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace Appvise\AppStoreNotifications\Model; | ||||||
|  | 
 | ||||||
|  | use Illuminate\Http\Request; | ||||||
|  | 
 | ||||||
|  | class NotificationPayload | ||||||
|  | { | ||||||
|  |     private $environment; | ||||||
|  |     private $notificationType; | ||||||
|  |     private $password; | ||||||
|  |     private $cancellationDate; | ||||||
|  |     private $cancellationDatePst; | ||||||
|  |     private $cancellationDateMs; | ||||||
|  |     private $webOrderLineItemId; | ||||||
|  |     private $latestReceipt; | ||||||
|  |     private $latestReceiptInfo; | ||||||
|  |     private $latestExpiredReceipt; | ||||||
|  |     private $latestExpiredReceiptInfo; | ||||||
|  |     private $autoRenewStatus; | ||||||
|  |     private $autoRenewProductId; | ||||||
|  |     private $autoRenewStatusChangeDate; | ||||||
|  |     private $autoRenewStatusChangeDatePst; | ||||||
|  |     private $autoRenewStatusChangeDateMs; | ||||||
|  |     private $pendingRenewalInfo; | ||||||
|  | 
 | ||||||
|  |     public function __construct() | ||||||
|  |     { | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     static function createFromRequest(Request $request) | ||||||
|  |     { | ||||||
|  |         $instance = new self(); | ||||||
|  |         $instance->environment = $request->input('environment'); | ||||||
|  |         $instance->password = $request->input('password'); | ||||||
|  |         $instance->notificationType = $request->input('notification_type'); | ||||||
|  |         $instance->cancellationDate = $request->input('cancellation_date'); | ||||||
|  |         $instance->cancellationDatePst = $request->input('cancellation_date_pst'); | ||||||
|  |         $instance->cancellationDateMs = $request->input('cancellation_date_ms'); | ||||||
|  |         $instance->webOrderLineItemId = $request->input('web_order_line_item_id'); | ||||||
|  |         $instance->latestReceipt = $request->input('latest_receipt'); | ||||||
|  |         $instance->latestReceiptInfo = Receipt::createFromArray($request->input('latest_receipt_info')); | ||||||
|  |         $instance->latestExpiredReceipt = $request->input('latest_expired_receipt'); | ||||||
|  |         $instance->latestExpiredReceiptInfo = Receipt::createFromArray($request->input('latest_expired_receipt_info')); | ||||||
|  |         $instance->autoRenewStatus = $request->input('auto_renew_status'); | ||||||
|  |         $instance->autoRenewProductId = $request->input('auto_renew_product_id'); | ||||||
|  |         $instance->autoRenewStatusChangeDate = $request->input('auto_renew_status_change_date'); | ||||||
|  |         $instance->autoRenewStatusChangeDatePst = $request->input('auto_renew_status_change_date_pst'); | ||||||
|  |         $instance->autoRenewStatusChangeDateMs = $request->input('auto_renew_status_change_date_ms'); | ||||||
|  |         foreach ($request->input('pending_renewal_info') as $pendingRenewalInfo) { | ||||||
|  |             $instance->pendingRenewalInfo[] = RenewalInfo::createFromRequest($pendingRenewalInfo); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return $instance; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of environment | ||||||
|  |      */ | ||||||
|  |     public function getEnvironment() | ||||||
|  |     { | ||||||
|  |         return $this->environment; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of notificationType | ||||||
|  |      */ | ||||||
|  |     public function getNotificationType() | ||||||
|  |     { | ||||||
|  |         return $this->notificationType; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of pendingRenewalInfo | ||||||
|  |      */ | ||||||
|  |     public function getPendingRenewalInfo() | ||||||
|  |     { | ||||||
|  |         return $this->pendingRenewalInfo; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of autoRenewStatusChangeDateMs | ||||||
|  |      */ | ||||||
|  |     public function getAutoRenewStatusChangeDateMs() | ||||||
|  |     { | ||||||
|  |         return $this->autoRenewStatusChangeDateMs; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of autoRenewStatusChangeDatePst | ||||||
|  |      */ | ||||||
|  |     public function getAutoRenewStatusChangeDatePst() | ||||||
|  |     { | ||||||
|  |         return $this->autoRenewStatusChangeDatePst; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of autoRenewStatusChangeDate | ||||||
|  |      */ | ||||||
|  |     public function getAutoRenewStatusChangeDate() | ||||||
|  |     { | ||||||
|  |         return $this->autoRenewStatusChangeDate; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of autoRenewProductId | ||||||
|  |      */ | ||||||
|  |     public function getAutoRenewProductId() | ||||||
|  |     { | ||||||
|  |         return $this->autoRenewProductId; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of autoRenewStatus | ||||||
|  |      */ | ||||||
|  |     public function getAutoRenewStatus() | ||||||
|  |     { | ||||||
|  |         return $this->autoRenewStatus; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of latestExpiredReceiptInfo | ||||||
|  |      */ | ||||||
|  |     public function getLatestExpiredReceiptInfo() | ||||||
|  |     { | ||||||
|  |         return $this->latestExpiredReceiptInfo; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of latestExpiredReceipt | ||||||
|  |      */ | ||||||
|  |     public function getLatestExpiredReceipt() | ||||||
|  |     { | ||||||
|  |         return $this->latestExpiredReceipt; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of latestReceiptInfo | ||||||
|  |      */ | ||||||
|  |     public function getLatestReceiptInfo() | ||||||
|  |     { | ||||||
|  |         return $this->latestReceiptInfo; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of latestReceipt | ||||||
|  |      */ | ||||||
|  |     public function getLatestReceipt() | ||||||
|  |     { | ||||||
|  |         return $this->latestReceipt; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of webOrderLineItemId | ||||||
|  |      */ | ||||||
|  |     public function getWebOrderLineItemId() | ||||||
|  |     { | ||||||
|  |         return $this->webOrderLineItemId; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of cancellationDateMs | ||||||
|  |      */ | ||||||
|  |     public function getCancellationDateMs() | ||||||
|  |     { | ||||||
|  |         return $this->cancellationDateMs; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of cancellationDatePst | ||||||
|  |      */ | ||||||
|  |     public function getCancellationDatePst() | ||||||
|  |     { | ||||||
|  |         return $this->cancellationDatePst; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of cancellationDate | ||||||
|  |      */ | ||||||
|  |     public function getCancellationDate() | ||||||
|  |     { | ||||||
|  |         return $this->cancellationDate; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of password | ||||||
|  |      */ | ||||||
|  |     public function getPassword() | ||||||
|  |     { | ||||||
|  |         return $this->password; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										18
									
								
								src/model/NotificationType.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/model/NotificationType.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace Appvise\AppStoreNotifications\Model; | ||||||
|  | 
 | ||||||
|  | use BenSampo\Enum\Enum; | ||||||
|  | 
 | ||||||
|  | class NotificationType extends Enum | ||||||
|  | { | ||||||
|  |     const INITIAL_BUY = 'initial_buy'; | ||||||
|  |     const CANCEL = 'cancel'; | ||||||
|  |     const RENEWAL = 'renewal'; | ||||||
|  |     const INTERACTIVE_RENEWAL = 'interactive_renewal'; | ||||||
|  |     const DID_CHANGE_RENEWAL_PREF = 'did_change_renewal_pref'; | ||||||
|  |     const DID_CHANGE_RENEWAL_STATUS = 'did_change_renewal_status'; | ||||||
|  |     const DID_FAIL_TO_RENEW = 'did_fail_to_renew'; | ||||||
|  |     const DID_RECOVER = 'did_recover'; // replaces RENEWAL
 | ||||||
|  |     const PRICE_INCREASE_CONSENT = 'price_increase_consent'; | ||||||
|  | } | ||||||
							
								
								
									
										301
									
								
								src/model/Receipt.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								src/model/Receipt.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,301 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace Appvise\AppStoreNotifications\Model; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Receipt | ||||||
|  | { | ||||||
|  |     private $originalTransactionId; | ||||||
|  |     private $webOrderLineItemId; | ||||||
|  |     private $productId; | ||||||
|  |     private $purchaseDateMs; | ||||||
|  |     private $purchaseDate; | ||||||
|  |     private $purchaseDatePst; | ||||||
|  |     private $originalPurchaseDate; | ||||||
|  |     private $originalPurchaseDateMs; | ||||||
|  |     private $originalPurchaseDatePst; | ||||||
|  |     private $cancellationReason; | ||||||
|  |     private $cancellationDate; | ||||||
|  |     private $cancellationDateMs; | ||||||
|  |     private $cancellationDatePst; | ||||||
|  |     private $expiresDate; | ||||||
|  |     private $expiresDateMs; | ||||||
|  |     private $expiresDateFormatted; | ||||||
|  |     private $expiresDateFormattedPst; | ||||||
|  |     private $quantity; | ||||||
|  |     private $uniqueIdentifier; | ||||||
|  |     private $uniqueVendorIdentifier; | ||||||
|  |     private $isInIntroOfferPeriod; | ||||||
|  |     private $isTrialPeriod; | ||||||
|  |     private $itemId; | ||||||
|  |     private $appItemId; | ||||||
|  |     private $versionExternalIdentifier; | ||||||
|  |     private $transactionId; | ||||||
|  |     private $bvrs; | ||||||
|  |     private $bid; | ||||||
|  | 
 | ||||||
|  |     public function __construct() | ||||||
|  |     { | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     static function createFromArray(array $receiptInfo) | ||||||
|  |     { | ||||||
|  |         $instance = new self(); | ||||||
|  |         $instance->originalTransactionId = $receiptInfo['original_transaction_id'] ?? null; | ||||||
|  |         $instance->webOrderLineItemId = $receiptInfo['web_order_line_item_id'] ?? null; | ||||||
|  |         $instance->productId = $receiptInfo['product_id'] ?? null; | ||||||
|  |         $instance->purchaseDateMs = $receiptInfo['purchase_date_ms'] ?? null; | ||||||
|  |         $instance->purchaseDate = $receiptInfo['purchase_date'] ?? null; | ||||||
|  |         $instance->purchaseDatePst = $receiptInfo['purchase_date_pst'] ?? null; | ||||||
|  |         $instance->originalPurchaseDate = $receiptInfo['original_purchase_date'] ?? null; | ||||||
|  |         $instance->originalPurchaseDateMs = $receiptInfo['original_purchase_date_ms'] ?? null; | ||||||
|  |         $instance->originalPurchaseDatePst = $receiptInfo['original_purchase_date_pst'] ?? null; | ||||||
|  |         $instance->cancellationReason = $receiptInfo['cancellation_reason'] ?? null; | ||||||
|  |         $instance->cancellationDate = $receiptInfo['cancellation_date'] ?? null; | ||||||
|  |         $instance->cancellationDateMs = $receiptInfo['cancellation_date_ms'] ?? null; | ||||||
|  |         $instance->cancellationDatePst = $receiptInfo['cancellation_date_pst'] ?? null; | ||||||
|  |         $instance->expiresDate = $receiptInfo['expires_date'] ?? null; | ||||||
|  |         $instance->expiresDateMs = $receiptInfo['expires_date_ms'] ?? null; | ||||||
|  |         $instance->expiresDateFormatted = $receiptInfo['expires_date_formatted'] ?? null; | ||||||
|  |         $instance->expiresDateFormattedPst = $receiptInfo['expires_date_formatted_pst'] ?? null; | ||||||
|  |         $instance->quantity = $receiptInfo['quantity'] ?? null; | ||||||
|  |         $instance->uniqueIdentifier = $receiptInfo['unique_identifier'] ?? null; | ||||||
|  |         $instance->uniqueVendorIdentifier = $receiptInfo['unique_vendor_identifier'] ?? null; | ||||||
|  |         $instance->isInIntroOfferPeriod = $receiptInfo['is_in_intro_offer_period'] ?? null; | ||||||
|  |         $instance->isTrialPeriod = $receiptInfo['is_trial_period'] ?? null; | ||||||
|  |         $instance->itemId = $receiptInfo['item_id'] ?? null; | ||||||
|  |         $instance->appItemId = $receiptInfo['app_item_id'] ?? null; | ||||||
|  |         $instance->versionExternalIdentifier = $receiptInfo['version_external_identifier'] ?? null; | ||||||
|  |         $instance->transactionId = $receiptInfo['transaction_id'] ?? null; | ||||||
|  |         $instance->bvrs = $receiptInfo['bvrs'] ?? null; | ||||||
|  |         $instance->bid = $receiptInfo['bid'] ?? null; | ||||||
|  |         return $instance; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of bid | ||||||
|  |      */ | ||||||
|  |     public function getBid() | ||||||
|  |     { | ||||||
|  |         return $this->bid; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of bvrs | ||||||
|  |      */ | ||||||
|  |     public function getBvrs() | ||||||
|  |     { | ||||||
|  |         return $this->bvrs; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of transactionId | ||||||
|  |      */ | ||||||
|  |     public function getTransactionId() | ||||||
|  |     { | ||||||
|  |         return $this->transactionId; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of versionExternalIdentifier | ||||||
|  |      */ | ||||||
|  |     public function getVersionExternalIdentifier() | ||||||
|  |     { | ||||||
|  |         return $this->versionExternalIdentifier; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of appItemId | ||||||
|  |      */ | ||||||
|  |     public function getAppItemId() | ||||||
|  |     { | ||||||
|  |         return $this->appItemId; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of itemId | ||||||
|  |      */ | ||||||
|  |     public function getItemId() | ||||||
|  |     { | ||||||
|  |         return $this->itemId; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of isTrialPeriod | ||||||
|  |      */ | ||||||
|  |     public function getIsTrialPeriod() | ||||||
|  |     { | ||||||
|  |         return $this->isTrialPeriod; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of isInIntroOfferPeriod | ||||||
|  |      */ | ||||||
|  |     public function getIsInIntroOfferPeriod() | ||||||
|  |     { | ||||||
|  |         return $this->isInIntroOfferPeriod; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of uniqueVendorIdentifier | ||||||
|  |      */ | ||||||
|  |     public function getUniqueVendorIdentifier() | ||||||
|  |     { | ||||||
|  |         return $this->uniqueVendorIdentifier; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of uniqueIdentifier | ||||||
|  |      */ | ||||||
|  |     public function getUniqueIdentifier() | ||||||
|  |     { | ||||||
|  |         return $this->uniqueIdentifier; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of quantity | ||||||
|  |      */ | ||||||
|  |     public function getQuantity() | ||||||
|  |     { | ||||||
|  |         return $this->quantity; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of expiresDateFormattedPst | ||||||
|  |      */ | ||||||
|  |     public function getExpiresDateFormattedPst() | ||||||
|  |     { | ||||||
|  |         return $this->expiresDateFormattedPst; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of expiresDateFormatted | ||||||
|  |      */ | ||||||
|  |     public function getExpiresDateFormatted() | ||||||
|  |     { | ||||||
|  |         return $this->expiresDateFormatted; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of expiresDateMs | ||||||
|  |      */ | ||||||
|  |     public function getExpiresDateMs() | ||||||
|  |     { | ||||||
|  |         return $this->expiresDateMs; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of expiresDate | ||||||
|  |      */ | ||||||
|  |     public function getExpiresDate() | ||||||
|  |     { | ||||||
|  |         return $this->expiresDate; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of cancellationDatePst | ||||||
|  |      */ | ||||||
|  |     public function getCancellationDatePst() | ||||||
|  |     { | ||||||
|  |         return $this->cancellationDatePst; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of cancellationDateMs | ||||||
|  |      */ | ||||||
|  |     public function getCancellationDateMs() | ||||||
|  |     { | ||||||
|  |         return $this->cancellationDateMs; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of cancellationDate | ||||||
|  |      */ | ||||||
|  |     public function getCancellationDate() | ||||||
|  |     { | ||||||
|  |         return $this->cancellationDate; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of cancellationReason | ||||||
|  |      */ | ||||||
|  |     public function getCancellationReason() | ||||||
|  |     { | ||||||
|  |         return $this->cancellationReason; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of originalPurchaseDatePst | ||||||
|  |      */ | ||||||
|  |     public function getOriginalPurchaseDatePst() | ||||||
|  |     { | ||||||
|  |         return $this->originalPurchaseDatePst; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of originalPurchaseDateMs | ||||||
|  |      */ | ||||||
|  |     public function getOriginalPurchaseDateMs() | ||||||
|  |     { | ||||||
|  |         return $this->originalPurchaseDateMs; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of originalPurchaseDate | ||||||
|  |      */ | ||||||
|  |     public function getOriginalPurchaseDate() | ||||||
|  |     { | ||||||
|  |         return $this->originalPurchaseDate; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of purchaseDatePst | ||||||
|  |      */ | ||||||
|  |     public function getPurchaseDatePst() | ||||||
|  |     { | ||||||
|  |         return $this->purchaseDatePst; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of purchaseDate | ||||||
|  |      */ | ||||||
|  |     public function getPurchaseDate() | ||||||
|  |     { | ||||||
|  |         return $this->purchaseDate; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of purchaseDateMs | ||||||
|  |      */ | ||||||
|  |     public function getPurchaseDateMs() | ||||||
|  |     { | ||||||
|  |         return $this->purchaseDateMs; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of productId | ||||||
|  |      */ | ||||||
|  |     public function getProductId() | ||||||
|  |     { | ||||||
|  |         return $this->productId; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of webOrderLineItemId | ||||||
|  |      */ | ||||||
|  |     public function getWebOrderLineItemId() | ||||||
|  |     { | ||||||
|  |         return $this->webOrderLineItemId; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of originalTransactionId | ||||||
|  |      */ | ||||||
|  |     public function getOriginalTransactionId() | ||||||
|  |     { | ||||||
|  |         return $this->originalTransactionId; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										116
									
								
								src/model/RenewalInfo.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								src/model/RenewalInfo.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,116 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace Appvise\AppStoreNotifications\Model; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class RenewalInfo | ||||||
|  | { | ||||||
|  |     private $autoRenewProductId; | ||||||
|  |     private $autoRenewStatus; | ||||||
|  |     private $expirationIntent; | ||||||
|  |     private $originalTransactionId; | ||||||
|  |     private $isInBillingRetryPeriod; | ||||||
|  |     private $productId; | ||||||
|  |     private $priceConsentStatus; | ||||||
|  |     private $gracePeriodExpiresDate; | ||||||
|  |     private $gracePeriodExpiresDateMs; | ||||||
|  |     private $gracePeriodExpiresDatePst; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     public function __construct() | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static function createFromRequest(array $pendingRenewalInfo) { | ||||||
|  |         $instance = new self(); | ||||||
|  |         $instance->autoRenewProductId = $pendingRenewalInfo['auto_renew_product_id'] ?? null; | ||||||
|  |         $instance->autoRenewStatus = $pendingRenewalInfo['auto_renew_status'] ?? null; | ||||||
|  |         $instance->expirationIntent = $pendingRenewalInfo['expiration_intent'] ?? null; | ||||||
|  |         $instance->originalTransactionId = $pendingRenewalInfo['original_transaction_id'] ?? null; | ||||||
|  |         $instance->isInBillingRetryPeriod = $pendingRenewalInfo['is_in_billing_retry_period'] ?? null; | ||||||
|  |         $instance->productId = $pendingRenewalInfo['product_id'] ?? null; | ||||||
|  |         $instance->priceConsentStatus = $pendingRenewalInfo['price_consent_status'] ?? null; | ||||||
|  |         $instance->gracePeriodExpiresDate = $pendingRenewalInfo['grace_period_expires_date'] ?? null; | ||||||
|  |         $instance->gracePeriodExpiresDatePst = $pendingRenewalInfo['grace_period_expires_date_pst'] ?? null; | ||||||
|  |         return $instance; | ||||||
|  |     } | ||||||
|  |     /** | ||||||
|  |      * Get the value of autoRenewProductId | ||||||
|  |      */ | ||||||
|  |     public function getAutoRenewProductId() | ||||||
|  |     { | ||||||
|  |         return $this->autoRenewProductId; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of autoRenewStatus | ||||||
|  |      */ | ||||||
|  |     public function getAutoRenewStatus() | ||||||
|  |     { | ||||||
|  |         return $this->autoRenewStatus; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of expirationIntent | ||||||
|  |      */ | ||||||
|  |     public function getExpirationIntent() | ||||||
|  |     { | ||||||
|  |         return $this->expirationIntent; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of originalTransactionId | ||||||
|  |      */ | ||||||
|  |     public function getOriginalTransactionId() | ||||||
|  |     { | ||||||
|  |         return $this->originalTransactionId; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of isInBillingRetryPeriod | ||||||
|  |      */ | ||||||
|  |     public function getIsInBillingRetryPeriod() | ||||||
|  |     { | ||||||
|  |         return $this->isInBillingRetryPeriod; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of productId | ||||||
|  |      */ | ||||||
|  |     public function getProductId() | ||||||
|  |     { | ||||||
|  |         return $this->productId; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of priceConsentStatus | ||||||
|  |      */ | ||||||
|  |     public function getPriceConsentStatus() | ||||||
|  |     { | ||||||
|  |         return $this->priceConsentStatus; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of gracePeriodExpiresDate | ||||||
|  |      */ | ||||||
|  |     public function getGracePeriodExpiresDate() | ||||||
|  |     { | ||||||
|  |         return $this->gracePeriodExpiresDate; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of gracePeriodExpiresDateMs | ||||||
|  |      */ | ||||||
|  |     public function getGracePeriodExpiresDateMs() | ||||||
|  |     { | ||||||
|  |         return $this->gracePeriodExpiresDateMs; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the value of gracePeriodExpiresDatePst | ||||||
|  |      */ | ||||||
|  |     public function getGracePeriodExpiresDatePst() | ||||||
|  |     { | ||||||
|  |         return $this->gracePeriodExpiresDatePst; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								src/routes.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/routes.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | Route::post('/apple/server/notifications', "\Appvise\AppStoreNotifications\WebhooksController"); | ||||||
							
								
								
									
										27
									
								
								tests/DummyJob.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								tests/DummyJob.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace Appvise\AppStoreNotifications\Tests; | ||||||
|  | 
 | ||||||
|  | use Appvise\AppStoreNotifications\Model\NotificationPayload; | ||||||
|  | use Illuminate\Bus\Queueable; | ||||||
|  | use Illuminate\Contracts\Queue\ShouldQueue; | ||||||
|  | use Illuminate\Foundation\Bus\Dispatchable; | ||||||
|  | use Illuminate\Queue\InteractsWithQueue; | ||||||
|  | use Illuminate\Queue\SerializesModels; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DummyJob implements ShouldQueue | ||||||
|  | { | ||||||
|  |     use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; | ||||||
|  | 
 | ||||||
|  |     public $payload; | ||||||
|  | 
 | ||||||
|  |     public function __construct(NotificationPayload $payload) | ||||||
|  |     { | ||||||
|  |         $this->payload = $payload; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     function handle() | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										79
									
								
								tests/IntegrationTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								tests/IntegrationTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace Appvise\AppStoreNotifications\Tests; | ||||||
|  | 
 | ||||||
|  | use Appvise\AppStoreNotifications\Model\AppleNotification; | ||||||
|  | use Illuminate\Support\Facades\Queue; | ||||||
|  | use Illuminate\Support\Facades\Route; | ||||||
|  | 
 | ||||||
|  | class IntegrationTest extends TestCase | ||||||
|  | { | ||||||
|  |     public function setUp(): void | ||||||
|  |     { | ||||||
|  |         parent::setUp(); | ||||||
|  | 
 | ||||||
|  |         Queue::fake(); | ||||||
|  | 
 | ||||||
|  |         Route::post('/apple/server/notifications', "\Appvise\AppStoreNotifications\WebhooksController"); | ||||||
|  | 
 | ||||||
|  |         config( | ||||||
|  |             [ | ||||||
|  |                 'appstore-server-notifications.jobs' => [ | ||||||
|  |                     'initial_buy' => DummyJob::class | ||||||
|  |                 ], | ||||||
|  |                 'appstore-server-notifications.shared_secret' => 'VALID_APPLE_PASSWORD', | ||||||
|  |             ] | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** @test */ | ||||||
|  |     public function it_can_handle_a_valid_request() | ||||||
|  |     { | ||||||
|  |         $payload = include_once __DIR__ . '/__fixtures__/request.php'; | ||||||
|  | 
 | ||||||
|  |         $payload['password'] = "VALID_APPLE_PASSWORD"; | ||||||
|  | 
 | ||||||
|  |         $this | ||||||
|  |             ->postJson('/apple/server/notifications', $payload) | ||||||
|  |             ->assertSuccessful(); | ||||||
|  | 
 | ||||||
|  |         $this->assertCount(1, AppleNotification::get()); | ||||||
|  | 
 | ||||||
|  |         $notification = AppleNotification::first(); | ||||||
|  | 
 | ||||||
|  |         $this->assertEquals('initial_buy', $notification->type); | ||||||
|  |         $this->assertInstanceOf(AppleNotification::class, $notification); | ||||||
|  | 
 | ||||||
|  |         Queue::assertPushed(DummyJob::class); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** @test */ | ||||||
|  |     public function a_request_with_an_invalid_password_wont_be_logged() | ||||||
|  |     { | ||||||
|  |         $payload = include_once __DIR__ . '/__fixtures__/request.php'; | ||||||
|  |         $payload['password'] = "NON_VALID_APPLE_PASSWORD"; | ||||||
|  | 
 | ||||||
|  |         $this | ||||||
|  |             ->postJson('/apple/server/notifications', $payload) | ||||||
|  |             ->assertStatus(400); | ||||||
|  | 
 | ||||||
|  |         $this->assertCount(0, AppleNotification::get()); | ||||||
|  |         $this->assertNull(AppleNotification::first()); | ||||||
|  | 
 | ||||||
|  |         Queue::assertNotPushed(DummyJob::class); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** @test */ | ||||||
|  |     public function a_request_with_an_invalid_payload_will_be_logged_but_jobs_will_not_be_dispatched() | ||||||
|  |     { | ||||||
|  |         $payload = ['payload' => 'INVALID']; | ||||||
|  | 
 | ||||||
|  |         $this | ||||||
|  |             ->postJson('/apple/server/notifications', $payload) | ||||||
|  |             ->assertStatus(500); | ||||||
|  | 
 | ||||||
|  |         Queue::assertNotPushed(DummyJob::class); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										75
									
								
								tests/TestCase.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								tests/TestCase.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,75 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace Appvise\AppStoreNotifications\Tests; | ||||||
|  | 
 | ||||||
|  | use Appvise\AppStoreNotifications\NotificationsServiceProvider; | ||||||
|  | use Exception; | ||||||
|  | use Illuminate\Foundation\Exceptions\Handler; | ||||||
|  | use Illuminate\Contracts\Debug\ExceptionHandler; | ||||||
|  | use Orchestra\Testbench\TestCase as OrchestraTestCase; | ||||||
|  | use CreateAppleNotificationsTable; | ||||||
|  | 
 | ||||||
|  | abstract class TestCase extends OrchestraTestCase | ||||||
|  | { | ||||||
|  |     public function setUp(): void | ||||||
|  |     { | ||||||
|  |         parent::setUp(); | ||||||
|  | 
 | ||||||
|  |         $this->setUpDatabase(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Set up the environment. | ||||||
|  |      * | ||||||
|  |      * @param \Illuminate\Foundation\Application $app | ||||||
|  |      */ | ||||||
|  |     protected function getEnvironmentSetUp($app) | ||||||
|  |     { | ||||||
|  |         $app['config']->set('database.default', 'sqlite'); | ||||||
|  |         $app['config']->set('database.connections.sqlite', [ | ||||||
|  |             'driver'   => 'sqlite', | ||||||
|  |             'database' => ':memory:', | ||||||
|  |             'prefix'   => '', | ||||||
|  |         ]); | ||||||
|  | 
 | ||||||
|  |         config(['appstore-server-notifications.shared_secret' => 'test_shared_secret']); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     protected function setUpDatabase() | ||||||
|  |     { | ||||||
|  |         include_once __DIR__.'/../database/migrations/create_apple_notifications_table.php.stub'; | ||||||
|  | 
 | ||||||
|  |         (new CreateAppleNotificationsTable())->up(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @param \Illuminate\Foundation\Application $app | ||||||
|  |      * | ||||||
|  |      * @return array | ||||||
|  |      */ | ||||||
|  |     protected function getPackageProviders($app) | ||||||
|  |     { | ||||||
|  |         return [ | ||||||
|  |             NotificationsServiceProvider::class, | ||||||
|  |         ]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     protected function disableExceptionHandling() | ||||||
|  |     { | ||||||
|  |         $this->app->instance(ExceptionHandler::class, new class extends Handler { | ||||||
|  |             public function __construct() | ||||||
|  |             { | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             public function report(Exception $e) | ||||||
|  |             { | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             public function render($request, Exception $exception) | ||||||
|  |             { | ||||||
|  |                 throw $exception; | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										126
									
								
								tests/__fixtures__/request.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								tests/__fixtures__/request.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,126 @@ | |||||||
|  | <?php | ||||||
|  | return json_decode('{ | ||||||
|  |         "environment": "Sandbox", | ||||||
|  |         "notification_type": "INITIAL_BUY", | ||||||
|  |         "password": "TEST_SHARED_SECRET", | ||||||
|  |         "cancellation_date": "2018-03-27 07:11:12 Etc/GMT", | ||||||
|  |         "cancellation_date_pst": "2018-03-27 00:11:12 America/Los_Angeles", | ||||||
|  |         "cancellation_date_ms": "1522134672000", | ||||||
|  |         "web_order_line_item_id": "1000000047417113", | ||||||
|  |         "latest_receipt": "BASE64ENCODED_RECEIPT_INFO", | ||||||
|  |         "latest_receipt_info": {}, | ||||||
|  |         "latest_expired_receipt" : "BASE64ENCODED_LATEST_RECEIPT_INFO", | ||||||
|  |         "latest_expired_receipt_info": { | ||||||
|  |             "purchase_date_ms": "1521893342000", | ||||||
|  |             "original_transaction_id": "1000000577061006", | ||||||
|  |             "web_order_line_item_id": "1000000047417113", | ||||||
|  |             "product_id": "PRODUCT_ID", | ||||||
|  |             "purchase_date": "2018-03-24 12:09:02 Etc/GMT", | ||||||
|  |             "purchase_date_pst": "2018-03-24 05:09:02 America/Los_Angeles", | ||||||
|  |             "original_purchase_date": "2018-03-17 12:09:03 Etc/GMT", | ||||||
|  |             "original_purchase_date_ms": "1521288543000", | ||||||
|  |             "original_purchase_date_pst": "2018-03-17 05:09:03 America/Los_Angeles", | ||||||
|  |             "cancellation_reason": "0", | ||||||
|  |             "cancellation_date": "2018-03-27 07:11:12 Etc/GMT", | ||||||
|  |             "cancellation_date_ms": "1522134672000", | ||||||
|  |             "cancellation_date_pst": "2018-03-27 00:11:12 America/Los_Angeles", | ||||||
|  |             "expires_date": "2019-10-09 07:43:26 Etc/GMT", | ||||||
|  |             "expires_date_ms": "1570607006000", | ||||||
|  |             "expires_date_formatted": "2019-03-24 12:09:02 Etc/GMT", | ||||||
|  |             "expires_date_formatted_pst": "2019-03-24 05:09:02 America/Los_Angeles", | ||||||
|  |             "quantity": "1", | ||||||
|  |             "unique_identifier": "UNIQUE_IDENTIFIER", | ||||||
|  |             "unique_vendor_identifier": "UNIQUE_VENDOR_IDENTIFIER", | ||||||
|  |             "is_in_intro_offer_period": "false", | ||||||
|  |             "is_trial_period": "false", | ||||||
|  |             "item_id": "ITEM_ID", | ||||||
|  |             "app_item_id": "APP_ITEM_ID", | ||||||
|  |             "version_external_identifier": "VERSION_EXTERNAL_IDENTIFIER", | ||||||
|  |             "transaction_id": "1000000577069202", | ||||||
|  |             "bvrs": "2", | ||||||
|  |             "bid": "com.example.app.ios" | ||||||
|  |         }, | ||||||
|  |         "auto_renew_status": "false", | ||||||
|  |         "auto_renew_product_id": "PRODUCT_ID", | ||||||
|  |         "auto_renew_status_change_date": "", | ||||||
|  |         "auto_renew_status_change_date_pst": "", | ||||||
|  |         "auto_renew_status_change_date_ms": "", | ||||||
|  |         "pending_renewal_info": [ | ||||||
|  |             { | ||||||
|  |                 "auto_renew_product_id": "PRODUCT_ID", | ||||||
|  |                 "auto_renew_status": "1", | ||||||
|  |                 "expiration_intent": "", | ||||||
|  |                 "original_transaction_id": "1000000577061006", | ||||||
|  |                 "is_in_billing_retry_period": "1", | ||||||
|  |                 "product_id": "PRODUCT_ID", | ||||||
|  |                 "price_consent_status": "0", | ||||||
|  |                 "grace_period_expires_date": "", | ||||||
|  |                 "grace_period_expires_date_ms": "", | ||||||
|  |                 "grace_period_expires_date_pst": "" | ||||||
|  |             } | ||||||
|  |         ] | ||||||
|  |     }', true | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     //{
 | ||||||
|  |     //     "environment": "Sandbox",
 | ||||||
|  |     //     "notification_type": "INITIAL_BUY",
 | ||||||
|  |     //     "password": "TEST_SHARED_SECRET",
 | ||||||
|  |     //     "cancellation_date": "2018-03-27 07:11:12 Etc/GMT",
 | ||||||
|  |     //     "cancellation_date_pst": "2018-03-27 00:11:12 America/Los_Angeles",
 | ||||||
|  |     //     "cancellation_date_ms": "1522134672000", // important for cancel
 | ||||||
|  |     //     "web_order_line_item_id": "1000000047417113",
 | ||||||
|  |     //     "latest_receipt": "BASE64ENCODED_RECEIPT_INFO",
 | ||||||
|  |     //     "latest_receipt_info": "ARRAY_WITH_RECEIPT_INFO",
 | ||||||
|  |     //     "latest_expired_receipt" : "BASE64ENCODED_LATEST_RECEIPT_INFO",
 | ||||||
|  |     //     "latest_expired_receipt_info": {
 | ||||||
|  |     //         "purchase_date_ms": "1521893342000", // important for initial buy,  interactive_renewal, did_recover
 | ||||||
|  |     //         "original_transaction_id": "1000000577061006", // important for initial buy, interactive_renewal, did_change_renewal_info, cancel, did_change_renewal_status, did_fail_to_renew, did_recover, price_increase_consent
 | ||||||
|  |     //         "web_order_line_item_id": "1000000047417113", // important for initial buy,  interactive_renewal
 | ||||||
|  |     //         "product_id": "PRODUCT_ID", // important for initial buy, interactive_renewal, cancel, did_change_renewal_status
 | ||||||
|  |     //         "purchase_date": "2018-03-24 12:09:02 Etc/GMT",
 | ||||||
|  |     //         "purchase_date_pst": "2018-03-24 05:09:02 America/Los_Angeles",
 | ||||||
|  |     //         "original_purchase_date": "2018-03-17 12:09:03 Etc/GMT",
 | ||||||
|  |     //         "original_purchase_date_ms": "1521288543000",
 | ||||||
|  |     //         "original_purchase_date_pst": "2018-03-17 05:09:03 America/Los_Angeles",
 | ||||||
|  |     //         "cancellation_reason": "0",
 | ||||||
|  |     //         "cancellation_date": "2018-03-27 07:11:12 Etc/GMT",
 | ||||||
|  |     //         "cancellation_date_ms": "1522134672000",
 | ||||||
|  |     //         "cancellation_date_pst": "2018-03-27 00:11:12 America/Los_Angeles",
 | ||||||
|  |     //         "expires_date": "2019-10-09 07:43:26 Etc/GMT",
 | ||||||
|  |     //         "expires_date_ms": "1570607006000", // important for did_recover, price_increase_consent
 | ||||||
|  |     //         "expires_date_formatted": "2019-03-24 12:09:02 Etc/GMT",
 | ||||||
|  |     //         "expires_date_formatted_pst": "2019-03-24 05:09:02 America/Los_Angeles",
 | ||||||
|  |     //         "quantity": "1",
 | ||||||
|  |     //         "unique_identifier": "UNIQUE_IDENTIFIER",
 | ||||||
|  |     //         "unique_vendor_identifier": "UNIQUE_VENDOR_IDENTIFIER",
 | ||||||
|  |     //         "is_in_intro_offer_period": "false",
 | ||||||
|  |     //         "is_trial_period": "false",
 | ||||||
|  |     //         "item_id": "ITEM_ID",
 | ||||||
|  |     //         "app_item_id": "APP_ITEM_ID",
 | ||||||
|  |     //         "version_external_identifier": "VERSION_EXTERNAL_IDENTIFIER",
 | ||||||
|  |     //         "transaction_id": "1000000577069202",
 | ||||||
|  |     //         "bvrs": "2",
 | ||||||
|  |     //         "bid": "com.example.ios.app"
 | ||||||
|  |     //     },
 | ||||||
|  |     //     "auto_renew_status": "false", // important for did_change_renewal_status
 | ||||||
|  |     //     "auto_renew_product_id": "PRODUCT_ID", // important for did_change_renewal_info
 | ||||||
|  |     //     "auto_renew_status_change_date": "",
 | ||||||
|  |     //     "auto_renew_status_change_date_pst": "",
 | ||||||
|  |     //     "auto_renew_status_change_date_ms": "", // important for did_change_renewal_status
 | ||||||
|  |     //     "pending_renewal_info": [ // important for did_fail_to_renew
 | ||||||
|  |     //         {
 | ||||||
|  |     //             "auto_renew_product_id": "PRODUCT_ID",
 | ||||||
|  |     //             "auto_renew_status": "1",
 | ||||||
|  |     //             "expiration_intent": "",
 | ||||||
|  |     //             "original_transaction_id": "1000000577061006",
 | ||||||
|  |     //             "is_in_billing_retry_period": "1",
 | ||||||
|  |     //             "product_id": "PRODUCT_ID",
 | ||||||
|  |     //             "price_consent_status": "0", // important for price_increase_consent
 | ||||||
|  |     //             "grace_period_expires_date": "",
 | ||||||
|  |     //             "grace_period_expires_date_ms": "",
 | ||||||
|  |     //             "grace_period_expires_date_pst": ""
 | ||||||
|  |     //         }
 | ||||||
|  |     //     ]
 | ||||||
|  |     // }
 | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Daan Geurts
						Daan Geurts