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