PluginProbe
GiveWP – Donation Plugin and Fundraising Platform / 4.15.4
GiveWP – Donation Plugin and Fundraising Platform v4.15.4
4.17.0 4.16.9 4.16.8.1 4.16.8 4.16.7.2 4.16.7.1 4.16.7 4.16.6.1 4.16.6 4.16.5.1 4.16.5 4.16.4 4.16.3 4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 2.3.2 All 256 releases
give / src / Framework / PaymentGateways / PaymentGateway.php

PaymentGateway.php in GiveWP – Donation Plugin and Fundraising Platform 4.15.4, at src/Framework/PaymentGateways/PaymentGateway.php

478 lines 13.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Give\Framework\PaymentGateways;
4
5 use Give\Donations\Models\Donation;
6 use Give\Framework\Exceptions\Primitives\Exception;
7 use Give\Framework\PaymentGateways\Actions\GenerateGatewayRouteUrl;
8 use Give\Framework\PaymentGateways\Contracts\PaymentGatewayInterface;
9 use Give\Framework\PaymentGateways\Contracts\PaymentGatewayRefundable;
10 use Give\Framework\PaymentGateways\Contracts\Subscription\SubscriptionAmountEditable;
11 use Give\Framework\PaymentGateways\Contracts\Subscription\SubscriptionDashboardLinkable;
12 use Give\Framework\PaymentGateways\Contracts\Subscription\SubscriptionPausable;
13 use Give\Framework\PaymentGateways\Contracts\Subscription\SubscriptionPaymentMethodEditable;
14 use Give\Framework\PaymentGateways\Contracts\Subscription\SubscriptionTransactionsSynchronizable;
15 use Give\Framework\PaymentGateways\Contracts\WebhookNotificationsListener;
16 use Give\Framework\PaymentGateways\Routes\RouteSignature;
17 use Give\Framework\PaymentGateways\Traits\HandleHttpResponses;
18 use Give\Framework\PaymentGateways\Traits\HasRouteMethods;
19 use Give\Framework\PaymentGateways\Webhooks\Webhook;
20 use Give\Framework\Support\ValueObjects\Money;
21 use Give\Log\Log;
22 use Give\Subscriptions\Models\Subscription;
23 use ReflectionException;
24 use ReflectionMethod;
25 use Give\Framework\Support\Contracts\Arrayable;
26 use JsonSerializable;
27
28 /**
29 * @since 4.6.0 Added JSONSerializable and Arrayable interfaces
30 * @since 2.30.0 added enqueueScript() and formSettings() methods.
31 * @since 2.18.0
32 */
33 abstract class PaymentGateway implements PaymentGatewayInterface,
34 SubscriptionDashboardLinkable,
35 SubscriptionAmountEditable,
36 SubscriptionPaymentMethodEditable,
37 SubscriptionTransactionsSynchronizable,
38 JsonSerializable,
39 Arrayable
40 {
41 use HandleHttpResponses;
42 use HasRouteMethods {
43 supportsMethodRoute as protected SupportsOwnMethodRoute;
44 callRouteMethod as protected CallOwnRouteMethod;
45 }
46
47 /**
48 * @since 2.20.0 Change variable type to SubscriptionModule.
49 * @var SubscriptionModule $subscriptionModule
50 */
51 public $subscriptionModule;
52
53 /**
54 * @since 4.5.0
55 *
56 * @var Webhook $webhook
57 */
58 public $webhook;
59
60 /**
61 * @since 4.6.0 add explicit nullable type to subscriptionModule parameter
62 * @since 4.5.0 Add the webhookEvents property
63 *
64 * @since 2.20.0 Change first argument type to SubscriptionModule abstract class.
65 * @since 2.18.0
66 *
67 * @param SubscriptionModule|null $subscriptionModule
68 */
69 public function __construct(?SubscriptionModule $subscriptionModule = null)
70 {
71 if ($subscriptionModule !== null) {
72 $subscriptionModule->setGateway($this);
73 }
74
75 $this->subscriptionModule = $subscriptionModule;
76 $this->webhook = new Webhook($this);
77 }
78
79 /**
80 * @since 4.5.0
81 */
82 public static function webhook(): Webhook
83 {
84 $instance = new static();
85
86 return $instance->webhook;
87 }
88
89 /**
90 * @since 4.5.0
91 */
92 public function canListeningWebhookNotifications(): bool
93 {
94 return $this instanceof WebhookNotificationsListener;
95 }
96
97 /**
98 * @since 2.30.0
99 */
100 public function supportsFormVersions(): array
101 {
102 $versions = [];
103
104 if (method_exists($this, 'getLegacyFormFieldMarkup') && $this->isFunctionImplementedInGatewayClass(
105 'getLegacyFormFieldMarkup'
106 )) {
107 $versions[] = 2;
108 }
109
110 if (method_exists($this, 'enqueueScript') && $this->isFunctionImplementedInGatewayClass('enqueueScript')) {
111 $versions[] = 3;
112 }
113
114 return $versions;
115 }
116
117 /**
118 * Enqueue gateway scripts using WordPress wp_enqueue_script().
119 *
120 * @since 2.30.0
121 *
122 * @return void
123 */
124 public function enqueueScript(int $formId)
125 {
126 //wp_enqueue_scripts();
127 }
128
129 /**
130 * Convenient way of localizing data to the JS gateway object accessible from `this.settings`.
131 *
132 * @since 2.30.0
133 */
134 public function formSettings(int $formId): array
135 {
136 return [];
137 }
138
139 /**
140 * @inheritDoc
141 * @since 4.6.0 updated to use PaymentGatewayRefundable interface
142 *
143 * @since 2.29.0
144 */
145 public function supportsRefund(): bool
146 {
147 if ($this instanceof PaymentGatewayRefundable) {
148 return true;
149 }
150
151 // backward compatibility for add-on legacy gateways that don't implement the PaymentGatewayRefundable interface
152 return method_exists($this, 'refundDonation');
153 }
154
155 /**
156 * @inheritDoc
157 */
158 public function supportsSubscriptions(): bool
159 {
160 return isset($this->subscriptionModule) || $this->isFunctionImplementedInGatewayClass('createSubscription');
161 }
162
163 /**
164 * If a subscription module isn't wanted this method can be overridden by a child class instead.
165 * Just make sure to override the supportsSubscriptions method as well.
166 *
167 * @inheritDoc
168 */
169 public function createSubscription(
170 Donation $donation,
171 Subscription $subscription,
172 $gatewayData
173 ) {
174 return $this->subscriptionModule->createSubscription($donation, $subscription, $gatewayData);
175 }
176
177 /**
178 * @inheritDoc
179 */
180 public function cancelSubscription(Subscription $subscription)
181 {
182 $this->subscriptionModule->cancelSubscription($subscription);
183 }
184
185 /**
186 * @inheritDoc
187 *
188 * @since 3.17.0
189 */
190 public function pauseSubscription(Subscription $subscription, array $data = []): void
191 {
192 if ($this->subscriptionModule instanceof SubscriptionPausable) {
193 $this->subscriptionModule->pauseSubscription($subscription, $data);
194
195 return;
196 }
197
198 throw new Exception('Gateway does not support pausing the subscription.');
199 }
200
201 /**
202 * @inheritDoc
203 *
204 * @since 3.17.0
205 */
206 public function resumeSubscription(Subscription $subscription): void
207 {
208 if ($this->subscriptionModule instanceof SubscriptionPausable) {
209 $this->subscriptionModule->resumeSubscription($subscription);
210
211 return;
212 }
213
214 throw new Exception('Gateway does not support resuming the subscription.');
215 }
216
217 /**
218 * @inheritDoc
219 *
220 * @since 3.17.0
221 */
222 public function canPauseSubscription(): bool
223 {
224 if ($this->subscriptionModule instanceof SubscriptionPausable) {
225 return $this->subscriptionModule->canPauseSubscription();
226 }
227
228 return false;
229 }
230
231 /**
232 * @since 2.21.2
233 * @inheritDoc
234 */
235 public function canSyncSubscriptionWithPaymentGateway(): bool
236 {
237 if ($this->subscriptionModule) {
238 return $this->subscriptionModule->canSyncSubscriptionWithPaymentGateway();
239 }
240
241 return $this->isFunctionImplementedInGatewayClass('synchronizeSubscription');
242 }
243
244 /**
245 * @since 2.21.2
246 * @inheritDoc
247 */
248 public function canUpdateSubscriptionAmount(): bool
249 {
250 if ($this->subscriptionModule) {
251 return $this->subscriptionModule->canUpdateSubscriptionAmount();
252 }
253
254 return $this->isFunctionImplementedInGatewayClass('updateSubscriptionAmount');
255 }
256
257 /**
258 * @since 2.21.2
259 * @inheritDoc
260 */
261 public function canUpdateSubscriptionPaymentMethod(): bool
262 {
263 if ($this->subscriptionModule) {
264 return $this->subscriptionModule->canUpdateSubscriptionPaymentMethod();
265 }
266
267 return $this->isFunctionImplementedInGatewayClass('updateSubscriptionPaymentMethod');
268 }
269
270 /**
271 * @since 2.25.0 update return logic
272 * @since 2.21.2
273 */
274 public function hasGatewayDashboardSubscriptionUrl(): bool
275 {
276 if ($this->subscriptionModule) {
277 return $this->subscriptionModule->hasGatewayDashboardSubscriptionUrl();
278 }
279
280 return $this->isFunctionImplementedInGatewayClass('gatewayDashboardSubscriptionUrl');
281 }
282
283 /**
284 * @since 2.33.0 Return synchronizeSubscription() instead nothing
285 * @since 2.21.2
286 * @inheritDoc
287 * @throws Exception
288 */
289 public function synchronizeSubscription(Subscription $subscription)
290 {
291 if ($this->subscriptionModule instanceof SubscriptionTransactionsSynchronizable) {
292 return $this->subscriptionModule->synchronizeSubscription($subscription);
293 }
294
295 throw new Exception('Gateway does not support syncing subscriptions.');
296 }
297
298 /**
299 * @since 2.21.2
300 * @inheritDoc
301 * @throws Exception
302 */
303 public function updateSubscriptionAmount(Subscription $subscription, Money $newRenewalAmount)
304 {
305 if ($this->subscriptionModule instanceof SubscriptionAmountEditable) {
306 $this->subscriptionModule->updateSubscriptionAmount($subscription, $newRenewalAmount);
307
308 return;
309 }
310
311 throw new Exception('Gateway does not support updating the subscription amount.');
312 }
313
314 /**
315 * @since 2.21.2
316 * @inheritDoc
317 * @throws Exception
318 */
319 public function updateSubscriptionPaymentMethod(Subscription $subscription, $gatewayData)
320 {
321 if ($this->subscriptionModule instanceof SubscriptionPaymentMethodEditable) {
322 $this->subscriptionModule->updateSubscriptionPaymentMethod($subscription, $gatewayData);
323
324 return;
325 }
326
327 throw new Exception('Gateway does not support updating the subscription payment method.');
328 }
329
330 /**
331 * @since 2.21.2
332 * @inheritDoc
333 */
334 public function gatewayDashboardSubscriptionUrl(Subscription $subscription): string
335 {
336 if ($this->subscriptionModule instanceof SubscriptionDashboardLinkable) {
337 return $this->subscriptionModule->gatewayDashboardSubscriptionUrl($subscription);
338 }
339
340 return false;
341 }
342
343 /**
344 * Generate gateway route url
345 *
346 * @since 2.18.0
347 * @since 2.19.0 remove $donationId param in favor of args
348 */
349 public function generateGatewayRouteUrl(string $gatewayMethod, array $args = []): string
350 {
351 return (new GenerateGatewayRouteUrl())(static::id(), $gatewayMethod, $args);
352 }
353
354 /**
355 * Generate secure gateway route url
356 *
357 * @since 2.19.5 replace nonce with hash and expiration
358 * @since 2.19.4 replace RouteSignature args with unique donationId
359 * @since 2.19.0
360 */
361 public function generateSecureGatewayRouteUrl(string $gatewayMethod, int $donationId, array $args = []): string
362 {
363 $signature = new RouteSignature(static::id(), $gatewayMethod, $donationId);
364
365 return (new GenerateGatewayRouteUrl())(
366 static::id(),
367 $gatewayMethod,
368 array_merge($args, [
369 'give-route-signature' => $signature->toHash(),
370 'give-route-signature-id' => $donationId,
371 'give-route-signature-expiration' => $signature->expiration,
372 ])
373 );
374 }
375
376 /**
377 * @since 2.20.0
378 */
379 public function supportsMethodRoute(string $method): bool
380 {
381 if ($this->subscriptionModule && $this->subscriptionModule->supportsMethodRoute($method)) {
382 return true;
383 }
384
385 return $this->supportsOwnMethodRoute($method);
386 }
387
388 /**
389 * @since 2.20.0
390 *
391 * @param string $method
392 *
393 * @throws Exception
394 */
395 public function callRouteMethod($method, $queryParams)
396 {
397 if ($this->subscriptionModule && $this->subscriptionModule->supportsMethodRoute($method)) {
398 return $this->subscriptionModule->callRouteMethod($method, $queryParams);
399 }
400
401 return $this->callOwnRouteMethod($method, $queryParams);
402 }
403
404 /**
405 * Checks to see if the provided method is being used by the child gateway class. This is used as a helper in the "can" methods
406 * to see if the gateway is implementing a recurring feature without using a subscription module.
407 *
408 * @since 2.21.2
409 */
410 private function isFunctionImplementedInGatewayClass(string $methodName): bool
411 {
412 try {
413 $reflector = new ReflectionMethod($this, $methodName);
414 } catch (ReflectionException $e) {
415 Log::error(
416 sprintf(
417 'ReflectionException thrown when trying to check if %s::%s is implemented in the gateway class.',
418 static::id(),
419 $methodName
420 ),
421 [
422 'exception' => $e,
423 ]
424 );
425
426 return false;
427 }
428
429 return ($reflector->getDeclaringClass()->getName() === get_class($this));
430 }
431
432 /**
433 * @since 4.6.0
434 */
435 public function getTransactionUrl(Donation $donation): ?string
436 {
437 $link = apply_filters('give_payment_details_transaction_id-' . $donation->gatewayId, $donation->gatewayTransactionId, $donation->id);
438
439 // If no link is returned, return null
440 if (empty($link)) {
441 return null;
442 }
443
444 // Extract URL from anchor tag using regex
445 if (preg_match('/href=["\']([^"\']+)["\']/', $link, $matches)) {
446 return $matches[1];
447 }
448
449 // If it's already a URL (not an anchor tag), return as is
450 if (filter_var($link, FILTER_VALIDATE_URL)) {
451 return $link;
452 }
453
454 return null;
455 }
456
457 /**
458 * @since 4.6.0
459 */
460 public function toArray(): array
461 {
462 return [
463 'id' => $this->id(),
464 'name' => $this->getName(),
465 'label' => $this->getPaymentMethodLabel(),
466 ];
467 }
468
469 /**
470 * @since 4.6.0
471 */
472 #[\ReturnTypeWillChange]
473 public function jsonSerialize()
474 {
475 return $this->toArray();
476 }
477 }
478