-
Notifications
You must be signed in to change notification settings - Fork 198
Feature: add API for gateway webhook events #7664
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 19 commits
Commits
Show all changes
42 commits
Select commit
Hold shift + click to select a range
a7b87c0
feature: add WebhookEvents api
glaubersilva 3e43409
feature: add service provider
glaubersilva 1d4ed35
feature: add subscription events
glaubersilva 5bd3a0a
refactor: remove parameter
glaubersilva ac58b07
refactor: move setGateway logic to constructor
glaubersilva be0c8af
refactor: remove status parameters
glaubersilva b818166
refactor: move register logic to give_init hook
glaubersilva 5a75b35
refactor: extract status logic to action classes
glaubersilva 911936d
refactor: remove unused value object
glaubersilva 13a759d
refactor: better naming
glaubersilva a3bd5c3
refactor: use sprintf
glaubersilva de63d93
tests: add tests for the new classes
glaubersilva c8100d6
refactor: deprecated subscription statuses
glaubersilva 9005cc4
refactor: instantiate the webhook events object
glaubersilva b51c389
feature: add missing event handler classes for subscription statuses
glaubersilva 1899c09
refactor: change parameter type
glaubersilva 44856df
tests: add new tests for new event handler classes
glaubersilva 160b685
Merge branch 'develop' into fun/gateway-webhook-events-api
glaubersilva d7e57e1
chore: update composer.lock
glaubersilva 374e0bd
refactor: add new methods for each status and tweak unit tests
glaubersilva 19f03ca
tests: move logic used only on tests and create new tests for the new…
glaubersilva 9d619c0
tests: simplify logic
glaubersilva e006205
refactor: rename methods related to subscription donations
glaubersilva aac3de3
feature: add new methods and contract for webhook notifications
glaubersilva e894899
doc: update unreleased tag
glaubersilva aee1666
refactor: add $args parameter
glaubersilva a617c1a
tests: add WebhookTest class
glaubersilva c91ce8b
doc: tweak comment
glaubersilva fa2ffe0
refactor: add condition before register route
glaubersilva dfbf875
fix: wrong condition
glaubersilva e5d8dcb
refactor: tweak exception message
glaubersilva d4dd611
Merge branch 'develop' into fun/gateway-webhook-events-api
glaubersilva 1068677
tests: fix broken unit tests
glaubersilva 6b96cf2
Merge branch 'develop' into fun/gateway-webhook-events-api
jonwaldstein 0ab2006
fix: wrong load for events
glaubersilva 3ff57d2
feature: add $gatewaySubscriptionId parameter
glaubersilva ce03cd3
refactor: better naming consistency
glaubersilva 0019af7
fix: broken unit tests
glaubersilva d5deba5
fix: check $gatewaySubscriptionId is not empty
glaubersilva 104f687
feature: add $setDonationComplete param
glaubersilva eb4ff83
fix: remove temp url
glaubersilva eab43eb
Merge branch 'develop' into fun/gateway-webhook-events-api
glaubersilva File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
<?php | ||
|
||
namespace Give\Framework\PaymentGateways; | ||
|
||
use Give\Donations\ValueObjects\DonationStatus; | ||
use Give\Framework\PaymentGateways\Webhooks\EventHandlers\Actions\GetEventHandlerClassByDonationStatus; | ||
use Give\Framework\PaymentGateways\Webhooks\EventHandlers\Actions\GetEventHandlerClassBySubscriptionStatus; | ||
use Give\Framework\PaymentGateways\Webhooks\EventHandlers\SubscriptionFirstDonationCompleted; | ||
use Give\Framework\PaymentGateways\Webhooks\EventHandlers\SubscriptionRenewalDonationCreated; | ||
use Give\Helpers\Hooks; | ||
use Give\ServiceProviders\ServiceProvider as ServiceProviderInterface; | ||
use Give\Subscriptions\ValueObjects\SubscriptionStatus; | ||
|
||
/** | ||
* @unreleased | ||
*/ | ||
class ServiceProvider implements ServiceProviderInterface | ||
{ | ||
/** | ||
* @unreleased | ||
*/ | ||
public function register() | ||
{ | ||
// TODO: Implement register() method. | ||
} | ||
|
||
/** | ||
* @unreleased | ||
*/ | ||
public function boot() | ||
{ | ||
$this->registerWebhookEventHandlers(); | ||
} | ||
|
||
private function registerWebhookEventHandlers() | ||
{ | ||
add_action('give_init', function () { | ||
$registeredPaymentGatewayIds = give()->gateways->getPaymentGateways(); | ||
foreach ($registeredPaymentGatewayIds as $gatewayId) { | ||
$this->addDonationStatusEventHandlers($gatewayId); | ||
$this->addSubscriptionStatusEventHandlers($gatewayId); | ||
$this->addSubscriptionFirstDonationEventHandler($gatewayId); | ||
$this->addSubscriptionRenewalDonationEventHandler($gatewayId); | ||
} | ||
}, 999); | ||
} | ||
|
||
/** | ||
* @unreleased | ||
*/ | ||
private function addDonationStatusEventHandlers(string $gatewayId) | ||
{ | ||
foreach (DonationStatus::values() as $status) { | ||
if ($eventHandlerClass = (new GetEventHandlerClassByDonationStatus())($status)) { | ||
Hooks::addAction( | ||
sprintf('givewp_%s_webhook_event_donation_status_%s', $gatewayId, $status->getValue()), | ||
$eventHandlerClass | ||
); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* @unreleased | ||
*/ | ||
private function addSubscriptionStatusEventHandlers(string $gatewayId) | ||
{ | ||
foreach (SubscriptionStatus::values() as $status) { | ||
if ($eventHandlerClass = (new GetEventHandlerClassBySubscriptionStatus())($status)) { | ||
Hooks::addAction( | ||
sprintf('givewp_%s_webhook_event_subscription_status_%s', $gatewayId, $status->getValue()), | ||
$eventHandlerClass | ||
); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* @unreleased | ||
*/ | ||
private function addSubscriptionFirstDonationEventHandler(string $gatewayId) | ||
{ | ||
Hooks::addAction( | ||
sprintf('givewp_%s_webhook_event_subscription_first_donation', $gatewayId), | ||
SubscriptionFirstDonationCompleted::class | ||
); | ||
} | ||
|
||
/** | ||
* @unreleased | ||
*/ | ||
private function addSubscriptionRenewalDonationEventHandler(string $gatewayId) | ||
{ | ||
Hooks::addAction( | ||
sprintf('givewp_%s_webhook_event_subscription_renewal_donation', $gatewayId), | ||
SubscriptionRenewalDonationCreated::class | ||
); | ||
} | ||
} |
49 changes: 49 additions & 0 deletions
49
...k/PaymentGateways/Webhooks/EventHandlers/Actions/GetEventHandlerClassByDonationStatus.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
<?php | ||
|
||
namespace Give\Framework\PaymentGateways\Webhooks\EventHandlers\Actions; | ||
|
||
use Give\Donations\ValueObjects\DonationStatus; | ||
use Give\Framework\PaymentGateways\Webhooks\EventHandlers\DonationAbandoned; | ||
use Give\Framework\PaymentGateways\Webhooks\EventHandlers\DonationCancelled; | ||
use Give\Framework\PaymentGateways\Webhooks\EventHandlers\DonationCompleted; | ||
use Give\Framework\PaymentGateways\Webhooks\EventHandlers\DonationFailed; | ||
use Give\Framework\PaymentGateways\Webhooks\EventHandlers\DonationPending; | ||
use Give\Framework\PaymentGateways\Webhooks\EventHandlers\DonationPreapproval; | ||
use Give\Framework\PaymentGateways\Webhooks\EventHandlers\DonationProcessing; | ||
use Give\Framework\PaymentGateways\Webhooks\EventHandlers\DonationRefunded; | ||
use Give\Framework\PaymentGateways\Webhooks\EventHandlers\DonationRevoked; | ||
|
||
/** | ||
* @unreleased | ||
*/ | ||
class GetEventHandlerClassByDonationStatus | ||
{ | ||
/** | ||
* @unreleased | ||
*/ | ||
public function __invoke(DonationStatus $status): string | ||
{ | ||
switch ($status) { | ||
case $status->isAbandoned(): | ||
return DonationAbandoned::class; | ||
case $status->isCancelled(): | ||
return DonationCancelled::class; | ||
case $status->isComplete(): | ||
return DonationCompleted::class; | ||
case $status->isFailed(): | ||
return DonationFailed::class; | ||
case $status->isPending(): | ||
return DonationPending::class; | ||
case $status->isPreapproval(): | ||
return DonationPreapproval::class; | ||
case $status->isProcessing(): | ||
return DonationProcessing::class; | ||
case $status->isRefunded(): | ||
return DonationRefunded::class; | ||
case $status->isRevoked(): | ||
return DonationRevoked::class; | ||
default: | ||
return ''; | ||
} | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
...ymentGateways/Webhooks/EventHandlers/Actions/GetEventHandlerClassBySubscriptionStatus.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
<?php | ||
|
||
namespace Give\Framework\PaymentGateways\Webhooks\EventHandlers\Actions; | ||
|
||
use Give\Framework\PaymentGateways\Webhooks\EventHandlers\SubscriptionActive; | ||
use Give\Framework\PaymentGateways\Webhooks\EventHandlers\SubscriptionCancelled; | ||
use Give\Framework\PaymentGateways\Webhooks\EventHandlers\SubscriptionCompleted; | ||
use Give\Framework\PaymentGateways\Webhooks\EventHandlers\SubscriptionExpired; | ||
use Give\Framework\PaymentGateways\Webhooks\EventHandlers\SubscriptionFailing; | ||
use Give\Framework\PaymentGateways\Webhooks\EventHandlers\SubscriptionPaused; | ||
use Give\Framework\PaymentGateways\Webhooks\EventHandlers\SubscriptionPending; | ||
use Give\Framework\PaymentGateways\Webhooks\EventHandlers\SubscriptionSuspended; | ||
use Give\Subscriptions\ValueObjects\SubscriptionStatus; | ||
|
||
/** | ||
* @unreleased | ||
*/ | ||
class GetEventHandlerClassBySubscriptionStatus | ||
{ | ||
/** | ||
* @unreleased | ||
*/ | ||
public function __invoke(SubscriptionStatus $status): string | ||
{ | ||
switch ($status) { | ||
case $status->isActive(): | ||
return SubscriptionActive::class; | ||
case $status->isCancelled(): | ||
return SubscriptionCancelled::class; | ||
case $status->isCompleted(): | ||
return SubscriptionCompleted::class; | ||
case $status->isExpired(): | ||
return SubscriptionExpired::class; | ||
case $status->isFailing(): | ||
return SubscriptionFailing::class; | ||
case $status->isPaused(): | ||
return SubscriptionPaused::class; | ||
case $status->isPending(): | ||
return SubscriptionPending::class; | ||
case $status->isSuspended(): | ||
return SubscriptionSuspended::class; | ||
default: | ||
return ''; | ||
} | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
src/Framework/PaymentGateways/Webhooks/EventHandlers/SubscriptionPaused.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<?php | ||
|
||
namespace Give\Framework\PaymentGateways\Webhooks\EventHandlers; | ||
|
||
use Exception; | ||
use Give\Framework\PaymentGateways\Webhooks\EventHandlers\Actions\UpdateSubscriptionStatus; | ||
use Give\Subscriptions\ValueObjects\SubscriptionStatus; | ||
|
||
/** | ||
* @unreleased | ||
*/ | ||
class SubscriptionPaused | ||
{ | ||
/** | ||
* @unreleased | ||
* | ||
* @throws Exception | ||
*/ | ||
public function __invoke(string $gatewaySubscriptionId, string $message = '') | ||
{ | ||
$subscription = give()->subscriptions->getByGatewaySubscriptionId($gatewaySubscriptionId); | ||
|
||
if ( ! $subscription || $subscription->status->isPaused()) { | ||
return; | ||
} | ||
|
||
(new UpdateSubscriptionStatus())($subscription, SubscriptionStatus::PAUSED(), $message); | ||
} | ||
|
||
} |
29 changes: 29 additions & 0 deletions
29
src/Framework/PaymentGateways/Webhooks/EventHandlers/SubscriptionPending.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<?php | ||
|
||
namespace Give\Framework\PaymentGateways\Webhooks\EventHandlers; | ||
|
||
use Exception; | ||
use Give\Framework\PaymentGateways\Webhooks\EventHandlers\Actions\UpdateSubscriptionStatus; | ||
use Give\Subscriptions\ValueObjects\SubscriptionStatus; | ||
|
||
/** | ||
* @unreleased | ||
*/ | ||
class SubscriptionPending | ||
{ | ||
/** | ||
* @unreleased | ||
* | ||
* @throws Exception | ||
*/ | ||
public function __invoke(string $gatewaySubscriptionId, string $message = '') | ||
{ | ||
$subscription = give()->subscriptions->getByGatewaySubscriptionId($gatewaySubscriptionId); | ||
|
||
if ( ! $subscription || $subscription->status->isPending()) { | ||
return; | ||
} | ||
|
||
(new UpdateSubscriptionStatus())($subscription, SubscriptionStatus::PENDING(), $message); | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, I know webhooks are very common and most if not all gateways use them, however I'm wondering if gateways should opt-in to webhook support before adding support out of the box. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jonwaldstein I think it makes sense. Do you have any suggestions about the best way to do this opt-in?
My initial idea is to enable it only for gateways that implement the
WebhookNotificationsListener
interface.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@glaubersilva yeah I do have some ideas that I think we should chat about.
Here's my initial thoughts:
Option 1.
WebhookNotificationsListener
interface. I I like how easy adding the interface is. It does a good job of abstracting away the webhook logic, but I have a slight concern that it could also be too clever for webhooks specifically and maybe too limiting in terms of the url and logic. I'm also not sure the gateway route methods supports POST requests? will have to double check that.Option 2. Some kind of explicit webhook registrar like
$gateway->registerWebhooks([$url, $listener])
(Maybe similiar to our subscription module?). This would make the webhooks more clear in their intention and flexible with the ability to add more than one, customize the url and listener, etc. It would also be useful to have a full list of all registered webhooks that admins could be aware of.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jonwaldstein I think option two will remove the simplicity of the approach implemented on this PR - the main idea here is to encapsulate the logic that gets repeated across multiple add-ons/gateways.
However, if some add-on/gateway requires something too specific, we still can create a customized webhook URL and listener like we do currently, but it will be the exception since our current webhook event handler classes cover the most common cases.
But I like the idea of having some way to customize the default URL used to listen to the webhook notifications, and also would like to explore if it supports POST requests - I think we have a request about it in some place.