From 0d0036fe8c6a857a7d3dfbb6b557dbaedd006e87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=C3=BCel=20van=20der=20Steege?= Date: Fri, 12 Jul 2024 16:04:33 +0200 Subject: [PATCH 1/8] Add support for payment method updates. --- src/Extension.php | 137 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/src/Extension.php b/src/Extension.php index 549d257..5655c01 100644 --- a/src/Extension.php +++ b/src/Extension.php @@ -13,11 +13,13 @@ use Pronamic\WordPress\DateTime\DateTime; use Pronamic\WordPress\DateTime\DateTimeImmutable; use Pronamic\WordPress\Pay\AbstractPluginIntegration; +use Pronamic\WordPress\Pay\Core\PaymentMethods; use Pronamic\WordPress\Pay\Payments\PaymentStatus as Core_PaymentStatus; use Pronamic\WordPress\Pay\Payments\Payment; use Pronamic\WordPress\Pay\Subscriptions\Subscription; use RCP_Membership; use RCP_Payments; +use WP_HTML_Tag_Processor; use WP_Query; /** @@ -85,6 +87,20 @@ public function setup() { \add_action( 'plugins_loaded', [ $this, 'plugins_loaded' ], 5 ); \add_action( 'rcp_after_membership_admin_update', [ $this, 'rcp_after_membership_admin_update' ] ); + + /* + * Filter if billing can be updated for membership. + * + * @link https://gitlab.com/pronamic-plugins/restrict-content-pro/-/blob/3.4.4/includes/memberships/class-rcp-membership.php#L3231-3240 + */ + \add_filter( 'rcp_membership_can_update_billing_card', [ $this, 'rcp_membership_can_update_billing' ], 10, 2 ); + + /* + * Filter subscription details actions HTML. + * + * @link https://gitlab.com/pronamic-plugins/restrict-content-pro/-/blob/3.4.4/templates/subscription.php#L156-164 + */ + \add_filter( 'rcp_subscription_details_actions', [ $this, 'rcp_subscription_details_actions' ], 10, 4 ); } /** @@ -128,6 +144,8 @@ public function plugins_loaded() { \add_filter( 'rcp_gateway_subscription_id_url', [ $this, 'rcp_gateway_subscription_id_url' ], 10, 3 ); + \add_action( 'save_post_pronamic_pay_subscr', [ $this, 'maybe_update_membership_gateway' ] ); + /** * Filter the subscription next payment delivery date. * @@ -599,6 +617,125 @@ public function rcp_membership_payment_profile_cancelled( $success, $gateway, $g return true; } + /** + * Can update membership billing. + * + * @param bool $can_update Whether or not billing can be updated. + * @param int $rcp_membership_id ID of the membership. + * @return bool + */ + public function rcp_membership_can_update_billing( $can_update, $rcp_membership_id ) { + $subscriptions = \get_pronamic_subscriptions_by_source( 'rcp_membership', $rcp_membership_id ); + + if ( 0 !== count( $subscriptions ) ) { + $can_update = true; + } + + return $can_update; + } + + /** + * Subscription action links HTML. + * + * @param string $actions Formatted HTML links. + * @param array $links Links. + * @param int $user_id Current user ID. + * @param RCP_Membership $rcp_membership Membership object. + * @return string + */ + public function rcp_subscription_details_actions( $actions, $links, $user_id, $rcp_membership ) { + $subscriptions = \get_pronamic_subscriptions_by_source( 'rcp_membership', $rcp_membership->get_id() ); + + if ( 0 === count( $subscriptions ) ) { + return $actions; + } + + $subscription = reset( $subscriptions ); + + foreach ( $links as $link ) { + if ( ! \str_contains( $link, 'rcp_sub_details_update_card' ) ) { + continue; + } + + $new_link = new WP_HTML_Tag_Processor( $link ); + + if ( $new_link->next_tag( 'a' ) ) { + $new_link->set_attribute( 'href', $subscription->get_mandate_selection_url() ); + } + + $actions = \str_replace( $link, $new_link, $actions ); + } + + return $actions; + } + + /** + * Maybe update membership gateway on subscription updates. + * + * @param int $post_id Subscription post ID. + * @return void + */ + public function maybe_update_membership_gateway( $post_id ) { + $subscription = \get_pronamic_subscription( $post_id ); + + if ( null === $subscription ) { + return; + } + + if ( 'rcp_membership' !== $subscription->get_source() ) { + return; + } + + $rcp_membership = \rcp_get_membership( $subscription->get_source_id() ); + + if ( false === $rcp_membership ) { + return; + } + + $payment_methods = [ + $subscription->get_payment_method(), + ]; + + $payments = $subscription->get_payments(); + + foreach ( $payments as $payment ) { + if ( 'subscription_payment_method_change' !== $payment->get_source() ) { + continue; + } + + $payment_methods[] = $payment->get_payment_method(); + + break; + } + + /* + * If the payment method has changed we have to update the Restrict Content Pro membership. + */ + foreach ( $payment_methods as $payment_method ) { + if ( PaymentMethods::CARD === $payment_method ) { + $payment_method = PaymentMethods::CREDIT_CARD; + } + + $gateway_id = 'pronamic_pay_' . $payment_method; + + if ( ! \rcp_is_gateway_enabled( $gateway_id ) ) { + continue; + } + + if ( $rcp_membership->get_gateway() === $gateway_id ) { + break; + } + + $rcp_membership->update( + [ + 'gateway' => $gateway_id, + ] + ); + + break; + } + } + /** * Source column * From 0c46dfe43fc27d2df6800f287e3141988d38fba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=C3=BCel=20van=20der=20Steege?= Date: Tue, 16 Jul 2024 11:42:42 +0200 Subject: [PATCH 2/8] =?UTF-8?q?Add=20custom=20=E2=80=98Update=20payment=20?= =?UTF-8?q?method=E2=80=99=20subscription=20action=20button=20instead=20of?= =?UTF-8?q?=20abusing=20billing=20card=20update.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Extension.php | 48 ++++++++++++----------------------------------- 1 file changed, 12 insertions(+), 36 deletions(-) diff --git a/src/Extension.php b/src/Extension.php index 5655c01..17c12e1 100644 --- a/src/Extension.php +++ b/src/Extension.php @@ -17,9 +17,9 @@ use Pronamic\WordPress\Pay\Payments\PaymentStatus as Core_PaymentStatus; use Pronamic\WordPress\Pay\Payments\Payment; use Pronamic\WordPress\Pay\Subscriptions\Subscription; +use Pronamic\WordPress\Pay\Subscriptions\SubscriptionStatus as Core_SubscriptionStatus; use RCP_Membership; use RCP_Payments; -use WP_HTML_Tag_Processor; use WP_Query; /** @@ -88,13 +88,6 @@ public function setup() { \add_action( 'rcp_after_membership_admin_update', [ $this, 'rcp_after_membership_admin_update' ] ); - /* - * Filter if billing can be updated for membership. - * - * @link https://gitlab.com/pronamic-plugins/restrict-content-pro/-/blob/3.4.4/includes/memberships/class-rcp-membership.php#L3231-3240 - */ - \add_filter( 'rcp_membership_can_update_billing_card', [ $this, 'rcp_membership_can_update_billing' ], 10, 2 ); - /* * Filter subscription details actions HTML. * @@ -617,23 +610,6 @@ public function rcp_membership_payment_profile_cancelled( $success, $gateway, $g return true; } - /** - * Can update membership billing. - * - * @param bool $can_update Whether or not billing can be updated. - * @param int $rcp_membership_id ID of the membership. - * @return bool - */ - public function rcp_membership_can_update_billing( $can_update, $rcp_membership_id ) { - $subscriptions = \get_pronamic_subscriptions_by_source( 'rcp_membership', $rcp_membership_id ); - - if ( 0 !== count( $subscriptions ) ) { - $can_update = true; - } - - return $can_update; - } - /** * Subscription action links HTML. * @@ -652,19 +628,19 @@ public function rcp_subscription_details_actions( $actions, $links, $user_id, $r $subscription = reset( $subscriptions ); - foreach ( $links as $link ) { - if ( ! \str_contains( $link, 'rcp_sub_details_update_card' ) ) { - continue; - } - - $new_link = new WP_HTML_Tag_Processor( $link ); + // Payment method can only be updated for active subscription. + if ( Core_SubscriptionStatus::ACTIVE !== $subscription->get_status() ) { + return $actions; + } - if ( $new_link->next_tag( 'a' ) ) { - $new_link->set_attribute( 'href', $subscription->get_mandate_selection_url() ); - } + $action = \sprintf( + '', + \esc_url( $subscription->get_mandate_selection_url() ), + \esc_attr( \__( 'Update payment method', 'pronamic_ideal' ) ), + \esc_html( \__( 'Update payment method', 'pronamic_ideal' ) ) + ); - $actions = \str_replace( $link, $new_link, $actions ); - } + $actions = \sprintf( '%s
%s', $action, $actions ); return $actions; } From 73a14c951afb7f1c6e63fa9fcd43383d0339f505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=C3=BCel=20van=20der=20Steege?= Date: Tue, 16 Jul 2024 12:12:52 +0200 Subject: [PATCH 3/8] Only retrieve last payment with subscription payment method change instead of all subscription payments. --- src/Extension.php | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Extension.php b/src/Extension.php index 17c12e1..970eec6 100644 --- a/src/Extension.php +++ b/src/Extension.php @@ -672,16 +672,23 @@ public function maybe_update_membership_gateway( $post_id ) { $subscription->get_payment_method(), ]; - $payments = $subscription->get_payments(); + $args = [ + 'meta_query' => [ + [ + 'key' => '_pronamic_payment_source', + 'value' => 'subscription_payment_method_change', + ], + [ + 'key' => '_pronamic_payment_subscription_id', + 'value' => $subscription->get_id(), + ], + ], + ]; - foreach ( $payments as $payment ) { - if ( 'subscription_payment_method_change' !== $payment->get_source() ) { - continue; - } + $payment = \get_pronamic_payment_by_meta( '', '', $args ); + if ( null !== $payment ) { $payment_methods[] = $payment->get_payment_method(); - - break; } /* From 9e17946ab316fb9e4ad7858a40d3ce097c17c486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=C3=BCel=20van=20der=20Steege?= Date: Wed, 17 Jul 2024 14:02:29 +0200 Subject: [PATCH 4/8] Remove superfluous whitespace. --- src/Extension.php | 6 +++--- src/Gateways/Gateway.php | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Extension.php b/src/Extension.php index 970eec6..eca5582 100644 --- a/src/Extension.php +++ b/src/Extension.php @@ -24,7 +24,7 @@ /** * Extension class - * + * * @link https://plugins.trac.wordpress.org/browser/restrict-content/tags/3.2.10/core/includes/gateways/class-rcp-payment-gateways.php#L47 * @phpstan-type RestrictContentProGatewayRegistration array{label: string, admin_label: string, class: class-string} * @phpstan-type RestrictContentProPaymentObject object{id: int, membership_id: int, status: string} @@ -1100,7 +1100,7 @@ public function next_payment_delivery_date( DateTimeImmutable $next_payment_deli /** * Restrict Content Pro after membership admin update. - * + * * @link https://plugins.trac.wordpress.org/browser/restrict-content/tags/3.2.10/core/includes/admin/memberships/membership-actions.php#L371 * @param RCP_Membership $rcp_membership Restrict Content Pro membership object. * @return void @@ -1119,7 +1119,7 @@ public function rcp_after_membership_admin_update( RCP_Membership $rcp_membershi /** * Restrict Content Pro gateway subscription ID URL. - * + * * @param string $url URL. * @param string $gateway Payment gateway slug. * @param string $subscription_id ID of the subscription in the gateway. diff --git a/src/Gateways/Gateway.php b/src/Gateways/Gateway.php index ef2f1d4..6ff9796 100644 --- a/src/Gateways/Gateway.php +++ b/src/Gateways/Gateway.php @@ -54,14 +54,14 @@ public function init() { $this->supports = [ /** * Price changes. - * + * * @link https://github.com/pronamic/wp-pronamic-pay-restrict-content-pro/issues/19 */ 'price-changes', 'recurring', /** * Renewal date changes. - * + * * @link https://github.com/pronamic/wp-pronamic-pay-restrict-content-pro/issues/17 * @link https://github.com/pronamic/wp-pronamic-pay-restrict-content-pro/pull/18#issuecomment-2107023059 * @link https://plugins.trac.wordpress.org/browser/restrict-content/tags/3.2.10/core/includes/memberships/class-rcp-membership.php#L3454 @@ -103,7 +103,7 @@ protected function get_pronamic_config_id() { /** * Get the Pronamic gateway. - * + * * @return PronamicGateway|null */ private function get_pronamic_gateway() { @@ -112,7 +112,7 @@ private function get_pronamic_gateway() { if ( null === $config_id ) { return null; } - + if ( '' === $config_id ) { return null; } @@ -276,7 +276,7 @@ public function fields() { /** * Process signup. - * + * * @return void * @throws \Exception Throws an exception if the Restrict Content data does not meet expectations. */ From 06fd27409100bc68fa18f9badd82a5aaba707cd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=C3=BCel=20van=20der=20Steege?= Date: Wed, 17 Jul 2024 14:02:59 +0200 Subject: [PATCH 5/8] Add card gateway. --- src/Extension.php | 1 + src/Gateways/CardGateway.php | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 src/Gateways/CardGateway.php diff --git a/src/Extension.php b/src/Extension.php index eca5582..022ca57 100644 --- a/src/Extension.php +++ b/src/Extension.php @@ -226,6 +226,7 @@ private function get_gateways() { 'pronamic_pay_bancontact' => Gateways\BancontactGateway::class, 'pronamic_pay_banktransfer' => Gateways\BankTransferGateway::class, 'pronamic_pay_bitcoin' => Gateways\BitcoinGateway::class, + 'pronamic_pay_card' => Gateways\CardGateway::class, 'pronamic_pay_credit_card' => Gateways\CreditCardGateway::class, 'pronamic_pay_direct_debit' => Gateways\DirectDebitGateway::class, 'pronamic_pay_direct_debit_bancontact' => Gateways\DirectDebitBancontactGateway::class, diff --git a/src/Gateways/CardGateway.php b/src/Gateways/CardGateway.php new file mode 100644 index 0000000..1aa5a3c --- /dev/null +++ b/src/Gateways/CardGateway.php @@ -0,0 +1,36 @@ + + * @copyright 2005-2023 Pronamic + * @license GPL-3.0-or-later + * @package Pronamic\WordPress\Pay\Extensions\RestrictContent + */ + +namespace Pronamic\WordPress\Pay\Extensions\RestrictContent\Gateways; + +use Pronamic\WordPress\Pay\Core\PaymentMethods; + +/** + * Card gateway + * + * @author ReĆ¼el van der Steege + * @version 4.6.0 + * @since 4.6.0 + */ +class CardGateway extends Gateway { + /** + * Gateway id. + * + * @var string + */ + protected $id = 'pronamic_pay_card'; + + /** + * Payment method. + * + * @var string + */ + protected $payment_method = PaymentMethods::CARD; +} From 69b2e0ec08400ae189a076e31aaa680760dce6c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=C3=BCel=20van=20der=20Steege?= Date: Wed, 17 Jul 2024 14:03:57 +0200 Subject: [PATCH 6/8] Add method to retrieve gateway payment method. --- src/Gateways/Gateway.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Gateways/Gateway.php b/src/Gateways/Gateway.php index 6ff9796..9d5fcca 100644 --- a/src/Gateways/Gateway.php +++ b/src/Gateways/Gateway.php @@ -36,7 +36,7 @@ class Gateway extends RCP_Payment_Gateway { /** * Payment method * - * @var string + * @var string|null */ protected $payment_method; @@ -73,6 +73,15 @@ public function init() { } } + /** + * Get payment method. + * + * @return string|null + */ + public function get_pronamic_payment_method() { + return $this->payment_method; + } + /** * Get the Pronamic configuration ID for this gateway. * From c40b2a308c94e179a8d255c7d0d10fc402b07669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=C3=BCel=20van=20der=20Steege?= Date: Wed, 17 Jul 2024 14:07:43 +0200 Subject: [PATCH 7/8] No need to check gateway activation status when updating membership gateway for changed subscription payment method. --- src/Extension.php | 42 +++++++++++++----------------------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/src/Extension.php b/src/Extension.php index 022ca57..6f25042 100644 --- a/src/Extension.php +++ b/src/Extension.php @@ -19,6 +19,7 @@ use Pronamic\WordPress\Pay\Subscriptions\Subscription; use Pronamic\WordPress\Pay\Subscriptions\SubscriptionStatus as Core_SubscriptionStatus; use RCP_Membership; +use RCP_Payment_Gateways; use RCP_Payments; use WP_Query; @@ -669,40 +670,23 @@ public function maybe_update_membership_gateway( $post_id ) { return; } - $payment_methods = [ - $subscription->get_payment_method(), - ]; - - $args = [ - 'meta_query' => [ - [ - 'key' => '_pronamic_payment_source', - 'value' => 'subscription_payment_method_change', - ], - [ - 'key' => '_pronamic_payment_subscription_id', - 'value' => $subscription->get_id(), - ], - ], - ]; + /** + * Update membership gateway. + */ + $rcp_gateways = new RCP_Payment_Gateways(); - $payment = \get_pronamic_payment_by_meta( '', '', $args ); + foreach ( $rcp_gateways->available_gateways as $gateway_id => $gateway ) { + if ( ! \array_key_exists( 'class', $gateway ) ) { + continue; + } - if ( null !== $payment ) { - $payment_methods[] = $payment->get_payment_method(); - } + $rcp_gateway = new $gateway['class'](); - /* - * If the payment method has changed we have to update the Restrict Content Pro membership. - */ - foreach ( $payment_methods as $payment_method ) { - if ( PaymentMethods::CARD === $payment_method ) { - $payment_method = PaymentMethods::CREDIT_CARD; + if ( ! \method_exists( $rcp_gateway, 'get_pronamic_payment_method' ) ) { + continue; } - $gateway_id = 'pronamic_pay_' . $payment_method; - - if ( ! \rcp_is_gateway_enabled( $gateway_id ) ) { + if ( $rcp_gateway->get_pronamic_payment_method() !== $subscription->get_payment_method() ) { continue; } From 26774d7db28d2ea1cb5a0cf5fe958b2177d702cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=C3=BCel=20van=20der=20Steege?= Date: Wed, 17 Jul 2024 14:20:04 +0200 Subject: [PATCH 8/8] Fix PHPStan issues. --- src/Extension.php | 4 ++-- src/Gateways/Gateway.php | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Extension.php b/src/Extension.php index 6f25042..96447ba 100644 --- a/src/Extension.php +++ b/src/Extension.php @@ -616,7 +616,7 @@ public function rcp_membership_payment_profile_cancelled( $success, $gateway, $g * Subscription action links HTML. * * @param string $actions Formatted HTML links. - * @param array $links Links. + * @param array $links Links. * @param int $user_id Current user ID. * @param RCP_Membership $rcp_membership Membership object. * @return string @@ -664,7 +664,7 @@ public function maybe_update_membership_gateway( $post_id ) { return; } - $rcp_membership = \rcp_get_membership( $subscription->get_source_id() ); + $rcp_membership = \rcp_get_membership( (int) $subscription->get_source_id() ); if ( false === $rcp_membership ) { return; diff --git a/src/Gateways/Gateway.php b/src/Gateways/Gateway.php index 9d5fcca..3dfc442 100644 --- a/src/Gateways/Gateway.php +++ b/src/Gateways/Gateway.php @@ -47,7 +47,7 @@ public function init() { // Set supported features based on gateway. $gateway = $this->get_pronamic_gateway(); - if ( null !== $gateway ) { + if ( null !== $gateway && null !== $this->payment_method ) { $payment_method = $gateway->get_payment_method( $this->payment_method ); if ( null !== $payment_method && $payment_method->supports( 'recurring' ) ) { @@ -260,6 +260,10 @@ public function fields() { return ''; } + if ( null === $this->payment_method ) { + return ''; + } + $payment_method = $gateway->get_payment_method( $this->payment_method ); if ( null === $payment_method ) {