From 5d5385325e3eb5df45d479ad9405d771ca5a0753 Mon Sep 17 00:00:00 2001 From: Gareth Nicholson Date: Tue, 28 Apr 2020 11:57:16 +0200 Subject: [PATCH 1/4] Start adding support for webhooks. --- config/peachpayment.php | 4 ++ .../create_payment_events_table.php.stub | 35 ++++++++++++++++++ readme.md | 10 +++++ src/Http/Controllers/Controller.php | 13 +++++++ src/Http/Controllers/WebhookController.php | 37 +++++++++++++++++++ src/Models/PaymentEvent.php | 21 +++++++++++ src/PeachPaymentServiceProvider.php | 1 + src/routes/web.php | 7 ++++ tests/Unit/WebhookTest.php | 18 +++++++++ 9 files changed, 146 insertions(+) create mode 100644 database/migrations/create_payment_events_table.php.stub create mode 100644 src/Http/Controllers/Controller.php create mode 100644 src/Http/Controllers/WebhookController.php create mode 100644 src/Models/PaymentEvent.php create mode 100644 src/routes/web.php create mode 100644 tests/Unit/WebhookTest.php diff --git a/config/peachpayment.php b/config/peachpayment.php index 4938930..3e56dbe 100644 --- a/config/peachpayment.php +++ b/config/peachpayment.php @@ -15,6 +15,8 @@ 'api_uri_live' => 'https://oppwa.com/', 'api_uri_version' => 'v1/', 'skip_3ds_for_stored_cards' => true, + 'webhook_url' => '/peach-webhook', + 'webhook_excluded' => ['card', 'authentication'], ]; } @@ -32,4 +34,6 @@ 'api_uri_live' => 'https://oppwa.com/', 'api_uri_version' => 'v1/', 'skip_3ds_for_stored_cards' => true, + 'webhook_url' => '/peach-webhook', + 'webhook_excluded' => ['card', 'authentication'], ]; diff --git a/database/migrations/create_payment_events_table.php.stub b/database/migrations/create_payment_events_table.php.stub new file mode 100644 index 0000000..0820b6e --- /dev/null +++ b/database/migrations/create_payment_events_table.php.stub @@ -0,0 +1,35 @@ +bigIncrements('id'); + $table->string('type'); + $table->string('action')->nullable(); + $table->text('payload'); + $table->timestamps(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('payment_events'); + } +} diff --git a/readme.md b/readme.md index c81c0a3..706d3d4 100644 --- a/readme.md +++ b/readme.md @@ -94,6 +94,16 @@ $response->isValidationError($resultCode); // true|false Saving the response is also available: `$response->save($result, $paymentCard);` +### Webhooks + +The following change needs to be made in your `app/Http/Middleware/VerifyCsrfToken.php` file: + +```php +protected $except = [ + config('peachpayment.webhook_url') + ]; +``` + ### Helpers #### Setting.php This class allows you to inject or modify your Peach Payment credentials diff --git a/src/Http/Controllers/Controller.php b/src/Http/Controllers/Controller.php new file mode 100644 index 0000000..c666710 --- /dev/null +++ b/src/Http/Controllers/Controller.php @@ -0,0 +1,13 @@ +model = $model; + } + + public function index(Request $request) + { + $event = new PaymentEvent(); + $payload = $request->get('payload'); + + $excludedValues = Config::get('peachpayment.webhook_excluded'); + + foreach ($excludedValues as $exclude) { + unset($payload[$exclude]); + } + + $event->type = $request->get('type'); + $event->action = $request->get('action'); + $event->payload = $payload; + $event->save(); + + return response([], Response::HTTP_OK); + } +} diff --git a/src/Models/PaymentEvent.php b/src/Models/PaymentEvent.php new file mode 100644 index 0000000..a13e0f2 --- /dev/null +++ b/src/Models/PaymentEvent.php @@ -0,0 +1,21 @@ + 'array', + ]; +} diff --git a/src/PeachPaymentServiceProvider.php b/src/PeachPaymentServiceProvider.php index 17d5422..0a7c931 100644 --- a/src/PeachPaymentServiceProvider.php +++ b/src/PeachPaymentServiceProvider.php @@ -22,6 +22,7 @@ public function boot() $this->publishes([ __DIR__.'/../database/migrations/create_payment_cards_table.php.stub' => database_path('migrations/' . date('Y_m_d_His', time()) . '_create_payment_cards_table.php'), __DIR__.'/../database/migrations/create_payment_results_table.php.stub' => database_path('migrations/' . date('Y_m_d_His', time()) . '_create_payment_results_table.php'), + __DIR__.'/../database/migrations/create_payment_events_table.php.stub' => database_path('migrations/' . date('Y_m_d_His', time()) . '_create_payment_events_table.php'), ], 'peachpayment-migrations'); } } diff --git a/src/routes/web.php b/src/routes/web.php new file mode 100644 index 0000000..99699e1 --- /dev/null +++ b/src/routes/web.php @@ -0,0 +1,7 @@ +name('peachpayment.index'); diff --git a/tests/Unit/WebhookTest.php b/tests/Unit/WebhookTest.php new file mode 100644 index 0000000..72a4809 --- /dev/null +++ b/tests/Unit/WebhookTest.php @@ -0,0 +1,18 @@ +postJson(route('peachpayment.index')); + + $response->assertOk(); + } +} From b7915e8cd0a58bb50f1ce210242a128edf19cd84 Mon Sep 17 00:00:00 2001 From: Gareth Nicholson Date: Tue, 28 Apr 2020 13:08:17 +0200 Subject: [PATCH 2/4] Add routes. --- {src/routes => routes}/web.php | 0 src/PeachPaymentServiceProvider.php | 2 ++ tests/Unit/WebhookTest.php | 15 ++++++++++++++- 3 files changed, 16 insertions(+), 1 deletion(-) rename {src/routes => routes}/web.php (100%) diff --git a/src/routes/web.php b/routes/web.php similarity index 100% rename from src/routes/web.php rename to routes/web.php diff --git a/src/PeachPaymentServiceProvider.php b/src/PeachPaymentServiceProvider.php index 0a7c931..945bb3e 100644 --- a/src/PeachPaymentServiceProvider.php +++ b/src/PeachPaymentServiceProvider.php @@ -24,6 +24,8 @@ public function boot() __DIR__.'/../database/migrations/create_payment_results_table.php.stub' => database_path('migrations/' . date('Y_m_d_His', time()) . '_create_payment_results_table.php'), __DIR__.'/../database/migrations/create_payment_events_table.php.stub' => database_path('migrations/' . date('Y_m_d_His', time()) . '_create_payment_events_table.php'), ], 'peachpayment-migrations'); + + $this->loadRoutesFrom(__DIR__.'/../routes/web.php'); } } diff --git a/tests/Unit/WebhookTest.php b/tests/Unit/WebhookTest.php index 72a4809..e7e2f80 100644 --- a/tests/Unit/WebhookTest.php +++ b/tests/Unit/WebhookTest.php @@ -2,16 +2,29 @@ namespace IoDigital\PeachPayment\Tests\Unit; +use Illuminate\Foundation\Testing\RefreshDatabase; use IoDigital\PeachPayment\Tests\TestCase; class WebhookTest extends TestCase { + use RefreshDatabase; + + /** + * Setup the test environment. + */ + public function setUp(): void + { + parent::setUp(); + + $this->artisan('migrate'); + } + /** * @test */ public function it_can_receive_an_event() { - $response = $this->postJson(route('peachpayment.index')); + $response = $this->postJson(route('peachpayment.index'), []); $response->assertOk(); } From 1b5ca7fcb1c14a4062e4ad56d9003f63d8323209 Mon Sep 17 00:00:00 2001 From: Gareth Nicholson Date: Tue, 28 Apr 2020 14:37:56 +0200 Subject: [PATCH 3/4] Add decryption functionality. --- config/peachpayment.php | 6 ++++-- src/Http/Controllers/WebhookController.php | 21 ++++++++++++++++++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/config/peachpayment.php b/config/peachpayment.php index 3e56dbe..ac45e72 100644 --- a/config/peachpayment.php +++ b/config/peachpayment.php @@ -16,7 +16,8 @@ 'api_uri_version' => 'v1/', 'skip_3ds_for_stored_cards' => true, 'webhook_url' => '/peach-webhook', - 'webhook_excluded' => ['card', 'authentication'], + 'webhook_excluded' => ['card', 'authentication'], + 'webhook_secret_key' => env('PEACH_PAYMENTS_WEBHOOK_SECRET_KEY'), ]; } @@ -35,5 +36,6 @@ 'api_uri_version' => 'v1/', 'skip_3ds_for_stored_cards' => true, 'webhook_url' => '/peach-webhook', - 'webhook_excluded' => ['card', 'authentication'], + 'webhook_excluded' => ['card', 'authentication'], + 'webhook_secret_key' => env('PEACH_PAYMENTS_WEBHOOK_SECRET_KEY'), ]; diff --git a/src/Http/Controllers/WebhookController.php b/src/Http/Controllers/WebhookController.php index 4683015..bc3754d 100644 --- a/src/Http/Controllers/WebhookController.php +++ b/src/Http/Controllers/WebhookController.php @@ -19,7 +19,8 @@ public function __construct(PaymentEvent $model) public function index(Request $request) { $event = new PaymentEvent(); - $payload = $request->get('payload'); + $body = $this->decryptMessage($request); + $payload = $body['payload']; $excludedValues = Config::get('peachpayment.webhook_excluded'); @@ -27,11 +28,25 @@ public function index(Request $request) unset($payload[$exclude]); } - $event->type = $request->get('type'); - $event->action = $request->get('action'); + $event->type = $body['type']; + $event->action = $body['action']; $event->payload = $payload; $event->save(); return response([], Response::HTTP_OK); } + + private function decryptMessage(Request $request) + { + $keyFromConfiguration = Config::get('peachpayment.webhook_secret_key'); + $ivFromHeader = $request->header('X-Initialization-Vector'); + $authTagFromHeader = $request->header('X-Authentication-Tag'); + + $key = hex2bin($keyFromConfiguration); + $iv = hex2bin($ivFromHeader); + $authTag = hex2bin($authTagFromHeader); + $cipherText = hex2bin($request->getContent()); + + return openssl_decrypt($cipherText, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $authTag); + } } From 8fb251b431ba0d058a2376e2eb8a40f16ee99fd2 Mon Sep 17 00:00:00 2001 From: Gareth Nicholson Date: Tue, 28 Apr 2020 15:51:25 +0200 Subject: [PATCH 4/4] Decode content received. --- src/Http/Controllers/WebhookController.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Http/Controllers/WebhookController.php b/src/Http/Controllers/WebhookController.php index bc3754d..63befa9 100644 --- a/src/Http/Controllers/WebhookController.php +++ b/src/Http/Controllers/WebhookController.php @@ -47,6 +47,8 @@ private function decryptMessage(Request $request) $authTag = hex2bin($authTagFromHeader); $cipherText = hex2bin($request->getContent()); - return openssl_decrypt($cipherText, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $authTag); + $result = openssl_decrypt($cipherText, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $authTag) + + return json_decode($result, true); } }