PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.7.0.5
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.7.0.5
1.8.13.1 1.8.13 1.8.12.3 1.8.12.2 1.8.12.1 1.8.12 1.8.11.3 1.8.11.2 1.8.11.1 1.8.11 1.6.6 1.6.60 1.6.7 1.6.8 1.6.9 1.7.0 1.7.0.1 1.7.0.11 1.7.0.12 1.7.0.14 1.7.0.2 1.7.0.3 1.7.0.5 1.7.0.6 1.7.0.7 All 212 releases
charitable / includes / stripe / gateway / class-charitable-stripe-webhook-api.php

class-charitable-stripe-webhook-api.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.7.0.5, at includes/stripe/gateway/class-charitable-stripe-webhook-api.php

400 lines 10.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Get, update and deactivate webhooks.
4 *
5 * @package Charitable Stripe/Classes/Charitable_Stripe_Webhook_API
6 * @author Eric Daams
7 * @copyright Copyright (c) 2021-2022, Studio 164a
8 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9 * @since 1.3.0
10 * @version 1.3.0
11 */
12
13 // Exit if accessed directly.
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit;
16 }
17
18 if ( ! class_exists( 'Charitable_Stripe_Webhook_API' ) ) :
19
20 /**
21 * Charitable_Stripe_Webhook_API
22 *
23 * @since 1.3.0
24 */
25 class Charitable_Stripe_Webhook_API {
26
27 /**
28 * Secret key.
29 *
30 * @since 1.3.0
31 *
32 * @var string
33 */
34 private $secret_key;
35
36 /**
37 * The webhook setting key, based on whether we're in test mode
38 * and whether this is for the Connect webhook.
39 *
40 * @since 1.3.0
41 *
42 * @var string
43 */
44 private $setting_key;
45
46 /**
47 * Whether this is for the Connect webhook.
48 *
49 * @since 1.3.0
50 *
51 * @var boolean
52 */
53 private $connect_application;
54
55 /**
56 * Gateway helper.
57 *
58 * @since 1.3.0
59 *
60 * @var Charitable_Gateway_Stripe_AM
61 */
62 private $gateway;
63
64 /**
65 * Create class object.
66 *
67 * @since 1.3.0
68 *
69 * @param boolean|null $test_mode Whether to use test mode or not. If left as
70 * null, the site test mode setting will be used.
71 * @param boolean|null $secret_key The api login id to use. If left as null, the
72 * stored setting will be used.
73 * @param boolean $connect_application Whether we want the Connect webhook.
74 */
75 public function __construct( $test_mode = null, $secret_key = null, $connect_application = false ) {
76 $this->test_mode = is_null( $test_mode ) ? charitable_get_option( 'test_mode', false ) : $test_mode;
77 $this->connect_application = $connect_application;
78 $this->secret_key = is_null( $secret_key ) ? $this->parse_secret_key() : $secret_key;
79 $this->setting_key = $this->parse_setting_key();
80 $this->gateway = new Charitable_Gateway_Stripe_AM();
81 }
82
83 /**
84 * Returns class properties.
85 *
86 * @since 1.3.0
87 *
88 * @param string $prop The property to return.
89 * @return mixed
90 */
91 public function __get( $prop ) {
92 return isset( $this->$prop ) ? $this->$prop : null;
93 }
94
95 /**
96 * Return the set of webhook event types we need to subscribe to.
97 *
98 * @since 1.3.0
99 *
100 * @return array
101 */
102 public function get_webhook_events() {
103 /**
104 * Filter the events that the webhook will be notified about.
105 *
106 * @since 1.3.0
107 *
108 * @param array $events The events that the webhook will be notified about.
109 */
110 return apply_filters(
111 'charitable_stripe_webhook_events',
112 [
113 'charge.refunded',
114 'invoice.created',
115 'invoice.payment_failed',
116 'invoice.payment_succeeded',
117 'invoice.payment_action_required',
118 'customer.subscription.updated',
119 'customer.subscription.deleted',
120 'payment_intent.payment_failed',
121 'payment_intent.succeeded',
122 'checkout.session.completed',
123 ],
124 $this->connect_application
125 );
126 }
127
128 /**
129 * Return the webhook listener endpoint.
130 *
131 * @since 1.3.0
132 *
133 * @return string
134 */
135 public function get_webhook_listener() {
136 return charitable_get_ipn_url( Charitable_Gateway_Stripe_AM::ID );
137 }
138
139 /**
140 * Add a new webhook.
141 *
142 * @since 1.3.0
143 *
144 * @return string The webhook id.
145 */
146 public function add_webhook() {
147 /**
148 * First check whether the webhook has already been added.
149 */
150 $webhook = $this->get_webhook();
151
152 error_log( 'add_webook' );
153 error_log( print_r( $webhook, true ) );
154
155 if ( false === $webhook ) {
156 try {
157
158 error_log( 'add_webook false 123' );
159 error_log( print_r( $this->secret_key, true ) );
160
161 $this->gateway->setup_api( $this->secret_key );
162
163 error_log( 'add_webook after setup_api' );
164
165 $webhook = \Stripe\WebhookEndpoint::create(
166 [
167 'url' => $this->get_webhook_listener(),
168 'enabled_events' => $this->get_webhook_events(),
169 'api_version' => Charitable_Gateway_Stripe_AM::STRIPE_API_VERSION,
170 'connect' => $this->connect_application,
171 ]
172 );
173
174 error_log( 'add_webook after WebhookEndpoint' );
175 error_log( print_r( $webhook, true ) );
176 // Override the signing secret, for teseting purposes (perhaps with the Stripe API CLI)
177 if ( defined( 'CHARITABLE_WEBHOOK_SIGNING_SECRET' ) && CHARITABLE_WEBHOOK_SIGNING_SECRET ) {
178 $webhook->secret = CHARITABLE_WEBHOOK_SIGNING_SECRET;
179 }
180 error_log( 'add_webook after WebhookEndpoint UPDATED FOR CLI' );
181 error_log( print_r( $webhook, true ) );
182
183 } catch ( Exception $e ) {
184 if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) {
185 error_log(
186 sprintf(
187 __( 'Error creating Stripe webhook: %s', 'charitable-stripe' ),
188 $e->getMessage()
189 )
190 );
191 }
192
193 error_log( 'ERROR' );
194 error_log( print_r( $e->getMessage(), true ) );
195
196 return 'invalid_request';
197 }
198 }
199
200 error_log( 'add_webook returning $webhook->id' );
201 error_log( print_r( $webhook->id, true ) );
202
203 return $webhook->id;
204 }
205
206 /**
207 * Returns the WebhookEndpoint object, or false if one doesn't exist yet.
208 *
209 * @since 1.3.0
210 *
211 * @return false|\Stripe\WebhookEndpoint
212 */
213 public function get_webhook() {
214 $webhook_id = charitable_get_option( [ 'gateways_stripe', $this->setting_key ], '' );
215
216 if ( ! $webhook_id ) {
217 error_log( 'get_webhook ! $webhook_id' );
218 return $this->has_webhook();
219 }
220
221 try {
222 error_log( 'get_webhook try setup_api using secret key ' . $this->secret_key );
223 $this->gateway->setup_api( $this->secret_key );
224
225 return \Stripe\WebhookEndpoint::retrieve( $webhook_id );
226 } catch ( Exception $e ) {
227 error_log( 'get_webhook try setup_api catch' );
228 return false;
229 }
230 }
231
232 /**
233 * Checks whether a matching webhook already exists within Stripe.
234 *
235 * @since 1.3.0
236 *
237 * @return false|\Stripe\WebhookEndpoint If a webhook exists, returns it.
238 * Otherwise, returns false.
239 */
240 public function has_webhook() {
241 try {
242 $this->gateway->setup_api( $this->secret_key );
243
244 $endpoints = \Stripe\WebhookEndpoint::all( [ 'limit' => 100 ] );
245
246 $endpoint_urls = $this->get_possible_endpoint_urls();
247
248 error_log( 'has_webhook endpoint_urls' );
249 error_log( print_r( $endpoint_urls, true ) );
250 error_log( print_r( $endpoints, true ) );
251
252 foreach ( $endpoints->data as $webhook ) {
253 if ( ! in_array( $webhook->url, $endpoint_urls ) ) {
254 continue;
255 }
256
257 /**
258 * If we're looking for a Connect application webhook, check that the application
259 * property is not null. Otherwise, make sure it is null.
260 */
261 if ( $this->connect_application ? is_null( $webhook->application ) : ! is_null( $webhook->application ) ) {
262 continue;
263 }
264
265 return $webhook;
266 }
267 } catch ( Exception $e ) {
268 if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) {
269 error_log( var_export( $e, true ) );
270 }
271
272 return false;
273 }
274
275 return false;
276 }
277
278 /**
279 * Checks if a webhook needs an update.
280 *
281 * @since 1.3.0
282 *
283 * @param \Stripe\WebhookEndpoint $webhook The webhook endpoint object.
284 * @return boolean
285 */
286 public function webhook_needs_update( $webhook ) {
287 /* The webhook is not enabled, so it needs an update. */
288 if ( 'enabled' != $webhook->status ) {
289 return true;
290 }
291
292 /* The webhook is not sending some events we need, so we need to update it. */
293 if ( ! in_array( '*', $webhook->enabled_events ) && ! empty( array_diff( $this->get_webhook_events(), $webhook->enabled_events ) ) ) {
294 return true;
295 }
296
297 return false;
298 }
299
300 /**
301 * Update the webhook.
302 *
303 * @since 1.3.0
304 *
305 * @return boolean
306 */
307 public function update_webhook() {
308 $webhook = $this->get_webhook();
309
310 if ( ! $webhook ) {
311 return false;
312 }
313
314 try {
315 $this->gateway->setup_api( $this->secret_key );
316
317 $webhook = \Stripe\WebhookEndpoint::retrieve( $webhook->id );
318 $webhook->disabled = false;
319 $webhook->enabled_events = $this->get_webhook_events();
320 $webhook->save();
321
322 return true;
323 } catch ( Exception $e ) {
324 return false;
325 }
326 }
327
328 /**
329 * Deactivate the webhook.
330 *
331 * @since 1.3.0
332 *
333 * @return boolean
334 */
335 public function deactivate_webhook() {
336 $webhook = $this->get_webhook();
337
338 if ( ! $webhook ) {
339 return false;
340 }
341
342 try {
343 $this->gateway->setup_api( $this->secret_key );
344
345 $webhook = \Stripe\WebhookEndpoint::retrieve( $webhook->id );
346 $webhook->disabled = true;
347 $webhook->save();
348 } catch ( Exception $e ) {
349 return false;
350 }
351 }
352
353 /**
354 * Returns the setting key to use based on whether it's test mode and whether it's for the Connect webhook.
355 *
356 * @since 1.3.0
357 *
358 * @return string
359 */
360 private function parse_setting_key() {
361 if ( $this->test_mode ) {
362 return $this->connect_application ? 'test_connect_webhook_id' : 'test_webhook_id';
363 }
364
365 return $this->connect_application ? 'live_connect_webhook_id' : 'live_webhook_id';
366 }
367
368 /**
369 * Return the secret key to use based on whether it's test mode.
370 *
371 * @since 1.3.0
372 *
373 * @return string
374 */
375 private function parse_secret_key() {
376 $setting = $this->test_mode ? 'test_secret_key' : 'live_secret_key';
377
378 return charitable_get_option( [ 'gateways_stripe', $setting ] );
379 }
380
381 /**
382 * Return all possible webhook URLs.
383 *
384 * @since 1.3.0
385 *
386 * @return string[]
387 */
388 private function get_possible_endpoint_urls() {
389 $home_url = home_url();
390
391 return [
392 sprintf( '%s/charitable-listener/%s', untrailingslashit( $home_url ), Charitable_Gateway_Stripe_AM::ID ),
393 esc_url_raw( add_query_arg( [ 'charitable-listener' => Charitable_Gateway_Stripe_AM::ID ], trailingslashit( $home_url ) ) ),
394 esc_url_raw( add_query_arg( [ 'charitable-listener' => Charitable_Gateway_Stripe_AM::ID ], untrailingslashit( $home_url ) ) ),
395 ];
396 }
397 }
398
399 endif;
400