setGateway($this); } $this->subscriptionModule = $subscriptionModule; $this->webhook = new Webhook($this); } /** * @since 4.5.0 */ public static function webhook(): Webhook { $instance = new static(); return $instance->webhook; } /** * @since 4.5.0 */ public function canListeningWebhookNotifications(): bool { return $this instanceof WebhookNotificationsListener; } /** * @since 2.30.0 */ public function supportsFormVersions(): array { $versions = []; if (method_exists($this, 'getLegacyFormFieldMarkup') && $this->isFunctionImplementedInGatewayClass( 'getLegacyFormFieldMarkup' )) { $versions[] = 2; } if (method_exists($this, 'enqueueScript') && $this->isFunctionImplementedInGatewayClass('enqueueScript')) { $versions[] = 3; } return $versions; } /** * Enqueue gateway scripts using WordPress wp_enqueue_script(). * * @since 2.30.0 * * @return void */ public function enqueueScript(int $formId) { //wp_enqueue_scripts(); } /** * Convenient way of localizing data to the JS gateway object accessible from `this.settings`. * * @since 2.30.0 */ public function formSettings(int $formId): array { return []; } /** * @inheritDoc * @since 4.6.0 updated to use PaymentGatewayRefundable interface * * @since 2.29.0 */ public function supportsRefund(): bool { if ($this instanceof PaymentGatewayRefundable) { return true; } // backward compatibility for add-on legacy gateways that don't implement the PaymentGatewayRefundable interface return method_exists($this, 'refundDonation'); } /** * @inheritDoc */ public function supportsSubscriptions(): bool { return isset($this->subscriptionModule) || $this->isFunctionImplementedInGatewayClass('createSubscription'); } /** * If a subscription module isn't wanted this method can be overridden by a child class instead. * Just make sure to override the supportsSubscriptions method as well. * * @inheritDoc */ public function createSubscription( Donation $donation, Subscription $subscription, $gatewayData ) { return $this->subscriptionModule->createSubscription($donation, $subscription, $gatewayData); } /** * @inheritDoc */ public function cancelSubscription(Subscription $subscription) { $this->subscriptionModule->cancelSubscription($subscription); } /** * @inheritDoc * * @since 3.17.0 */ public function pauseSubscription(Subscription $subscription, array $data = []): void { if ($this->subscriptionModule instanceof SubscriptionPausable) { $this->subscriptionModule->pauseSubscription($subscription, $data); return; } throw new Exception('Gateway does not support pausing the subscription.'); } /** * @inheritDoc * * @since 3.17.0 */ public function resumeSubscription(Subscription $subscription): void { if ($this->subscriptionModule instanceof SubscriptionPausable) { $this->subscriptionModule->resumeSubscription($subscription); return; } throw new Exception('Gateway does not support resuming the subscription.'); } /** * @inheritDoc * * @since 3.17.0 */ public function canPauseSubscription(): bool { if ($this->subscriptionModule instanceof SubscriptionPausable) { return $this->subscriptionModule->canPauseSubscription(); } return false; } /** * @since 2.21.2 * @inheritDoc */ public function canSyncSubscriptionWithPaymentGateway(): bool { if ($this->subscriptionModule) { return $this->subscriptionModule->canSyncSubscriptionWithPaymentGateway(); } return $this->isFunctionImplementedInGatewayClass('synchronizeSubscription'); } /** * @since 2.21.2 * @inheritDoc */ public function canUpdateSubscriptionAmount(): bool { if ($this->subscriptionModule) { return $this->subscriptionModule->canUpdateSubscriptionAmount(); } return $this->isFunctionImplementedInGatewayClass('updateSubscriptionAmount'); } /** * @since 2.21.2 * @inheritDoc */ public function canUpdateSubscriptionPaymentMethod(): bool { if ($this->subscriptionModule) { return $this->subscriptionModule->canUpdateSubscriptionPaymentMethod(); } return $this->isFunctionImplementedInGatewayClass('updateSubscriptionPaymentMethod'); } /** * @since 2.25.0 update return logic * @since 2.21.2 */ public function hasGatewayDashboardSubscriptionUrl(): bool { if ($this->subscriptionModule) { return $this->subscriptionModule->hasGatewayDashboardSubscriptionUrl(); } return $this->isFunctionImplementedInGatewayClass('gatewayDashboardSubscriptionUrl'); } /** * @since 2.33.0 Return synchronizeSubscription() instead nothing * @since 2.21.2 * @inheritDoc * @throws Exception */ public function synchronizeSubscription(Subscription $subscription) { if ($this->subscriptionModule instanceof SubscriptionTransactionsSynchronizable) { return $this->subscriptionModule->synchronizeSubscription($subscription); } throw new Exception('Gateway does not support syncing subscriptions.'); } /** * @since 2.21.2 * @inheritDoc * @throws Exception */ public function updateSubscriptionAmount(Subscription $subscription, Money $newRenewalAmount) { if ($this->subscriptionModule instanceof SubscriptionAmountEditable) { $this->subscriptionModule->updateSubscriptionAmount($subscription, $newRenewalAmount); return; } throw new Exception('Gateway does not support updating the subscription amount.'); } /** * @since 2.21.2 * @inheritDoc * @throws Exception */ public function updateSubscriptionPaymentMethod(Subscription $subscription, $gatewayData) { if ($this->subscriptionModule instanceof SubscriptionPaymentMethodEditable) { $this->subscriptionModule->updateSubscriptionPaymentMethod($subscription, $gatewayData); return; } throw new Exception('Gateway does not support updating the subscription payment method.'); } /** * @since 2.21.2 * @inheritDoc */ public function gatewayDashboardSubscriptionUrl(Subscription $subscription): string { if ($this->subscriptionModule instanceof SubscriptionDashboardLinkable) { return $this->subscriptionModule->gatewayDashboardSubscriptionUrl($subscription); } return false; } /** * Generate gateway route url * * @since 2.18.0 * @since 2.19.0 remove $donationId param in favor of args */ public function generateGatewayRouteUrl(string $gatewayMethod, array $args = []): string { return (new GenerateGatewayRouteUrl())(static::id(), $gatewayMethod, $args); } /** * Generate secure gateway route url * * @since 4.16.8 put the signed values on the URL encoded, so a raw value with an ampersand round-trips * @since 4.16.8 cover $args with the signature * @since 2.19.5 replace nonce with hash and expiration * @since 2.19.4 replace RouteSignature args with unique donationId * @since 2.19.0 */ public function generateSecureGatewayRouteUrl(string $gatewayMethod, int $donationId, array $args = []): string { $args = RouteSignature::normalizeArgs(array_diff_key($args, array_flip(GatewayRouteData::ROUTE_PARAMS))); $signature = new RouteSignature(static::id(), $gatewayMethod, $donationId, null, $args); return (new GenerateGatewayRouteUrl())( static::id(), $gatewayMethod, array_merge(RouteSignature::encodeArgs($args), [ 'give-route-signature' => $signature->toHash(), 'give-route-signature-id' => $donationId, 'give-route-signature-expiration' => $signature->expiration, // Named here so the route knows which args to rebuild the signature from, and so a gateway // appending its own on the way back does not break it. The list is itself signed. 'give-route-signature-args' => implode(',', $signature->argKeys), ]) ); } /** * @since 2.20.0 */ public function supportsMethodRoute(string $method): bool { if ($this->subscriptionModule && $this->subscriptionModule->supportsMethodRoute($method)) { return true; } return $this->supportsOwnMethodRoute($method); } /** * @since 2.20.0 * * @param string $method * * @throws Exception */ public function callRouteMethod($method, $queryParams) { if ($this->subscriptionModule && $this->subscriptionModule->supportsMethodRoute($method)) { return $this->subscriptionModule->callRouteMethod($method, $queryParams); } return $this->callOwnRouteMethod($method, $queryParams); } /** * 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 * to see if the gateway is implementing a recurring feature without using a subscription module. * * @since 2.21.2 */ private function isFunctionImplementedInGatewayClass(string $methodName): bool { try { $reflector = new ReflectionMethod($this, $methodName); } catch (ReflectionException $e) { Log::error( sprintf( 'ReflectionException thrown when trying to check if %s::%s is implemented in the gateway class.', static::id(), $methodName ), [ 'exception' => $e, ] ); return false; } return ($reflector->getDeclaringClass()->getName() === get_class($this)); } /** * @since 4.6.0 */ public function getTransactionUrl(Donation $donation): ?string { $link = apply_filters('give_payment_details_transaction_id-' . $donation->gatewayId, $donation->gatewayTransactionId, $donation->id); // If no link is returned, return null if (empty($link)) { return null; } // Extract URL from anchor tag using regex if (preg_match('/href=["\']([^"\']+)["\']/', $link, $matches)) { return $matches[1]; } // If it's already a URL (not an anchor tag), return as is if (filter_var($link, FILTER_VALIDATE_URL)) { return $link; } return null; } /** * @since 4.6.0 */ public function toArray(): array { return [ 'id' => $this->id(), 'name' => $this->getName(), 'label' => $this->getPaymentMethodLabel(), ]; } /** * @since 4.6.0 */ #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->toArray(); } }