Skip to content
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

Webhook Configuration #10

Merged
merged 10 commits into from
Jun 14, 2024
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
name: ci
uses: craftcms/.github/.github/workflows/ci.yml@v3
with:
craft_version: '5'
craft_version: "5"
jobs: '["ecs", "phpstan", "prettier"]'
notify_slack: true
slack_subteam: <!subteam^S01CWPYH9D5>
Expand Down
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
src/web/assets/stripecp/dist
composer.lock
README.md
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Release Notes for Stripe

## 1.0.1 - 2024-05-07

- Fixed an error that could occur on the My Account page, due to a plugin conflict. ([#4](https://github.com/craftcms/stripe/issues/4))
- Fixed a SQL error that could occur on MariaDB. ([#5](https://github.com/craftcms/stripe/pull/5))

## 1.0.0 - 2024-04-30

- Initial release
22 changes: 18 additions & 4 deletions src/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -563,15 +563,29 @@ private function handleUserElementChanges(): void
Event::on(User::class, User::EVENT_AFTER_SAVE, function(ModelEvent $event) {
/** @var User|StripeCustomerBehavior $user */
$user = $event->sender;
if (!empty($user->email) && $user->getStripeCustomers()->isEmpty()) {
// search for customer in Stripe by their email address

// Do they have an email at all?
if (empty($user->email)) {
return;
}

// Do we have any existing Stripe customer records for them?
if (!$user->getStripeCustomers()->isEmpty()) {
return;
}

// Search for customer in Stripe by their email address:
try {
// If the plugin isn't configured yet, this may fail:
$stripe = $this->getApi()->getClient();
$stripeCustomers = $stripe->customers->search(['query' => "email:'{$user->email}'"]);
// if we found Stripe customers with that email address - kick off the queue job to sync data

// If we found Stripe customers with that email address, kick off the queue job to sync data:
if (!$stripeCustomers->isEmpty()) {
// sync data via the queue
Queue::push(new SyncData());
}
} catch (\Stripe\Exception\ExceptionInterface $e) {
Craft::error("Tried to synchronize user data, but the plugin was not fully configured: {$e->getMessage()}", 'stripe');
}
});
}
Expand Down
26 changes: 18 additions & 8 deletions src/controllers/WebhooksController.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Stripe\WebhookEndpoint;
use yii\base\InvalidConfigException;
use yii\web\Response as YiiResponse;
use yii\web\ServerErrorHttpException;

/**
* The WebhooksController handles Stripe webhook event.
Expand All @@ -28,9 +29,20 @@
class WebhooksController extends Controller
{
public $defaultAction = 'handle';
public $enableCsrfValidation = false;
public array|bool|int $allowAnonymous = ['handle'];

/**
* @inheritdoc
*/
public function beforeAction($action): bool
{
// Disable CSRF only for incoming webhooks (to agree with `allowAnonymous`, above):
if ($action->id === 'handle') {
$this->enableCsrfValidation = false;
}

return parent::beforeAction($action);
}
/**
* Handle incoming Stripe webhook event
*
Expand Down Expand Up @@ -82,7 +94,7 @@ public function actionEdit(): YiiResponse
$pluginSettings = $plugin->getSettings();

if (!$pluginSettings->secretKey) {
throw new InvalidConfigException('No Stripe API key found, check credentials in settings.');
throw new ServerErrorHttpException('No Stripe API key found. Make sure you have added one in the plugin’s settings screen.');
}

$webhookInfo = [];
Expand Down Expand Up @@ -125,7 +137,7 @@ public function actionCreate()
$pluginSettings = $plugin->getSettings();

if (!$pluginSettings->secretKey) {
throw new InvalidConfigException('No Stripe API key found, check credentials in settings.');
throw new ServerErrorHttpException('No Stripe API key found, check credentials in settings.');
}

$stripe = $plugin->getApi()->getClient();
Expand Down Expand Up @@ -218,7 +230,7 @@ private function saveWebhookData(Plugin $plugin, Settings $settings, WebhookEndp
$configService->setDotEnvVar('STRIPE_WH_KEY', $response->secret ?? '');
} catch (\Throwable $e) {
$success = false;
Craft::error('Couldn\'t save you Stripe Webhook Signing Secret in the .env file. ' . $e->getMessage());
Craft::error('Couldn\'t save the Stripe Webhook Signing Secret in the .env file. ' . $e->getMessage());
}
$success ? $settings->webhookSigningSecret = '$STRIPE_WH_KEY' : $response->secret;

Expand All @@ -227,15 +239,14 @@ private function saveWebhookData(Plugin $plugin, Settings $settings, WebhookEndp
$configService->setDotEnvVar('STRIPE_WH_ID', $response->id ?? '');
} catch (\Throwable $e) {
$success = false;
Craft::error('Couldn\'t save you Stripe Webhook Id in the .env file. ' . $e->getMessage());
Craft::error('Couldn\'t save the Stripe Webhook ID in the .env file. ' . $e->getMessage());
}
$success ? $settings->webhookId = '$STRIPE_WH_ID' : $response->id;

if (!Craft::$app->getPlugins()->savePluginSettings($plugin, $settings->toArray())) {
Craft::$app->getSession()->setNotice(Craft::t(
'stripe',
'Webhook registered successfully, but we had trouble saving the Webhook Signing Secret.
Please go to your Stripe Dashboard, get the webhook signing secret and add it to your plugin’s settings.')
'Webhook registered successfully, but we had trouble saving the Webhook Signing Secret. Please go to your Stripe Dashboard, get the webhook signing secret and add it to your plugin’s settings.')
);
} else {
$this->setSuccessFlash(Craft::t(
Expand All @@ -251,7 +262,6 @@ private function saveWebhookData(Plugin $plugin, Settings $settings, WebhookEndp
* @param Plugin $plugin
* @param string $webhookId
* @return WebhookEndpoint
* @throws InvalidConfigException
* @throws \Stripe\Exception\ApiErrorException
*/
private function getWebhookInfo(Plugin $plugin, string $webhookId): WebhookEndpoint
Expand Down
40 changes: 22 additions & 18 deletions src/templates/webhooks/_index.twig
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,29 @@

{% block content %}

<h3>{{ "Webhook Management"|t('stripe') }}</h3>
<h2>{{ "Webhook Management"|t('stripe') }}</h2>

<div class="{{ hasWebhook ? 'hidden' : '' }}">
<p>{{ "Create the webhook for the current environment."|t('stripe') }}</p>
{% if not hasWebhook %}
<p>{{ "Create the webhook for the current environment."|t('stripe') }} <a class="go" href="https://github.com/craftcms/stripe#webhooks">{{ 'Learn more'|t('app') }}</a></p>
{% endif %}

{# Divs needed for the Admin Table js below #}
<div class="pane tablepane hairline">
<div id="webhooks-container">
</div>
</div>

{% if not hasWebhook %}
<form method="POST">
{{ actionInput('stripe/webhooks/create') }}
{{ redirectInput('stripe/webhooks') }}
{{ csrfInput() }}
<button class="btn primary" type="submit">{{ "Create"|t('stripe') }}</button>
</form>
</div>
{% endif %}

{# Divs needed for the Admin Table js below #}
<div class="field">
<div id="webhooks-container">
</div>
</div>
{% set tableData = [] %}

{% set tableData = [] %}
{% if webhookInfo is not empty %}
{% set tableData = [{
id: webhookInfo.id,
Expand All @@ -40,11 +44,11 @@

{% js %}
var columns = [
{ name: '__slot:title', title: '{{ 'Topic'|t('stripe') }}' },
{ name: 'apiVersion', title: '{{ 'API Version'|t('stripe') }}' },
{ name: 'livemode', title: '{{ 'Live Mode'|t('stripe') }}' },
{ name: 'whStatus', title: '{{ 'Status'|t('stripe') }}' },
{ name: 'address', title: '{{ 'URL'|t('stripe') }}' },
{ name: '__slot:title', title: '{{ 'Topic'|t('stripe') }}' },
{ name: 'apiVersion', title: '{{ 'API Version'|t('stripe') }}' },
{ name: 'livemode', title: '{{ 'Live Mode'|t('stripe') }}' },
{ name: 'whStatus', title: '{{ 'Status'|t('stripe') }}' },
{ name: 'address', title: '{{ 'URL'|t('stripe') }}' },
];

new Craft.VueAdminTable({
Expand All @@ -57,9 +61,9 @@
deleteSuccessMessage: Craft.t('stripe', "Webhook deleted"),
emptyMessage: Craft.t('stripe', 'No webhooks exist yet.'),
tableData: {{ tableData|json_encode|raw }},
deleteCallback: function(){
window.location.reload(); // We need to reload to get the create button showing again
}
deleteCallback: function() {
window.location.reload(); // We need to reload to get the create button showing again
},
});
{% endjs %}
{% endblock %}
Expand Down
Loading