From 0e4b88b8f3754aba19c90333e86f9ae447be98ec Mon Sep 17 00:00:00 2001 From: Lukman Bello Date: Wed, 14 Oct 2020 13:13:57 +0100 Subject: [PATCH 01/91] add debug backtrace --- class.pmprogateway_paystack.php | 42 +++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index eeddad4..e6f2514 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -676,9 +676,7 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) do_action('pmpro_after_checkout', $morder->user_id, $morder); //-------------------------------------------------- - if (strlen($morder->subscription_transaction_id) > 3) { - $enddate = "'" . date("Y-m-d", strtotime("+ " . $morder->subscription_transaction_id, current_time("timestamp"))) . "'"; - } elseif (!empty($pmpro_level->expiration_number)) { + if (!empty($pmpro_level->expiration_number)) { $enddate = "'" . date("Y-m-d", strtotime("+ " . $pmpro_level->expiration_number . " " . $pmpro_level->expiration_period, current_time("timestamp"))) . "'"; } else { $enddate = "NULL"; @@ -923,6 +921,7 @@ function cancel(&$order) 'headers' => $headers, 'timeout' => 60 ); + $backtrace = self::get_caller_info(); $request = wp_remote_get($paystack_url, $args); if (!is_wp_error($request) && 200 == wp_remote_retrieve_response_code($request)) { $paystack_response = json_decode(wp_remote_retrieve_body($request)); @@ -936,12 +935,13 @@ function cancel(&$order) $body = array( 'code' => $paystack_response->data->subscription_code, 'token' => $paystack_response->data->email_token, - + 'debug_trace'=> $backtrace ); $args = array( 'body' => json_encode($body), 'headers' => $headers, - 'timeout' => 60 + 'timeout' => 60, + 'user-agent' => $backtrace ); $request = wp_remote_post($paystack_url, $args); @@ -955,6 +955,38 @@ function cancel(&$order) global $wpdb; $wpdb->query("DELETE FROM $wpdb->pmpro_membership_orders WHERE id = '" . $order->id . "'"); } + function get_caller_info() { + $c = ''; + $file = ''; + $func = ''; + $class = ''; + $trace = debug_backtrace(); + if (isset($trace[2])) { + $file = $trace[1]['file']; + $func = $trace[2]['function']; + if ((substr($func, 0, 7) == 'include') || (substr($func, 0, 7) == 'require')) { + $func = ''; + } + } else if (isset($trace[1])) { + $file = $trace[1]['file']; + $func = ''; + } + if (isset($trace[3]['class'])) { + $class = $trace[3]['class']; + $func = $trace[3]['function']; + $file = $trace[2]['file']; + } else if (isset($trace[2]['class'])) { + $class = $trace[2]['class']; + $func = $trace[2]['function']; + $file = $trace[1]['file']; + } + if ($file != '') $file = basename($file); + $c = $file . ": "; + $c .= ($class != '') ? ":" . $class . "->" : ""; + $c .= ($func != '') ? $func . "(): " : ""; + return($c); + } + function delete(&$order) { //no matter what happens below, we're going to cancel the order in our system From 9538d8164c86c80736eb8a1a74ff902767e7a9ce Mon Sep 17 00:00:00 2001 From: Lukman Bello Date: Wed, 14 Oct 2020 15:21:06 +0100 Subject: [PATCH 02/91] add debug backtrace --- class.pmprogateway_paystack.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index e6f2514..dd09790 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -922,6 +922,7 @@ function cancel(&$order) 'timeout' => 60 ); $backtrace = self::get_caller_info(); + $furtherbacktrace = wp_debug_backtrace_summary(); $request = wp_remote_get($paystack_url, $args); if (!is_wp_error($request) && 200 == wp_remote_retrieve_response_code($request)) { $paystack_response = json_decode(wp_remote_retrieve_body($request)); @@ -935,13 +936,13 @@ function cancel(&$order) $body = array( 'code' => $paystack_response->data->subscription_code, 'token' => $paystack_response->data->email_token, - 'debug_trace'=> $backtrace + 'debug_trace'=> $backtrace . " ". $furtherbacktrace ); $args = array( 'body' => json_encode($body), 'headers' => $headers, 'timeout' => 60, - 'user-agent' => $backtrace + 'user-agent' => . 'Wordpress/ '. $backtrace . " ". $furtherbacktrace ); $request = wp_remote_post($paystack_url, $args); From 46033991b1d938fbe5d9e2fa1d2ee0f84da336cc Mon Sep 17 00:00:00 2001 From: Lukman Bello Date: Wed, 14 Oct 2020 16:17:38 +0100 Subject: [PATCH 03/91] add debug backtrace --- class.pmprogateway_paystack.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index dd09790..79ad49f 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -901,6 +901,8 @@ function cancelMembership(&$user){ } function cancel(&$order) { + $backtrace = self::get_caller_info(); + $furtherbacktrace = wp_debug_backtrace_summary(); //no matter what happens below, we're going to cancel the order in our system $order->updateStatus("cancelled"); @@ -914,15 +916,16 @@ function cancel(&$order) } if ($order->subscription_transaction_id != "") { $paystack_url = 'https://api.paystack.co/subscription/' . $code; + $headers = array( 'Authorization' => 'Bearer ' . $key ); $args = array( 'headers' => $headers, - 'timeout' => 60 + 'timeout' => 60, + 'user-agent' => . 'Wordpress/ '. $backtrace . " ". $furtherbacktrace ); - $backtrace = self::get_caller_info(); - $furtherbacktrace = wp_debug_backtrace_summary(); + $request = wp_remote_get($paystack_url, $args); if (!is_wp_error($request) && 200 == wp_remote_retrieve_response_code($request)) { $paystack_response = json_decode(wp_remote_retrieve_body($request)); From 943359ab5b7efafe794ad45b7498184db558d21c Mon Sep 17 00:00:00 2001 From: Lukman Bello Date: Wed, 14 Oct 2020 16:41:49 +0100 Subject: [PATCH 04/91] add debug backtrace --- class.pmprogateway_paystack.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 79ad49f..b7276bc 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -923,7 +923,7 @@ function cancel(&$order) $args = array( 'headers' => $headers, 'timeout' => 60, - 'user-agent' => . 'Wordpress/ '. $backtrace . " ". $furtherbacktrace + 'user-agent' => 'Wordpress/ '. $backtrace . " ". $furtherbacktrace ); $request = wp_remote_get($paystack_url, $args); From 5b0288885d7059daf3161f949e42442cf5616c71 Mon Sep 17 00:00:00 2001 From: Lukman Bello Date: Wed, 14 Oct 2020 16:42:29 +0100 Subject: [PATCH 05/91] add debug backtrace --- class.pmprogateway_paystack.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index b7276bc..fff2ce7 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -945,7 +945,7 @@ function cancel(&$order) 'body' => json_encode($body), 'headers' => $headers, 'timeout' => 60, - 'user-agent' => . 'Wordpress/ '. $backtrace . " ". $furtherbacktrace + 'user-agent' => 'Wordpress/ '. $backtrace . " ". $furtherbacktrace ); $request = wp_remote_post($paystack_url, $args); From 2d36e0adeac668f3266f60a7f390e47730a0d444 Mon Sep 17 00:00:00 2001 From: Lukman Bello Date: Mon, 19 Apr 2021 10:09:25 +0100 Subject: [PATCH 06/91] add debug plugin --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index 3b77d14..47c9186 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,6 +8,8 @@ RUN apt-get -y install wget unzip zip ADD https://downloads.wordpress.org/plugin/paid-memberships-pro.latest-stable.zip /dist RUN cd /dist && unzip paid-memberships-pro.latest-stable.zip && rm paid-memberships-pro.latest-stable.zip +ADD https://downloads.wordpress.org/plugin/wp-debugging.2.9.2.zip /dist +RUN cd /dist && unzip wp-debugging.2.9.2.zip && rm wp-debugging.2.9.2.zip FROM wordpress:php7.2 From 4466181c55dea63bc0cf5905d253860aa44bebf0 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Tue, 4 Jul 2023 15:40:23 +0200 Subject: [PATCH 07/91] Push Github workflows --- .gitattributes | 5 +++++ .github/workflows/generate-translations.yml | 0 .github/workflows/sync-labels.yml | 0 3 files changed, 5 insertions(+) create mode 100644 .gitattributes create mode 100644 .github/workflows/generate-translations.yml create mode 100644 .github/workflows/sync-labels.yml diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5d3873b --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +.gitattributes export-ignore +.github export-ignore +CONTRIBUTING.md export-ignore +README.md export-ignore +Dockerfile export-ignore \ No newline at end of file diff --git a/.github/workflows/generate-translations.yml b/.github/workflows/generate-translations.yml new file mode 100644 index 0000000..e69de29 diff --git a/.github/workflows/sync-labels.yml b/.github/workflows/sync-labels.yml new file mode 100644 index 0000000..e69de29 From e4dcde828a6672e5d1495fc0728dfcffbe59cb9c Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Tue, 4 Jul 2023 15:45:41 +0200 Subject: [PATCH 08/91] Push workflows to .github now. --- .github/workflows/generate-translations.yml | 18 ++++++++++++++++ .github/workflows/sync-labels.yml | 23 +++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/.github/workflows/generate-translations.yml b/.github/workflows/generate-translations.yml index e69de29..426177b 100644 --- a/.github/workflows/generate-translations.yml +++ b/.github/workflows/generate-translations.yml @@ -0,0 +1,18 @@ +name: Generate Translations +on: workflow_dispatch +jobs: + generate-translations: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: WordPress POT/PO/MO Generator + uses: strangerstudios/action-wp-pot-po-mo-generator@main + with: + generate_pot: 1 + generate_po: 1 + generate_mo: 1 + generate_lang_packs: 1 + merge_changes: 1 + headers: '{"Report-Msgid-Bugs-To":"info@paidmembershipspro.com","Last-Translator":"Paid Memberships Pro ","Language-Team":"Paid Memberships Pro "}' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/sync-labels.yml b/.github/workflows/sync-labels.yml index e69de29..67cfbaf 100644 --- a/.github/workflows/sync-labels.yml +++ b/.github/workflows/sync-labels.yml @@ -0,0 +1,23 @@ +name: Sync labels +on: + # You can run this with every type of event, but it's better to run it only when you actually need it. + workflow_dispatch: + +jobs: + labels: + runs-on: ubuntu-latest + + steps: + - uses: strangerstudios/label-sync@v2 + with: + # If you want to use a source repo, you can put is name here (only the owner/repo format is accepted) + source-repo: strangerstudios/paid-memberships-pro + + # If you want to delete any additional label, set this to true + delete-other-labels: true + + #If you want the action just to show you the preview of the changes, without actually editing the labels, set this to tru + dry-run: false + + # You can change the token used to change the labels, this is the default one + token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 6ae83648fdfcfd87b9b329c0ee8cb5b627f03fc9 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Mon, 4 Sep 2023 14:13:42 +0200 Subject: [PATCH 09/91] Bug fix: Static function called incorrectly. --- class.pmprogateway_paystack.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 888a7ec..16b5366 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -211,8 +211,8 @@ function kkd_pmprosd_convert_date( $date ) { return $set_date; } - function kkd_pmpro_paystack_ipn() - { + + static function kkd_pmpro_paystack_ipn() { global $wpdb; // if ((strtoupper($_SERVER['REQUEST_METHOD']) != 'POST' ) || !array_key_exists('HTTP_X_PAYSTACK_SIGNATURE', $_SERVER) ) { // exit(); From ac7741a576e539d81be02594a3c6a546e41244b3 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Mon, 4 Sep 2023 14:29:13 +0200 Subject: [PATCH 10/91] Minor changes to UI in settings --- class.pmprogateway_paystack.php | 36 ++++++++++++++++----------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 16b5366..046749a 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -217,14 +217,20 @@ static function kkd_pmpro_paystack_ipn() { // if ((strtoupper($_SERVER['REQUEST_METHOD']) != 'POST' ) || !array_key_exists('HTTP_X_PAYSTACK_SIGNATURE', $_SERVER) ) { // exit(); // } - define('SHORTINIT', true); + $input = @file_get_contents("php://input"); $event = json_decode($input); + + // No event found, bail. + if ( empty( $event ) ) { + return; + } // echo "
";
                     // print_r($event);
                     // if(!$_SERVER['HTTP_X_PAYSTACK_SIGNATURE'] || ($_SERVER['HTTP_X_PAYSTACK_SIGNATURE'] !== hash_hmac('sha512', $input, paystack_recurrent_billing_get_secret_key()))){
                     //   exit();
                     // }
+
                     switch($event->event){
                     case 'subscription.create':
 
@@ -235,13 +241,13 @@ static function kkd_pmpro_paystack_ipn() {
                         $subscription_code = $event->data->subscription_code;
                         $email = $event->data->customer->email;
                         $morder->Email = $email;
-                        $users_row = $wpdb->get_row( "SELECT ID, display_name FROM $wpdb->users WHERE user_email = '" . $email. "' LIMIT 1" );
+                        $users_row = $wpdb->get_row( "SELECT ID, display_name FROM $wpdb->users WHERE user_email = '" . esc_sql( $email ). "' LIMIT 1" );
                         if ( ! empty( $users_row )  ) {
                             $user_id = $users_row->ID;
                             $user = get_userdata($user_id);
                             $user->membership_level = pmpro_getMembershipLevelForUser($user_id);
                         }
-                        if (empty($user)) {
+                        if ( empty( $user ) ) {
                             print_r('Could not get user');
                             exit();
                         }
@@ -310,20 +316,6 @@ static function pmpro_payment_options($options)
                 static function pmpro_payment_option_fields($values, $gateway)
                 {
                     ?>
-                    style="display: none;">
-                        
-                            
-                        
-                    
-                    style="display: none;">
-                        
-                            
-                        
-                        
-                            

- - - style="display: none;"> @@ -356,8 +348,15 @@ static function pmpro_payment_option_fields($values, $gateway) - + style="display: none;"> + + + + +


+ + From cd422f461c49dbe7aee6ef7b523adae7e85201b1 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Tue, 5 Sep 2023 13:55:32 +0200 Subject: [PATCH 11/91] General fixes to class + intervals. --- class.pmprogateway_paystack.php | 96 ++++++++++++--------------------- 1 file changed, 33 insertions(+), 63 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 046749a..e558a70 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -68,9 +68,10 @@ static function init() add_filter('pmpro_checkout_before_change_membership_level', array('PMProGateway_Paystack', 'pmpro_checkout_before_change_membership_level'), 10, 2); add_filter('pmpro_gateways_with_pending_status', array('PMProGateway_Paystack', 'pmpro_gateways_with_pending_status')); - add_filter('pmpro_pages_shortcode_checkout', array('PMProGateway_Paystack', 'pmpro_pages_shortcode_checkout'), 20, 1); + add_filter('pmpro_checkout_default_submit_button', array('PMProGateway_Paystack', 'pmpro_checkout_default_submit_button')); // custom confirmation page + add_filter('pmpro_pages_shortcode_confirmation', array('PMProGateway_Paystack', 'pmpro_pages_shortcode_confirmation'), 20, 1); } } @@ -502,12 +503,10 @@ function sendToPaystack(&$order) wp_redirect($url); exit; } else { - $order->Gateway->delete($order); wp_redirect(pmpro_url("checkout", "?level=" . $order->membership_level->id . "&error=" . $paystack_response->message)); exit(); } } else { - $order->Gateway->delete($order); wp_redirect(pmpro_url("checkout", "?level=" . $order->membership_level->id . "&error=Failed")); exit(); } @@ -599,26 +598,6 @@ static function renewpayment($event) } - static function pmpro_pages_shortcode_checkout($content) - { - $morder = new MemberOrder(); - $found = $morder->getLastMemberOrder(get_current_user_id(), apply_filters("pmpro_confirmation_order_status", array("pending"))); - if ($found) { - $morder->Gateway->delete($morder); - } - - if (isset($_REQUEST['error'])) { - global $pmpro_msg, $pmpro_msgt; - - $pmpro_msg = __("IMPORTANT: Something went wrong during the payment. Please try again later or contact the site owner to fix this issue.
" . urldecode($_REQUEST['error']), "pmpro"); - $pmpro_msgt = "pmpro_error"; - - $content = "
" . $pmpro_msg . "
" . $content; - } - - return $content; - } - /** * Custom confirmation page */ @@ -676,42 +655,40 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) do_action('pmpro_after_checkout', $morder->user_id, $morder); //-------------------------------------------------- - if (strlen($morder->subscription_transaction_id) > 3) { - $enddate = "'" . date("Y-m-d", strtotime("+ " . $morder->subscription_transaction_id, current_time("timestamp"))) . "'"; - - // Override the previous calculation - $__date = date_create(date("Y-m-d H:i:s", current_time("timestamp"))); - date_add($__date, date_interval_create_from_date_string("".$pmpro_level->cycle_number." ".$pmpro_level->cycle_period."")); - - $enddate = "'" . $__date->format("Y-m-d H:i:s") . "'"; - } elseif (!empty($pmpro_level->expiration_number)) { - $enddate = "'" . date("Y-m-d", strtotime("+ " . $pmpro_level->expiration_number . " " . $pmpro_level->expiration_period, current_time("timestamp"))) . "'"; + // Let's make sure we're setting an expiration date if we've set one. + if ( ! empty( $pmpro_level->expiration_number ) ) { + $enddate = "'" . date( "Y-m-d H:i:00", strtotime( "+ " . $pmpro_level->expiration_number . " " . $pmpro_level->expiration_period, current_time( 'timestamp' ) ) ) . "'"; } else { - $enddate = "NULL"; + $enddate = "0000-00-00 00:00:00"; } - if (($pmpro_level->cycle_number > 0) && ($pmpro_level->billing_amount > 0) && ($pmpro_level->cycle_period != "")) { - if ($pmpro_level->cycle_number < 10 && $pmpro_level->cycle_period == 'Day') { - $interval = 'weekly'; - } elseif (($pmpro_level->cycle_number == 90) && ($pmpro_level->cycle_period == 'Day')) { - $interval = 'quarterly'; + + + // There's recurring settings, lets convert to Paystack intervals now. + if ( $pmpro_level->billing_amount > 0 ) { + + if ( $pmpro_level->cycle_period == 'Day' ) { + $interval = 'daily'; } - elseif (($pmpro_level->cycle_number == 180) && ($pmpro_level->cycle_period == 'Day')) { - $interval = 'biannually'; + + if ( $pmpro_level->cycle_period == 'Week' ) { + $interval = 'weekly'; } - elseif (($pmpro_level->cycle_number >= 10) && ($pmpro_level->cycle_period == 'Day')) { + + if ( $pmpro_level->cycle_period == 'Month' ) { $interval = 'monthly'; - } elseif (($pmpro_level->cycle_number == 3) && ($pmpro_level->cycle_period == 'Month')) { + } + + if ( $pmpro_level->cycle_period == 'Year' ) { + $interval = 'annually'; + } + + // Biannual and quarterly conversion. + if ( $pmpro_level->cycle_number == 3 && $pmpro_level->cycle_period == 'Month' ) { $interval = 'quarterly'; - } - elseif (($pmpro_level->cycle_number == 6) && ($pmpro_level->cycle_period == 'Month')) { + + if ( $pmpro_level->cycle_number == 6 && $pmpro_level->cycle_period == 'Month' ) { $interval = 'biannually'; - - } - elseif (($pmpro_level->cycle_number > 0) && ($pmpro_level->cycle_period == 'Month')) { - $interval = 'monthly'; - } elseif (($pmpro_level->cycle_number > 0) && ($pmpro_level->cycle_period == 'Year')) { - $interval = 'annually'; } $amount = $pmpro_level->billing_amount; @@ -907,13 +884,16 @@ function cancelMembership(&$user){ print_r("No records were found with user - ". $user_id." level - ". $level_to_cancel); } } - function cancel(&$order) + function cancel(&$order, $update_status = true) { $backtrace = self::get_caller_info(); $furtherbacktrace = wp_debug_backtrace_summary(); //no matter what happens below, we're going to cancel the order in our system - $order->updateStatus("cancelled"); + if ( $update_status ) { + $order->updateStatus( "cancelled" ); + } + $mode = pmpro_getOption("gateway_environment"); $code = $order->subscription_transaction_id; if ($mode == 'sandbox') { @@ -964,8 +944,6 @@ function cancel(&$order) } } } - global $wpdb; - $wpdb->query("DELETE FROM $wpdb->pmpro_membership_orders WHERE id = '" . $order->id . "'"); } function get_caller_info() { $c = ''; @@ -998,14 +976,6 @@ function get_caller_info() { $c .= ($func != '') ? $func . "(): " : ""; return($c); } - - function delete(&$order) - { - //no matter what happens below, we're going to cancel the order in our system - $order->updateStatus("cancelled"); - global $wpdb; - $wpdb->query("DELETE FROM $wpdb->pmpro_membership_orders WHERE id = '" . $order->id . "'"); - } } } } From 3fec00337c15ce5a62d23e347475a25558756fff Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Wed, 6 Sep 2023 14:43:39 +0200 Subject: [PATCH 12/91] Change plugin domain - Change plugin domain for i18n functions. --- class.pmprogateway_paystack.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index e558a70..2ce041e 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -102,7 +102,7 @@ static function pmpro_checkout_default_submit_button($show) ?> - + style="display: none;"> - + @@ -327,7 +327,7 @@ static function pmpro_payment_option_fields($values, $gateway) style="display: none;"> - + @@ -335,7 +335,7 @@ static function pmpro_payment_option_fields($values, $gateway) style="display: none;"> - + @@ -343,7 +343,7 @@ static function pmpro_payment_option_fields($values, $gateway) style="display: none;"> - + @@ -351,10 +351,10 @@ static function pmpro_payment_option_fields($values, $gateway) style="display: none;"> - + -


+


From 2ddf63be2f648f61022b79e9431f3c1e7c6e62b9 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Wed, 6 Sep 2023 14:44:50 +0200 Subject: [PATCH 13/91] Update text domain instead of constant. --- class.pmprogateway_paystack.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 2ce041e..cbb743f 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -13,8 +13,6 @@ if (!function_exists('Paystack_Pmp_Gateway_load')) { add_action('plugins_loaded', 'Paystack_Pmp_Gateway_load', 20); - DEFINE('KKD_PAYSTACKPMP', "paystack-paidmembershipspro"); - function Paystack_Pmp_Gateway_load() { // paid memberships pro required @@ -88,7 +86,7 @@ static function plugin_action_links($links, $file) } if ($file == $this_plugin) { - $settings_link = ''.__('Settings', KKD_PAYSTACKPMP).''; + $settings_link = ''.__('Settings', 'paystack-gateway-paid-memberships-pro').''; array_unshift($links, $settings_link); } @@ -115,7 +113,7 @@ static function pmpro_checkout_default_submit_button($show) static function pmpro_gateways($gateways) { if (empty($gateways['paystack'])) { - $gateways = array_slice($gateways, 0, 1) + array("paystack" => __('Paystack', KKD_PAYSTACKPMP)) + array_slice($gateways, 1); + $gateways = array_slice($gateways, 0, 1) + array("paystack" => __('Paystack', 'paystack-gateway-paid-memberships-pro')) + array_slice($gateways, 1); } return $gateways; } @@ -823,10 +821,10 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) // echo "
";
                                     // print_r($pmpro_level);
                                     $content = "
    -
  • ".__('Account', KKD_PAYSTACKPMP).": ".$current_user->display_name." (".$current_user->user_email.")
  • -
  • ".__('Order', KKD_PAYSTACKPMP).": ".$pmpro_invoice->code."
  • -
  • ".__('Membership Level', KKD_PAYSTACKPMP).": ".$pmpro_level->name."
  • -
  • ".__('Amount Paid', KKD_PAYSTACKPMP).": ".$pmpro_invoice->total." ".$pmpro_currency."
  • +
  • ".__('Account', 'paystack-gateway-paid-memberships-pro').": ".$current_user->display_name." (".$current_user->user_email.")
  • +
  • ".__('Order', 'paystack-gateway-paid-memberships-pro').": ".$pmpro_invoice->code."
  • +
  • ".__('Membership Level', 'paystack-gateway-paid-memberships-pro').": ".$pmpro_level->name."
  • +
  • ".__('Amount Paid', 'paystack-gateway-paid-memberships-pro').": ".$pmpro_invoice->total." ".$pmpro_currency."
"; ob_start(); if (file_exists(get_stylesheet_directory() . "/paid-memberships-pro/pages/confirmation.php")) { From f643f66a34051caf2b35fdb52519bfa388f233aa Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Thu, 7 Sep 2023 14:18:05 +0200 Subject: [PATCH 14/91] Improvements to the webhook handler. --- class.pmprogateway_paystack.php | 64 +++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index cbb743f..1995af0 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -1,11 +1,14 @@ "; - // print_r($event); - // if(!$_SERVER['HTTP_X_PAYSTACK_SIGNATURE'] || ($_SERVER['HTTP_X_PAYSTACK_SIGNATURE'] !== hash_hmac('sha512', $input, paystack_recurrent_billing_get_secret_key()))){ - // exit(); - // } - switch($event->event){ + $event = json_decode($input); + + switch( $event->event ){ case 'subscription.create': break; @@ -352,7 +380,7 @@ static function pmpro_payment_option_fields($values, $gateway) -


+


From d1a80929967de94949d9882e66db06f56cb23cad Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 8 Sep 2023 12:21:40 +0200 Subject: [PATCH 15/91] Create generate-translations.yml --- .github/workflows/generate-translations.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/generate-translations.yml diff --git a/.github/workflows/generate-translations.yml b/.github/workflows/generate-translations.yml new file mode 100644 index 0000000..4aa4ca4 --- /dev/null +++ b/.github/workflows/generate-translations.yml @@ -0,0 +1,18 @@ +name: Generate Translations +on: workflow_dispatch +jobs: + generate-translations: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: WordPress POT/PO/MO Generator + uses: strangerstudios/action-wp-pot-po-mo-generator@main + with: + generate_pot: 1 + generate_po: 1 + generate_mo: 1 + generate_lang_packs: 1 + merge_changes: 1 + headers: '{"Report-Msgid-Bugs-To":"info@paidmembershipspro.com","Last-Translator":"Paid Memberships Pro ","Language-Team":"Paid Memberships Pro "}' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From c9192fbd4277d936d88dafbeaa4769f4a4697743 Mon Sep 17 00:00:00 2001 From: WordPress POT/PO/MO Generator <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 Sep 2023 10:22:14 +0000 Subject: [PATCH 16/91] =?UTF-8?q?=F0=9F=94=84=20Regenerate=20translation?= =?UTF-8?q?=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../paystack-gateway-paid-memberships-pro.mo | Bin 0 -> 558 bytes .../paystack-gateway-paid-memberships-pro.po | 91 ++++++++++++++++++ .../paystack-gateway-paid-memberships-pro.pot | 91 ++++++++++++++++++ 3 files changed, 182 insertions(+) create mode 100644 languages/paystack-gateway-paid-memberships-pro.mo create mode 100644 languages/paystack-gateway-paid-memberships-pro.po create mode 100644 languages/paystack-gateway-paid-memberships-pro.pot diff --git a/languages/paystack-gateway-paid-memberships-pro.mo b/languages/paystack-gateway-paid-memberships-pro.mo new file mode 100644 index 0000000000000000000000000000000000000000..38084cb8490d82ffbaeaa7d6211e84f409ae5fde GIT binary patch literal 558 zcmbVJJx{|h5G{x)BQt{swsOG!saG!Y9MxGyFh;ke~;gSqg0du zF>uo9bk@7)yZ1h~w>~;HHf-+L+-({gdu?g!W~T#9pDOvpasyETcT8z61wkoYYD4p9 zG^B>T(IuKmWgmHgVpdHpYR+qo>?HEsBiD1rtd`0^tV>?N<)YM(NrJeT$&1=fRqLTz zNjI03Goso+rl`;_)JWBQ`3w8$9~9@7LzD|zGRPRM{>2bSak#$PbFtHx!Z2Z1Th@#q z!`{t){X)4oLHV32&CJ~f{T)%V9Je?CK>buy}cLVFi1O~FQ8xvHoT!gZ95gISxhZ$hqa5`T#Td*tu literal 0 HcmV?d00001 diff --git a/languages/paystack-gateway-paid-memberships-pro.po b/languages/paystack-gateway-paid-memberships-pro.po new file mode 100644 index 0000000..f3c6fd4 --- /dev/null +++ b/languages/paystack-gateway-paid-memberships-pro.po @@ -0,0 +1,91 @@ +# Copyright (C) 2023 Paystack, Paid Memberships Pro +# This file is distributed under the GPLv2 or later. +msgid "" +msgstr "" +"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.1\n" +"Report-Msgid-Bugs-To: info@paidmembershipspro.com\n" +"Last-Translator: Paid Memberships Pro \n" +"Language-Team: Paid Memberships Pro \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"POT-Creation-Date: 2023-09-08T10:22:14+00:00\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"X-Generator: WP-CLI 2.8.1\n" +"X-Domain: paystack-gateway-paid-memberships-pro\n" + +#. Plugin Name of the plugin +msgid "Paystack Gateway for Paid Memberships Pro" +msgstr "" + +#. Plugin URI of the plugin +msgid "https://www.paidmembershipspro.com/add-ons/paystack-gateway/" +msgstr "" + +#. Description of the plugin +msgid "Plugin to add Paystack payment gateway into Paid Memberships Pro" +msgstr "" + +#. Author of the plugin +msgid "Paystack, Paid Memberships Pro" +msgstr "" + +#. Author URI of the plugin +msgid "https://www.paidmembershipspro.com" +msgstr "" + +#: class.pmprogateway_paystack.php:97 +msgid "Settings" +msgstr "" + +#: class.pmprogateway_paystack.php:111 +msgid "Check Out with Paystack" +msgstr "" + +#: class.pmprogateway_paystack.php:111 +msgid "Submit and Confirm" +msgstr "" + +#: class.pmprogateway_paystack.php:124 +msgid "Paystack" +msgstr "" + +#: class.pmprogateway_paystack.php:348 +msgid "Test Secret Key" +msgstr "" + +#: class.pmprogateway_paystack.php:356 +msgid "Test Public Key" +msgstr "" + +#: class.pmprogateway_paystack.php:364 +msgid "Live Secret Key" +msgstr "" + +#: class.pmprogateway_paystack.php:372 +msgid "Live Public Key" +msgstr "" + +#: class.pmprogateway_paystack.php:380 +msgid "Webhook" +msgstr "" + +#: class.pmprogateway_paystack.php:383 +msgid "To fully integrate with Paystack, be sure to use the following for your Webhook URL to" +msgstr "" + +#: class.pmprogateway_paystack.php:852 +msgid "Account" +msgstr "" + +#: class.pmprogateway_paystack.php:853 +msgid "Order" +msgstr "" + +#: class.pmprogateway_paystack.php:854 +msgid "Membership Level" +msgstr "" + +#: class.pmprogateway_paystack.php:855 +msgid "Amount Paid" +msgstr "" diff --git a/languages/paystack-gateway-paid-memberships-pro.pot b/languages/paystack-gateway-paid-memberships-pro.pot new file mode 100644 index 0000000..f3c6fd4 --- /dev/null +++ b/languages/paystack-gateway-paid-memberships-pro.pot @@ -0,0 +1,91 @@ +# Copyright (C) 2023 Paystack, Paid Memberships Pro +# This file is distributed under the GPLv2 or later. +msgid "" +msgstr "" +"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.1\n" +"Report-Msgid-Bugs-To: info@paidmembershipspro.com\n" +"Last-Translator: Paid Memberships Pro \n" +"Language-Team: Paid Memberships Pro \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"POT-Creation-Date: 2023-09-08T10:22:14+00:00\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"X-Generator: WP-CLI 2.8.1\n" +"X-Domain: paystack-gateway-paid-memberships-pro\n" + +#. Plugin Name of the plugin +msgid "Paystack Gateway for Paid Memberships Pro" +msgstr "" + +#. Plugin URI of the plugin +msgid "https://www.paidmembershipspro.com/add-ons/paystack-gateway/" +msgstr "" + +#. Description of the plugin +msgid "Plugin to add Paystack payment gateway into Paid Memberships Pro" +msgstr "" + +#. Author of the plugin +msgid "Paystack, Paid Memberships Pro" +msgstr "" + +#. Author URI of the plugin +msgid "https://www.paidmembershipspro.com" +msgstr "" + +#: class.pmprogateway_paystack.php:97 +msgid "Settings" +msgstr "" + +#: class.pmprogateway_paystack.php:111 +msgid "Check Out with Paystack" +msgstr "" + +#: class.pmprogateway_paystack.php:111 +msgid "Submit and Confirm" +msgstr "" + +#: class.pmprogateway_paystack.php:124 +msgid "Paystack" +msgstr "" + +#: class.pmprogateway_paystack.php:348 +msgid "Test Secret Key" +msgstr "" + +#: class.pmprogateway_paystack.php:356 +msgid "Test Public Key" +msgstr "" + +#: class.pmprogateway_paystack.php:364 +msgid "Live Secret Key" +msgstr "" + +#: class.pmprogateway_paystack.php:372 +msgid "Live Public Key" +msgstr "" + +#: class.pmprogateway_paystack.php:380 +msgid "Webhook" +msgstr "" + +#: class.pmprogateway_paystack.php:383 +msgid "To fully integrate with Paystack, be sure to use the following for your Webhook URL to" +msgstr "" + +#: class.pmprogateway_paystack.php:852 +msgid "Account" +msgstr "" + +#: class.pmprogateway_paystack.php:853 +msgid "Order" +msgstr "" + +#: class.pmprogateway_paystack.php:854 +msgid "Membership Level" +msgstr "" + +#: class.pmprogateway_paystack.php:855 +msgid "Amount Paid" +msgstr "" From d0f58e0764c57a6e0ff661ca562cedceb9ffe8c1 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 8 Sep 2023 12:26:49 +0200 Subject: [PATCH 17/91] Add localizations --- class.pmprogateway_paystack.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 1995af0..7b4cd1f 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -54,6 +54,8 @@ static function init() { //make sure Paystack is a gateway option add_filter('pmpro_gateways', array('PMProGateway_Paystack', 'pmpro_gateways')); + add_action( 'plugins_loaded', array('PMProGateway_Paystack', 'pmpro_paystack_load_textdomain' ) ); + //add fields to payment settings add_filter('pmpro_payment_options', array('PMProGateway_Paystack', 'pmpro_payment_options')); @@ -82,6 +84,15 @@ static function init() } } + /** + * Enable localization for the plugin. + * + * @return void + */ + static function pmpro_paystack_load_textdomain() { + load_plugin_textdomain( 'paystack-gateway-paid-memberships-pro', false, basename( dirname( __FILE__ ) ) . '/languages' ); + } + /** * Redirect Settings to PMPro settings */ From 35f446e2b99b95da2dab444dfd01ac3289d70503 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 8 Sep 2023 13:47:48 +0200 Subject: [PATCH 18/91] Version bump + Readme update --- class.pmprogateway_paystack.php | 2 +- readme.txt | 28 ++++++++++++---------------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 7b4cd1f..57ab4a8 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -247,7 +247,7 @@ static function pmpro_paystack_ipn() { global $wpdb; // Let's make sure the request came from Paystack by checking the secret key - if ( ( strtoupper($_SERVER['REQUEST_METHOD']) != 'POST' ) || ! array_key_exists( 'HTTP_X_PAYSTACK_SIGNATURE', $_SERVER ) ) { + if ( ( strtoupper( $_SERVER['REQUEST_METHOD'] ) != 'POST' ) || ! array_key_exists( 'HTTP_X_PAYSTACK_SIGNATURE', $_SERVER ) ) { exit; } diff --git a/readme.txt b/readme.txt index 0f60db0..4ee8f86 100755 --- a/readme.txt +++ b/readme.txt @@ -1,9 +1,9 @@ === Paystack Gateway for Paid Membership Pro === -Contributors: paystack, kendysond, steveamaza, lukman008 +Contributors: paystack, kendysond, steveamaza, lukman008, andrewza, strangerstudios Donate link: https://paystack.com/demo -Tags: paystack, recurrent payments, nigeria, mastercard, visa, target, Naira, payments, verve, paid membership pro -Requires at least: 3.1 -Tested up to: 5.9 +Tags: paystack, recurrent payments, nigeria, mastercard, visa, target, Naira, payments, verve, paid membership pro, pmpro, paystack pmpro +Requires at least: 5.0 +Tested up to: 6.3 Stable tag: 1.7.0 License: GPLv3 License URI: http://www.gnu.org/licenses/gpl-3.0.html @@ -47,21 +47,12 @@ We also have a developer community on Slack where we share product announcements = Minimum Requirements = * Confirm that your server can conclude a TLSv1.2 connection to Paystack's servers. More information about this requirement can be gleaned here: [TLS v1.2 requirement](https://developers.paystack.co/blog/tls-v12-requirement). -* Installed and activated Paid Membership Pro Plugin - -= Automatic installation = - -Automatic installation is the easiest option as WordPress handles the file transfers itself and you don’t need to leave your web browser. To do an automatic install of WPJobster Paystack Gateway, log in to your WordPress dashboard, navigate to the Plugins menu and click Add New. - -In the search field type "Paystack Gateway for Paid Membership Pro” and click Search Plugins. Once you’ve found our payment plugin you can view details about it such as the point release, rating and description. Most importantly of course, you can install it by simply clicking “Install Now”. +* Installed and activated [Paid Membership Pro Plugin] (https://www.paidmembershipspro.com) = Manual installation = The manual installation method involves downloading our payment plugin and uploading it to your webserver via your favourite FTP application. The WordPress codex contains [instructions on how to do this here](https://codex.wordpress.org/Managing_Plugins#Manual_Plugin_Installation). -= Updating = - -Automatic updates should work like a charm; as always though, ensure you backup your site just in case. == Frequently Asked Questions == @@ -71,17 +62,22 @@ You can find help and information on Paystack on our [Help Desk](https://paystac = Where can I get support or talk to other users? = -If you get stuck, you can ask for help in the [Paystack Gateway for Paid Membership Pro Plugin Forum](https://wordpress.org/support/plugin/paystack-gateway-paid-memberships-pro). You can also directly email support@paystack.com for assistance. +If you get stuck, you can ask for help in the [Paystack Gateway for Paid Membership Pro Plugin Forum](https://wordpress.org/support/plugin/paystack-gateway-paid-memberships-pro). = Paystack Gateway for Paid Membership Pro is awesome! Can I contribute? = -Yes you can! Join in on our [GitHub repository](https://github.com/PaystackHQ/paystack-gateway-for-paid-memberships-pro) :) +Yes you can! Join in on our [GitHub repository](https://github.com/strangerstudios/paystack-gateway-paid-memberships-pro) :) == Screenshots == 1. The slick Paystack settings panel. == Changelog == += 1.7.1 = +* SECURITY: Improved security to the webhook handler, this now checks for the presence of the Paystack signature header before processing the request. +* BUG FIX: Fixed an issue where intervals weren't being set correctly. This now supports all intervals, for quarterly and biannually please use 3 month and 6 month in the recurring subscription fields respectively. +* BUG FIX: Fixed an issue where non-expiring memberships would obtain an expiration date incorrectly. +* REFACTOR: Minor improvements to the settings UI page to align with other Paid Memberships Pro gateways. = 1.4 = * Add quarterly subscription option for recurring payments set to 3 months or 90 days cycle period. From 1b662b443403f12aab45e25257bfa3f7327817e9 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 8 Sep 2023 13:49:58 +0200 Subject: [PATCH 19/91] Update readme --- readme.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.txt b/readme.txt index 4ee8f86..ace9e56 100755 --- a/readme.txt +++ b/readme.txt @@ -1,7 +1,7 @@ === Paystack Gateway for Paid Membership Pro === Contributors: paystack, kendysond, steveamaza, lukman008, andrewza, strangerstudios Donate link: https://paystack.com/demo -Tags: paystack, recurrent payments, nigeria, mastercard, visa, target, Naira, payments, verve, paid membership pro, pmpro, paystack pmpro +Tags: paid memberships pro, pmpro, paystack, gateway, credit card, Naira, payment Requires at least: 5.0 Tested up to: 6.3 Stable tag: 1.7.0 @@ -14,7 +14,7 @@ Pay with Paystack on Paid Membership Pro Paid Membership Pro is a complete member management and membership subscriptions plugin for WordPress. Paid Memberships Pro is designed for premium content sites, clubs/associations, subscription products, newsletters and more! -The **Paystack Gateway for Paid Membership Pro** allows site owners from Nigeria and Ghana to accept payments from their customers via Paid Membership Pro. +The **Paystack Gateway for Paid Membership Pro** allows site owners from Nigeria, Ghana, Kenya and South Africa to accept payments from their customers via Paid Membership Pro. To be able to use the Paystack Gateway for Paid Membership Pro, you must [have an account on Paystack](https://dashboard.paystack.com) from which you will get Test and Live API keys to connect the plugin to your Paystack business. Here are some benefits of using Paystack! From 2d6d8c22f62dcc78d82c7143877add6b0063f3a8 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Tue, 12 Sep 2023 14:33:46 +0200 Subject: [PATCH 20/91] Only allow card channel for subs. --- class.pmprogateway_paystack.php | 7 +++++++ readme.txt | 1 + 2 files changed, 8 insertions(+) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 57ab4a8..a0db88d 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -525,6 +525,13 @@ function sendToPaystack(&$order) ), 'custom_filters' => array("recurring" => true))), ); + + // If the level is recurring only allow card payments for the subscription as other methods don't work. + $level = $order->getMembershipLevel(); + if ( pmpro_isLevelRecurring( $level ) ) { + $body['channels'] = array( 'card' ); + } + $args = array( 'body' => json_encode($body), 'headers' => $headers, diff --git a/readme.txt b/readme.txt index 4ee8f86..fd5803a 100755 --- a/readme.txt +++ b/readme.txt @@ -75,6 +75,7 @@ Yes you can! Join in on our [GitHub repository](https://github.com/strangerstudi == Changelog == = 1.7.1 = * SECURITY: Improved security to the webhook handler, this now checks for the presence of the Paystack signature header before processing the request. +* ENHANCEMENT: Only allow card checkout for recurring subscriptions as other payment options don't allow subscriptions. * BUG FIX: Fixed an issue where intervals weren't being set correctly. This now supports all intervals, for quarterly and biannually please use 3 month and 6 month in the recurring subscription fields respectively. * BUG FIX: Fixed an issue where non-expiring memberships would obtain an expiration date incorrectly. * REFACTOR: Minor improvements to the settings UI page to align with other Paid Memberships Pro gateways. From 83b846c7624b0ab0cfd84fb5b6cff0745b0416cf Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Wed, 13 Sep 2023 11:22:11 +0200 Subject: [PATCH 21/91] Update readme and get ready for release. --- class.pmprogateway_paystack.php | 4 ++-- readme.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index a0db88d..86eae5f 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -4,7 +4,7 @@ * Plugin URI: https://www.paidmembershipspro.com/add-ons/paystack-gateway/ * Description: Plugin to add Paystack payment gateway into Paid Memberships Pro * Version: 1.7.1 - * Author: Paystack, Paid Memberships Pro + * Author: Paid Memberships Pro, Paystack * Author URI: https://www.paidmembershipspro.com * License: GPLv2 or later * Text Domain: paystack-gateway-paid-memberships-pro @@ -235,7 +235,7 @@ function kkd_pmprosd_convert_date( $date ) { * DEPRECATED use pmpro_paystack_ipn instead. * @since 1.0 */ - static function kdd_pmpro_paystack_ipn() { + static function kkd_pmpro_paystack_ipn() { pmpro_paystack_ipn(); } diff --git a/readme.txt b/readme.txt index fd5803a..502dc3c 100755 --- a/readme.txt +++ b/readme.txt @@ -73,7 +73,7 @@ Yes you can! Join in on our [GitHub repository](https://github.com/strangerstudi 1. The slick Paystack settings panel. == Changelog == -= 1.7.1 = += 1.7.1 - 2023-09-13 = * SECURITY: Improved security to the webhook handler, this now checks for the presence of the Paystack signature header before processing the request. * ENHANCEMENT: Only allow card checkout for recurring subscriptions as other payment options don't allow subscriptions. * BUG FIX: Fixed an issue where intervals weren't being set correctly. This now supports all intervals, for quarterly and biannually please use 3 month and 6 month in the recurring subscription fields respectively. From 0fe5c47261d4842738ab6b7d1881df4b587f6c7c Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Wed, 27 Sep 2023 13:40:22 +0200 Subject: [PATCH 22/91] Add readme.MD for PMPro repo. --- README.md | 44 ++++++++++++++++++++++---------------- pmpro-paystack-banner.jpg | Bin 0 -> 44234 bytes readme.txt | 4 ++-- 3 files changed, 27 insertions(+), 21 deletions(-) create mode 100644 pmpro-paystack-banner.jpg diff --git a/README.md b/README.md index 177a98d..d93d5cd 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,37 @@ -

Paystack Paid Membership Pro

+![](pmpro-sample-banner.png) -# Paystack Gateway for Paid Membership Pro +# [Plugin Name](https://www.paidmembershipspro.com/add-ons/paystack-gateway/) # -Welcome to the Paystack Gateway for Paid Membership Pro repository on GitHub. +![WordPress Plugin Downloads](https://img.shields.io/wordpress/plugin/dy/paystack-gateway-paid-memberships-pro?style=flat-square) ![License](https://img.shields.io/badge/license-GPL--2.0%2B-red.svg?style=flat-square) -The *Paystack Gateway for Paid Membership Pro* plugin allows Wordpress site owners from Nigeria and Ghana to accept payments from their customers via Paid Membership Pro. +### Welcome to the Plugin Name GitHub Repository +Add a description of the product here. -Here you can browse the source, look at open issues and keep track of development. +For more information please visit [paidmembershipspro.com/add-ons/paystack-gateway/](https://www.paidmembershipspro.com/add-ons/paystack-gateway/) -## Installation +## Installation ## +For detailed installation steps, visit the [documentation](https://www.paidmembershipspro.com/add-ons/paystack-gateway/) page. -1. Install the [Paystack Gateway for Paid Membership Pro](https://wordpress.org/plugins/paystack-gateway-paid-memberships-pro/) via the Plugins section of your WordPress Dashboard. -2. Go to the settings section of the plugin and enter your Public and Secret Keys which are available on your [Paystack Dashboard](https://dashboard.paystack.com/#/settings/developer). +1. Download the current development ZIP file directly: `https://github.com/strangerstudios/repo-slug/archive/dev.zip` -## Documentation -* [Paystack Documentation](https://developers.paystack.co/v2.0/docs/) -* [Paystack Helpdesk](https://paystack.com/help) +**Please ensure that once installing this version of the plugin to remove `-dev` from the plugin's folder name.** -## Support -For bug reports and feature requests directly related to this plugin, please use the [issue tracker](https://github.com/PaystackHQ/plugin-paid-membership-pro/issues). +## Bugs ## +If you find an issue/bug, let us know by [creating a detailed GitHub issue](https://github.com/strangerstudios/repo-slug/issues/new). -For questions related to using the plugin, please post an inquiry to the plugin [support forum](https://wordpress.org/support/plugin/paystack-gateway-paid-memberships-pro). +## Support ## +This is a developer's portal for Plugin Name. We do not offer support on this channel. **Any support related questions should be directed to [paidmembershipspro.com/add-ons/paystack-gateway/](https://www.paidmembershipspro.com/add-ons/paystack-gateway/).** -For general support or questions about your Paystack account, you can reach out by sending a message from [our website](https://paystack.com/contact). +## Contributing to Plugin Name ## +We encourage and welcome any contribution to Plugin Name. Please read the [guidelines for contributing](https://github.com/strangerstudios/paid-memberships-pro/blob/dev/.github/CONTRIBUTING.md) to this repository. -## Community -If you are a developer, please join our Developer Community on [Slack](https://slack.paystack.com). +There are various **ways to the help development** of Plugin Name: -## Contributing to Paystack Gateway for Paid Membership Pro -If you have a patch or have stumbled upon an issue with the Paystack Gateway for Paid Membership Pro plugin, you can contribute this back to the code. Please read our [contributor guidelines](https://github.com/PaystackHQ/wordpress-paid-membership-pro-paystack/blob/master/CONTRIBUTING.md) for more information how you can do this. +1. Report [bugs/issues](https://github.com/strangerstudios/repo-slug/issues/new) on GitHub. +2. Work on any issues by submitting a Pull Request. + +Here are some ways for **non-developers to contribute** to Plugin Name: + +1. Translate Plugin Name into your own [language](https://www.paidmembershipspro.com/paid-memberships-pro-in-your-language/). +2. [Purchase a paid membership](https://paidmembershipspro.com/pricing) to help fund ongoing development and bug fixes. +3. Leave an honest review for [Plugin Name](https://wordpress.org/support/plugin/repo-slug/reviews/#new-post). \ No newline at end of file diff --git a/pmpro-paystack-banner.jpg b/pmpro-paystack-banner.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e8b4e2cdb46291fba1d5562570ecf562148454ed GIT binary patch literal 44234 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 literal 0 HcmV?d00001 diff --git a/readme.txt b/readme.txt index 6dce896..eb58b43 100755 --- a/readme.txt +++ b/readme.txt @@ -1,10 +1,10 @@ === Paystack Gateway for Paid Membership Pro === -Contributors: paystack, kendysond, steveamaza, lukman008, andrewza, strangerstudios +Contributors: paystack, kendysond, steveamaza, lukman008, andrewza, strangerstudios, paidmembershipspro Donate link: https://paystack.com/demo Tags: paid memberships pro, pmpro, paystack, gateway, credit card, Naira, payment Requires at least: 5.0 Tested up to: 6.3 -Stable tag: 1.7.0 +Stable tag: 1.7.1 License: GPLv3 License URI: http://www.gnu.org/licenses/gpl-3.0.html From 54b571d21aafdfc73477492f9bf02d4ad8226045 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Wed, 27 Sep 2023 13:41:39 +0200 Subject: [PATCH 23/91] Added plugin name to readme.MD --- .gitattributes | 3 ++- README.md | 29 ++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.gitattributes b/.gitattributes index 5d3873b..2d8b5d0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,4 +2,5 @@ .github export-ignore CONTRIBUTING.md export-ignore README.md export-ignore -Dockerfile export-ignore \ No newline at end of file +Dockerfile export-ignore +pmpro-paystack-banner.png export-ignore \ No newline at end of file diff --git a/README.md b/README.md index d93d5cd..b9f8da3 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,36 @@ -![](pmpro-sample-banner.png) +![](pmpro-paystack-banner.png) -# [Plugin Name](https://www.paidmembershipspro.com/add-ons/paystack-gateway/) # +# [Paid Memberships Pro - Paystack Gateway](https://www.paidmembershipspro.com/add-ons/paystack-gateway/) # ![WordPress Plugin Downloads](https://img.shields.io/wordpress/plugin/dy/paystack-gateway-paid-memberships-pro?style=flat-square) ![License](https://img.shields.io/badge/license-GPL--2.0%2B-red.svg?style=flat-square) -### Welcome to the Plugin Name GitHub Repository -Add a description of the product here. +### Welcome to the Paid Memberships Pro - Paystack Gateway GitHub Repository +Add Paystack as a gateway option for Paid Memberships Pro and accept payments from members around Africa. For more information please visit [paidmembershipspro.com/add-ons/paystack-gateway/](https://www.paidmembershipspro.com/add-ons/paystack-gateway/) ## Installation ## For detailed installation steps, visit the [documentation](https://www.paidmembershipspro.com/add-ons/paystack-gateway/) page. -1. Download the current development ZIP file directly: `https://github.com/strangerstudios/repo-slug/archive/dev.zip` +1. Download the current development ZIP file directly: `https://github.com/strangerstudios/paystack-gateway-paid-memberships-pro/archive/dev.zip` **Please ensure that once installing this version of the plugin to remove `-dev` from the plugin's folder name.** ## Bugs ## -If you find an issue/bug, let us know by [creating a detailed GitHub issue](https://github.com/strangerstudios/repo-slug/issues/new). +If you find an issue/bug, let us know by [creating a detailed GitHub issue](https://github.com/strangerstudios/paystack-gateway-paid-memberships-pro/issues/new). ## Support ## -This is a developer's portal for Plugin Name. We do not offer support on this channel. **Any support related questions should be directed to [paidmembershipspro.com/add-ons/paystack-gateway/](https://www.paidmembershipspro.com/add-ons/paystack-gateway/).** +This is a developer's portal for Paid Memberships Pro - Paystack Gateway. We do not offer support on this channel. **Any support related questions should be directed to [paidmembershipspro.com/add-ons/paystack-gateway/](https://www.paidmembershipspro.com/add-ons/paystack-gateway/).** -## Contributing to Plugin Name ## -We encourage and welcome any contribution to Plugin Name. Please read the [guidelines for contributing](https://github.com/strangerstudios/paid-memberships-pro/blob/dev/.github/CONTRIBUTING.md) to this repository. +## Contributing to Paid Memberships Pro - Paystack Gateway ## +We encourage and welcome any contribution to Paid Memberships Pro - Paystack Gateway. Please read the [guidelines for contributing](https://github.com/strangerstudios/paid-memberships-pro/blob/dev/.github/CONTRIBUTING.md) to this repository. -There are various **ways to the help development** of Plugin Name: +There are various **ways to the help development** of Paid Memberships Pro - Paystack Gateway: -1. Report [bugs/issues](https://github.com/strangerstudios/repo-slug/issues/new) on GitHub. +1. Report [bugs/issues](https://github.com/strangerstudios/paystack-gateway-paid-memberships-pro/issues/new) on GitHub. 2. Work on any issues by submitting a Pull Request. -Here are some ways for **non-developers to contribute** to Plugin Name: +Here are some ways for **non-developers to contribute** to Paid Memberships Pro - Paystack Gateway: -1. Translate Plugin Name into your own [language](https://www.paidmembershipspro.com/paid-memberships-pro-in-your-language/). -2. [Purchase a paid membership](https://paidmembershipspro.com/pricing) to help fund ongoing development and bug fixes. -3. Leave an honest review for [Plugin Name](https://wordpress.org/support/plugin/repo-slug/reviews/#new-post). \ No newline at end of file +1. Translate Paid Memberships Pro - Paystack Gateway into your own [language](https://www.paidmembershipspro.com/paid-memberships-pro-in-your-language/). +2. [Purchase a paid membership](https://paidmembershipspro.com/pricing) to help fund ongoing development and bug fixes. \ No newline at end of file From f980a2d5e65393d1db866c0f42c68cf88862f310 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Wed, 27 Sep 2023 13:42:01 +0200 Subject: [PATCH 24/91] jpg image --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b9f8da3..8f1cfe5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![](pmpro-paystack-banner.png) +![](pmpro-paystack-banner.jpg) # [Paid Memberships Pro - Paystack Gateway](https://www.paidmembershipspro.com/add-ons/paystack-gateway/) # From eb61d5c8214f076f3abfd9c7f2d34b210c4feef5 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Mon, 29 Jan 2024 14:08:03 +0200 Subject: [PATCH 25/91] Support subscription cancellations for PMPro 3.0 * ENHANCEMENT: Added support for Paid Memberships Pro 3.0 and improved cancellations. * BUG FIX: Fixed an issue where the API calls were failing due to passing a user_agent. Something must have changed from their API to stop this. --- class.pmprogateway_paystack.php | 173 ++++++++++++++++++++++++++------ 1 file changed, 142 insertions(+), 31 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 86eae5f..a6de27d 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -710,23 +710,11 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) // There's recurring settings, lets convert to Paystack intervals now. if ( $pmpro_level->billing_amount > 0 ) { - if ( $pmpro_level->cycle_period == 'Day' ) { - $interval = 'daily'; - } - - if ( $pmpro_level->cycle_period == 'Week' ) { - $interval = 'weekly'; - } + // Convert the PMPro cycle to match that of paystacks. + $obj = new self(); + $interval = $obj->convert_interval_for_paystack( $pmpro_level->cycle_period ); - if ( $pmpro_level->cycle_period == 'Month' ) { - $interval = 'monthly'; - } - - if ( $pmpro_level->cycle_period == 'Year' ) { - $interval = 'annually'; - } - - // Biannual and quarterly conversion. + // Biannual and quarterly conversion for special cases. if ( $pmpro_level->cycle_number == 3 && $pmpro_level->cycle_period == 'Month' ) { $interval = 'quarterly'; } @@ -780,7 +768,7 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) } $subscription_delay = get_option( 'pmpro_subscription_delay_' . $pmpro_level->id, 0 ); - if($subscription_delay) + if ( ! is_numeric( $subscription_delay ) ) { $start_date = kkd_pmprosd_convert_date( $subscription_delay ); } else { @@ -805,14 +793,8 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) $token = $paystack_response->data->email_token; $morder->subscription_transaction_id = $subscription_code; $morder->subscription_token = $token; - - - } - } - // - // die(); $custom_level = array( 'user_id' => $morder->user_id, @@ -928,11 +910,11 @@ function cancelMembership(&$user){ print_r("No records were found with user - ". $user_id." level - ". $level_to_cancel); } } - function cancel(&$order, $update_status = true) + function cancel(&$order, $update_status = true ) { $backtrace = self::get_caller_info(); $furtherbacktrace = wp_debug_backtrace_summary(); - + //no matter what happens below, we're going to cancel the order in our system if ( $update_status ) { $order->updateStatus( "cancelled" ); @@ -946,7 +928,8 @@ function cancel(&$order, $update_status = true) $key = pmpro_getOption("paystack_lsk"); } - if ($order->subscription_transaction_id != "") { + + if ( $code != "") { $paystack_url = 'https://api.paystack.co/subscription/' . $code; $headers = array( @@ -955,7 +938,6 @@ function cancel(&$order, $update_status = true) $args = array( 'headers' => $headers, 'timeout' => 60, - 'user-agent' => 'Wordpress/ '. $backtrace . " ". $furtherbacktrace ); $request = wp_remote_get($paystack_url, $args); @@ -977,18 +959,147 @@ function cancel(&$order, $update_status = true) 'body' => json_encode($body), 'headers' => $headers, 'timeout' => 60, - 'user-agent' => 'Wordpress/ '. $backtrace . " ". $furtherbacktrace ); $request = wp_remote_post($paystack_url, $args); - // print_r($request); - if (!is_wp_error($request)) { - $paystack_response = json_decode(wp_remote_retrieve_body($request)); + + if ( ! is_wp_error( $request ) ) { + return true; + } else { + return false; // There was an error cancelling for some reason. } } } } + return true; + } + + /// Used for updating subscription stuff. + public function update_subscription_info( $subscription ) { + $subscription_id = $subscription->get_subscription_transaction_id(); + $backtrace = self::get_caller_info(); + $furtherbacktrace = wp_debug_backtrace_summary(); + + $mode = pmpro_getOption("gateway_environment"); + if ( $mode == "sandbox" ) { + $key = pmpro_getOption("paystack_tsk"); + + } else { + $key = pmpro_getOption("paystack_lsk"); + } + + $paystack_url = 'https://api.paystack.co/subscription/' . $subscription_id; + + $headers = array( + 'Authorization' => 'Bearer ' . $key + ); + + $args = array( + 'headers' => $headers, + 'timeout' => 60, + ); + + $request = wp_remote_get( $paystack_url, $args ); + + // Request is okay, so let's get the data now and update what we need to. + if ( ! is_wp_error( $request ) && 200 == wp_remote_retrieve_response_code( $request ) ) { + $update_array = array(); + + $response = json_decode( wp_remote_retrieve_body( $request ) ); + $sub_info = $response->data; + + // The response status isn't active, so we're most likely already cancelled. + if ( $sub_info->status !== 'active' ) { + $update_array['status'] = 'cancelled'; // Does it + } else { + $update_array['status'] = 'active'; + } + + + // Let's make sure the cycle_numbers are correctly set based on the interval from Paystack. + switch( $sub_info->plan->interval ) { + case 'quarterly': + $update_array['cycle_number'] = 3; + break; + case 'biannually': + $update_array['cycle_number'] = 6; + break; + } + + // Update the subscription. + $update_array['next_payment_date'] = sanitize_text_field( $sub_info->next_payment_date ); // [YYYY]-[MM]-[DD + $update_array['startdate'] = sanitize_text_field( $sub_info->createdAt ); + $update_array['billing_amount'] = (float) $sub_info->amount/100; // Get currency value + $update_array['cycle_period'] = $this->convert_interval_for_pmpro( $sub_info->plan->interval ); // Convert interval for PMPro format (which sanitizes it) + } else { + // Throw an error here. + } + + $subscription->set( $update_array ); + } + + /** + * Undocumented function + * + * @param string $interval The pmpro paystack + * @return string $interval The required interval for PayStack to recognize. + */ + function convert_interval_for_paystack( $interval ) { + + $interval = strtolower( $interval ); + + switch( $interval ) { + case 'day': + $interval = 'daily'; + break; + case 'week': + $interval = 'weekly'; + break; + case 'month': + $interval = 'monthly'; + break; + case 'year': + $interval = 'annually'; + break; + default: + $interval = 'monthly'; + } + + return $interval; + + } + + function convert_interval_for_pmpro( $interval ) { + + $interval = strtolower( $interval ); + + switch( $interval ) { + case 'daily': + $interval = 'Day'; + break; + case 'weekly': + $interval = 'Week'; + break; + case 'monthly': + $interval = 'Month'; + break; + case 'annually': + $interval = 'Year'; + break; + case 'quarterly': + $interval = 'Month'; + break; + case 'biannually': + $interval = 'Month'; + break; + default: + $interval = 'Month'; + } + + return $interval; + } + function get_caller_info() { $c = ''; $file = ''; From fb39ff742e0f05738aa15d97ffe897940be45ecc Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Mon, 29 Jan 2024 14:13:29 +0200 Subject: [PATCH 26/91] rename variable * Rename variable --- class.pmprogateway_paystack.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index a6de27d..bdc269a 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -711,8 +711,8 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) if ( $pmpro_level->billing_amount > 0 ) { // Convert the PMPro cycle to match that of paystacks. - $obj = new self(); - $interval = $obj->convert_interval_for_paystack( $pmpro_level->cycle_period ); + $pmpro_paystack = new self(); + $interval = $pmpro_paystack->convert_interval_for_paystack( $pmpro_level->cycle_period ); // Biannual and quarterly conversion for special cases. if ( $pmpro_level->cycle_number == 3 && $pmpro_level->cycle_period == 'Month' ) { From 1e2dbc7627988cd2ceab4b7b5270d7a064b13bc2 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Mon, 29 Jan 2024 14:18:53 +0200 Subject: [PATCH 27/91] Added doc block --- class.pmprogateway_paystack.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index bdc269a..9be2b31 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -1069,6 +1069,12 @@ function convert_interval_for_paystack( $interval ) { } + /** + * Convert Paystack's intervals for PMPro's format. + * + * @param string $interval The received Paystack interval (i.e. Weekly, Monthly etc ) + * @return string $interval The converted interval for PMPro. + */ function convert_interval_for_pmpro( $interval ) { $interval = strtolower( $interval ); From ea360dd1aacc4a38a237cdb3a61bb549075c2b54 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Mon, 29 Jan 2024 16:55:50 +0200 Subject: [PATCH 28/91] Only set the subscription if it works * Only set the subscription if the API was successful. --- class.pmprogateway_paystack.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 9be2b31..420e90d 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -1031,11 +1031,8 @@ public function update_subscription_info( $subscription ) { $update_array['startdate'] = sanitize_text_field( $sub_info->createdAt ); $update_array['billing_amount'] = (float) $sub_info->amount/100; // Get currency value $update_array['cycle_period'] = $this->convert_interval_for_pmpro( $sub_info->plan->interval ); // Convert interval for PMPro format (which sanitizes it) - } else { - // Throw an error here. + $subscription->set( $update_array ); } - - $subscription->set( $update_array ); } /** From c009e995f2712bd59faeba431f98e3aea27bd22e Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Tue, 30 Jan 2024 18:01:19 +0200 Subject: [PATCH 29/91] Added error messages if API fails. --- class.pmprogateway_paystack.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 420e90d..13af79f 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -1002,10 +1002,15 @@ public function update_subscription_info( $subscription ) { $request = wp_remote_get( $paystack_url, $args ); // Request is okay, so let's get the data now and update what we need to. - if ( ! is_wp_error( $request ) && 200 == wp_remote_retrieve_response_code( $request ) ) { - $update_array = array(); - + if ( ! is_wp_error( $request ) ) { $response = json_decode( wp_remote_retrieve_body( $request ) ); + + if ( 200 !== wp_remote_retrieve_response_code( $request ) ) { + // Throw an error here from the API + return esc_html__( sprintf( 'Paystack error: %s', $response->message ), 'paystack-gateway-paid-memberships-pro' ); + } + + $update_array = array(); $sub_info = $response->data; // The response status isn't active, so we're most likely already cancelled. @@ -1032,6 +1037,8 @@ public function update_subscription_info( $subscription ) { $update_array['billing_amount'] = (float) $sub_info->amount/100; // Get currency value $update_array['cycle_period'] = $this->convert_interval_for_pmpro( $sub_info->plan->interval ); // Convert interval for PMPro format (which sanitizes it) $subscription->set( $update_array ); + } else { + return esc_html__( 'There was an error communicating with Paystack. Please confirm your connectivity and API details and try again.', 'paystack-gateway-paid-memberships-pro' ); } } From 2b2786b61ba0c8d03ea9a77a17470946fc5d0e41 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Mon, 5 Feb 2024 15:16:34 +0200 Subject: [PATCH 30/91] Add supports functionality * Add support function for Paystack to support PMPro 3.0 --- class.pmprogateway_paystack.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 13af79f..0c55457 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -315,6 +315,25 @@ static function pmpro_paystack_ipn() { exit(); } + /** + * Check whether or not a gateway supports a specific feature. + * + * @param string $feature The feature we need to check if it is supported. + * @return string|boolean $supports In some cases, we may need to return strings for the feature or a boolean value if it's supported or not. + */ + public static function supports( $feature ) { + $supports = array( + 'subscription_sync' => true, + 'payment_method_updates' => false + ); + + if ( empty( $supports[$feature] ) ) { + return false; + } + + return $supports[$feature]; + } + /** * Get a list of payment options that the Paystack gateway needs/supports. */ From f95d3a0f103173604ff3672cffed307af5793b3a Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 9 Feb 2024 15:21:50 +0200 Subject: [PATCH 31/91] Fix issue with discount codes and expiration * BUG FIX: Fixed an issue where discount codes weren't captured during checkout correctly and wouldn't reflect the right amount. * BUG FIX: Fixed an issue where a discount code expiration date wouldn't be set. --- class.pmprogateway_paystack.php | 88 ++++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 30 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 86eae5f..2535f97 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -309,7 +309,6 @@ static function pmpro_paystack_ipn() { self::renewpayment($event); case 'invoice.update': self::renewpayment($event); - } http_response_code(200); exit(); @@ -437,25 +436,56 @@ static function pmpro_gateways_with_pending_status($gateways) { /** * Instead of change membership levels, send users to Paystack payment page. */ - static function pmpro_checkout_before_change_membership_level($user_id, $morder) - { + static function pmpro_checkout_before_change_membership_level( $user_id, $morder ) { global $wpdb, $discount_code_id; //if no order, no need to pay - if (empty($morder)) { + if ( empty( $morder )) { return; } - if (empty($morder->code)) + + if ( empty( $morder->code ) ) { $morder->code = $morder->getRandomCode(); + } $morder->payment_type = "paystack"; $morder->status = "pending"; $morder->user_id = $user_id; $morder->saveOrder(); - //save discount code use - if (!empty($discount_code_id)) - $wpdb->query("INSERT INTO $wpdb->pmpro_discount_codes_uses (code_id, user_id, order_id, timestamp) VALUES('" . $discount_code_id . "', '" . $user_id . "', '" . $morder->id . "', now())"); + // Try to get the discount_code from a query param. + if ( empty( $discount_code_id ) ) { + // PMPro 3.0+ + if ( isset( $_REQUEST['pmpro_discount_code'] ) ) { + $discount_code = sanitize_text_field( $_REQUEST['pmpro_discount_code'] ); + } + + // PMPro < 3.0 + if ( isset( $_REQUEST['discount_code'] ) ) { + $discount_code = sanitize_text_field( $_REQUEST['discount_code'] ); + } + } + // if global is empty but query is available. PMPro 3.0 + if ( empty( $discount_code_id ) && ! empty( $discount_code ) ) { + $discount_code_id = $wpdb->get_var( "SELECT id FROM $wpdb->pmpro_discount_codes WHERE code = '" . esc_sql( $discount_code ) . "'" ); + } + + // save discount code use + if ( ! empty( $discount_code_id ) ) { + $wpdb->query( + $wpdb->prepare( + "INSERT INTO $wpdb->pmpro_discount_codes_uses + (code_id, user_id, order_id, timestamp) + VALUES( %d , %d, %d, %s )", + $discount_code_id, + $user_id, + $morder->id, + current_time( 'mysql' ) + ) + ); + } + + do_action("pmpro_before_send_to_paystack", $user_id, $morder); $morder->Gateway->sendToPaystack($morder); } @@ -593,21 +623,21 @@ static function renewpayment($event) $morder->Email = $email; $pmpro_level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . (int)$morder->membership_id . "' LIMIT 1"); $pmpro_level = apply_filters("pmpro_checkout_level", $pmpro_level); - $startdate = apply_filters("pmpro_checkout_start_date", "'" . current_time("mysql") . "'", $morder->user_id, $pmpro_level); + $startdate = apply_filters( 'pmpro_checkout_start_date', "'" . current_time( 'mysql' ) . "'", $morder->user_id, $morder->membership_level ); - $enddate = "'" . date("Y-m-d", strtotime("+ " . $pmpro_level->expiration_number . " " . $pmpro_level->expiration_period, current_time("timestamp"))) . "'"; + $enddate = "'" . date("Y-m-d", strtotime("+ " . $morder->membership_level->expiration_number . " " . $morder->membership_level->expiration_period, current_time("timestamp"))) . "'"; $custom_level = array( 'user_id' => $morder->user_id, - 'membership_id' => $pmpro_level->id, + 'membership_id' => $morder->memberhsip_level->id, 'code_id' => '', - 'initial_payment' => $pmpro_level->initial_payment, - 'billing_amount' => $pmpro_level->billing_amount, - 'cycle_number' => $pmpro_level->cycle_number, - 'cycle_period' => $pmpro_level->cycle_period, - 'billing_limit' => $pmpro_level->billing_limit, - 'trial_amount' => $pmpro_level->trial_amount, - 'trial_limit' => $pmpro_level->trial_limit, + 'initial_payment' => $morder->memberhsip_level->initial_payment, + 'billing_amount' => $morder->memberhsip_level->billing_amount, + 'cycle_number' => $morder->memberhsip_level->cycle_number, + 'cycle_period' => $morder->memberhsip_level->cycle_period, + 'billing_limit' => $morder->memberhsip_level->billing_limit, + 'trial_amount' => $morder->memberhsip_level->trial_amount, + 'trial_limit' => $morder->memberhsip_level->trial_limit, 'startdate' => $startdate, 'enddate' => $enddate ); @@ -811,20 +841,18 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) } } - // - // die(); $custom_level = array( 'user_id' => $morder->user_id, - 'membership_id' => $pmpro_level->id, + 'membership_id' => $morder->membership_level->id, 'code_id' => '', - 'initial_payment' => $pmpro_level->initial_payment, - 'billing_amount' => $pmpro_level->billing_amount, - 'cycle_number' => $pmpro_level->cycle_number, - 'cycle_period' => $pmpro_level->cycle_period, - 'billing_limit' => $pmpro_level->billing_limit, - 'trial_amount' => $pmpro_level->trial_amount, - 'trial_limit' => $pmpro_level->trial_limit, + 'initial_payment' => $morder->membership_level->initial_payment, + 'billing_amount' => $morder->membership_level->billing_amount, + 'cycle_number' => $morder->membership_level->cycle_number, + 'cycle_period' => $morder->membership_level->cycle_period, + 'billing_limit' => $morder->membership_level->billing_limit, + 'trial_amount' => $morder->membership_level->trial_amount, + 'trial_limit' => $morder->membership_level->trial_limit, 'startdate' => $startdate, 'enddate' => $enddate ); @@ -833,7 +861,7 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) $_REQUEST['cancel_membership'] = false; // Do NOT cancel gateway subscription if (pmpro_changeMembershipLevel($custom_level, $morder->user_id, 'changed')) { - $morder->membership_id = $pmpro_level->id; + $morder->membership_id = $morder->membership_level->id; $morder->payment_transaction_id = $_REQUEST['trxref']; $morder->status = "success"; $morder->saveOrder(); @@ -1023,4 +1051,4 @@ function get_caller_info() { } } } -} +} \ No newline at end of file From e7180f36a742798ec4c00ac9fa85661f7f555a39 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 9 Feb 2024 15:39:11 +0200 Subject: [PATCH 32/91] Update class.pmprogateway_paystack.php --- class.pmprogateway_paystack.php | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 2535f97..0c4da61 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -627,10 +627,20 @@ static function renewpayment($event) $enddate = "'" . date("Y-m-d", strtotime("+ " . $morder->membership_level->expiration_number . " " . $morder->membership_level->expiration_period, current_time("timestamp"))) . "'"; + // get discount code (NOTE: but discount_code isn't set here. How to handle discount codes for PayPal Standard?) + $morder->getDiscountCode(); + if ( ! empty( $morder->discount_code ) ) { + // update membership level + $morder->getMembershipLevel( true ); + $discount_code_id = $morder->discount_code->id; + } else { + $discount_code_id = ''; + } + $custom_level = array( 'user_id' => $morder->user_id, 'membership_id' => $morder->memberhsip_level->id, - 'code_id' => '', + 'code_id' => $discount_code_id, 'initial_payment' => $morder->memberhsip_level->initial_payment, 'billing_amount' => $morder->memberhsip_level->billing_amount, 'cycle_number' => $morder->memberhsip_level->cycle_number, @@ -842,10 +852,20 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) } + // get discount code (NOTE: but discount_code isn't set here. How to handle discount codes for PayPal Standard?) + $morder->getDiscountCode(); + if ( ! empty( $morder->discount_code ) ) { + // update membership level + $morder->getMembershipLevel( true ); + $discount_code_id = $morder->discount_code->id; + } else { + $discount_code_id = ''; + } + $custom_level = array( 'user_id' => $morder->user_id, 'membership_id' => $morder->membership_level->id, - 'code_id' => '', + 'code_id' => $discount_code_id, 'initial_payment' => $morder->membership_level->initial_payment, 'billing_amount' => $morder->membership_level->billing_amount, 'cycle_number' => $morder->membership_level->cycle_number, From 8da3cbc80fe28cc8bc3c4e28e22dc14cdd3ff4e8 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Tue, 13 Feb 2024 16:12:26 +0200 Subject: [PATCH 33/91] Fixed expiration dates not being set * BUG FIX: Fixed an issue where expiration dates were also not being set. --- class.pmprogateway_paystack.php | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 0c4da61..02e2f0b 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -625,8 +625,6 @@ static function renewpayment($event) $pmpro_level = apply_filters("pmpro_checkout_level", $pmpro_level); $startdate = apply_filters( 'pmpro_checkout_start_date', "'" . current_time( 'mysql' ) . "'", $morder->user_id, $morder->membership_level ); - $enddate = "'" . date("Y-m-d", strtotime("+ " . $morder->membership_level->expiration_number . " " . $morder->membership_level->expiration_period, current_time("timestamp"))) . "'"; - // get discount code (NOTE: but discount_code isn't set here. How to handle discount codes for PayPal Standard?) $morder->getDiscountCode(); if ( ! empty( $morder->discount_code ) ) { @@ -636,6 +634,13 @@ static function renewpayment($event) } else { $discount_code_id = ''; } + + //fix expiration date + if ( ! empty( $morder->membership_level->expiration_number ) ) { + $enddate = "'" . date_i18n( "Y-m-d", strtotime( "+ " . $morder->membership_level->expiration_number . " " . $morder->membership_level->expiration_period, current_time( "timestamp" ) ) ) . "'"; + } else { + $enddate = "NULL"; + } $custom_level = array( 'user_id' => $morder->user_id, @@ -695,7 +700,6 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) $_REQUEST['trxref'] = $reference; } - if (empty($pmpro_invoice)) { $morder = new MemberOrder($_REQUEST['trxref']); // $morder = new MemberOrder(); @@ -737,15 +741,6 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) $pstk_logger = new pmpro_paystack_plugin_tracker('pm-pro',$pk); $pstk_logger->log_transaction_success($_REQUEST['trxref']); do_action('pmpro_after_checkout', $morder->user_id, $morder); - //-------------------------------------------------- - - // Let's make sure we're setting an expiration date if we've set one. - if ( ! empty( $pmpro_level->expiration_number ) ) { - $enddate = "'" . date( "Y-m-d H:i:00", strtotime( "+ " . $pmpro_level->expiration_number . " " . $pmpro_level->expiration_period, current_time( 'timestamp' ) ) ) . "'"; - } else { - $enddate = "0000-00-00 00:00:00"; - } - // There's recurring settings, lets convert to Paystack intervals now. if ( $pmpro_level->billing_amount > 0 ) { @@ -845,9 +840,6 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) $token = $paystack_response->data->email_token; $morder->subscription_transaction_id = $subscription_code; $morder->subscription_token = $token; - - - } } @@ -862,6 +854,13 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) $discount_code_id = ''; } + // Get the expiration date. + if ( ! empty( $morder->membership_level->expiration_number ) ) { + $enddate = "'" . date_i18n( "Y-m-d", strtotime( "+ " . $morder->membership_level->expiration_number . " " . $morder->membership_level->expiration_period, current_time( "timestamp" ) ) ) . "'"; + } else { + $enddate = "NULL"; + } + $custom_level = array( 'user_id' => $morder->user_id, 'membership_id' => $morder->membership_level->id, From 6a12cc4c1eeccb928917c0f2307af9433ee84dc4 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Wed, 14 Feb 2024 13:48:04 +0200 Subject: [PATCH 34/91] Fix minor issue with discount codes. --- class.pmprogateway_paystack.php | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index d929a0f..e7917e2 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -762,22 +762,22 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) do_action('pmpro_after_checkout', $morder->user_id, $morder); // There's recurring settings, lets convert to Paystack intervals now. - if ( $pmpro_level->billing_amount > 0 ) { + if ( $morder->membership_level->billing_amount > 0 ) { // Convert the PMPro cycle to match that of paystacks. $pmpro_paystack = new self(); $interval = $pmpro_paystack->convert_interval_for_paystack( $pmpro_level->cycle_period ); // Biannual and quarterly conversion for special cases. - if ( $pmpro_level->cycle_number == 3 && $pmpro_level->cycle_period == 'Month' ) { + if ( $morder->membership_level->cycle_number == 3 && $morder->membership_level->cycle_period == 'Month' ) { $interval = 'quarterly'; } - if ( $pmpro_level->cycle_number == 6 && $pmpro_level->cycle_period == 'Month' ) { + if ( $morder->membership_level->cycle_number == 6 && $morder->membership_level->cycle_period == 'Month' ) { $interval = 'biannually'; } - $amount = $pmpro_level->billing_amount; + $amount = $morder->membership_level->billing_amount; $koboamount = $amount*100; //Create Plan $paystack_url = 'https://api.paystack.co/plan'; @@ -821,12 +821,17 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) } } + $subscription_delay = get_option( 'pmpro_subscription_delay_' . $pmpro_level->id, 0 ); - if ( ! is_numeric( $subscription_delay ) ) { - $start_date = kkd_pmprosd_convert_date( $subscription_delay ); + if ( $subscription_delay ) { + if ( ! is_numeric( $subscription_delay ) ) { + $start_date = kkd_pmprosd_convert_date( $subscription_delay ); + } else { + $start_date = date( 'Y-m-d', strtotime( '+ ' . intval( $subscription_delay ) . ' Days', current_time( 'timestamp' ) ) ); + } } else { - $start_date = date( 'Y-m-d', strtotime( '+ ' . intval( $subscription_delay ) . ' Days', current_time( 'timestamp' ) ) ); + $start_date = current_time( 'mysql' ); } $body = array( @@ -843,10 +848,12 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) $request = wp_remote_post($subscription_url, $args); if (!is_wp_error($request)) { $paystack_response = json_decode(wp_remote_retrieve_body($request)); - $subscription_code = $paystack_response->data->subscription_code; - $token = $paystack_response->data->email_token; - $morder->subscription_transaction_id = $subscription_code; - $morder->subscription_token = $token; + if ( isset( $paystack_response->data->status ) && 'success' == $paystack_response->data->status ) { + $subscription_code = $paystack_response->data->subscription_code; + $token = $paystack_response->data->email_token; + $morder->subscription_transaction_id = $subscription_code; + $morder->subscription_token = $token; + } } } From 67baf7ef0df7ecd8c1c12213bc179afd96088b16 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Thu, 15 Feb 2024 12:11:15 +0200 Subject: [PATCH 35/91] Version bump --- class.pmprogateway_paystack.php | 2 +- readme.txt | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index e7917e2..df148e7 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -3,7 +3,7 @@ * Plugin Name: Paystack Gateway for Paid Memberships Pro * Plugin URI: https://www.paidmembershipspro.com/add-ons/paystack-gateway/ * Description: Plugin to add Paystack payment gateway into Paid Memberships Pro - * Version: 1.7.1 + * Version: 1.7.2 * Author: Paid Memberships Pro, Paystack * Author URI: https://www.paidmembershipspro.com * License: GPLv2 or later diff --git a/readme.txt b/readme.txt index eb58b43..5c47304 100755 --- a/readme.txt +++ b/readme.txt @@ -2,9 +2,9 @@ Contributors: paystack, kendysond, steveamaza, lukman008, andrewza, strangerstudios, paidmembershipspro Donate link: https://paystack.com/demo Tags: paid memberships pro, pmpro, paystack, gateway, credit card, Naira, payment -Requires at least: 5.0 -Tested up to: 6.3 -Stable tag: 1.7.1 +Requires at least: 5.2 +Tested up to: 6.4 +Stable tag: 1.7.2 License: GPLv3 License URI: http://www.gnu.org/licenses/gpl-3.0.html @@ -73,6 +73,10 @@ Yes you can! Join in on our [GitHub repository](https://github.com/strangerstudi 1. The slick Paystack settings panel. == Changelog == += 1.7.2 - 2024-02-15 = +* ENHANCEMENT: Added support for Paid Memberships Pro 3.0+ subscriptions. +* BUG FIX: Fixed an issue where discount codes were not reflecting on Paid Memberships Pro side. + = 1.7.1 - 2023-09-13 = * SECURITY: Improved security to the webhook handler, this now checks for the presence of the Paystack signature header before processing the request. * ENHANCEMENT: Only allow card checkout for recurring subscriptions as other payment options don't allow subscriptions. From 30d6f2fd76b8db05535b7289d65fe89772846c7f Mon Sep 17 00:00:00 2001 From: WordPress POT/PO/MO Generator <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 15 Feb 2024 10:17:37 +0000 Subject: [PATCH 36/91] =?UTF-8?q?=F0=9F=94=84=20Regenerate=20translation?= =?UTF-8?q?=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../paystack-gateway-paid-memberships-pro.mo | Bin 558 -> 559 bytes .../paystack-gateway-paid-memberships-pro.po | 35 +++++++++++-- .../paystack-gateway-paid-memberships-pro.pot | 47 +++++++++++------- 3 files changed, 59 insertions(+), 23 deletions(-) diff --git a/languages/paystack-gateway-paid-memberships-pro.mo b/languages/paystack-gateway-paid-memberships-pro.mo index 38084cb8490d82ffbaeaa7d6211e84f409ae5fde..4ecaa37d55d76e9a51ea034c685416d4a5e86c51 100644 GIT binary patch delta 47 zcmZ3-vYusv3M1o0RWC-PjRl&F0w%f!M!JTkA%+H4hUQkr=95=42Cx_!=ow7@!)OEm D9M235 delta 46 zcmZ3_vW{hf3M0crRWC-vjRl&F0>-)qmbwNOA%+H4Mn+bKCX-h(1~6Ob8BYGoXaoQe C=nLWi diff --git a/languages/paystack-gateway-paid-memberships-pro.po b/languages/paystack-gateway-paid-memberships-pro.po index f3c6fd4..f51f22a 100644 --- a/languages/paystack-gateway-paid-memberships-pro.po +++ b/languages/paystack-gateway-paid-memberships-pro.po @@ -1,28 +1,31 @@ -# Copyright (C) 2023 Paystack, Paid Memberships Pro +# Copyright (C) 2024 Paid Memberships Pro, Paystack # This file is distributed under the GPLv2 or later. msgid "" msgstr "" -"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.1\n" +"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.2\n" "Report-Msgid-Bugs-To: info@paidmembershipspro.com\n" "Last-Translator: Paid Memberships Pro \n" "Language-Team: Paid Memberships Pro \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2023-09-08T10:22:14+00:00\n" +"POT-Creation-Date: 2024-02-15T10:17:37+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"X-Generator: WP-CLI 2.8.1\n" +"X-Generator: WP-CLI 2.10.0\n" "X-Domain: paystack-gateway-paid-memberships-pro\n" #. Plugin Name of the plugin +#: class.pmprogateway_paystack.php msgid "Paystack Gateway for Paid Memberships Pro" msgstr "" #. Plugin URI of the plugin +#: class.pmprogateway_paystack.php msgid "https://www.paidmembershipspro.com/add-ons/paystack-gateway/" msgstr "" #. Description of the plugin +#: class.pmprogateway_paystack.php msgid "Plugin to add Paystack payment gateway into Paid Memberships Pro" msgstr "" @@ -31,61 +34,85 @@ msgid "Paystack, Paid Memberships Pro" msgstr "" #. Author URI of the plugin +#: class.pmprogateway_paystack.php msgid "https://www.paidmembershipspro.com" msgstr "" #: class.pmprogateway_paystack.php:97 +#: class.pmprogateway_paystack.php:108 msgid "Settings" msgstr "" #: class.pmprogateway_paystack.php:111 +#: class.pmprogateway_paystack.php:122 msgid "Check Out with Paystack" msgstr "" #: class.pmprogateway_paystack.php:111 +#: class.pmprogateway_paystack.php:122 msgid "Submit and Confirm" msgstr "" #: class.pmprogateway_paystack.php:124 +#: class.pmprogateway_paystack.php:135 msgid "Paystack" msgstr "" #: class.pmprogateway_paystack.php:348 +#: class.pmprogateway_paystack.php:377 msgid "Test Secret Key" msgstr "" #: class.pmprogateway_paystack.php:356 +#: class.pmprogateway_paystack.php:385 msgid "Test Public Key" msgstr "" #: class.pmprogateway_paystack.php:364 +#: class.pmprogateway_paystack.php:393 msgid "Live Secret Key" msgstr "" #: class.pmprogateway_paystack.php:372 +#: class.pmprogateway_paystack.php:401 msgid "Live Public Key" msgstr "" #: class.pmprogateway_paystack.php:380 +#: class.pmprogateway_paystack.php:409 msgid "Webhook" msgstr "" #: class.pmprogateway_paystack.php:383 +#: class.pmprogateway_paystack.php:412 msgid "To fully integrate with Paystack, be sure to use the following for your Webhook URL to" msgstr "" #: class.pmprogateway_paystack.php:852 +#: class.pmprogateway_paystack.php:930 msgid "Account" msgstr "" #: class.pmprogateway_paystack.php:853 +#: class.pmprogateway_paystack.php:931 msgid "Order" msgstr "" #: class.pmprogateway_paystack.php:854 +#: class.pmprogateway_paystack.php:932 msgid "Membership Level" msgstr "" #: class.pmprogateway_paystack.php:855 +#: class.pmprogateway_paystack.php:933 msgid "Amount Paid" msgstr "" + +#. Author of the plugin +#: class.pmprogateway_paystack.php +msgid "Paid Memberships Pro, Paystack" +msgstr "" + +#: class.pmprogateway_paystack.php:1119 +msgid "There was an error communicating with Paystack. Please confirm your connectivity and API details and try again." +msgstr "" diff --git a/languages/paystack-gateway-paid-memberships-pro.pot b/languages/paystack-gateway-paid-memberships-pro.pot index f3c6fd4..09d6b83 100644 --- a/languages/paystack-gateway-paid-memberships-pro.pot +++ b/languages/paystack-gateway-paid-memberships-pro.pot @@ -1,91 +1,100 @@ -# Copyright (C) 2023 Paystack, Paid Memberships Pro +# Copyright (C) 2024 Paid Memberships Pro, Paystack # This file is distributed under the GPLv2 or later. msgid "" msgstr "" -"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.1\n" +"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.2\n" "Report-Msgid-Bugs-To: info@paidmembershipspro.com\n" "Last-Translator: Paid Memberships Pro \n" "Language-Team: Paid Memberships Pro \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2023-09-08T10:22:14+00:00\n" +"POT-Creation-Date: 2024-02-15T10:17:37+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"X-Generator: WP-CLI 2.8.1\n" +"X-Generator: WP-CLI 2.10.0\n" "X-Domain: paystack-gateway-paid-memberships-pro\n" #. Plugin Name of the plugin +#: class.pmprogateway_paystack.php msgid "Paystack Gateway for Paid Memberships Pro" msgstr "" #. Plugin URI of the plugin +#: class.pmprogateway_paystack.php msgid "https://www.paidmembershipspro.com/add-ons/paystack-gateway/" msgstr "" #. Description of the plugin +#: class.pmprogateway_paystack.php msgid "Plugin to add Paystack payment gateway into Paid Memberships Pro" msgstr "" #. Author of the plugin -msgid "Paystack, Paid Memberships Pro" +#: class.pmprogateway_paystack.php +msgid "Paid Memberships Pro, Paystack" msgstr "" #. Author URI of the plugin +#: class.pmprogateway_paystack.php msgid "https://www.paidmembershipspro.com" msgstr "" -#: class.pmprogateway_paystack.php:97 +#: class.pmprogateway_paystack.php:108 msgid "Settings" msgstr "" -#: class.pmprogateway_paystack.php:111 +#: class.pmprogateway_paystack.php:122 msgid "Check Out with Paystack" msgstr "" -#: class.pmprogateway_paystack.php:111 +#: class.pmprogateway_paystack.php:122 msgid "Submit and Confirm" msgstr "" -#: class.pmprogateway_paystack.php:124 +#: class.pmprogateway_paystack.php:135 msgid "Paystack" msgstr "" -#: class.pmprogateway_paystack.php:348 +#: class.pmprogateway_paystack.php:377 msgid "Test Secret Key" msgstr "" -#: class.pmprogateway_paystack.php:356 +#: class.pmprogateway_paystack.php:385 msgid "Test Public Key" msgstr "" -#: class.pmprogateway_paystack.php:364 +#: class.pmprogateway_paystack.php:393 msgid "Live Secret Key" msgstr "" -#: class.pmprogateway_paystack.php:372 +#: class.pmprogateway_paystack.php:401 msgid "Live Public Key" msgstr "" -#: class.pmprogateway_paystack.php:380 +#: class.pmprogateway_paystack.php:409 msgid "Webhook" msgstr "" -#: class.pmprogateway_paystack.php:383 +#: class.pmprogateway_paystack.php:412 msgid "To fully integrate with Paystack, be sure to use the following for your Webhook URL to" msgstr "" -#: class.pmprogateway_paystack.php:852 +#: class.pmprogateway_paystack.php:930 msgid "Account" msgstr "" -#: class.pmprogateway_paystack.php:853 +#: class.pmprogateway_paystack.php:931 msgid "Order" msgstr "" -#: class.pmprogateway_paystack.php:854 +#: class.pmprogateway_paystack.php:932 msgid "Membership Level" msgstr "" -#: class.pmprogateway_paystack.php:855 +#: class.pmprogateway_paystack.php:933 msgid "Amount Paid" msgstr "" + +#: class.pmprogateway_paystack.php:1119 +msgid "There was an error communicating with Paystack. Please confirm your connectivity and API details and try again." +msgstr "" From 3c0aefc0871e4138521e4d11798370cff7ea9021 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Tue, 9 Apr 2024 16:42:16 +0200 Subject: [PATCH 37/91] Change WordPress tested up to 6.5 --- readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.txt b/readme.txt index 5c47304..eddd13b 100755 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ Contributors: paystack, kendysond, steveamaza, lukman008, andrewza, strangerstud Donate link: https://paystack.com/demo Tags: paid memberships pro, pmpro, paystack, gateway, credit card, Naira, payment Requires at least: 5.2 -Tested up to: 6.4 +Tested up to: 6.5 Stable tag: 1.7.2 License: GPLv3 License URI: http://www.gnu.org/licenses/gpl-3.0.html From 07003699f5b734270be919324d48673864c3c1a9 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Wed, 10 Apr 2024 11:42:10 +0200 Subject: [PATCH 38/91] Remove extra tags for .org --- readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.txt b/readme.txt index eddd13b..545c6e9 100755 --- a/readme.txt +++ b/readme.txt @@ -1,7 +1,7 @@ === Paystack Gateway for Paid Membership Pro === Contributors: paystack, kendysond, steveamaza, lukman008, andrewza, strangerstudios, paidmembershipspro Donate link: https://paystack.com/demo -Tags: paid memberships pro, pmpro, paystack, gateway, credit card, Naira, payment +Tags: paid memberships pro, paystack, gateway, credit card, Naira Requires at least: 5.2 Tested up to: 6.5 Stable tag: 1.7.2 From 13b454de1f3dfa3272d8d3bdb612e5252b50db21 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 12 Apr 2024 11:48:47 +0200 Subject: [PATCH 39/91] Added refund functionality to PMPro dashboard --- class.pmprogateway_paystack.php | 98 +++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index df148e7..7300540 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -81,6 +81,12 @@ static function init() // custom confirmation page add_filter('pmpro_pages_shortcode_confirmation', array('PMProGateway_Paystack', 'pmpro_pages_shortcode_confirmation'), 20, 1); + + // Refund functionality. + add_filter( 'pmpro_allowed_refunds_gateways', array( 'PMProGateway_Paystack', 'pmpro_allowed_refunds_gateways' ) ); + add_filter( 'pmpro_process_refund_paystack', array( 'PMProGateway_Paystack', 'process_refund' ), 10, 2 ); + + } } @@ -136,6 +142,17 @@ static function pmpro_gateways($gateways) } return $gateways; } + + /** + * Enable refund functionality for paystack. + * @since TBD. + */ + static function pmpro_allowed_refunds_gateways( $gateways ) { + $gateways[] = 'paystack'; + return $gateways; + } + + function kkd_pmprosd_convert_date( $date ) { // handle lower-cased y/m values. $set_date = strtoupper($date); @@ -1120,6 +1137,87 @@ public function update_subscription_info( $subscription ) { } } + // Process the refund /// CHANGE THIS. + public static function process_refund( $success, $order ) { + global $current_user; + + //default to using the payment id from the order + if ( !empty( $order->payment_transaction_id ) ) { + $transaction_id = $order->payment_transaction_id; + } + + //need a transaction id + if ( empty( $transaction_id ) ) { + return false; + } + + // OKAY do the refund now. + // Make the API call to PayStack to refund the order. + $mode = pmpro_getOption("gateway_environment"); + if ( $mode == "sandbox" ) { + $key = pmpro_getOption("paystack_tsk"); + + } else { + $key = pmpro_getOption("paystack_lsk"); + } + + $paystack_url = 'https://api.paystack.co/refund/'; + + $headers = array( + 'Authorization' => 'Bearer ' . $key, + 'Cache-Control' => 'no-cache' + ); + + // The transaction ID for the refund. + $fields = array( + 'transaction' => $transaction_id + ); + + $args = array( + 'headers' => $headers, + 'timeout' => 60, + 'body' => $fields + ); + + + $success = false; + + // Try to make the API call now. + $request = wp_remote_post( $paystack_url, $args ); + + if ( ! is_wp_error( $request ) ) { + + $response = json_decode( wp_remote_retrieve_body( $request ) ); + + // If not successful throw an error. + if ( ! $response->status ) { + $order->notes = trim( $order->notes.' '.sprintf( __('Admin: Order refund failed on %1$s for transaction ID %2$s by %3$s. Order may have already been refunded.', 'paid-memberships-pro' ), date_i18n('Y-m-d H:i:s'), $transaction_id, $current_user->display_name ) ); + $order->saveOrder(); + } else { + // Set the order status to refunded and save it and return true + $order->status = 'refunded'; + + $success = true; + + $order->notes = trim( $order->notes.' '.sprintf( __('Admin: Order successfully refunded on %1$s for transaction ID %2$s by %3$s.', 'paid-memberships-pro' ), date_i18n('Y-m-d H:i:s'), $transaction_id, $current_user->display_name ) ); + + $user = get_user_by( 'id', $order->user_id ); + //send an email to the member + $myemail = new PMProEmail(); + $myemail->sendRefundedEmail( $user, $order ); + + //send an email to the admin + $myemail = new PMProEmail(); + $myemail->sendRefundedAdminEmail( $user, $order ); + + $order->saveOrder(); + } + } + + return $success; + + } + /** * Undocumented function * From 34b3e1ff3c05ecfc5aea5e486a652a24ee4c443b Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 12 Apr 2024 16:34:25 +0200 Subject: [PATCH 40/91] Fix small API status change to save orders. --- class.pmprogateway_paystack.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 7300540..f2b3b83 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -865,11 +865,12 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) $request = wp_remote_post($subscription_url, $args); if (!is_wp_error($request)) { $paystack_response = json_decode(wp_remote_retrieve_body($request)); - if ( isset( $paystack_response->data->status ) && 'success' == $paystack_response->data->status ) { + if ( isset( $paystack_response->data->status ) && 'active' == $paystack_response->data->status ) { $subscription_code = $paystack_response->data->subscription_code; $token = $paystack_response->data->email_token; $morder->subscription_transaction_id = $subscription_code; $morder->subscription_token = $token; + $morder->saveOrder(); } } } From e5191168bc8b8fa5d2d92d52088b0e19b8b81036 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 12 Apr 2024 16:37:57 +0200 Subject: [PATCH 41/91] update doc blocks --- class.pmprogateway_paystack.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index f2b3b83..094e673 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -1070,7 +1070,10 @@ function cancel(&$order, $update_status = true ) return true; } - /// Used for updating subscription stuff. + /** + * Allow "Sync" with gateway for subscriptions. + * @since 1.7.2 + */ public function update_subscription_info( $subscription ) { $subscription_id = $subscription->get_subscription_transaction_id(); $backtrace = self::get_caller_info(); @@ -1138,7 +1141,10 @@ public function update_subscription_info( $subscription ) { } } - // Process the refund /// CHANGE THIS. + /** + * Allow refunds from within Paid Memberships Pro and Paystack. + * @since TBD + */ public static function process_refund( $success, $order ) { global $current_user; From 3f1dd57e43e893d93c800200b158ad273d7b2d09 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Mon, 15 Apr 2024 13:29:59 +0200 Subject: [PATCH 42/91] Version bump to 1.7.3 --- class.pmprogateway_paystack.php | 16 ++++++++-------- readme.txt | 9 +++++++-- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 094e673..7c95104 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -3,7 +3,7 @@ * Plugin Name: Paystack Gateway for Paid Memberships Pro * Plugin URI: https://www.paidmembershipspro.com/add-ons/paystack-gateway/ * Description: Plugin to add Paystack payment gateway into Paid Memberships Pro - * Version: 1.7.2 + * Version: 1.7.3 * Author: Paid Memberships Pro, Paystack * Author URI: https://www.paidmembershipspro.com * License: GPLv2 or later @@ -125,7 +125,7 @@ static function pmpro_checkout_default_submit_button($show) ?> - + style="display: none;"> - + @@ -399,7 +399,7 @@ static function pmpro_payment_option_fields($values, $gateway) style="display: none;"> - + @@ -407,7 +407,7 @@ static function pmpro_payment_option_fields($values, $gateway) style="display: none;"> - + @@ -415,7 +415,7 @@ static function pmpro_payment_option_fields($values, $gateway) style="display: none;"> - + @@ -423,10 +423,10 @@ static function pmpro_payment_option_fields($values, $gateway) style="display: none;"> - + -


+


diff --git a/readme.txt b/readme.txt index 545c6e9..fe4e5b5 100755 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Donate link: https://paystack.com/demo Tags: paid memberships pro, paystack, gateway, credit card, Naira Requires at least: 5.2 Tested up to: 6.5 -Stable tag: 1.7.2 +Stable tag: 1.7.3 License: GPLv3 License URI: http://www.gnu.org/licenses/gpl-3.0.html @@ -66,13 +66,18 @@ If you get stuck, you can ask for help in the [Paystack Gateway for Paid Members = Paystack Gateway for Paid Membership Pro is awesome! Can I contribute? = -Yes you can! Join in on our [GitHub repository](https://github.com/strangerstudios/paystack-gateway-paid-memberships-pro) :) +Yes you can! Join in on our [GitHub repository](https://github.com/strangerstudios/paystack-gateway-paid-memberships-pro) == Screenshots == 1. The slick Paystack settings panel. == Changelog == += 1.7.3 - 2024-04-15 = +* SECURITY: Improved sanitization to output of translatable strings. +* ENHANCEMENT: Added functionality for refunds within Paid Memberships Pro. Supports full refunds only. +* BUG FIX: Fixed an issue where subscriptions weren't being linked inside Paid Memberships Pro correctly when confirming the membership. + = 1.7.2 - 2024-02-15 = * ENHANCEMENT: Added support for Paid Memberships Pro 3.0+ subscriptions. * BUG FIX: Fixed an issue where discount codes were not reflecting on Paid Memberships Pro side. From 2ed3451317584263b11fbfe391160d3d934376cc Mon Sep 17 00:00:00 2001 From: WordPress POT/PO/MO Generator <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 11:30:44 +0000 Subject: [PATCH 43/91] =?UTF-8?q?=F0=9F=94=84=20Regenerate=20translation?= =?UTF-8?q?=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../paystack-gateway-paid-memberships-pro.mo | Bin 559 -> 559 bytes .../paystack-gateway-paid-memberships-pro.po | 19 ++++++++-- .../paystack-gateway-paid-memberships-pro.pot | 34 +++++++++--------- 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/languages/paystack-gateway-paid-memberships-pro.mo b/languages/paystack-gateway-paid-memberships-pro.mo index 4ecaa37d55d76e9a51ea034c685416d4a5e86c51..a191690c1a6a7f4db34f7625a9d5789e67c4d389 100644 GIT binary patch delta 30 lcmZ3_vYur^KBMu*0v$$P6J0~o5JN*NV*@J_lgTR?+W>}t2y_4d delta 30 lcmZ3_vYur^KBLjb0v$$PBV9w&5JLkiLvt%*^T{h2+W>}Y2zLMg diff --git a/languages/paystack-gateway-paid-memberships-pro.po b/languages/paystack-gateway-paid-memberships-pro.po index f51f22a..729409f 100644 --- a/languages/paystack-gateway-paid-memberships-pro.po +++ b/languages/paystack-gateway-paid-memberships-pro.po @@ -2,14 +2,14 @@ # This file is distributed under the GPLv2 or later. msgid "" msgstr "" -"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.2\n" +"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.3\n" "Report-Msgid-Bugs-To: info@paidmembershipspro.com\n" "Last-Translator: Paid Memberships Pro \n" "Language-Team: Paid Memberships Pro \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2024-02-15T10:17:37+00:00\n" +"POT-Creation-Date: 2024-04-15T11:30:44+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "X-Generator: WP-CLI 2.10.0\n" "X-Domain: paystack-gateway-paid-memberships-pro\n" @@ -40,71 +40,85 @@ msgstr "" #: class.pmprogateway_paystack.php:97 #: class.pmprogateway_paystack.php:108 +#: class.pmprogateway_paystack.php:114 msgid "Settings" msgstr "" #: class.pmprogateway_paystack.php:111 #: class.pmprogateway_paystack.php:122 +#: class.pmprogateway_paystack.php:128 msgid "Check Out with Paystack" msgstr "" #: class.pmprogateway_paystack.php:111 #: class.pmprogateway_paystack.php:122 +#: class.pmprogateway_paystack.php:128 msgid "Submit and Confirm" msgstr "" #: class.pmprogateway_paystack.php:124 #: class.pmprogateway_paystack.php:135 +#: class.pmprogateway_paystack.php:141 msgid "Paystack" msgstr "" #: class.pmprogateway_paystack.php:348 #: class.pmprogateway_paystack.php:377 +#: class.pmprogateway_paystack.php:394 msgid "Test Secret Key" msgstr "" #: class.pmprogateway_paystack.php:356 #: class.pmprogateway_paystack.php:385 +#: class.pmprogateway_paystack.php:402 msgid "Test Public Key" msgstr "" #: class.pmprogateway_paystack.php:364 #: class.pmprogateway_paystack.php:393 +#: class.pmprogateway_paystack.php:410 msgid "Live Secret Key" msgstr "" #: class.pmprogateway_paystack.php:372 #: class.pmprogateway_paystack.php:401 +#: class.pmprogateway_paystack.php:418 msgid "Live Public Key" msgstr "" #: class.pmprogateway_paystack.php:380 #: class.pmprogateway_paystack.php:409 +#: class.pmprogateway_paystack.php:426 msgid "Webhook" msgstr "" #: class.pmprogateway_paystack.php:383 #: class.pmprogateway_paystack.php:412 +#: class.pmprogateway_paystack.php:429 msgid "To fully integrate with Paystack, be sure to use the following for your Webhook URL to" msgstr "" #: class.pmprogateway_paystack.php:852 #: class.pmprogateway_paystack.php:930 +#: class.pmprogateway_paystack.php:948 msgid "Account" msgstr "" #: class.pmprogateway_paystack.php:853 #: class.pmprogateway_paystack.php:931 +#: class.pmprogateway_paystack.php:949 msgid "Order" msgstr "" #: class.pmprogateway_paystack.php:854 #: class.pmprogateway_paystack.php:932 +#: class.pmprogateway_paystack.php:950 msgid "Membership Level" msgstr "" #: class.pmprogateway_paystack.php:855 #: class.pmprogateway_paystack.php:933 +#: class.pmprogateway_paystack.php:951 msgid "Amount Paid" msgstr "" @@ -114,5 +128,6 @@ msgid "Paid Memberships Pro, Paystack" msgstr "" #: class.pmprogateway_paystack.php:1119 +#: class.pmprogateway_paystack.php:1140 msgid "There was an error communicating with Paystack. Please confirm your connectivity and API details and try again." msgstr "" diff --git a/languages/paystack-gateway-paid-memberships-pro.pot b/languages/paystack-gateway-paid-memberships-pro.pot index 09d6b83..04954d4 100644 --- a/languages/paystack-gateway-paid-memberships-pro.pot +++ b/languages/paystack-gateway-paid-memberships-pro.pot @@ -2,14 +2,14 @@ # This file is distributed under the GPLv2 or later. msgid "" msgstr "" -"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.2\n" +"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.3\n" "Report-Msgid-Bugs-To: info@paidmembershipspro.com\n" "Last-Translator: Paid Memberships Pro \n" "Language-Team: Paid Memberships Pro \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2024-02-15T10:17:37+00:00\n" +"POT-Creation-Date: 2024-04-15T11:30:44+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "X-Generator: WP-CLI 2.10.0\n" "X-Domain: paystack-gateway-paid-memberships-pro\n" @@ -39,62 +39,62 @@ msgstr "" msgid "https://www.paidmembershipspro.com" msgstr "" -#: class.pmprogateway_paystack.php:108 +#: class.pmprogateway_paystack.php:114 msgid "Settings" msgstr "" -#: class.pmprogateway_paystack.php:122 +#: class.pmprogateway_paystack.php:128 msgid "Check Out with Paystack" msgstr "" -#: class.pmprogateway_paystack.php:122 +#: class.pmprogateway_paystack.php:128 msgid "Submit and Confirm" msgstr "" -#: class.pmprogateway_paystack.php:135 +#: class.pmprogateway_paystack.php:141 msgid "Paystack" msgstr "" -#: class.pmprogateway_paystack.php:377 +#: class.pmprogateway_paystack.php:394 msgid "Test Secret Key" msgstr "" -#: class.pmprogateway_paystack.php:385 +#: class.pmprogateway_paystack.php:402 msgid "Test Public Key" msgstr "" -#: class.pmprogateway_paystack.php:393 +#: class.pmprogateway_paystack.php:410 msgid "Live Secret Key" msgstr "" -#: class.pmprogateway_paystack.php:401 +#: class.pmprogateway_paystack.php:418 msgid "Live Public Key" msgstr "" -#: class.pmprogateway_paystack.php:409 +#: class.pmprogateway_paystack.php:426 msgid "Webhook" msgstr "" -#: class.pmprogateway_paystack.php:412 +#: class.pmprogateway_paystack.php:429 msgid "To fully integrate with Paystack, be sure to use the following for your Webhook URL to" msgstr "" -#: class.pmprogateway_paystack.php:930 +#: class.pmprogateway_paystack.php:948 msgid "Account" msgstr "" -#: class.pmprogateway_paystack.php:931 +#: class.pmprogateway_paystack.php:949 msgid "Order" msgstr "" -#: class.pmprogateway_paystack.php:932 +#: class.pmprogateway_paystack.php:950 msgid "Membership Level" msgstr "" -#: class.pmprogateway_paystack.php:933 +#: class.pmprogateway_paystack.php:951 msgid "Amount Paid" msgstr "" -#: class.pmprogateway_paystack.php:1119 +#: class.pmprogateway_paystack.php:1140 msgid "There was an error communicating with Paystack. Please confirm your connectivity and API details and try again." msgstr "" From a42761a9f03d7b2fe7ad0fe4a6a520cf6313305c Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 19 Apr 2024 09:47:17 +0200 Subject: [PATCH 44/91] Added filter for tweaking the level. * Added the filter `pmpro_paystack_webhook_level` for filtering things before the user gets given the level and runs after the payment is processed. --- class.pmprogateway_paystack.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 7c95104..5b32c4c 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -750,6 +750,9 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) $pmpro_level = apply_filters("pmpro_checkout_level", $pmpro_level); $startdate = apply_filters("pmpro_checkout_start_date", "'" . current_time("mysql") . "'", $morder->user_id, $pmpro_level); + // The level object from the order that can be filtered when returning from Paystack and user is getting their membership level. + $morder->membership_level = apply_filters( 'pmpro_paystack_webhook_level', $morder->membership_level, $morder->user_id ); + $mode = pmpro_getOption("gateway_environment"); if ($mode == "sandbox") { $key = pmpro_getOption("paystack_tsk"); From 6053e84da4866dd8c314a09e27dfe463d3f64b57 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Wed, 24 Apr 2024 14:26:26 +0200 Subject: [PATCH 45/91] V1.7.4 readme update and changelog. Update version and readme. --- class.pmprogateway_paystack.php | 2 +- readme.txt | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 5b32c4c..92c45c3 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -3,7 +3,7 @@ * Plugin Name: Paystack Gateway for Paid Memberships Pro * Plugin URI: https://www.paidmembershipspro.com/add-ons/paystack-gateway/ * Description: Plugin to add Paystack payment gateway into Paid Memberships Pro - * Version: 1.7.3 + * Version: 1.7.4 * Author: Paid Memberships Pro, Paystack * Author URI: https://www.paidmembershipspro.com * License: GPLv2 or later diff --git a/readme.txt b/readme.txt index fe4e5b5..60fdb6e 100755 --- a/readme.txt +++ b/readme.txt @@ -73,6 +73,9 @@ Yes you can! Join in on our [GitHub repository](https://github.com/strangerstudi 1. The slick Paystack settings panel. == Changelog == += 1.7.4 - 2024-04-24 = +* ENHANCEMENT: Added new filter `pmpro_paystack_webhook_level` to tweak the level given to members after checkout. This includes support for the Set Expiration Dates Add On. + = 1.7.3 - 2024-04-15 = * SECURITY: Improved sanitization to output of translatable strings. * ENHANCEMENT: Added functionality for refunds within Paid Memberships Pro. Supports full refunds only. From 8462653ea72ea05ac640ba1a4d0c524059ff688b Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 3 May 2024 13:15:23 +0200 Subject: [PATCH 46/91] Support custom fields * ENHANCEMENT: Supported saving custom fields and clearing things up. --- class.pmprogateway_paystack.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 92c45c3..273916e 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -70,6 +70,9 @@ static function init() //code to add at checkout $gateway = pmpro_getGateway(); if ($gateway == "paystack") { + // Add support for custom fields. + add_action( 'pmpro_before_send_to_paystack', 'pmpro_after_checkout_save_fields', 20, 2 ); + add_filter('pmpro_include_billing_address_fields', '__return_false'); add_filter('pmpro_required_billing_fields', array('PMProGateway_Paystack', 'pmpro_required_billing_fields')); add_filter('pmpro_include_payment_information_fields', '__return_false'); From 19c62780e7011ccf73ccc53b3b7b0c9389337538 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Mon, 24 Jun 2024 14:18:27 +0200 Subject: [PATCH 47/91] Remove check for amount. * REFACTOR: Removed logic to check amount passed back as this is redundant and can differ from country to country. Solves an issue where some Paystack accounts charge extra amounts for additional fees etc. --- class.pmprogateway_paystack.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 273916e..3d3a2d2 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -775,8 +775,8 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) $request = wp_remote_get($paystack_url, $args); if (!is_wp_error($request) && 200 == wp_remote_retrieve_response_code($request) ) { $paystack_response = json_decode(wp_remote_retrieve_body($request)); - // if ('success' == $paystack_response->data->status && $pmpro_level->initial_payment == ($paystack_response->data->amount/100)) { - if ('success' == $paystack_response->data->status && $morder->total == ($paystack_response->data->amount / 100)) { + + if ( 'success' == $paystack_response->data->status ) { $customer_code = $paystack_response->data->customer->customer_code; //Add logger here From d4753883a7aacbeab87f3e76f0ae6ad2eb54b5ed Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Tue, 25 Jun 2024 14:00:18 +0200 Subject: [PATCH 48/91] Functionality for IPN Logging * ENHANCEMENT: Use 'PMPRO_PAYSTACK_IPN_DEBUG' constant to log IPN requests to files. Be sure to delete the .txt file once you are done debugging and turn this off in production. --- class.pmprogateway_paystack.php | 110 ++++++++++++++++++++++++++++---- 1 file changed, 98 insertions(+), 12 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 3d3a2d2..e169b8a 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -10,9 +10,10 @@ * Text Domain: paystack-gateway-paid-memberships-pro * Domain Path: /languages */ - include_once plugin_dir_path(__FILE__) . 'class-paystack-plugin-tracker.php'; + defined('ABSPATH') or die('No script kiddies please!'); + if (!function_exists('Paystack_Pmp_Gateway_load')) { add_action('plugins_loaded', 'Paystack_Pmp_Gateway_load', 20); @@ -268,9 +269,13 @@ static function pmpro_paystack_ipn() { // Let's make sure the request came from Paystack by checking the secret key if ( ( strtoupper( $_SERVER['REQUEST_METHOD'] ) != 'POST' ) || ! array_key_exists( 'HTTP_X_PAYSTACK_SIGNATURE', $_SERVER ) ) { - exit; + pmpro_paystack_ipn_log( 'Paystack signature not found' ); + pmpro_paystack_ipn_exit(); } + // Log all the $_POST data to the IPN log. + pmpro_paystack_ipn_log( print_r( $_POST, true ) ); + // Get the relevant secret key based on gateway environment. $mode = pmpro_getOption("gateway_environment"); if ($mode == 'sandbox') { @@ -284,10 +289,12 @@ static function pmpro_paystack_ipn() { // The Paystack signature doesn't match the secret key, let's bail. if ( $_SERVER['HTTP_X_PAYSTACK_SIGNATURE'] !== hash_hmac('sha512', $input, $secret_key ) ) { - exit; + pmpro_paystack_ipn_log( 'Paystack signature does not match.' ); + pmpro_paystack_ipn_exit(); } - $event = json_decode($input); + $event = json_decode( $input ); + pmpro_paystack_ipn_log( 'Event: ' . print_r( $event, true ) ); switch( $event->event ){ case 'subscription.create': @@ -305,9 +312,10 @@ static function pmpro_paystack_ipn() { $user = get_userdata($user_id); $user->membership_level = pmpro_getMembershipLevelForUser($user_id); } + if ( empty( $user ) ) { - print_r('Could not get user'); - exit(); + pmpro_paystack_ipn_log( 'Could not get user' ); + pmpro_paystack_ipn_exit(); } self::cancelMembership($user); break; @@ -324,6 +332,7 @@ static function pmpro_paystack_ipn() { } $pstk_logger = new pmpro_paystack_plugin_tracker('pm-pro',$pk); $pstk_logger->log_transaction_success($event->data->reference); + pmpro_paystack_ipn_log( 'Charge success. Reference: ' . $event->data->reference ); break; case 'invoice.create': self::renewpayment($event); @@ -331,7 +340,7 @@ static function pmpro_paystack_ipn() { self::renewpayment($event); } http_response_code(200); - exit(); + pmpro_paystack_ipn_exit(); } /** @@ -638,14 +647,16 @@ static function renewpayment($event) $old_order->getLastMemberOrderBySubscriptionTransactionID($subscription_code); if (empty($old_order)) { - exit(); + pmpro_paystack_ipn_log( 'Could not find last order for subscription code: ' . $subscription_code ); + pmpro_paystack_ipn_exit(); } $user_id = $old_order->user_id; $user = get_userdata($user_id); $user->membership_level = pmpro_getMembershipLevelForUser($user_id); if (empty($user)) { - exit(); + pmpro_paystack_ipn_log( 'Could not get user for renewal payment' ); + pmpro_paystack_ipn_exit(); } $morder = new MemberOrder(); @@ -703,6 +714,13 @@ static function renewpayment($event) $morder->ExpirationDate_YdashM = $morder->expirationyear . "-" . $morder->expirationmonth; + // Save entire order data to IPN Log - loop through the order object. + $order_data = array(); + foreach ( $morder as $key => $value ) { + $order_data[ $key ] = $value; + } + pmpro_paystack_ipn_log( 'Order data: ' . print_r( $order_data, true ) ); + //save if ($morder->status != 'success') { @@ -721,7 +739,8 @@ static function renewpayment($event) $pmproemail->sendInvoiceEmail($user, $morder); do_action('pmpro_subscription_payment_completed', $morder); - exit(); + pmpro_paystack_ipn_log( sprintf( 'Subscription payment completed for user with ID: %d. Order ID: %s' ), $user_id, $morder->code ); + pmpro_paystack_ipn_exit(); } } @@ -985,8 +1004,7 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) } - function cancelMembership(&$user){ -// + function cancelMembership(&$user){ if (empty($user)) { print_r("Empty user object"); exit(); @@ -1333,4 +1351,72 @@ function get_caller_info() { } } } +} + +/** + * Create the log string for debugging purposes. + * + * @param string $s The error/information you want to log to the IPN log. + * @return string $logstr A formatted message for the logfile. + * + * @since TBD + */ +function pmpro_paystack_ipn_log( $s ) { + global $logstr; + $logstr .= "\t" . $s . "\n"; +} + +/** + * Write to the log file and exit. + * + * @since TBD + */ +function pmpro_paystack_ipn_exit() { + global $logstr; + + //for log + if ( $logstr ) { + $logstr = "Logged On: " . date_i18n( "m/d/Y H:i:s" ) . "\n" . $logstr . "\n-------------\n"; + + echo esc_html( $logstr ); + + //log or dont log? log in file or email? + //- dont log if constant is undefined or defined but false + //- log to file if constant is set to TRUE or 'log' + //- log to file if constant is defined to a valid email address + if ( defined( 'PMPRO_PAYSTACK_IPN_DEBUG' ) ) { + if( PMPRO_PAYSTACK_IPN_DEBUG === false ){ + //dont log here. false mean no. + //should avoid counterintuitive interpretation of false. + } elseif ( PMPRO_PAYSTACK_IPN_DEBUG === "log" ) { + //file + $logfile = apply_filters( 'pmpro_paystack_ipn_logfile', dirname( __FILE__ ) . "/logs/ipn.txt" ); + + // Check if the dir exists, if not let's create it. + $logdir = dirname( $logfile ); + if ( ! file_exists( $logdir ) ) { + mkdir( $logdir, 0775 ); + } + + // If the log file doesn't exist let's create it. + if ( ! file_exists( $logfile ) ) { + // Create a blank logfile + file_put_contents( $logfile, "" ); + } + + $loghandle = fopen( $logfile, "a+" ); + fwrite( $loghandle, $logstr ); + fclose( $loghandle ); + } elseif ( is_email( PMPRO_PAYSTACK_IPN_DEBUG ) ) { + //email to specified address + wp_mail( PMPRO_PAYSTACK_IPN_DEBUG, get_option( "blogname" ) . " IPN Log", nl2br( esc_html( $logstr ) ) ); + } else { + //email to admin + wp_mail( get_option( "admin_email" ), get_option( "blogname" ) . " IPN Log", nl2br( esc_html( $logstr ) ) ); + } + } + } + + exit; + } \ No newline at end of file From c2dbc1e2084acffec2e9d3c9f346a6074d9ef482 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Mon, 1 Jul 2024 14:29:36 +0200 Subject: [PATCH 49/91] Fixed some bugs with renewal payments --- class.pmprogateway_paystack.php | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index e169b8a..b4bbbec 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -336,8 +336,10 @@ static function pmpro_paystack_ipn() { break; case 'invoice.create': self::renewpayment($event); + break; case 'invoice.update': self::renewpayment($event); + break; } http_response_code(200); pmpro_paystack_ipn_exit(); @@ -565,20 +567,9 @@ function sendToPaystack(&$order) if ($key == '') { echo "Api keys not set"; } - // $txn_code = $txn.'_'.$order_id; $koboamount = $amount*100; - // $mcurrency = - - // foreach($level_currencies as $level_currency_id => $level_currency) - // { - // if($level_id == $level_currency_id) - // { - // $pmpro_currency = $level_currency[0]; - // $pmpro_currency_symbol = $level_currency[1]; - // } - // } $paystack_url = 'https://api.paystack.co/transaction/initialize'; $headers = array( @@ -646,6 +637,7 @@ static function renewpayment($event) $email = $event->data->customer->email; $old_order->getLastMemberOrderBySubscriptionTransactionID($subscription_code); + if (empty($old_order)) { pmpro_paystack_ipn_log( 'Could not find last order for subscription code: ' . $subscription_code ); pmpro_paystack_ipn_exit(); @@ -660,6 +652,12 @@ static function renewpayment($event) } $morder = new MemberOrder(); + + // Set the orders date to time it was paid. + if ( ! empty( $event->data->paid_at ) ) { + $morder->timestamp = strtotime( sanitize_text_field( $event->data->paid_at ) ); + } + $morder->user_id = $old_order->user_id; $morder->membership_id = $old_order->membership_id; $morder->InitialPayment = $amount; //not the initial payment, but the order class is expecting this @@ -685,6 +683,7 @@ static function renewpayment($event) $discount_code_id = ''; } + //fix expiration date if ( ! empty( $morder->membership_level->expiration_number ) ) { $enddate = "'" . date_i18n( "Y-m-d", strtotime( "+ " . $morder->membership_level->expiration_number . " " . $morder->membership_level->expiration_period, current_time( "timestamp" ) ) ) . "'"; @@ -721,25 +720,28 @@ static function renewpayment($event) } pmpro_paystack_ipn_log( 'Order data: ' . print_r( $order_data, true ) ); + //save if ($morder->status != 'success') { $_REQUEST['cancel_membership'] = false; // Do NOT cancel gateway subscription - if (pmpro_changeMembershipLevel($custom_level, $morder->user_id, 'changed')) { + if ( pmpro_changeMembershipLevel( $custom_level, $morder->user_id, 'changed' ) !== false ) { $morder->status = "success"; - $morder->saveOrder(); } } - $morder->getMemberOrderByID($morder->id); + + // Save the order before emailing the customer about it. + $morder->saveOrder(); + $morder->getMemberOrderByID( $morder->id ); //email the user their invoice $pmproemail = new PMProEmail(); $pmproemail->sendInvoiceEmail($user, $morder); do_action('pmpro_subscription_payment_completed', $morder); - pmpro_paystack_ipn_log( sprintf( 'Subscription payment completed for user with ID: %d. Order ID: %s' ), $user_id, $morder->code ); + pmpro_paystack_ipn_log( sprintf( 'Subscription payment completed for user with ID: %d. Order ID: %s', $user_id, $morder->code ) ); pmpro_paystack_ipn_exit(); } From 55073832959610f1a93d9dbdf301bab01b2d4797 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Mon, 1 Jul 2024 14:33:52 +0200 Subject: [PATCH 50/91] Rename webhook log and debug file prefixes. --- class.pmprogateway_paystack.php | 50 ++++++++++++++++----------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index b4bbbec..64ed294 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -269,12 +269,12 @@ static function pmpro_paystack_ipn() { // Let's make sure the request came from Paystack by checking the secret key if ( ( strtoupper( $_SERVER['REQUEST_METHOD'] ) != 'POST' ) || ! array_key_exists( 'HTTP_X_PAYSTACK_SIGNATURE', $_SERVER ) ) { - pmpro_paystack_ipn_log( 'Paystack signature not found' ); - pmpro_paystack_ipn_exit(); + pmpro_paystack_webhook_log( 'Paystack signature not found' ); + pmpro_paystack_webhook_exit(); } // Log all the $_POST data to the IPN log. - pmpro_paystack_ipn_log( print_r( $_POST, true ) ); + pmpro_paystack_webhook_log( print_r( $_POST, true ) ); // Get the relevant secret key based on gateway environment. $mode = pmpro_getOption("gateway_environment"); @@ -289,12 +289,12 @@ static function pmpro_paystack_ipn() { // The Paystack signature doesn't match the secret key, let's bail. if ( $_SERVER['HTTP_X_PAYSTACK_SIGNATURE'] !== hash_hmac('sha512', $input, $secret_key ) ) { - pmpro_paystack_ipn_log( 'Paystack signature does not match.' ); - pmpro_paystack_ipn_exit(); + pmpro_paystack_webhook_log( 'Paystack signature does not match.' ); + pmpro_paystack_webhook_exit(); } $event = json_decode( $input ); - pmpro_paystack_ipn_log( 'Event: ' . print_r( $event, true ) ); + pmpro_paystack_webhook_log( 'Event: ' . print_r( $event, true ) ); switch( $event->event ){ case 'subscription.create': @@ -314,8 +314,8 @@ static function pmpro_paystack_ipn() { } if ( empty( $user ) ) { - pmpro_paystack_ipn_log( 'Could not get user' ); - pmpro_paystack_ipn_exit(); + pmpro_paystack_webhook_log( 'Could not get user' ); + pmpro_paystack_webhook_exit(); } self::cancelMembership($user); break; @@ -332,7 +332,7 @@ static function pmpro_paystack_ipn() { } $pstk_logger = new pmpro_paystack_plugin_tracker('pm-pro',$pk); $pstk_logger->log_transaction_success($event->data->reference); - pmpro_paystack_ipn_log( 'Charge success. Reference: ' . $event->data->reference ); + pmpro_paystack_webhook_log( 'Charge success. Reference: ' . $event->data->reference ); break; case 'invoice.create': self::renewpayment($event); @@ -342,7 +342,7 @@ static function pmpro_paystack_ipn() { break; } http_response_code(200); - pmpro_paystack_ipn_exit(); + pmpro_paystack_webhook_exit(); } /** @@ -639,16 +639,16 @@ static function renewpayment($event) if (empty($old_order)) { - pmpro_paystack_ipn_log( 'Could not find last order for subscription code: ' . $subscription_code ); - pmpro_paystack_ipn_exit(); + pmpro_paystack_webhook_log( 'Could not find last order for subscription code: ' . $subscription_code ); + pmpro_paystack_webhook_exit(); } $user_id = $old_order->user_id; $user = get_userdata($user_id); $user->membership_level = pmpro_getMembershipLevelForUser($user_id); if (empty($user)) { - pmpro_paystack_ipn_log( 'Could not get user for renewal payment' ); - pmpro_paystack_ipn_exit(); + pmpro_paystack_webhook_log( 'Could not get user for renewal payment' ); + pmpro_paystack_webhook_exit(); } $morder = new MemberOrder(); @@ -718,7 +718,7 @@ static function renewpayment($event) foreach ( $morder as $key => $value ) { $order_data[ $key ] = $value; } - pmpro_paystack_ipn_log( 'Order data: ' . print_r( $order_data, true ) ); + pmpro_paystack_webhook_log( 'Order data: ' . print_r( $order_data, true ) ); //save @@ -741,8 +741,8 @@ static function renewpayment($event) $pmproemail->sendInvoiceEmail($user, $morder); do_action('pmpro_subscription_payment_completed', $morder); - pmpro_paystack_ipn_log( sprintf( 'Subscription payment completed for user with ID: %d. Order ID: %s', $user_id, $morder->code ) ); - pmpro_paystack_ipn_exit(); + pmpro_paystack_webhook_log( sprintf( 'Subscription payment completed for user with ID: %d. Order ID: %s', $user_id, $morder->code ) ); + pmpro_paystack_webhook_exit(); } } @@ -1363,7 +1363,7 @@ function get_caller_info() { * * @since TBD */ -function pmpro_paystack_ipn_log( $s ) { +function pmpro_paystack_webhook_log( $s ) { global $logstr; $logstr .= "\t" . $s . "\n"; } @@ -1373,7 +1373,7 @@ function pmpro_paystack_ipn_log( $s ) { * * @since TBD */ -function pmpro_paystack_ipn_exit() { +function pmpro_paystack_webhook_exit() { global $logstr; //for log @@ -1386,13 +1386,13 @@ function pmpro_paystack_ipn_exit() { //- dont log if constant is undefined or defined but false //- log to file if constant is set to TRUE or 'log' //- log to file if constant is defined to a valid email address - if ( defined( 'PMPRO_PAYSTACK_IPN_DEBUG' ) ) { - if( PMPRO_PAYSTACK_IPN_DEBUG === false ){ + if ( defined( 'PMPRO_PAYSTACK_WEBHOOK_DEBUG' ) ) { + if( PMPRO_PAYSTACK_WEBHOOK_DEBUG === false ){ //dont log here. false mean no. //should avoid counterintuitive interpretation of false. - } elseif ( PMPRO_PAYSTACK_IPN_DEBUG === "log" ) { + } elseif ( PMPRO_PAYSTACK_WEBHOOK_DEBUG === "log" ) { //file - $logfile = apply_filters( 'pmpro_paystack_ipn_logfile', dirname( __FILE__ ) . "/logs/ipn.txt" ); + $logfile = apply_filters( 'pmpro_paystack_webhook_log_file', dirname( __FILE__ ) . "/logs/ipn.txt" ); // Check if the dir exists, if not let's create it. $logdir = dirname( $logfile ); @@ -1409,9 +1409,9 @@ function pmpro_paystack_ipn_exit() { $loghandle = fopen( $logfile, "a+" ); fwrite( $loghandle, $logstr ); fclose( $loghandle ); - } elseif ( is_email( PMPRO_PAYSTACK_IPN_DEBUG ) ) { + } elseif ( is_email( PMPRO_PAYSTACK_WEBHOOK_DEBUG ) ) { //email to specified address - wp_mail( PMPRO_PAYSTACK_IPN_DEBUG, get_option( "blogname" ) . " IPN Log", nl2br( esc_html( $logstr ) ) ); + wp_mail( PMPRO_PAYSTACK_WEBHOOK_DEBUG, get_option( "blogname" ) . " IPN Log", nl2br( esc_html( $logstr ) ) ); } else { //email to admin wp_mail( get_option( "admin_email" ), get_option( "blogname" ) . " IPN Log", nl2br( esc_html( $logstr ) ) ); From a33544bd290fd9c187e84fb53402d35d6d881c7a Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Wed, 3 Jul 2024 10:56:15 +0200 Subject: [PATCH 51/91] V1.7.5 bump --- class.pmprogateway_paystack.php | 2 +- readme.txt | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 64ed294..d5bd6ce 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -3,7 +3,7 @@ * Plugin Name: Paystack Gateway for Paid Memberships Pro * Plugin URI: https://www.paidmembershipspro.com/add-ons/paystack-gateway/ * Description: Plugin to add Paystack payment gateway into Paid Memberships Pro - * Version: 1.7.4 + * Version: 1.7.5 * Author: Paid Memberships Pro, Paystack * Author URI: https://www.paidmembershipspro.com * License: GPLv2 or later diff --git a/readme.txt b/readme.txt index 60fdb6e..24e0d0e 100755 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Donate link: https://paystack.com/demo Tags: paid memberships pro, paystack, gateway, credit card, Naira Requires at least: 5.2 Tested up to: 6.5 -Stable tag: 1.7.3 +Stable tag: 1.7.4 License: GPLv3 License URI: http://www.gnu.org/licenses/gpl-3.0.html @@ -73,6 +73,10 @@ Yes you can! Join in on our [GitHub repository](https://github.com/strangerstudi 1. The slick Paystack settings panel. == Changelog == += 1.7.5 - 2024-07-03 = +* ENHANCEMENT: Added webhook logging functionality to debug incoming webhook data. You may use the constant 'PMPRO_PAYSTACK_WEBHOOK_LOG' to enable this feature. Please delete logs and disable this constant after completing debugging. +* BUG FIX: Fixed an issue where the webhook handler was not correctly storing the order amount if the subscription amount changed. + = 1.7.4 - 2024-04-24 = * ENHANCEMENT: Added new filter `pmpro_paystack_webhook_level` to tweak the level given to members after checkout. This includes support for the Set Expiration Dates Add On. From db8bd6595fee86c47cfae59054c0ba846bc947b4 Mon Sep 17 00:00:00 2001 From: WordPress POT/PO/MO Generator <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 09:00:29 +0000 Subject: [PATCH 52/91] =?UTF-8?q?=F0=9F=94=84=20Regenerate=20translation?= =?UTF-8?q?=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../paystack-gateway-paid-memberships-pro.mo | Bin 559 -> 559 bytes .../paystack-gateway-paid-memberships-pro.po | 19 ++++++++-- .../paystack-gateway-paid-memberships-pro.pot | 34 +++++++++--------- 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/languages/paystack-gateway-paid-memberships-pro.mo b/languages/paystack-gateway-paid-memberships-pro.mo index a191690c1a6a7f4db34f7625a9d5789e67c4d389..5b5af6724e64a130a142e5a2577954191d936803 100644 GIT binary patch delta 30 lcmZ3_vYur^KBMWz0v$$Pb6o@D5Ccmq0|P4~%gHMl+W?012z>wm delta 30 lcmZ3_vYur^KBMu*0v$$P6J0~o5JN*NV*@J_lgTR?+W>}t2y_4d diff --git a/languages/paystack-gateway-paid-memberships-pro.po b/languages/paystack-gateway-paid-memberships-pro.po index 729409f..4dcc05b 100644 --- a/languages/paystack-gateway-paid-memberships-pro.po +++ b/languages/paystack-gateway-paid-memberships-pro.po @@ -2,14 +2,14 @@ # This file is distributed under the GPLv2 or later. msgid "" msgstr "" -"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.3\n" +"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.5\n" "Report-Msgid-Bugs-To: info@paidmembershipspro.com\n" "Last-Translator: Paid Memberships Pro \n" "Language-Team: Paid Memberships Pro \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2024-04-15T11:30:44+00:00\n" +"POT-Creation-Date: 2024-07-03T09:00:29+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "X-Generator: WP-CLI 2.10.0\n" "X-Domain: paystack-gateway-paid-memberships-pro\n" @@ -41,84 +41,98 @@ msgstr "" #: class.pmprogateway_paystack.php:97 #: class.pmprogateway_paystack.php:108 #: class.pmprogateway_paystack.php:114 +#: class.pmprogateway_paystack.php:118 msgid "Settings" msgstr "" #: class.pmprogateway_paystack.php:111 #: class.pmprogateway_paystack.php:122 #: class.pmprogateway_paystack.php:128 +#: class.pmprogateway_paystack.php:132 msgid "Check Out with Paystack" msgstr "" #: class.pmprogateway_paystack.php:111 #: class.pmprogateway_paystack.php:122 #: class.pmprogateway_paystack.php:128 +#: class.pmprogateway_paystack.php:132 msgid "Submit and Confirm" msgstr "" #: class.pmprogateway_paystack.php:124 #: class.pmprogateway_paystack.php:135 #: class.pmprogateway_paystack.php:141 +#: class.pmprogateway_paystack.php:145 msgid "Paystack" msgstr "" #: class.pmprogateway_paystack.php:348 #: class.pmprogateway_paystack.php:377 #: class.pmprogateway_paystack.php:394 +#: class.pmprogateway_paystack.php:408 msgid "Test Secret Key" msgstr "" #: class.pmprogateway_paystack.php:356 #: class.pmprogateway_paystack.php:385 #: class.pmprogateway_paystack.php:402 +#: class.pmprogateway_paystack.php:416 msgid "Test Public Key" msgstr "" #: class.pmprogateway_paystack.php:364 #: class.pmprogateway_paystack.php:393 #: class.pmprogateway_paystack.php:410 +#: class.pmprogateway_paystack.php:424 msgid "Live Secret Key" msgstr "" #: class.pmprogateway_paystack.php:372 #: class.pmprogateway_paystack.php:401 #: class.pmprogateway_paystack.php:418 +#: class.pmprogateway_paystack.php:432 msgid "Live Public Key" msgstr "" #: class.pmprogateway_paystack.php:380 #: class.pmprogateway_paystack.php:409 #: class.pmprogateway_paystack.php:426 +#: class.pmprogateway_paystack.php:440 msgid "Webhook" msgstr "" #: class.pmprogateway_paystack.php:383 #: class.pmprogateway_paystack.php:412 #: class.pmprogateway_paystack.php:429 +#: class.pmprogateway_paystack.php:443 msgid "To fully integrate with Paystack, be sure to use the following for your Webhook URL to" msgstr "" #: class.pmprogateway_paystack.php:852 #: class.pmprogateway_paystack.php:930 #: class.pmprogateway_paystack.php:948 +#: class.pmprogateway_paystack.php:975 msgid "Account" msgstr "" #: class.pmprogateway_paystack.php:853 #: class.pmprogateway_paystack.php:931 #: class.pmprogateway_paystack.php:949 +#: class.pmprogateway_paystack.php:976 msgid "Order" msgstr "" #: class.pmprogateway_paystack.php:854 #: class.pmprogateway_paystack.php:932 #: class.pmprogateway_paystack.php:950 +#: class.pmprogateway_paystack.php:977 msgid "Membership Level" msgstr "" #: class.pmprogateway_paystack.php:855 #: class.pmprogateway_paystack.php:933 #: class.pmprogateway_paystack.php:951 +#: class.pmprogateway_paystack.php:978 msgid "Amount Paid" msgstr "" @@ -129,5 +143,6 @@ msgstr "" #: class.pmprogateway_paystack.php:1119 #: class.pmprogateway_paystack.php:1140 +#: class.pmprogateway_paystack.php:1166 msgid "There was an error communicating with Paystack. Please confirm your connectivity and API details and try again." msgstr "" diff --git a/languages/paystack-gateway-paid-memberships-pro.pot b/languages/paystack-gateway-paid-memberships-pro.pot index 04954d4..380d828 100644 --- a/languages/paystack-gateway-paid-memberships-pro.pot +++ b/languages/paystack-gateway-paid-memberships-pro.pot @@ -2,14 +2,14 @@ # This file is distributed under the GPLv2 or later. msgid "" msgstr "" -"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.3\n" +"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.5\n" "Report-Msgid-Bugs-To: info@paidmembershipspro.com\n" "Last-Translator: Paid Memberships Pro \n" "Language-Team: Paid Memberships Pro \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2024-04-15T11:30:44+00:00\n" +"POT-Creation-Date: 2024-07-03T09:00:29+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "X-Generator: WP-CLI 2.10.0\n" "X-Domain: paystack-gateway-paid-memberships-pro\n" @@ -39,62 +39,62 @@ msgstr "" msgid "https://www.paidmembershipspro.com" msgstr "" -#: class.pmprogateway_paystack.php:114 +#: class.pmprogateway_paystack.php:118 msgid "Settings" msgstr "" -#: class.pmprogateway_paystack.php:128 +#: class.pmprogateway_paystack.php:132 msgid "Check Out with Paystack" msgstr "" -#: class.pmprogateway_paystack.php:128 +#: class.pmprogateway_paystack.php:132 msgid "Submit and Confirm" msgstr "" -#: class.pmprogateway_paystack.php:141 +#: class.pmprogateway_paystack.php:145 msgid "Paystack" msgstr "" -#: class.pmprogateway_paystack.php:394 +#: class.pmprogateway_paystack.php:408 msgid "Test Secret Key" msgstr "" -#: class.pmprogateway_paystack.php:402 +#: class.pmprogateway_paystack.php:416 msgid "Test Public Key" msgstr "" -#: class.pmprogateway_paystack.php:410 +#: class.pmprogateway_paystack.php:424 msgid "Live Secret Key" msgstr "" -#: class.pmprogateway_paystack.php:418 +#: class.pmprogateway_paystack.php:432 msgid "Live Public Key" msgstr "" -#: class.pmprogateway_paystack.php:426 +#: class.pmprogateway_paystack.php:440 msgid "Webhook" msgstr "" -#: class.pmprogateway_paystack.php:429 +#: class.pmprogateway_paystack.php:443 msgid "To fully integrate with Paystack, be sure to use the following for your Webhook URL to" msgstr "" -#: class.pmprogateway_paystack.php:948 +#: class.pmprogateway_paystack.php:975 msgid "Account" msgstr "" -#: class.pmprogateway_paystack.php:949 +#: class.pmprogateway_paystack.php:976 msgid "Order" msgstr "" -#: class.pmprogateway_paystack.php:950 +#: class.pmprogateway_paystack.php:977 msgid "Membership Level" msgstr "" -#: class.pmprogateway_paystack.php:951 +#: class.pmprogateway_paystack.php:978 msgid "Amount Paid" msgstr "" -#: class.pmprogateway_paystack.php:1140 +#: class.pmprogateway_paystack.php:1166 msgid "There was an error communicating with Paystack. Please confirm your connectivity and API details and try again." msgstr "" From c28a6fac1dd6e21209d2495db5f0155889178cb0 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Wed, 3 Jul 2024 12:06:30 +0200 Subject: [PATCH 53/91] Remove user->membership_level as it's not needed * BUG FIX: Fixed an issue where $user->membership_level was not needed and causing a fatal error for renewal payments. --- class.pmprogateway_paystack.php | 3 +-- readme.txt | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index d5bd6ce..f31ac8a 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -3,7 +3,7 @@ * Plugin Name: Paystack Gateway for Paid Memberships Pro * Plugin URI: https://www.paidmembershipspro.com/add-ons/paystack-gateway/ * Description: Plugin to add Paystack payment gateway into Paid Memberships Pro - * Version: 1.7.5 + * Version: 1.7.6 * Author: Paid Memberships Pro, Paystack * Author URI: https://www.paidmembershipspro.com * License: GPLv2 or later @@ -644,7 +644,6 @@ static function renewpayment($event) } $user_id = $old_order->user_id; $user = get_userdata($user_id); - $user->membership_level = pmpro_getMembershipLevelForUser($user_id); if (empty($user)) { pmpro_paystack_webhook_log( 'Could not get user for renewal payment' ); diff --git a/readme.txt b/readme.txt index 24e0d0e..939f86d 100755 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Donate link: https://paystack.com/demo Tags: paid memberships pro, paystack, gateway, credit card, Naira Requires at least: 5.2 Tested up to: 6.5 -Stable tag: 1.7.4 +Stable tag: 1.7.6 License: GPLv3 License URI: http://www.gnu.org/licenses/gpl-3.0.html @@ -73,9 +73,10 @@ Yes you can! Join in on our [GitHub repository](https://github.com/strangerstudi 1. The slick Paystack settings panel. == Changelog == -= 1.7.5 - 2024-07-03 = += 1.7.6 - 2024-07-03 = * ENHANCEMENT: Added webhook logging functionality to debug incoming webhook data. You may use the constant 'PMPRO_PAYSTACK_WEBHOOK_LOG' to enable this feature. Please delete logs and disable this constant after completing debugging. * BUG FIX: Fixed an issue where the webhook handler was not correctly storing the order amount if the subscription amount changed. +* BUG FIX: Fixed an issue when renewal payments were being processed and failing to complete the order. = 1.7.4 - 2024-04-24 = * ENHANCEMENT: Added new filter `pmpro_paystack_webhook_level` to tweak the level given to members after checkout. This includes support for the Set Expiration Dates Add On. From 3a5c8dcdaaac9cef25496abe9ec707afcd59c84f Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Wed, 3 Jul 2024 15:35:03 +0200 Subject: [PATCH 54/91] Load include payment information later * ENHANCEMENT: Load the function to hide onsite credit card fields later. This adds support for V3.1+ --- class.pmprogateway_paystack.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index f31ac8a..7a4f449 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -76,7 +76,7 @@ static function init() add_filter('pmpro_include_billing_address_fields', '__return_false'); add_filter('pmpro_required_billing_fields', array('PMProGateway_Paystack', 'pmpro_required_billing_fields')); - add_filter('pmpro_include_payment_information_fields', '__return_false'); + add_filter('pmpro_include_payment_information_fields', '__return_false', 20); add_filter('pmpro_checkout_before_change_membership_level', array('PMProGateway_Paystack', 'pmpro_checkout_before_change_membership_level'), 10, 2); add_filter('pmpro_gateways_with_pending_status', array('PMProGateway_Paystack', 'pmpro_gateways_with_pending_status')); From ba368b8ea65cadf02a409e7dc8d52c76c486a6dd Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Thu, 4 Jul 2024 12:34:04 +0200 Subject: [PATCH 55/91] make cancelMembership static * Make cancelMembership static function as it's called statically. --- class.pmprogateway_paystack.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 7a4f449..70d7ea1 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -1005,7 +1005,7 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) } - function cancelMembership(&$user){ + static function cancelMembership(&$user){ if (empty($user)) { print_r("Empty user object"); exit(); From 69cbf062bec02b14ece8bf403b901fa39f2c0d32 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Wed, 10 Jul 2024 11:51:38 +0200 Subject: [PATCH 56/91] Remove redundant gateway defined option. --- class.pmprogateway_paystack.php | 1 - 1 file changed, 1 deletion(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 70d7ea1..d2c233a 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -374,7 +374,6 @@ static function getGatewayOptions() 'paystack_tpk', 'paystack_lsk', 'paystack_lpk', - 'gateway_environment', 'currency', 'tax_state', 'tax_rate' From 2076fa7d29b052ca4ac39b350cc448600a40d0ef Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Mon, 15 Jul 2024 16:34:40 +0200 Subject: [PATCH 57/91] Use pmpro_get_element_class * ENHANCEMENT: Updated to use pmpro_get_element_class for the submit button class. --- class.pmprogateway_paystack.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index d2c233a..1799488 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -76,7 +76,7 @@ static function init() add_filter('pmpro_include_billing_address_fields', '__return_false'); add_filter('pmpro_required_billing_fields', array('PMProGateway_Paystack', 'pmpro_required_billing_fields')); - add_filter('pmpro_include_payment_information_fields', '__return_false', 20); + add_filter('pmpro_include_payment_information_fields', '__return_false', 20 ); add_filter('pmpro_checkout_before_change_membership_level', array('PMProGateway_Paystack', 'pmpro_checkout_before_change_membership_level'), 10, 2); add_filter('pmpro_gateways_with_pending_status', array('PMProGateway_Paystack', 'pmpro_gateways_with_pending_status')); @@ -129,7 +129,7 @@ static function pmpro_checkout_default_submit_button($show) ?> - + Date: Thu, 18 Jul 2024 14:59:21 +0200 Subject: [PATCH 58/91] Version bump 1.7.7 * Version bump and readme update for V1.7.7 --- class.pmprogateway_paystack.php | 4 ++-- readme.txt | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 1799488..df2c504 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -3,7 +3,7 @@ * Plugin Name: Paystack Gateway for Paid Memberships Pro * Plugin URI: https://www.paidmembershipspro.com/add-ons/paystack-gateway/ * Description: Plugin to add Paystack payment gateway into Paid Memberships Pro - * Version: 1.7.6 + * Version: 1.7.7 * Author: Paid Memberships Pro, Paystack * Author URI: https://www.paidmembershipspro.com * License: GPLv2 or later @@ -129,7 +129,7 @@ static function pmpro_checkout_default_submit_button($show) ?> - + Date: Thu, 18 Jul 2024 13:18:06 +0000 Subject: [PATCH 59/91] =?UTF-8?q?=F0=9F=94=84=20Regenerate=20translation?= =?UTF-8?q?=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../paystack-gateway-paid-memberships-pro.mo | Bin 559 -> 559 bytes .../paystack-gateway-paid-memberships-pro.po | 15 ++++++++-- .../paystack-gateway-paid-memberships-pro.pot | 26 +++++++++--------- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/languages/paystack-gateway-paid-memberships-pro.mo b/languages/paystack-gateway-paid-memberships-pro.mo index 5b5af6724e64a130a142e5a2577954191d936803..67386f895166610d6429ade2daef800b21eee7f0 100644 GIT binary patch delta 28 jcmZ3_vYur^KBM`@0zF1\n" "Language-Team: Paid Memberships Pro \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2024-07-03T09:00:29+00:00\n" +"POT-Creation-Date: 2024-07-18T13:18:06+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "X-Generator: WP-CLI 2.10.0\n" "X-Domain: paystack-gateway-paid-memberships-pro\n" @@ -70,6 +70,7 @@ msgstr "" #: class.pmprogateway_paystack.php:377 #: class.pmprogateway_paystack.php:394 #: class.pmprogateway_paystack.php:408 +#: class.pmprogateway_paystack.php:407 msgid "Test Secret Key" msgstr "" @@ -77,6 +78,7 @@ msgstr "" #: class.pmprogateway_paystack.php:385 #: class.pmprogateway_paystack.php:402 #: class.pmprogateway_paystack.php:416 +#: class.pmprogateway_paystack.php:415 msgid "Test Public Key" msgstr "" @@ -84,6 +86,7 @@ msgstr "" #: class.pmprogateway_paystack.php:393 #: class.pmprogateway_paystack.php:410 #: class.pmprogateway_paystack.php:424 +#: class.pmprogateway_paystack.php:423 msgid "Live Secret Key" msgstr "" @@ -91,6 +94,7 @@ msgstr "" #: class.pmprogateway_paystack.php:401 #: class.pmprogateway_paystack.php:418 #: class.pmprogateway_paystack.php:432 +#: class.pmprogateway_paystack.php:431 msgid "Live Public Key" msgstr "" @@ -98,6 +102,7 @@ msgstr "" #: class.pmprogateway_paystack.php:409 #: class.pmprogateway_paystack.php:426 #: class.pmprogateway_paystack.php:440 +#: class.pmprogateway_paystack.php:439 msgid "Webhook" msgstr "" @@ -105,6 +110,7 @@ msgstr "" #: class.pmprogateway_paystack.php:412 #: class.pmprogateway_paystack.php:429 #: class.pmprogateway_paystack.php:443 +#: class.pmprogateway_paystack.php:442 msgid "To fully integrate with Paystack, be sure to use the following for your Webhook URL to" msgstr "" @@ -112,6 +118,7 @@ msgstr "" #: class.pmprogateway_paystack.php:930 #: class.pmprogateway_paystack.php:948 #: class.pmprogateway_paystack.php:975 +#: class.pmprogateway_paystack.php:973 msgid "Account" msgstr "" @@ -119,6 +126,7 @@ msgstr "" #: class.pmprogateway_paystack.php:931 #: class.pmprogateway_paystack.php:949 #: class.pmprogateway_paystack.php:976 +#: class.pmprogateway_paystack.php:974 msgid "Order" msgstr "" @@ -126,6 +134,7 @@ msgstr "" #: class.pmprogateway_paystack.php:932 #: class.pmprogateway_paystack.php:950 #: class.pmprogateway_paystack.php:977 +#: class.pmprogateway_paystack.php:975 msgid "Membership Level" msgstr "" @@ -133,6 +142,7 @@ msgstr "" #: class.pmprogateway_paystack.php:933 #: class.pmprogateway_paystack.php:951 #: class.pmprogateway_paystack.php:978 +#: class.pmprogateway_paystack.php:976 msgid "Amount Paid" msgstr "" @@ -144,5 +154,6 @@ msgstr "" #: class.pmprogateway_paystack.php:1119 #: class.pmprogateway_paystack.php:1140 #: class.pmprogateway_paystack.php:1166 +#: class.pmprogateway_paystack.php:1164 msgid "There was an error communicating with Paystack. Please confirm your connectivity and API details and try again." msgstr "" diff --git a/languages/paystack-gateway-paid-memberships-pro.pot b/languages/paystack-gateway-paid-memberships-pro.pot index 380d828..f2bf3d2 100644 --- a/languages/paystack-gateway-paid-memberships-pro.pot +++ b/languages/paystack-gateway-paid-memberships-pro.pot @@ -2,14 +2,14 @@ # This file is distributed under the GPLv2 or later. msgid "" msgstr "" -"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.5\n" +"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.7\n" "Report-Msgid-Bugs-To: info@paidmembershipspro.com\n" "Last-Translator: Paid Memberships Pro \n" "Language-Team: Paid Memberships Pro \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2024-07-03T09:00:29+00:00\n" +"POT-Creation-Date: 2024-07-18T13:18:06+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "X-Generator: WP-CLI 2.10.0\n" "X-Domain: paystack-gateway-paid-memberships-pro\n" @@ -55,46 +55,46 @@ msgstr "" msgid "Paystack" msgstr "" -#: class.pmprogateway_paystack.php:408 +#: class.pmprogateway_paystack.php:407 msgid "Test Secret Key" msgstr "" -#: class.pmprogateway_paystack.php:416 +#: class.pmprogateway_paystack.php:415 msgid "Test Public Key" msgstr "" -#: class.pmprogateway_paystack.php:424 +#: class.pmprogateway_paystack.php:423 msgid "Live Secret Key" msgstr "" -#: class.pmprogateway_paystack.php:432 +#: class.pmprogateway_paystack.php:431 msgid "Live Public Key" msgstr "" -#: class.pmprogateway_paystack.php:440 +#: class.pmprogateway_paystack.php:439 msgid "Webhook" msgstr "" -#: class.pmprogateway_paystack.php:443 +#: class.pmprogateway_paystack.php:442 msgid "To fully integrate with Paystack, be sure to use the following for your Webhook URL to" msgstr "" -#: class.pmprogateway_paystack.php:975 +#: class.pmprogateway_paystack.php:973 msgid "Account" msgstr "" -#: class.pmprogateway_paystack.php:976 +#: class.pmprogateway_paystack.php:974 msgid "Order" msgstr "" -#: class.pmprogateway_paystack.php:977 +#: class.pmprogateway_paystack.php:975 msgid "Membership Level" msgstr "" -#: class.pmprogateway_paystack.php:978 +#: class.pmprogateway_paystack.php:976 msgid "Amount Paid" msgstr "" -#: class.pmprogateway_paystack.php:1166 +#: class.pmprogateway_paystack.php:1164 msgid "There was an error communicating with Paystack. Please confirm your connectivity and API details and try again." msgstr "" From cd34de8bfd48230e5dd2ea4a588e52b9cf421c3e Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Thu, 18 Jul 2024 15:21:10 +0200 Subject: [PATCH 60/91] Fix gitattributes file. --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 2d8b5d0..4a511e2 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,4 +3,4 @@ CONTRIBUTING.md export-ignore README.md export-ignore Dockerfile export-ignore -pmpro-paystack-banner.png export-ignore \ No newline at end of file +pmpro-paystack-banner.jpg export-ignore \ No newline at end of file From b373f95ebe62944cacce046874ac472d7504c862 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 19 Jul 2024 09:25:04 +0200 Subject: [PATCH 61/91] Fix an issue where the subscription would start same day * BUG FIX: Fixed an issue where the subscription would be set to same date. --- class.pmprogateway_paystack.php | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index df2c504..086b47b 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -865,6 +865,11 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) } $subscription_delay = get_option( 'pmpro_subscription_delay_' . $pmpro_level->id, 0 ); + + $body = array( + 'customer' => $customer_code, + 'plan' => $plancode + ); if ( $subscription_delay ) { if ( ! is_numeric( $subscription_delay ) ) { @@ -873,14 +878,16 @@ static function pmpro_pages_shortcode_confirmation($content,$reference = null) $start_date = date( 'Y-m-d', strtotime( '+ ' . intval( $subscription_delay ) . ' Days', current_time( 'timestamp' ) ) ); } } else { - $start_date = current_time( 'mysql' ); + // $start_date = current_time( 'mysql' ); + $start_date = NULL; + } + + // If we are tweaking the start date via Subscription Delays Add On, let's set that to the subscription. + if ( ! empty( $start_date ) ) { + $body['start_date'] = apply_filters( 'pmpro_paystack_subscription_start_date', $start_date ); } - $body = array( - 'customer' => $customer_code, - 'plan' => $plancode, - 'start_date' => $start_date - ); + $args = array( 'body' => json_encode($body), 'headers' => $headers, @@ -1419,4 +1426,4 @@ function pmpro_paystack_webhook_exit() { exit; -} \ No newline at end of file +} From 126278ebe7117b5a458f65182b957a5fe1701393 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 19 Jul 2024 09:26:35 +0200 Subject: [PATCH 62/91] Update readme --- readme.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/readme.txt b/readme.txt index 52cd6ca..6eda1fa 100755 --- a/readme.txt +++ b/readme.txt @@ -73,9 +73,10 @@ Yes you can! Join in on our [GitHub repository](https://github.com/strangerstudi 1. The slick Paystack settings panel. == Changelog == -= 1.7.7 - 2024-07-18 = += 1.7.7 - 2024-07-19 = * ENHANCEMENT: Added support for Paid Memberships Pro V3.1+ and used `pmpro_get_element_class` method on the frontend. (@andrewlimaza) * BUG FIX: Fixed an issue where cancellations weren't working correctly in some cases/instances. (@andrewlimaza) +* BUG FIX: Fixed an issue where billing would bill later in the day at the start of the subscription. (@andrewlimaza) = 1.7.6 - 2024-07-03 = * ENHANCEMENT: Added webhook logging functionality to debug incoming webhook data. You may use the constant 'PMPRO_PAYSTACK_WEBHOOK_LOG' to enable this feature. Please delete logs and disable this constant after completing debugging. From f778856fb3cac62c89b387c6a9e1f032cb80b033 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Tue, 8 Oct 2024 08:38:52 +0200 Subject: [PATCH 63/91] Fix free checkout fatal error * BUG FIX: Fixes an issue for free checkouts, where fatal errors would occur with Paystack as the active gateway. --- class.pmprogateway_paystack.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 086b47b..4477e19 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -493,6 +493,10 @@ static function pmpro_checkout_before_change_membership_level( $user_id, $morder return; } + if ( $morder->gateway != "paystack" ) { + return; + } + if ( empty( $morder->code ) ) { $morder->code = $morder->getRandomCode(); } From 1a018ad17608dd5b95b6dd4a83802c519b175aaa Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Wed, 9 Oct 2024 10:12:08 +0200 Subject: [PATCH 64/91] V1.7.8 bump --- class.pmprogateway_paystack.php | 2 +- readme.txt | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index 4477e19..a2ff84f 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -3,7 +3,7 @@ * Plugin Name: Paystack Gateway for Paid Memberships Pro * Plugin URI: https://www.paidmembershipspro.com/add-ons/paystack-gateway/ * Description: Plugin to add Paystack payment gateway into Paid Memberships Pro - * Version: 1.7.7 + * Version: 1.7.8 * Author: Paid Memberships Pro, Paystack * Author URI: https://www.paidmembershipspro.com * License: GPLv2 or later diff --git a/readme.txt b/readme.txt index 6eda1fa..8aa0935 100755 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Donate link: https://paystack.com/demo Tags: paid memberships pro, paystack, gateway, credit card, Naira Requires at least: 5.2 Tested up to: 6.6 -Stable tag: 1.7.7 +Stable tag: 1.7.8 License: GPLv3 License URI: http://www.gnu.org/licenses/gpl-3.0.html @@ -73,6 +73,9 @@ Yes you can! Join in on our [GitHub repository](https://github.com/strangerstudi 1. The slick Paystack settings panel. == Changelog == += 1.7.8 - 2024-10-09 = +* BUG FIX: Fixed an issue where free checkouts would cause a fatal error. + = 1.7.7 - 2024-07-19 = * ENHANCEMENT: Added support for Paid Memberships Pro V3.1+ and used `pmpro_get_element_class` method on the frontend. (@andrewlimaza) * BUG FIX: Fixed an issue where cancellations weren't working correctly in some cases/instances. (@andrewlimaza) From 3c555fee880949d6d88a130b428fb3511e61d810 Mon Sep 17 00:00:00 2001 From: WordPress POT/PO/MO Generator <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 08:26:20 +0000 Subject: [PATCH 65/91] =?UTF-8?q?=F0=9F=94=84=20Regenerate=20translation?= =?UTF-8?q?=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../paystack-gateway-paid-memberships-pro.mo | Bin 559 -> 559 bytes .../paystack-gateway-paid-memberships-pro.po | 11 ++++++++--- .../paystack-gateway-paid-memberships-pro.pot | 16 ++++++++-------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/languages/paystack-gateway-paid-memberships-pro.mo b/languages/paystack-gateway-paid-memberships-pro.mo index 67386f895166610d6429ade2daef800b21eee7f0..3e27e17dd1df70fbe909dd44376cff27ddc0ac03 100644 GIT binary patch delta 37 tcmZ3_vYur^KBL9P0&PY\n" "Language-Team: Paid Memberships Pro \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2024-07-18T13:18:06+00:00\n" +"POT-Creation-Date: 2024-10-09T08:26:19+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"X-Generator: WP-CLI 2.10.0\n" +"X-Generator: WP-CLI 2.11.0\n" "X-Domain: paystack-gateway-paid-memberships-pro\n" #. Plugin Name of the plugin @@ -119,6 +119,7 @@ msgstr "" #: class.pmprogateway_paystack.php:948 #: class.pmprogateway_paystack.php:975 #: class.pmprogateway_paystack.php:973 +#: class.pmprogateway_paystack.php:984 msgid "Account" msgstr "" @@ -127,6 +128,7 @@ msgstr "" #: class.pmprogateway_paystack.php:949 #: class.pmprogateway_paystack.php:976 #: class.pmprogateway_paystack.php:974 +#: class.pmprogateway_paystack.php:985 msgid "Order" msgstr "" @@ -135,6 +137,7 @@ msgstr "" #: class.pmprogateway_paystack.php:950 #: class.pmprogateway_paystack.php:977 #: class.pmprogateway_paystack.php:975 +#: class.pmprogateway_paystack.php:986 msgid "Membership Level" msgstr "" @@ -143,6 +146,7 @@ msgstr "" #: class.pmprogateway_paystack.php:951 #: class.pmprogateway_paystack.php:978 #: class.pmprogateway_paystack.php:976 +#: class.pmprogateway_paystack.php:987 msgid "Amount Paid" msgstr "" @@ -155,5 +159,6 @@ msgstr "" #: class.pmprogateway_paystack.php:1140 #: class.pmprogateway_paystack.php:1166 #: class.pmprogateway_paystack.php:1164 +#: class.pmprogateway_paystack.php:1175 msgid "There was an error communicating with Paystack. Please confirm your connectivity and API details and try again." msgstr "" diff --git a/languages/paystack-gateway-paid-memberships-pro.pot b/languages/paystack-gateway-paid-memberships-pro.pot index f2bf3d2..55eb0ea 100644 --- a/languages/paystack-gateway-paid-memberships-pro.pot +++ b/languages/paystack-gateway-paid-memberships-pro.pot @@ -2,16 +2,16 @@ # This file is distributed under the GPLv2 or later. msgid "" msgstr "" -"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.7\n" +"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.8\n" "Report-Msgid-Bugs-To: info@paidmembershipspro.com\n" "Last-Translator: Paid Memberships Pro \n" "Language-Team: Paid Memberships Pro \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2024-07-18T13:18:06+00:00\n" +"POT-Creation-Date: 2024-10-09T08:26:19+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"X-Generator: WP-CLI 2.10.0\n" +"X-Generator: WP-CLI 2.11.0\n" "X-Domain: paystack-gateway-paid-memberships-pro\n" #. Plugin Name of the plugin @@ -79,22 +79,22 @@ msgstr "" msgid "To fully integrate with Paystack, be sure to use the following for your Webhook URL to" msgstr "" -#: class.pmprogateway_paystack.php:973 +#: class.pmprogateway_paystack.php:984 msgid "Account" msgstr "" -#: class.pmprogateway_paystack.php:974 +#: class.pmprogateway_paystack.php:985 msgid "Order" msgstr "" -#: class.pmprogateway_paystack.php:975 +#: class.pmprogateway_paystack.php:986 msgid "Membership Level" msgstr "" -#: class.pmprogateway_paystack.php:976 +#: class.pmprogateway_paystack.php:987 msgid "Amount Paid" msgstr "" -#: class.pmprogateway_paystack.php:1164 +#: class.pmprogateway_paystack.php:1175 msgid "There was an error communicating with Paystack. Please confirm your connectivity and API details and try again." msgstr "" From 3b61568ef4b36ffabff068b6a677f0d9dfcd9665 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Thu, 17 Oct 2024 16:00:23 +0200 Subject: [PATCH 66/91] Remove files before adding them back. --- Dockerfile | 19 - README.md | 36 - class-paystack-plugin-tracker.php | 42 - class.pmprogateway_paystack.php | 1433 ----------------- .../paystack-gateway-paid-memberships-pro.mo | Bin 559 -> 0 bytes .../paystack-gateway-paid-memberships-pro.po | 164 -- .../paystack-gateway-paid-memberships-pro.pot | 100 -- readme.txt | 127 -- 8 files changed, 1921 deletions(-) delete mode 100644 Dockerfile delete mode 100644 README.md delete mode 100644 class-paystack-plugin-tracker.php delete mode 100755 class.pmprogateway_paystack.php delete mode 100644 languages/paystack-gateway-paid-memberships-pro.mo delete mode 100644 languages/paystack-gateway-paid-memberships-pro.po delete mode 100644 languages/paystack-gateway-paid-memberships-pro.pot delete mode 100755 readme.txt diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 47c9186..0000000 --- a/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -FROM ubuntu:18.04 AS builder - -WORKDIR /dist - -RUN apt-get -y install && apt-get update -RUN apt-get -y install wget unzip zip - -ADD https://downloads.wordpress.org/plugin/paid-memberships-pro.latest-stable.zip /dist -RUN cd /dist && unzip paid-memberships-pro.latest-stable.zip && rm paid-memberships-pro.latest-stable.zip - -ADD https://downloads.wordpress.org/plugin/wp-debugging.2.9.2.zip /dist -RUN cd /dist && unzip wp-debugging.2.9.2.zip && rm wp-debugging.2.9.2.zip - -FROM wordpress:php7.2 - -WORKDIR /var/www/html/ - -COPY . ./wp-content/plugins/paystack -COPY --from=builder /dist/ ./wp-content/plugins diff --git a/README.md b/README.md deleted file mode 100644 index 8f1cfe5..0000000 --- a/README.md +++ /dev/null @@ -1,36 +0,0 @@ -![](pmpro-paystack-banner.jpg) - -# [Paid Memberships Pro - Paystack Gateway](https://www.paidmembershipspro.com/add-ons/paystack-gateway/) # - -![WordPress Plugin Downloads](https://img.shields.io/wordpress/plugin/dy/paystack-gateway-paid-memberships-pro?style=flat-square) ![License](https://img.shields.io/badge/license-GPL--2.0%2B-red.svg?style=flat-square) - -### Welcome to the Paid Memberships Pro - Paystack Gateway GitHub Repository -Add Paystack as a gateway option for Paid Memberships Pro and accept payments from members around Africa. - -For more information please visit [paidmembershipspro.com/add-ons/paystack-gateway/](https://www.paidmembershipspro.com/add-ons/paystack-gateway/) - -## Installation ## -For detailed installation steps, visit the [documentation](https://www.paidmembershipspro.com/add-ons/paystack-gateway/) page. - -1. Download the current development ZIP file directly: `https://github.com/strangerstudios/paystack-gateway-paid-memberships-pro/archive/dev.zip` - -**Please ensure that once installing this version of the plugin to remove `-dev` from the plugin's folder name.** - -## Bugs ## -If you find an issue/bug, let us know by [creating a detailed GitHub issue](https://github.com/strangerstudios/paystack-gateway-paid-memberships-pro/issues/new). - -## Support ## -This is a developer's portal for Paid Memberships Pro - Paystack Gateway. We do not offer support on this channel. **Any support related questions should be directed to [paidmembershipspro.com/add-ons/paystack-gateway/](https://www.paidmembershipspro.com/add-ons/paystack-gateway/).** - -## Contributing to Paid Memberships Pro - Paystack Gateway ## -We encourage and welcome any contribution to Paid Memberships Pro - Paystack Gateway. Please read the [guidelines for contributing](https://github.com/strangerstudios/paid-memberships-pro/blob/dev/.github/CONTRIBUTING.md) to this repository. - -There are various **ways to the help development** of Paid Memberships Pro - Paystack Gateway: - -1. Report [bugs/issues](https://github.com/strangerstudios/paystack-gateway-paid-memberships-pro/issues/new) on GitHub. -2. Work on any issues by submitting a Pull Request. - -Here are some ways for **non-developers to contribute** to Paid Memberships Pro - Paystack Gateway: - -1. Translate Paid Memberships Pro - Paystack Gateway into your own [language](https://www.paidmembershipspro.com/paid-memberships-pro-in-your-language/). -2. [Purchase a paid membership](https://paidmembershipspro.com/pricing) to help fund ongoing development and bug fixes. \ No newline at end of file diff --git a/class-paystack-plugin-tracker.php b/class-paystack-plugin-tracker.php deleted file mode 100644 index f09528d..0000000 --- a/class-paystack-plugin-tracker.php +++ /dev/null @@ -1,42 +0,0 @@ -plugin_name = $plugin; - $this->public_key = $pk; - } - - - - function log_transaction_success($trx_ref){ - //send reference to logger along with plugin name and public key - $url = "https://plugin-tracker.paystackintegrations.com/log/charge_success"; - - $fields = [ - 'plugin_name' => $this->plugin_name, - 'transaction_reference' => $trx_ref, - 'public_key' => $this->public_key - ]; - - $fields_string = http_build_query($fields); - - $ch = curl_init(); - - curl_setopt($ch,CURLOPT_URL, $url); - curl_setopt($ch,CURLOPT_POST, true); - curl_setopt($ch,CURLOPT_POSTFIELDS, $fields_string); - - curl_setopt($ch,CURLOPT_RETURNTRANSFER, true); - - //execute post - $result = curl_exec($ch); - // echo $result; - } -} - -?> diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php deleted file mode 100755 index a2ff84f..0000000 --- a/class.pmprogateway_paystack.php +++ /dev/null @@ -1,1433 +0,0 @@ -gateway = $gateway; - $this->gateway_environment = pmpro_getOption("gateway_environment"); - - return $this->gateway; - } - - /** - * Run on WP init - */ - static function init() - { - //make sure Paystack is a gateway option - add_filter('pmpro_gateways', array('PMProGateway_Paystack', 'pmpro_gateways')); - add_action( 'plugins_loaded', array('PMProGateway_Paystack', 'pmpro_paystack_load_textdomain' ) ); - - - //add fields to payment settings - add_filter('pmpro_payment_options', array('PMProGateway_Paystack', 'pmpro_payment_options')); - add_filter('pmpro_payment_option_fields', array('PMProGateway_Paystack', 'pmpro_payment_option_fields'), 10, 2); - add_action('wp_ajax_pmpro_paystack_ipn', array('PMProGateway_Paystack', 'pmpro_paystack_ipn')); - add_action('wp_ajax_nopriv_pmpro_paystack_ipn', array('PMProGateway_Paystack', 'pmpro_paystack_ipn')); - - // Keeping the deprecated action for backwards compatibility. - add_action('wp_ajax_kkd_pmpro_paystack_ipn', array('PMProGateway_Paystack', 'kkd_pmpro_paystack_ipn')); - add_action('wp_ajax_nopriv_kkd_pmpro_paystack_ipn', array('PMProGateway_Paystack', 'kkd_pmpro_paystack_ipn')); - - //code to add at checkout - $gateway = pmpro_getGateway(); - if ($gateway == "paystack") { - // Add support for custom fields. - add_action( 'pmpro_before_send_to_paystack', 'pmpro_after_checkout_save_fields', 20, 2 ); - - add_filter('pmpro_include_billing_address_fields', '__return_false'); - add_filter('pmpro_required_billing_fields', array('PMProGateway_Paystack', 'pmpro_required_billing_fields')); - add_filter('pmpro_include_payment_information_fields', '__return_false', 20 ); - add_filter('pmpro_checkout_before_change_membership_level', array('PMProGateway_Paystack', 'pmpro_checkout_before_change_membership_level'), 10, 2); - - add_filter('pmpro_gateways_with_pending_status', array('PMProGateway_Paystack', 'pmpro_gateways_with_pending_status')); - - add_filter('pmpro_checkout_default_submit_button', array('PMProGateway_Paystack', 'pmpro_checkout_default_submit_button')); - // custom confirmation page - - add_filter('pmpro_pages_shortcode_confirmation', array('PMProGateway_Paystack', 'pmpro_pages_shortcode_confirmation'), 20, 1); - - // Refund functionality. - add_filter( 'pmpro_allowed_refunds_gateways', array( 'PMProGateway_Paystack', 'pmpro_allowed_refunds_gateways' ) ); - add_filter( 'pmpro_process_refund_paystack', array( 'PMProGateway_Paystack', 'process_refund' ), 10, 2 ); - - - } - } - - /** - * Enable localization for the plugin. - * - * @return void - */ - static function pmpro_paystack_load_textdomain() { - load_plugin_textdomain( 'paystack-gateway-paid-memberships-pro', false, basename( dirname( __FILE__ ) ) . '/languages' ); - } - - /** - * Redirect Settings to PMPro settings - */ - static function plugin_action_links($links, $file) - { - static $this_plugin; - - if (false === isset($this_plugin) || true === empty($this_plugin)) { - $this_plugin = plugin_basename(__FILE__); - } - - if ($file == $this_plugin) { - $settings_link = '
'.__('Settings', 'paystack-gateway-paid-memberships-pro').''; - array_unshift($links, $settings_link); - } - - return $links; - } - static function pmpro_checkout_default_submit_button($show) - { - global $gateway, $pmpro_requirebilling; - - //show our submit buttons - ?> - - - - - __('Paystack', 'paystack-gateway-paid-memberships-pro')) + array_slice($gateways, 1); - } - return $gateways; - } - - /** - * Enable refund functionality for paystack. - * @since TBD. - */ - static function pmpro_allowed_refunds_gateways( $gateways ) { - $gateways[] = 'paystack'; - return $gateways; - } - - - function kkd_pmprosd_convert_date( $date ) { - // handle lower-cased y/m values. - $set_date = strtoupper($date); - - // Change "M-" and "Y-" to "M1-" and "Y1-". - $set_date = preg_replace('/Y-/', 'Y1-', $set_date); - $set_date = preg_replace('/M-/', 'M1-', $set_date); - - // Get number of months and years to add. - $m_pos = stripos( $set_date, 'M' ); - $y_pos = stripos( $set_date, 'Y' ); - if($m_pos !== false) { - $add_months = intval( pmpro_getMatches( '/M([0-9]*)/', $set_date, true ) ); - } - if($y_pos !== false) { - $add_years = intval( pmpro_getMatches( '/Y([0-9]*)/', $set_date, true ) ); - } - - // Allow new dates to be set from a custom date. - if(empty($current_date)) $current_date = current_time( 'timestamp' ); - - // Get current date parts. - $current_y = intval(date('Y', $current_date)); - $current_m = intval(date('m', $current_date)); - $current_d = intval(date('d', $current_date)); - - // Get set date parts. - $date_parts = explode( '-', $set_date); - $set_y = intval($date_parts[0]); - $set_m = intval($date_parts[1]); - $set_d = intval($date_parts[2]); - - // Get temporary date parts. - $temp_y = $set_y > 0 ? $set_y : $current_y; - $temp_m = $set_m > 0 ? $set_m : $current_m; - $temp_d = $set_d; - - // Add months. - if(!empty($add_months)) { - for($i = 0; $i < $add_months; $i++) { - // If "M1", only add months if current date of month has already passed. - if(0 == $i) { - if($temp_d < $current_d) { - $temp_m++; - $add_months--; - } - } else { - $temp_m++; - } - - // If we hit 13, reset to Jan of next year and subtract one of the years to add. - if($temp_m == 13) { - $temp_m = 1; - $temp_y++; - $add_years--; - } - } - } - - // Add years. - if(!empty($add_years)) { - for($i = 0; $i < $add_years; $i++) { - // If "Y1", only add years if current date has already passed. - if(0 == $i) { - $temp_date = strtotime(date("{$temp_y}-{$temp_m}-{$temp_d}")); - if($temp_date < $current_date) { - $temp_y++; - $add_years--; - } - } else { - $temp_y++; - } - } - } - - // Pad dates if necessary. - $temp_m = str_pad($temp_m, 2, '0', STR_PAD_LEFT); - $temp_d = str_pad($temp_d, 2, '0', STR_PAD_LEFT); - - // Put it all together. - $set_date = date("{$temp_y}-{$temp_m}-{$temp_d}"); - - // Make sure we use the right day of the month for dates > 28 - // From: http://stackoverflow.com/a/654378/1154321 - $dotm = pmpro_getMatches('/\-([0-3][0-9]$)/', $set_date, true); - if ( $temp_m == '02' && intval($dotm) > 28 || intval($dotm) > 30 ) { - $set_date = date('Y-m-t', strtotime(substr($set_date, 0, 8) . "01")); - } - - - - return $set_date; - } - - /** - * Wrapper function for newly named function instead to be more inline with PMPro naming conventions. - * DEPRECATED use pmpro_paystack_ipn instead. - * @since 1.0 - */ - static function kkd_pmpro_paystack_ipn() { - pmpro_paystack_ipn(); - } - - /** - * Webhook handler for Paystack. - * @since 1.0 (Renamed in 1.7.1) - */ - static function pmpro_paystack_ipn() { - global $wpdb; - - // Let's make sure the request came from Paystack by checking the secret key - if ( ( strtoupper( $_SERVER['REQUEST_METHOD'] ) != 'POST' ) || ! array_key_exists( 'HTTP_X_PAYSTACK_SIGNATURE', $_SERVER ) ) { - pmpro_paystack_webhook_log( 'Paystack signature not found' ); - pmpro_paystack_webhook_exit(); - } - - // Log all the $_POST data to the IPN log. - pmpro_paystack_webhook_log( print_r( $_POST, true ) ); - - // Get the relevant secret key based on gateway environment. - $mode = pmpro_getOption("gateway_environment"); - if ($mode == 'sandbox') { - $secret_key = pmpro_getOption("paystack_tsk"); - } else { - $secret_key = pmpro_getOption("paystack_lsk"); - } - - - $input = @file_get_contents("php://input"); - - // The Paystack signature doesn't match the secret key, let's bail. - if ( $_SERVER['HTTP_X_PAYSTACK_SIGNATURE'] !== hash_hmac('sha512', $input, $secret_key ) ) { - pmpro_paystack_webhook_log( 'Paystack signature does not match.' ); - pmpro_paystack_webhook_exit(); - } - - $event = json_decode( $input ); - pmpro_paystack_webhook_log( 'Event: ' . print_r( $event, true ) ); - - switch( $event->event ){ - case 'subscription.create': - - break; - case 'subscription.disable': - $amount = $event->data->subscription->amount/100; - $morder = new MemberOrder(); - $subscription_code = $event->data->subscription_code; - $email = $event->data->customer->email; - $morder->Email = $email; - $users_row = $wpdb->get_row( "SELECT ID, display_name FROM $wpdb->users WHERE user_email = '" . esc_sql( $email ). "' LIMIT 1" ); - if ( ! empty( $users_row ) ) { - $user_id = $users_row->ID; - $user = get_userdata($user_id); - $user->membership_level = pmpro_getMembershipLevelForUser($user_id); - } - - if ( empty( $user ) ) { - pmpro_paystack_webhook_log( 'Could not get user' ); - pmpro_paystack_webhook_exit(); - } - self::cancelMembership($user); - break; - case 'charge.success': - $morder = new MemberOrder($event->data->reference); - $morder->getMembershipLevel(); - $morder->getUser(); - $morder->Gateway->pmpro_pages_shortcode_confirmation('', $event->data->reference); - $mode = pmpro_getOption("gateway_environment"); - if ($mode == 'sandbox') { - $pk = pmpro_getOption("paystack_tpk"); - } else { - $pk = pmpro_getOption("paystack_lpk"); - } - $pstk_logger = new pmpro_paystack_plugin_tracker('pm-pro',$pk); - $pstk_logger->log_transaction_success($event->data->reference); - pmpro_paystack_webhook_log( 'Charge success. Reference: ' . $event->data->reference ); - break; - case 'invoice.create': - self::renewpayment($event); - break; - case 'invoice.update': - self::renewpayment($event); - break; - } - http_response_code(200); - pmpro_paystack_webhook_exit(); - } - - /** - * Check whether or not a gateway supports a specific feature. - * - * @param string $feature The feature we need to check if it is supported. - * @return string|boolean $supports In some cases, we may need to return strings for the feature or a boolean value if it's supported or not. - */ - public static function supports( $feature ) { - $supports = array( - 'subscription_sync' => true, - 'payment_method_updates' => false - ); - - if ( empty( $supports[$feature] ) ) { - return false; - } - - return $supports[$feature]; - } - - /** - * Get a list of payment options that the Paystack gateway needs/supports. - */ - static function getGatewayOptions() - { - $options = array ( - 'paystack_tsk', - 'paystack_tpk', - 'paystack_lsk', - 'paystack_lpk', - 'currency', - 'tax_state', - 'tax_rate' - ); - - return $options; - } - - /** - * Set payment options for payment settings page. - */ - static function pmpro_payment_options($options) - { - //get Paystack options - $paystack_options = self::getGatewayOptions(); - - //merge with others. - $options = array_merge($paystack_options, $options); - - return $options; - } - - /** - * Display fields for Paystack options. - */ - static function pmpro_payment_option_fields($values, $gateway) - { - ?> - style="display: none;"> - - - - - - - - style="display: none;"> - - - - - - - - style="display: none;"> - - - - - - - - style="display: none;"> - - - - - - - - style="display: none;"> - - - - -


- - - - getLastMemberOrder(get_current_user_id(), apply_filters("pmpro_confirmation_order_status", array("pending"))); - - if ((!in_array("paystack", $gateways)) && $found) { - array_push($gateways, "paystack"); - } elseif (($key = array_search("paystack", $gateways)) !== false) { - unset($gateways[$key]); - } - - return $gateways; - } - - /** - * Instead of change membership levels, send users to Paystack payment page. - */ - static function pmpro_checkout_before_change_membership_level( $user_id, $morder ) { - global $wpdb, $discount_code_id; - - //if no order, no need to pay - if ( empty( $morder )) { - return; - } - - if ( $morder->gateway != "paystack" ) { - return; - } - - if ( empty( $morder->code ) ) { - $morder->code = $morder->getRandomCode(); - } - - $morder->payment_type = "paystack"; - $morder->status = "pending"; - $morder->user_id = $user_id; - $morder->saveOrder(); - - // Try to get the discount_code from a query param. - if ( empty( $discount_code_id ) ) { - // PMPro 3.0+ - if ( isset( $_REQUEST['pmpro_discount_code'] ) ) { - $discount_code = sanitize_text_field( $_REQUEST['pmpro_discount_code'] ); - } - - // PMPro < 3.0 - if ( isset( $_REQUEST['discount_code'] ) ) { - $discount_code = sanitize_text_field( $_REQUEST['discount_code'] ); - } - } - // if global is empty but query is available. PMPro 3.0 - if ( empty( $discount_code_id ) && ! empty( $discount_code ) ) { - $discount_code_id = $wpdb->get_var( "SELECT id FROM $wpdb->pmpro_discount_codes WHERE code = '" . esc_sql( $discount_code ) . "'" ); - } - - // save discount code use - if ( ! empty( $discount_code_id ) ) { - $wpdb->query( - $wpdb->prepare( - "INSERT INTO $wpdb->pmpro_discount_codes_uses - (code_id, user_id, order_id, timestamp) - VALUES( %d , %d, %d, %s )", - $discount_code_id, - $user_id, - $morder->id, - current_time( 'mysql' ) - ) - ); - } - - do_action("pmpro_before_send_to_paystack", $user_id, $morder); - - $morder->Gateway->sendToPaystack($morder); - } - - function sendToPaystack(&$order) - { - global $wp, $pmpro_currency; - - do_action("pmpro_paypalexpress_session_vars"); - - $params = array(); - $amount = $order->PaymentAmount; - $amount_tax = $order->getTaxForPrice($amount); - $amount = round((float)$amount + (float)$amount_tax, 2); - - //call directkit to get Webkit Token - $amount = floatval($order->InitialPayment); - - // echo pmpro_url("confirmation", "?level=" . $order->membership_level->id); - // die(); - $mode = pmpro_getOption("gateway_environment"); - if ($mode == 'sandbox') { - $key = pmpro_getOption("paystack_tsk"); - $pk = pmpro_getOption("paystack_tpk"); - } else { - $key = pmpro_getOption("paystack_lsk"); - $pk = pmpro_getOption("paystack_lpk"); - } - if ($key == '') { - echo "Api keys not set"; - } - - $koboamount = $amount*100; - - - $paystack_url = 'https://api.paystack.co/transaction/initialize'; - $headers = array( - 'Content-Type' => 'application/json', - 'Authorization' => 'Bearer '.$key - ); - - //Create Plan - $body = array( - 'email' => $order->Email, - 'amount' => $koboamount, - 'reference' => $order->code, - 'currency' => $pmpro_currency, - 'callback_url' => pmpro_url("confirmation", "?level=" . $order->membership_level->id), - 'metadata' => json_encode(array('custom_fields' => array( - array( - "display_name"=>"Plugin", - "variable_name"=>"plugin", - "value"=>"pm-pro" - ), - - ), 'custom_filters' => array("recurring" => true))), - - ); - - // If the level is recurring only allow card payments for the subscription as other methods don't work. - $level = $order->getMembershipLevel(); - if ( pmpro_isLevelRecurring( $level ) ) { - $body['channels'] = array( 'card' ); - } - - $args = array( - 'body' => json_encode($body), - 'headers' => $headers, - 'timeout' => 60 - ); - - $request = wp_remote_post($paystack_url, $args); - // print_r($request); - if (!is_wp_error($request)) { - $paystack_response = json_decode(wp_remote_retrieve_body($request)); - if ($paystack_response->status){ - $url = $paystack_response->data->authorization_url; - wp_redirect($url); - exit; - } else { - wp_redirect(pmpro_url("checkout", "?level=" . $order->membership_level->id . "&error=" . $paystack_response->message)); - exit(); - } - } else { - wp_redirect(pmpro_url("checkout", "?level=" . $order->membership_level->id . "&error=Failed")); - exit(); - } - exit; - } - static function renewpayment($event) - { - global $wp,$wpdb; - - if (isset($event->data->paid) && ($event->data->paid == 1)) { - - $amount = $event->data->subscription->amount/100; - $old_order = new MemberOrder(); - $subscription_code = $event->data->subscription->subscription_code; - $email = $event->data->customer->email; - $old_order->getLastMemberOrderBySubscriptionTransactionID($subscription_code); - - - if (empty($old_order)) { - pmpro_paystack_webhook_log( 'Could not find last order for subscription code: ' . $subscription_code ); - pmpro_paystack_webhook_exit(); - } - $user_id = $old_order->user_id; - $user = get_userdata($user_id); - - if (empty($user)) { - pmpro_paystack_webhook_log( 'Could not get user for renewal payment' ); - pmpro_paystack_webhook_exit(); - } - - $morder = new MemberOrder(); - - // Set the orders date to time it was paid. - if ( ! empty( $event->data->paid_at ) ) { - $morder->timestamp = strtotime( sanitize_text_field( $event->data->paid_at ) ); - } - - $morder->user_id = $old_order->user_id; - $morder->membership_id = $old_order->membership_id; - $morder->InitialPayment = $amount; //not the initial payment, but the order class is expecting this - $morder->PaymentAmount = $amount; - $morder->payment_transaction_id = $event->data->invoice_code; - $morder->subscription_transaction_id = $subscription_code; - - $morder->gateway = $old_order->gateway; - $morder->gateway_environment = $old_order->gateway_environment; - - $morder->Email = $email; - $pmpro_level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . (int)$morder->membership_id . "' LIMIT 1"); - $pmpro_level = apply_filters("pmpro_checkout_level", $pmpro_level); - $startdate = apply_filters( 'pmpro_checkout_start_date', "'" . current_time( 'mysql' ) . "'", $morder->user_id, $morder->membership_level ); - - // get discount code (NOTE: but discount_code isn't set here. How to handle discount codes for PayPal Standard?) - $morder->getDiscountCode(); - if ( ! empty( $morder->discount_code ) ) { - // update membership level - $morder->getMembershipLevel( true ); - $discount_code_id = $morder->discount_code->id; - } else { - $discount_code_id = ''; - } - - - //fix expiration date - if ( ! empty( $morder->membership_level->expiration_number ) ) { - $enddate = "'" . date_i18n( "Y-m-d", strtotime( "+ " . $morder->membership_level->expiration_number . " " . $morder->membership_level->expiration_period, current_time( "timestamp" ) ) ) . "'"; - } else { - $enddate = "NULL"; - } - - $custom_level = array( - 'user_id' => $morder->user_id, - 'membership_id' => $morder->memberhsip_level->id, - 'code_id' => $discount_code_id, - 'initial_payment' => $morder->memberhsip_level->initial_payment, - 'billing_amount' => $morder->memberhsip_level->billing_amount, - 'cycle_number' => $morder->memberhsip_level->cycle_number, - 'cycle_period' => $morder->memberhsip_level->cycle_period, - 'billing_limit' => $morder->memberhsip_level->billing_limit, - 'trial_amount' => $morder->memberhsip_level->trial_amount, - 'trial_limit' => $morder->memberhsip_level->trial_limit, - 'startdate' => $startdate, - 'enddate' => $enddate - ); - - //get CC info that is on file - $morder->expirationmonth = get_user_meta($user_id, "pmpro_ExpirationMonth", true); - $morder->expirationyear = get_user_meta($user_id, "pmpro_ExpirationYear", true); - $morder->ExpirationDate = $morder->expirationmonth . $morder->expirationyear; - $morder->ExpirationDate_YdashM = $morder->expirationyear . "-" . $morder->expirationmonth; - - - // Save entire order data to IPN Log - loop through the order object. - $order_data = array(); - foreach ( $morder as $key => $value ) { - $order_data[ $key ] = $value; - } - pmpro_paystack_webhook_log( 'Order data: ' . print_r( $order_data, true ) ); - - - //save - if ($morder->status != 'success') { - - $_REQUEST['cancel_membership'] = false; // Do NOT cancel gateway subscription - - if ( pmpro_changeMembershipLevel( $custom_level, $morder->user_id, 'changed' ) !== false ) { - $morder->status = "success"; - } - - } - - // Save the order before emailing the customer about it. - $morder->saveOrder(); - $morder->getMemberOrderByID( $morder->id ); - - //email the user their invoice - $pmproemail = new PMProEmail(); - $pmproemail->sendInvoiceEmail($user, $morder); - - do_action('pmpro_subscription_payment_completed', $morder); - pmpro_paystack_webhook_log( sprintf( 'Subscription payment completed for user with ID: %d. Order ID: %s', $user_id, $morder->code ) ); - pmpro_paystack_webhook_exit(); - } - - } - - /** - * Custom confirmation page - */ - static function pmpro_pages_shortcode_confirmation($content,$reference = null) - { - global $wpdb, $current_user, $pmpro_invoice, $pmpro_currency,$gateway; - if (!isset($_REQUEST['trxref'])) { - $_REQUEST['trxref'] = null; - } - if ($reference != null) { - $_REQUEST['trxref'] = $reference; - } - - if (empty($pmpro_invoice)) { - $morder = new MemberOrder($_REQUEST['trxref']); - // $morder = new MemberOrder(); - // $morder->getLastMemberOrder(get_current_user_id(), apply_filters("pmpro_confirmation_order_status", array("pending", "success"))); - if (!empty($morder) && $morder->gateway == "paystack") $pmpro_invoice = $morder; - } - - if (!empty($pmpro_invoice) && $pmpro_invoice->gateway == "paystack" && isset($pmpro_invoice->total) && $pmpro_invoice->total > 0) { - $morder = $pmpro_invoice; - if ($morder->code == $_REQUEST['trxref']) { - $pmpro_level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . (int)$morder->membership_id . "' LIMIT 1"); - $pmpro_level = apply_filters("pmpro_checkout_level", $pmpro_level); - $startdate = apply_filters("pmpro_checkout_start_date", "'" . current_time("mysql") . "'", $morder->user_id, $pmpro_level); - - // The level object from the order that can be filtered when returning from Paystack and user is getting their membership level. - $morder->membership_level = apply_filters( 'pmpro_paystack_webhook_level', $morder->membership_level, $morder->user_id ); - - $mode = pmpro_getOption("gateway_environment"); - if ($mode == "sandbox") { - $key = pmpro_getOption("paystack_tsk"); - $pk = pmpro_getOption("paystack_tpk"); - } else { - $key = pmpro_getOption("paystack_lsk"); - $pk = pmpro_getOption("paystack_lpk"); - } - $paystack_url = 'https://api.paystack.co/transaction/verify/' . $_REQUEST['trxref']; - $headers = array( - 'Authorization' => 'Bearer ' . $key - ); - $args = array( - 'headers' => $headers, - 'timeout' => 60 - ); - $request = wp_remote_get($paystack_url, $args); - if (!is_wp_error($request) && 200 == wp_remote_retrieve_response_code($request) ) { - $paystack_response = json_decode(wp_remote_retrieve_body($request)); - - if ( 'success' == $paystack_response->data->status ) { - $customer_code = $paystack_response->data->customer->customer_code; - - //Add logger here - $pstk_logger = new pmpro_paystack_plugin_tracker('pm-pro',$pk); - $pstk_logger->log_transaction_success($_REQUEST['trxref']); - do_action('pmpro_after_checkout', $morder->user_id, $morder); - - // There's recurring settings, lets convert to Paystack intervals now. - if ( $morder->membership_level->billing_amount > 0 ) { - - // Convert the PMPro cycle to match that of paystacks. - $pmpro_paystack = new self(); - $interval = $pmpro_paystack->convert_interval_for_paystack( $pmpro_level->cycle_period ); - - // Biannual and quarterly conversion for special cases. - if ( $morder->membership_level->cycle_number == 3 && $morder->membership_level->cycle_period == 'Month' ) { - $interval = 'quarterly'; - } - - if ( $morder->membership_level->cycle_number == 6 && $morder->membership_level->cycle_period == 'Month' ) { - $interval = 'biannually'; - } - - $amount = $morder->membership_level->billing_amount; - $koboamount = $amount*100; - //Create Plan - $paystack_url = 'https://api.paystack.co/plan'; - $subscription_url = 'https://api.paystack.co/subscription'; - $check_url = 'https://api.paystack.co/plan?amount='.$koboamount.'&interval='.$interval; - $headers = array( - 'Content-Type' => 'application/json', - 'Authorization' => 'Bearer ' . $key - ); - - $checkargs = array( - 'headers' => $headers, - 'timeout' => 60 - ); - // Check if plan exist - $checkrequest = wp_remote_get($check_url, $checkargs); - if (!is_wp_error($checkrequest)) { - $response = json_decode(wp_remote_retrieve_body($checkrequest)); - if ($response->meta->total >= 1) { - $plan = $response->data[0]; - $plancode = $plan->plan_code; - - } else { - //Create Plan - $body = array( - 'name' => '('.number_format($amount).') - '.$interval.' - ['.$pmpro_level->cycle_number.' - '.$pmpro_level->cycle_period.']' , - 'amount' => $koboamount, - 'interval' => $interval - ); - $args = array( - 'body' => json_encode($body), - 'headers' => $headers, - 'timeout' => 60 - ); - - $request = wp_remote_post($paystack_url, $args); - if (!is_wp_error($request)) { - $paystack_response = json_decode(wp_remote_retrieve_body($request)); - $plancode = $paystack_response->data->plan_code; - } - } - - } - - $subscription_delay = get_option( 'pmpro_subscription_delay_' . $pmpro_level->id, 0 ); - - $body = array( - 'customer' => $customer_code, - 'plan' => $plancode - ); - - if ( $subscription_delay ) { - if ( ! is_numeric( $subscription_delay ) ) { - $start_date = kkd_pmprosd_convert_date( $subscription_delay ); - } else { - $start_date = date( 'Y-m-d', strtotime( '+ ' . intval( $subscription_delay ) . ' Days', current_time( 'timestamp' ) ) ); - } - } else { - // $start_date = current_time( 'mysql' ); - $start_date = NULL; - } - - // If we are tweaking the start date via Subscription Delays Add On, let's set that to the subscription. - if ( ! empty( $start_date ) ) { - $body['start_date'] = apply_filters( 'pmpro_paystack_subscription_start_date', $start_date ); - } - - - $args = array( - 'body' => json_encode($body), - 'headers' => $headers, - 'timeout' => 60 - ); - - $request = wp_remote_post($subscription_url, $args); - if (!is_wp_error($request)) { - $paystack_response = json_decode(wp_remote_retrieve_body($request)); - if ( isset( $paystack_response->data->status ) && 'active' == $paystack_response->data->status ) { - $subscription_code = $paystack_response->data->subscription_code; - $token = $paystack_response->data->email_token; - $morder->subscription_transaction_id = $subscription_code; - $morder->subscription_token = $token; - $morder->saveOrder(); - } - } - } - - // get discount code (NOTE: but discount_code isn't set here. How to handle discount codes for PayPal Standard?) - $morder->getDiscountCode(); - if ( ! empty( $morder->discount_code ) ) { - // update membership level - $morder->getMembershipLevel( true ); - $discount_code_id = $morder->discount_code->id; - } else { - $discount_code_id = ''; - } - - // Get the expiration date. - if ( ! empty( $morder->membership_level->expiration_number ) ) { - $enddate = "'" . date_i18n( "Y-m-d", strtotime( "+ " . $morder->membership_level->expiration_number . " " . $morder->membership_level->expiration_period, current_time( "timestamp" ) ) ) . "'"; - } else { - $enddate = "NULL"; - } - - $custom_level = array( - 'user_id' => $morder->user_id, - 'membership_id' => $morder->membership_level->id, - 'code_id' => $discount_code_id, - 'initial_payment' => $morder->membership_level->initial_payment, - 'billing_amount' => $morder->membership_level->billing_amount, - 'cycle_number' => $morder->membership_level->cycle_number, - 'cycle_period' => $morder->membership_level->cycle_period, - 'billing_limit' => $morder->membership_level->billing_limit, - 'trial_amount' => $morder->membership_level->trial_amount, - 'trial_limit' => $morder->membership_level->trial_limit, - 'startdate' => $startdate, - 'enddate' => $enddate - ); - if ($morder->status != 'success') { - - $_REQUEST['cancel_membership'] = false; // Do NOT cancel gateway subscription - - if (pmpro_changeMembershipLevel($custom_level, $morder->user_id, 'changed')) { - $morder->membership_id = $morder->membership_level->id; - $morder->payment_transaction_id = $_REQUEST['trxref']; - $morder->status = "success"; - $morder->saveOrder(); - } - - } - // echo "
";
-                                    // print_r($morder);
-                                    // die();
-                                    //setup some values for the emails
-                                    if (!empty($morder)) {
-                                        $pmpro_invoice = new MemberOrder($morder->id);
-                                    } else {
-                                        $pmpro_invoice = null;
-                                    }
-
-                                    $current_user->membership_level = $pmpro_level; //make sure they have the right level info
-                                    $current_user->membership_level->enddate = $enddate;
-                                    if ($current_user->ID) {
-                                        $current_user->membership_level = pmpro_getMembershipLevelForUser($current_user->ID);
-                                        // echo "interesting";
-                                    }
-
-                                    //send email to member
-                                    $pmproemail = new PMProEmail();
-                                    $pmproemail->sendCheckoutEmail($current_user, $pmpro_invoice);
-
-                                    //send email to admin
-                                    $pmproemail = new PMProEmail();
-                                    $pmproemail->sendCheckoutAdminEmail($current_user, $pmpro_invoice);
-                                    // echo "
";
-                                    // print_r($pmpro_level);
-                                    $content = "
    -
  • ".__('Account', 'paystack-gateway-paid-memberships-pro').": ".$current_user->display_name." (".$current_user->user_email.")
  • -
  • ".__('Order', 'paystack-gateway-paid-memberships-pro').": ".$pmpro_invoice->code."
  • -
  • ".__('Membership Level', 'paystack-gateway-paid-memberships-pro').": ".$pmpro_level->name."
  • -
  • ".__('Amount Paid', 'paystack-gateway-paid-memberships-pro').": ".$pmpro_invoice->total." ".$pmpro_currency."
  • -
"; - ob_start(); - if (file_exists(get_stylesheet_directory() . "/paid-memberships-pro/pages/confirmation.php")) { - include get_stylesheet_directory() . "/paid-memberships-pro/pages/confirmation.php"; - } else { - include PMPRO_DIR . "/pages/confirmation.php"; - } - - $content .= ob_get_contents(); - ob_end_clean(); - } else { - $content = 'Invalid Reference'; - - } - - } else { - $content = 'Unable to Verify Transaction'; - - } - - } else { - $content = 'Invalid Transaction Reference'; - } - } - - - return $content; - - } - - static function cancelMembership(&$user){ - if (empty($user)) { - print_r("Empty user object"); - exit(); - } - $user_id = $user->ID; - $level_to_cancel = $user->membership_level->ID; - if(empty($user_id) || empty($level_to_cancel)){ - exit(); - } - global $wpdb; - $memberships_users_row = $wpdb->get_row( "SELECT * FROM $wpdb->pmpro_memberships_users WHERE user_id = '" . $user_id. "' AND membership_id = '" . $level_to_cancel . "' AND status = 'active' LIMIT 1" ); - if ( ! empty( $memberships_users_row ) ) { - - $days_grace = 0; - $new_enddate = date( 'Y-m-d H:i:s', current_time( 'timestamp' ) + 3600 * 24 * $days_grace ); - $result = $wpdb->update( $wpdb->pmpro_memberships_users, array( 'enddate' => $new_enddate ), array( - 'user_id' => $user_id, - 'membership_id' => $level_to_cancel, - 'status' => 'active' - ), array( '%s' ), array( '%d', '%d', '%s' ) ); - print_r($result); - }else{ - print_r("No records were found with user - ". $user_id." level - ". $level_to_cancel); - } - } - function cancel(&$order, $update_status = true ) - { - $backtrace = self::get_caller_info(); - $furtherbacktrace = wp_debug_backtrace_summary(); - - //no matter what happens below, we're going to cancel the order in our system - if ( $update_status ) { - $order->updateStatus( "cancelled" ); - } - - $mode = pmpro_getOption("gateway_environment"); - $code = $order->subscription_transaction_id; - if ($mode == 'sandbox') { - $key = pmpro_getOption("paystack_tsk"); - } else { - $key = pmpro_getOption("paystack_lsk"); - - } - - if ( $code != "") { - $paystack_url = 'https://api.paystack.co/subscription/' . $code; - - $headers = array( - 'Authorization' => 'Bearer ' . $key - ); - $args = array( - 'headers' => $headers, - 'timeout' => 60, - ); - - $request = wp_remote_get($paystack_url, $args); - if (!is_wp_error($request) && 200 == wp_remote_retrieve_response_code($request)) { - $paystack_response = json_decode(wp_remote_retrieve_body($request)); - if ('active' == $paystack_response->data->status && $code == $paystack_response->data->subscription_code && '1' == $paystack_response->status) { - - $paystack_url = 'https://api.paystack.co/subscription/disable'; - $headers = array( - 'Content-Type' => 'application/json', - 'Authorization' => "Bearer ".$key - ); - $body = array( - 'code' => $paystack_response->data->subscription_code, - 'token' => $paystack_response->data->email_token, - 'debug_trace'=> $backtrace . " ". $furtherbacktrace - ); - $args = array( - 'body' => json_encode($body), - 'headers' => $headers, - 'timeout' => 60, - ); - - $request = wp_remote_post($paystack_url, $args); - - if ( ! is_wp_error( $request ) ) { - return true; - } else { - return false; // There was an error cancelling for some reason. - } - } - } - } - return true; - } - - /** - * Allow "Sync" with gateway for subscriptions. - * @since 1.7.2 - */ - public function update_subscription_info( $subscription ) { - $subscription_id = $subscription->get_subscription_transaction_id(); - $backtrace = self::get_caller_info(); - $furtherbacktrace = wp_debug_backtrace_summary(); - - $mode = pmpro_getOption("gateway_environment"); - if ( $mode == "sandbox" ) { - $key = pmpro_getOption("paystack_tsk"); - - } else { - $key = pmpro_getOption("paystack_lsk"); - } - - $paystack_url = 'https://api.paystack.co/subscription/' . $subscription_id; - - $headers = array( - 'Authorization' => 'Bearer ' . $key - ); - - $args = array( - 'headers' => $headers, - 'timeout' => 60, - ); - - $request = wp_remote_get( $paystack_url, $args ); - - // Request is okay, so let's get the data now and update what we need to. - if ( ! is_wp_error( $request ) ) { - $response = json_decode( wp_remote_retrieve_body( $request ) ); - - if ( 200 !== wp_remote_retrieve_response_code( $request ) ) { - // Throw an error here from the API - return esc_html__( sprintf( 'Paystack error: %s', $response->message ), 'paystack-gateway-paid-memberships-pro' ); - } - - $update_array = array(); - $sub_info = $response->data; - - // The response status isn't active, so we're most likely already cancelled. - if ( $sub_info->status !== 'active' ) { - $update_array['status'] = 'cancelled'; // Does it - } else { - $update_array['status'] = 'active'; - } - - - // Let's make sure the cycle_numbers are correctly set based on the interval from Paystack. - switch( $sub_info->plan->interval ) { - case 'quarterly': - $update_array['cycle_number'] = 3; - break; - case 'biannually': - $update_array['cycle_number'] = 6; - break; - } - - // Update the subscription. - $update_array['next_payment_date'] = sanitize_text_field( $sub_info->next_payment_date ); // [YYYY]-[MM]-[DD - $update_array['startdate'] = sanitize_text_field( $sub_info->createdAt ); - $update_array['billing_amount'] = (float) $sub_info->amount/100; // Get currency value - $update_array['cycle_period'] = $this->convert_interval_for_pmpro( $sub_info->plan->interval ); // Convert interval for PMPro format (which sanitizes it) - $subscription->set( $update_array ); - } else { - return esc_html__( 'There was an error communicating with Paystack. Please confirm your connectivity and API details and try again.', 'paystack-gateway-paid-memberships-pro' ); - } - } - - /** - * Allow refunds from within Paid Memberships Pro and Paystack. - * @since TBD - */ - public static function process_refund( $success, $order ) { - global $current_user; - - //default to using the payment id from the order - if ( !empty( $order->payment_transaction_id ) ) { - $transaction_id = $order->payment_transaction_id; - } - - //need a transaction id - if ( empty( $transaction_id ) ) { - return false; - } - - // OKAY do the refund now. - // Make the API call to PayStack to refund the order. - $mode = pmpro_getOption("gateway_environment"); - if ( $mode == "sandbox" ) { - $key = pmpro_getOption("paystack_tsk"); - - } else { - $key = pmpro_getOption("paystack_lsk"); - } - - $paystack_url = 'https://api.paystack.co/refund/'; - - $headers = array( - 'Authorization' => 'Bearer ' . $key, - 'Cache-Control' => 'no-cache' - ); - - // The transaction ID for the refund. - $fields = array( - 'transaction' => $transaction_id - ); - - $args = array( - 'headers' => $headers, - 'timeout' => 60, - 'body' => $fields - ); - - - $success = false; - - // Try to make the API call now. - $request = wp_remote_post( $paystack_url, $args ); - - if ( ! is_wp_error( $request ) ) { - - $response = json_decode( wp_remote_retrieve_body( $request ) ); - - // If not successful throw an error. - if ( ! $response->status ) { - $order->notes = trim( $order->notes.' '.sprintf( __('Admin: Order refund failed on %1$s for transaction ID %2$s by %3$s. Order may have already been refunded.', 'paid-memberships-pro' ), date_i18n('Y-m-d H:i:s'), $transaction_id, $current_user->display_name ) ); - $order->saveOrder(); - } else { - // Set the order status to refunded and save it and return true - $order->status = 'refunded'; - - $success = true; - - $order->notes = trim( $order->notes.' '.sprintf( __('Admin: Order successfully refunded on %1$s for transaction ID %2$s by %3$s.', 'paid-memberships-pro' ), date_i18n('Y-m-d H:i:s'), $transaction_id, $current_user->display_name ) ); - - $user = get_user_by( 'id', $order->user_id ); - //send an email to the member - $myemail = new PMProEmail(); - $myemail->sendRefundedEmail( $user, $order ); - - //send an email to the admin - $myemail = new PMProEmail(); - $myemail->sendRefundedAdminEmail( $user, $order ); - - $order->saveOrder(); - } - } - - return $success; - - } - - /** - * Undocumented function - * - * @param string $interval The pmpro paystack - * @return string $interval The required interval for PayStack to recognize. - */ - function convert_interval_for_paystack( $interval ) { - - $interval = strtolower( $interval ); - - switch( $interval ) { - case 'day': - $interval = 'daily'; - break; - case 'week': - $interval = 'weekly'; - break; - case 'month': - $interval = 'monthly'; - break; - case 'year': - $interval = 'annually'; - break; - default: - $interval = 'monthly'; - } - - return $interval; - - } - - /** - * Convert Paystack's intervals for PMPro's format. - * - * @param string $interval The received Paystack interval (i.e. Weekly, Monthly etc ) - * @return string $interval The converted interval for PMPro. - */ - function convert_interval_for_pmpro( $interval ) { - - $interval = strtolower( $interval ); - - switch( $interval ) { - case 'daily': - $interval = 'Day'; - break; - case 'weekly': - $interval = 'Week'; - break; - case 'monthly': - $interval = 'Month'; - break; - case 'annually': - $interval = 'Year'; - break; - case 'quarterly': - $interval = 'Month'; - break; - case 'biannually': - $interval = 'Month'; - break; - default: - $interval = 'Month'; - } - - return $interval; - - } - - function get_caller_info() { - $c = ''; - $file = ''; - $func = ''; - $class = ''; - $trace = debug_backtrace(); - if (isset($trace[2])) { - $file = $trace[1]['file']; - $func = $trace[2]['function']; - if ((substr($func, 0, 7) == 'include') || (substr($func, 0, 7) == 'require')) { - $func = ''; - } - } else if (isset($trace[1])) { - $file = $trace[1]['file']; - $func = ''; - } - if (isset($trace[3]['class'])) { - $class = $trace[3]['class']; - $func = $trace[3]['function']; - $file = $trace[2]['file']; - } else if (isset($trace[2]['class'])) { - $class = $trace[2]['class']; - $func = $trace[2]['function']; - $file = $trace[1]['file']; - } - if ($file != '') $file = basename($file); - $c = $file . ": "; - $c .= ($class != '') ? ":" . $class . "->" : ""; - $c .= ($func != '') ? $func . "(): " : ""; - return($c); - } - } - } - } -} - -/** - * Create the log string for debugging purposes. - * - * @param string $s The error/information you want to log to the IPN log. - * @return string $logstr A formatted message for the logfile. - * - * @since TBD - */ -function pmpro_paystack_webhook_log( $s ) { - global $logstr; - $logstr .= "\t" . $s . "\n"; -} - -/** - * Write to the log file and exit. - * - * @since TBD - */ -function pmpro_paystack_webhook_exit() { - global $logstr; - - //for log - if ( $logstr ) { - $logstr = "Logged On: " . date_i18n( "m/d/Y H:i:s" ) . "\n" . $logstr . "\n-------------\n"; - - echo esc_html( $logstr ); - - //log or dont log? log in file or email? - //- dont log if constant is undefined or defined but false - //- log to file if constant is set to TRUE or 'log' - //- log to file if constant is defined to a valid email address - if ( defined( 'PMPRO_PAYSTACK_WEBHOOK_DEBUG' ) ) { - if( PMPRO_PAYSTACK_WEBHOOK_DEBUG === false ){ - //dont log here. false mean no. - //should avoid counterintuitive interpretation of false. - } elseif ( PMPRO_PAYSTACK_WEBHOOK_DEBUG === "log" ) { - //file - $logfile = apply_filters( 'pmpro_paystack_webhook_log_file', dirname( __FILE__ ) . "/logs/ipn.txt" ); - - // Check if the dir exists, if not let's create it. - $logdir = dirname( $logfile ); - if ( ! file_exists( $logdir ) ) { - mkdir( $logdir, 0775 ); - } - - // If the log file doesn't exist let's create it. - if ( ! file_exists( $logfile ) ) { - // Create a blank logfile - file_put_contents( $logfile, "" ); - } - - $loghandle = fopen( $logfile, "a+" ); - fwrite( $loghandle, $logstr ); - fclose( $loghandle ); - } elseif ( is_email( PMPRO_PAYSTACK_WEBHOOK_DEBUG ) ) { - //email to specified address - wp_mail( PMPRO_PAYSTACK_WEBHOOK_DEBUG, get_option( "blogname" ) . " IPN Log", nl2br( esc_html( $logstr ) ) ); - } else { - //email to admin - wp_mail( get_option( "admin_email" ), get_option( "blogname" ) . " IPN Log", nl2br( esc_html( $logstr ) ) ); - } - } - } - - exit; - -} diff --git a/languages/paystack-gateway-paid-memberships-pro.mo b/languages/paystack-gateway-paid-memberships-pro.mo deleted file mode 100644 index 3e27e17dd1df70fbe909dd44376cff27ddc0ac03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 559 zcmbVJJx{|h5KR$NMrH;NY~_LzAxO=KC~YW_8WL3j3Y(j_siCnW#|8RB_XO*TU(X|O`jM)yQlj*Ex24d&DZKG(>g5bGTd-HKdXtE~fILGF#iMb|Nj!uDf)6MPdU$R5Lh38q%s k-6zl+hRAc=hQb*7vNR_XRIP1\n" -"Language-Team: Paid Memberships Pro \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2024-10-09T08:26:19+00:00\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"X-Generator: WP-CLI 2.11.0\n" -"X-Domain: paystack-gateway-paid-memberships-pro\n" - -#. Plugin Name of the plugin -#: class.pmprogateway_paystack.php -msgid "Paystack Gateway for Paid Memberships Pro" -msgstr "" - -#. Plugin URI of the plugin -#: class.pmprogateway_paystack.php -msgid "https://www.paidmembershipspro.com/add-ons/paystack-gateway/" -msgstr "" - -#. Description of the plugin -#: class.pmprogateway_paystack.php -msgid "Plugin to add Paystack payment gateway into Paid Memberships Pro" -msgstr "" - -#. Author of the plugin -msgid "Paystack, Paid Memberships Pro" -msgstr "" - -#. Author URI of the plugin -#: class.pmprogateway_paystack.php -msgid "https://www.paidmembershipspro.com" -msgstr "" - -#: class.pmprogateway_paystack.php:97 -#: class.pmprogateway_paystack.php:108 -#: class.pmprogateway_paystack.php:114 -#: class.pmprogateway_paystack.php:118 -msgid "Settings" -msgstr "" - -#: class.pmprogateway_paystack.php:111 -#: class.pmprogateway_paystack.php:122 -#: class.pmprogateway_paystack.php:128 -#: class.pmprogateway_paystack.php:132 -msgid "Check Out with Paystack" -msgstr "" - -#: class.pmprogateway_paystack.php:111 -#: class.pmprogateway_paystack.php:122 -#: class.pmprogateway_paystack.php:128 -#: class.pmprogateway_paystack.php:132 -msgid "Submit and Confirm" -msgstr "" - -#: class.pmprogateway_paystack.php:124 -#: class.pmprogateway_paystack.php:135 -#: class.pmprogateway_paystack.php:141 -#: class.pmprogateway_paystack.php:145 -msgid "Paystack" -msgstr "" - -#: class.pmprogateway_paystack.php:348 -#: class.pmprogateway_paystack.php:377 -#: class.pmprogateway_paystack.php:394 -#: class.pmprogateway_paystack.php:408 -#: class.pmprogateway_paystack.php:407 -msgid "Test Secret Key" -msgstr "" - -#: class.pmprogateway_paystack.php:356 -#: class.pmprogateway_paystack.php:385 -#: class.pmprogateway_paystack.php:402 -#: class.pmprogateway_paystack.php:416 -#: class.pmprogateway_paystack.php:415 -msgid "Test Public Key" -msgstr "" - -#: class.pmprogateway_paystack.php:364 -#: class.pmprogateway_paystack.php:393 -#: class.pmprogateway_paystack.php:410 -#: class.pmprogateway_paystack.php:424 -#: class.pmprogateway_paystack.php:423 -msgid "Live Secret Key" -msgstr "" - -#: class.pmprogateway_paystack.php:372 -#: class.pmprogateway_paystack.php:401 -#: class.pmprogateway_paystack.php:418 -#: class.pmprogateway_paystack.php:432 -#: class.pmprogateway_paystack.php:431 -msgid "Live Public Key" -msgstr "" - -#: class.pmprogateway_paystack.php:380 -#: class.pmprogateway_paystack.php:409 -#: class.pmprogateway_paystack.php:426 -#: class.pmprogateway_paystack.php:440 -#: class.pmprogateway_paystack.php:439 -msgid "Webhook" -msgstr "" - -#: class.pmprogateway_paystack.php:383 -#: class.pmprogateway_paystack.php:412 -#: class.pmprogateway_paystack.php:429 -#: class.pmprogateway_paystack.php:443 -#: class.pmprogateway_paystack.php:442 -msgid "To fully integrate with Paystack, be sure to use the following for your Webhook URL to" -msgstr "" - -#: class.pmprogateway_paystack.php:852 -#: class.pmprogateway_paystack.php:930 -#: class.pmprogateway_paystack.php:948 -#: class.pmprogateway_paystack.php:975 -#: class.pmprogateway_paystack.php:973 -#: class.pmprogateway_paystack.php:984 -msgid "Account" -msgstr "" - -#: class.pmprogateway_paystack.php:853 -#: class.pmprogateway_paystack.php:931 -#: class.pmprogateway_paystack.php:949 -#: class.pmprogateway_paystack.php:976 -#: class.pmprogateway_paystack.php:974 -#: class.pmprogateway_paystack.php:985 -msgid "Order" -msgstr "" - -#: class.pmprogateway_paystack.php:854 -#: class.pmprogateway_paystack.php:932 -#: class.pmprogateway_paystack.php:950 -#: class.pmprogateway_paystack.php:977 -#: class.pmprogateway_paystack.php:975 -#: class.pmprogateway_paystack.php:986 -msgid "Membership Level" -msgstr "" - -#: class.pmprogateway_paystack.php:855 -#: class.pmprogateway_paystack.php:933 -#: class.pmprogateway_paystack.php:951 -#: class.pmprogateway_paystack.php:978 -#: class.pmprogateway_paystack.php:976 -#: class.pmprogateway_paystack.php:987 -msgid "Amount Paid" -msgstr "" - -#. Author of the plugin -#: class.pmprogateway_paystack.php -msgid "Paid Memberships Pro, Paystack" -msgstr "" - -#: class.pmprogateway_paystack.php:1119 -#: class.pmprogateway_paystack.php:1140 -#: class.pmprogateway_paystack.php:1166 -#: class.pmprogateway_paystack.php:1164 -#: class.pmprogateway_paystack.php:1175 -msgid "There was an error communicating with Paystack. Please confirm your connectivity and API details and try again." -msgstr "" diff --git a/languages/paystack-gateway-paid-memberships-pro.pot b/languages/paystack-gateway-paid-memberships-pro.pot deleted file mode 100644 index 55eb0ea..0000000 --- a/languages/paystack-gateway-paid-memberships-pro.pot +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright (C) 2024 Paid Memberships Pro, Paystack -# This file is distributed under the GPLv2 or later. -msgid "" -msgstr "" -"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.8\n" -"Report-Msgid-Bugs-To: info@paidmembershipspro.com\n" -"Last-Translator: Paid Memberships Pro \n" -"Language-Team: Paid Memberships Pro \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2024-10-09T08:26:19+00:00\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"X-Generator: WP-CLI 2.11.0\n" -"X-Domain: paystack-gateway-paid-memberships-pro\n" - -#. Plugin Name of the plugin -#: class.pmprogateway_paystack.php -msgid "Paystack Gateway for Paid Memberships Pro" -msgstr "" - -#. Plugin URI of the plugin -#: class.pmprogateway_paystack.php -msgid "https://www.paidmembershipspro.com/add-ons/paystack-gateway/" -msgstr "" - -#. Description of the plugin -#: class.pmprogateway_paystack.php -msgid "Plugin to add Paystack payment gateway into Paid Memberships Pro" -msgstr "" - -#. Author of the plugin -#: class.pmprogateway_paystack.php -msgid "Paid Memberships Pro, Paystack" -msgstr "" - -#. Author URI of the plugin -#: class.pmprogateway_paystack.php -msgid "https://www.paidmembershipspro.com" -msgstr "" - -#: class.pmprogateway_paystack.php:118 -msgid "Settings" -msgstr "" - -#: class.pmprogateway_paystack.php:132 -msgid "Check Out with Paystack" -msgstr "" - -#: class.pmprogateway_paystack.php:132 -msgid "Submit and Confirm" -msgstr "" - -#: class.pmprogateway_paystack.php:145 -msgid "Paystack" -msgstr "" - -#: class.pmprogateway_paystack.php:407 -msgid "Test Secret Key" -msgstr "" - -#: class.pmprogateway_paystack.php:415 -msgid "Test Public Key" -msgstr "" - -#: class.pmprogateway_paystack.php:423 -msgid "Live Secret Key" -msgstr "" - -#: class.pmprogateway_paystack.php:431 -msgid "Live Public Key" -msgstr "" - -#: class.pmprogateway_paystack.php:439 -msgid "Webhook" -msgstr "" - -#: class.pmprogateway_paystack.php:442 -msgid "To fully integrate with Paystack, be sure to use the following for your Webhook URL to" -msgstr "" - -#: class.pmprogateway_paystack.php:984 -msgid "Account" -msgstr "" - -#: class.pmprogateway_paystack.php:985 -msgid "Order" -msgstr "" - -#: class.pmprogateway_paystack.php:986 -msgid "Membership Level" -msgstr "" - -#: class.pmprogateway_paystack.php:987 -msgid "Amount Paid" -msgstr "" - -#: class.pmprogateway_paystack.php:1175 -msgid "There was an error communicating with Paystack. Please confirm your connectivity and API details and try again." -msgstr "" diff --git a/readme.txt b/readme.txt deleted file mode 100755 index 8aa0935..0000000 --- a/readme.txt +++ /dev/null @@ -1,127 +0,0 @@ -=== Paystack Gateway for Paid Membership Pro === -Contributors: paystack, kendysond, steveamaza, lukman008, andrewza, strangerstudios, paidmembershipspro -Donate link: https://paystack.com/demo -Tags: paid memberships pro, paystack, gateway, credit card, Naira -Requires at least: 5.2 -Tested up to: 6.6 -Stable tag: 1.7.8 -License: GPLv3 -License URI: http://www.gnu.org/licenses/gpl-3.0.html - -Pay with Paystack on Paid Membership Pro - -== Description == - -Paid Membership Pro is a complete member management and membership subscriptions plugin for WordPress. Paid Memberships Pro is designed for premium content sites, clubs/associations, subscription products, newsletters and more! - -The **Paystack Gateway for Paid Membership Pro** allows site owners from Nigeria, Ghana, Kenya and South Africa to accept payments from their customers via Paid Membership Pro. - -To be able to use the Paystack Gateway for Paid Membership Pro, you must [have an account on Paystack](https://dashboard.paystack.com) from which you will get Test and Live API keys to connect the plugin to your Paystack business. Here are some benefits of using Paystack! - -= Intuitive Dashboard = -Use the Paystack dashboard to manage your customers, payments, and track your growth. - -= Fraud Protection = -For Paystack, stopping fraud is top priority. We've used machine learning to minimize risks, reduce chargebacks and its associated costs. Paystack's fraud systems is built to learn. And so it's continually adapting to both local and international fraud. - -We screen every transaction by checking the IP, history, geolocation etc. to proactively identify fraudulent transactions. The entire network is used to prevent fraud. We learn from card and device fingerprints used to pay across different merchants. - -= Multiple Channels = -We've done all the heavy lifting such that you can immediately start accepting payments across all channels. Allow your customers make payments via their credit/debit card, bank accounts, USSD and Mobile Money. - -= Paystack Go! = -Track your business performance in the palm of your hand with Paystack Go! - This is a Progressive Web App that gives you access to your dashboard even when you are offline. You can easily look up transactions, track your businesses, and send invoices on the go. - -If your Paystack business has been activated, simply visit [go.paystack.com](https://go.paystack.com) on your mobile phone to use Paystack Go. - -= Join our growing community = - -When you download Paystack, you join a community of more than ten thousand merchants, developers, and enthusiasts. We're one of the fastest-growing open source communities online, and no matter your skill level we'd love to have you! - -If you’re interested in contributing to Paystack plugins and libraries we’ve got more than 100 contributors, and there’s always room for more. Head over to the [Paystack GitHub Repository](https://github.com/paystackHQ/) to find out how you can pitch in. - -We also have a developer community on Slack where we share product announcements, private events and discuss contributions to open source library and plugins. Join the Payslack Community [here](https://payslack.slack.com/). - -== Installation == - -= Minimum Requirements = - -* Confirm that your server can conclude a TLSv1.2 connection to Paystack's servers. More information about this requirement can be gleaned here: [TLS v1.2 requirement](https://developers.paystack.co/blog/tls-v12-requirement). -* Installed and activated [Paid Membership Pro Plugin] (https://www.paidmembershipspro.com) - -= Manual installation = - -The manual installation method involves downloading our payment plugin and uploading it to your webserver via your favourite FTP application. The WordPress codex contains [instructions on how to do this here](https://codex.wordpress.org/Managing_Plugins#Manual_Plugin_Installation). - - -== Frequently Asked Questions == - -= Where can I find help and documentation to understand Paystack? = - -You can find help and information on Paystack on our [Help Desk](https://paystack.com/help) - -= Where can I get support or talk to other users? = - -If you get stuck, you can ask for help in the [Paystack Gateway for Paid Membership Pro Plugin Forum](https://wordpress.org/support/plugin/paystack-gateway-paid-memberships-pro). - -= Paystack Gateway for Paid Membership Pro is awesome! Can I contribute? = - -Yes you can! Join in on our [GitHub repository](https://github.com/strangerstudios/paystack-gateway-paid-memberships-pro) - -== Screenshots == - -1. The slick Paystack settings panel. - -== Changelog == -= 1.7.8 - 2024-10-09 = -* BUG FIX: Fixed an issue where free checkouts would cause a fatal error. - -= 1.7.7 - 2024-07-19 = -* ENHANCEMENT: Added support for Paid Memberships Pro V3.1+ and used `pmpro_get_element_class` method on the frontend. (@andrewlimaza) -* BUG FIX: Fixed an issue where cancellations weren't working correctly in some cases/instances. (@andrewlimaza) -* BUG FIX: Fixed an issue where billing would bill later in the day at the start of the subscription. (@andrewlimaza) - -= 1.7.6 - 2024-07-03 = -* ENHANCEMENT: Added webhook logging functionality to debug incoming webhook data. You may use the constant 'PMPRO_PAYSTACK_WEBHOOK_LOG' to enable this feature. Please delete logs and disable this constant after completing debugging. -* BUG FIX: Fixed an issue where the webhook handler was not correctly storing the order amount if the subscription amount changed. -* BUG FIX: Fixed an issue when renewal payments were being processed and failing to complete the order. - -= 1.7.4 - 2024-04-24 = -* ENHANCEMENT: Added new filter `pmpro_paystack_webhook_level` to tweak the level given to members after checkout. This includes support for the Set Expiration Dates Add On. - -= 1.7.3 - 2024-04-15 = -* SECURITY: Improved sanitization to output of translatable strings. -* ENHANCEMENT: Added functionality for refunds within Paid Memberships Pro. Supports full refunds only. -* BUG FIX: Fixed an issue where subscriptions weren't being linked inside Paid Memberships Pro correctly when confirming the membership. - -= 1.7.2 - 2024-02-15 = -* ENHANCEMENT: Added support for Paid Memberships Pro 3.0+ subscriptions. -* BUG FIX: Fixed an issue where discount codes were not reflecting on Paid Memberships Pro side. - -= 1.7.1 - 2023-09-13 = -* SECURITY: Improved security to the webhook handler, this now checks for the presence of the Paystack signature header before processing the request. -* ENHANCEMENT: Only allow card checkout for recurring subscriptions as other payment options don't allow subscriptions. -* BUG FIX: Fixed an issue where intervals weren't being set correctly. This now supports all intervals, for quarterly and biannually please use 3 month and 6 month in the recurring subscription fields respectively. -* BUG FIX: Fixed an issue where non-expiring memberships would obtain an expiration date incorrectly. -* REFACTOR: Minor improvements to the settings UI page to align with other Paid Memberships Pro gateways. - -= 1.4 = -* Add quarterly subscription option for recurring payments set to 3 months or 90 days cycle period. - -= 1.5 = -* Added plugin metrics tracker -* Add biannual subscription option for recurring payments set to 6 months or 180 days cycle period. - - -= 1.6.0 = -* Add functionalty to delay subscriptions renewal using the PM Pro Subscription Delays addon. -= 1.6.1 = -* BUG-FIX - Disabled non-recurring payment methods like USSD, QR for subscription plans etc. -= 1.6.2 = -* Implement webhook to listen for cancelled subscriptions. -= 1.6.3 = -* BUG FIX - Fix issue where setting webhook URL automatically cancels customer subscriptions. -= 1.7.0 = -* BUG FIX - Fix issue where an 'Invalid Reference' error is being thrown when discount codes are used. -* BUG FIX - Fix issue where end date for subbscriptions is '1970-01-01'. -* Compatibility with WordPress 5.9 From efbdf196c6cdb3f618650cf3928b20a80a80b3f4 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 18 Oct 2024 14:29:09 +0200 Subject: [PATCH 67/91] Initial commit for the refactor --- .DS_Store | Bin 0 -> 6148 bytes classes/class.pmprogateway_paystack.php | 657 ++++++++++++++++++++++ classes/paystack-tracker.php | 42 ++ paystack-gateway-paid-memberships-pro.php | 124 ++++ services/paystack-webhook.php | 439 +++++++++++++++ 5 files changed, 1262 insertions(+) create mode 100644 .DS_Store create mode 100644 classes/class.pmprogateway_paystack.php create mode 100644 classes/paystack-tracker.php create mode 100644 paystack-gateway-paid-memberships-pro.php create mode 100644 services/paystack-webhook.php diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..8372f54f8a9fde228b4f1b868d10fe190aac6a4e GIT binary patch literal 6148 zcmeHKJ5Iwu5Su1a3B_cCejQd1ABFdqRoi2uNg#D~7=|l@V4X-hq^oN&q zHE%_#;jc2l&n}}wn$Q(pC(rNr<+Y^xTbqQO3aeMQAH+niv{o^0~^J(MVIOq3` zwm<9AlonJb)oJ&n9nsvw!`s2$>-{6|VP&1F{@Tg414B3h05h0_U>^SMfR+tF7tssC0x^;blvERs7)H`z zw=vE|^n#L3CRD~cv9gIL6ceh$ZbLX3C#a(`pbVr8Y{_Mh`~TVc^M9J8SIU4guu%+{ ztQZyp+*0hVt()UsYoQ}33&(lEWeO&t6(g2g@jf&N?3O1$7tssC0?{9VK!Xm-z^^j! E0r^HtE&u=k literal 0 HcmV?d00001 diff --git a/classes/class.pmprogateway_paystack.php b/classes/class.pmprogateway_paystack.php new file mode 100644 index 0000000..354a84f --- /dev/null +++ b/classes/class.pmprogateway_paystack.php @@ -0,0 +1,657 @@ +gateway = $gateway; + return $this->gateway; + } + + /** + * Run on WP init. + * This method will run all the necessary gateway hooks that are needed. + */ + public static function init() { + + // Make sure Paystack is a gateway option. + add_filter( 'pmpro_gateways', array( 'PMProGateway_paystack', 'pmpro_gateways' ) ); + + // Add fields to payment settings. + add_filter( 'pmpro_payment_options', array( 'PMProGateway_Paystack', 'pmpro_payment_options' ) ); + add_filter( 'pmpro_payment_option_fields', array( 'PMProGateway_Paystack', 'pmpro_payment_option_fields' ), 10, 2 ); + add_action( 'wp_ajax_pmpro_paystack_ipn', array( 'PMProGateway_Paystack', 'pmpro_paystack_ipn' ) ); + add_action( 'wp_ajax_nopriv_pmpro_paystack_ipn', array( 'PMProGateway_Paystack', 'pmpro_paystack_ipn' ) ); + + // Keeping the deprecated action for backwards compatibility. + add_action( 'wp_ajax_kkd_pmpro_paystack_ipn', array( 'PMProGateway_Paystack', 'kkd_pmpro_paystack_ipn' ) ); + add_action( 'wp_ajax_nopriv_kkd_pmpro_paystack_ipn', array( 'PMProGateway_Paystack', 'kkd_pmpro_paystack_ipn' ) ); + + // Adjust the confirmation message when waiting for Paystack to process the payment. + add_filter( 'pmpro_confirmation_payment_incomplete_message', array( 'PMProGateway_Paystack', 'pmpro_confirmation_incomplete_message' ), 10, 2 ); + + //code to add at checkout + $gateway = pmpro_getGateway(); + if ( $gateway == 'paystack' ) { + add_filter( 'pmpro_include_billing_address_fields', '__return_false' ); + add_filter( 'pmpro_include_payment_information_fields', '__return_false' ); + add_filter( 'pmpro_billing_show_payment_method', '__return_false' ); + + // Refund functionality. + add_filter( 'pmpro_allowed_refunds_gateways', array( 'PMProGateway_Paystack', 'pmpro_allowed_refunds_gateways' ) ); + add_filter( 'pmpro_process_refund_paystack', array( 'PMProGateway_Paystack', 'process_refund' ), 10, 2 ); + } + + } + + /** + * Add Paystack to the gateway list for PMPro + */ + public static function pmpro_gateways( $gateways ) { + if ( empty( $gateways['paystack'] ) ) { + $gateways['paystack'] = __( 'Paystack', 'text-domain' ); + } + + return $gateways; + } + + /** + * Wrapper function for newly named function instead to be more inline with PMPro naming conventions. + * DEPRECATED use pmpro_paystack_ipn instead. + * @since 1.0 + */ + static function kkd_pmpro_paystack_ipn() { + pmpro_paystack_ipn(); + } + + /** + * Webhook handler for Paystack. + * @since 1.0 (Renamed in 1.7.1) + */ + static function pmpro_paystack_ipn() { + require_once PMPRO_PAYSTACK_DIR . 'services/paystack-webhook.php'; + exit; + } + + /** + * Check whether or not a gateway supports a specific feature. + * + * @param string $feature The feature we need to check if it is supported. + * @return string|boolean $supports In some cases, we may need to return strings for the feature or a boolean value if it's supported or not. + */ + public static function supports( $feature ) { + $supports = array( + 'subscription_sync' => true, + 'payment_method_updates' => false + ); + + if ( empty( $supports[$feature] ) ) { + return false; + } + + return $supports[$feature]; + } + + /** + * Get a list of payment options that the Paystack gateway needs/supports. + */ + static function getGatewayOptions() { + $options = array ( + 'paystack_tsk', + 'paystack_tpk', + 'paystack_lsk', + 'paystack_lpk', + 'currency', + 'tax_state', + 'tax_rate' + ); + + return $options; + } + + /** + * Set payment options for payment settings page. + */ + static function pmpro_payment_options( $options ) { + //get Paystack options + $paystack_options = self::getGatewayOptions(); + + //merge with others. + $options = array_merge( $paystack_options, $options ); + + return $options; + } + + /** + * Display fields for Paystack options. + */ + static function pmpro_payment_option_fields( $values, $gateway ) { + ?> + style="display: none;"> + + + + + + + + style="display: none;"> + + + + + + + + style="display: none;"> + + + + + + + + style="display: none;"> + + + + + + + + style="display: none;"> + + + + +


+ + + + code ) ) { + $order->code = $order->getRandomCode(); + } + + // clean up some values and save the token order so we can complete it later. + $order->payment_type = 'Paystack'; + $order->CardType = ''; + $order->cardtype = ''; + $order->status = 'token'; + $order->saveOrder(); + + pmpro_save_checkout_data_to_order( $order ); + + do_action( 'pmpro_before_send_to_paystack', $order->user_id, $order ); + + $this->sendToPaystack( $order ); + } + + /** + * Redirect to Paystack to charge the payment. + */ + + /// Todo: Refactor using newew order code methods. + function sendToPaystack( &$order ) { + global $pmpro_currency; + + // Use this filter for other Add On compatibility that may use this filter. + do_action( 'pmpro_paypalexpress_session_vars' ); + + $params = array(); + $amount = $order->PaymentAmount; // This must change as well. + $amount_tax = $order->getTaxForPrice($amount); + $amount = round((float)$amount + (float)$amount_tax, 2); + + $amount = floatval($order->InitialPayment); /// This must change. + + $mode = pmpro_getOption("gateway_environment"); + if ($mode == 'sandbox') { + $key = pmpro_getOption("paystack_tsk"); + $pk = pmpro_getOption("paystack_tpk"); + } else { + $key = pmpro_getOption("paystack_lsk"); + $pk = pmpro_getOption("paystack_lpk"); + } + if ($key == '') { + echo "Api keys not set"; + } + + $koboamount = $amount*100; + + + $paystack_url = 'https://api.paystack.co/transaction/initialize'; + $headers = array( + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer '.$key + ); + + //Create Plan + $body = array( + 'email' => $order->Email, + 'amount' => $koboamount, + 'reference' => $order->code, + 'currency' => $pmpro_currency, + 'callback_url' => pmpro_url("confirmation", "?level=" . $order->membership_level->id), + 'metadata' => json_encode(array('custom_fields' => array( + array( + "display_name"=>"Plugin", + "variable_name"=>"plugin", + "value"=>"pm-pro" + ), + + ), 'custom_filters' => array("recurring" => true))), + ); + + // If the level is recurring only allow card payments for the subscription as other methods don't work. + $level = $order->getMembershipLevel(); + if ( pmpro_isLevelRecurring( $level ) ) { + $body['channels'] = array( 'card' ); + } + + $args = array( + 'body' => json_encode($body), + 'headers' => $headers, + 'timeout' => 60 + ); + + $request = wp_remote_post( $paystack_url, $args ); + // print_r($request); + if (!is_wp_error($request)) { + $paystack_response = json_decode( wp_remote_retrieve_body($request )); + if ($paystack_response->status){ + $url = $paystack_response->data->authorization_url; + wp_redirect($url); + exit; + } else { + wp_redirect(pmpro_url("checkout", "?level=" . $order->membership_level->id . "&error=" . $paystack_response->message)); + exit(); + } + } else { + wp_redirect(pmpro_url("checkout", "?level=" . $order->membership_level->id . "&error=Failed")); + exit(); + } + exit; + } + + /** + * Allow "Sync" with gateway for subscriptions. + * @since 1.7.2 + */ + public function update_subscription_info( $subscription ) { + $subscription_id = $subscription->get_subscription_transaction_id(); + $backtrace = self::get_caller_info(); + $furtherbacktrace = wp_debug_backtrace_summary(); + + $mode = get_option( 'pmpro_gateway_environment'); + if ( $mode == 'sandbox' ) { + $key = get_option( 'pmpro_paystack_tsk' ); + } else { + $key = get_option( 'pmpro_paystack_lsk' ); + } + + $paystack_url = 'https://api.paystack.co/subscription/' . $subscription_id; + + $headers = array( + 'Authorization' => 'Bearer ' . $key + ); + + $args = array( + 'headers' => $headers, + 'timeout' => 60, + ); + + $request = wp_remote_get( $paystack_url, $args ); + + // Request is okay, so let's get the data now and update what we need to. + if ( ! is_wp_error( $request ) ) { + $response = json_decode( wp_remote_retrieve_body( $request ) ); + + if ( 200 !== wp_remote_retrieve_response_code( $request ) ) { + // Throw an error here from the API + return esc_html__( sprintf( 'Paystack error: %s', $response->message ), 'paystack-gateway-paid-memberships-pro' ); + } + + $update_array = array(); + $sub_info = $response->data; + + // The response status isn't active, so we're most likely already cancelled. + if ( $sub_info->status !== 'active' ) { + $update_array['status'] = 'cancelled'; // Does it + } else { + $update_array['status'] = 'active'; + } + + + // Let's make sure the cycle_numbers are correctly set based on the interval from Paystack. + switch( $sub_info->plan->interval ) { + case 'quarterly': + $update_array['cycle_number'] = 3; + break; + case 'biannually': + $update_array['cycle_number'] = 6; + break; + } + + // Update the subscription. + $update_array['next_payment_date'] = sanitize_text_field( $sub_info->next_payment_date ); // [YYYY]-[MM]-[DD + $update_array['startdate'] = sanitize_text_field( $sub_info->createdAt ); + $update_array['billing_amount'] = (float) $sub_info->amount/100; // Get currency value + $update_array['cycle_period'] = $this->convert_interval_for_pmpro( $sub_info->plan->interval ); // Convert interval for PMPro format (which sanitizes it) + $subscription->set( $update_array ); + } else { + return esc_html__( 'There was an error communicating with Paystack. Please confirm your connectivity and API details and try again.', 'paystack-gateway-paid-memberships-pro' ); + } + } + + /** + * Allow refunds from within Paid Memberships Pro and Paystack. + * @since TBD + */ + public static function process_refund( $success, $order ) { + global $current_user; + + //default to using the payment id from the order + if ( !empty( $order->payment_transaction_id ) ) { + $transaction_id = $order->payment_transaction_id; + } + + //need a transaction id + if ( empty( $transaction_id ) ) { + return false; + } + + // OKAY do the refund now. + // Make the API call to PayStack to refund the order. + $mode = pmpro_getOption("gateway_environment"); + if ( $mode == "sandbox" ) { + $key = pmpro_getOption("paystack_tsk"); + + } else { + $key = pmpro_getOption("paystack_lsk"); + } + + $paystack_url = 'https://api.paystack.co/refund/'; + + $headers = array( + 'Authorization' => 'Bearer ' . $key, + 'Cache-Control' => 'no-cache' + ); + + // The transaction ID for the refund. + $fields = array( + 'transaction' => $transaction_id + ); + + $args = array( + 'headers' => $headers, + 'timeout' => 60, + 'body' => $fields + ); + + $success = false; + + // Try to make the API call now. + $request = wp_remote_post( $paystack_url, $args ); + + if ( ! is_wp_error( $request ) ) { + + $response = json_decode( wp_remote_retrieve_body( $request ) ); + + // If not successful throw an error. + if ( ! $response->status ) { + $order->notes = trim( $order->notes.' '.sprintf( __('Admin: Order refund failed on %1$s for transaction ID %2$s by %3$s. Order may have already been refunded.', 'paid-memberships-pro' ), date_i18n('Y-m-d H:i:s'), $transaction_id, $current_user->display_name ) ); + $order->saveOrder(); + } else { + // Set the order status to refunded and save it and return true + $order->status = 'refunded'; + + $success = true; + + $order->notes = trim( $order->notes.' '.sprintf( __('Admin: Order successfully refunded on %1$s for transaction ID %2$s by %3$s.', 'paid-memberships-pro' ), date_i18n('Y-m-d H:i:s'), $transaction_id, $current_user->display_name ) ); + + $user = get_user_by( 'id', $order->user_id ); + //send an email to the member + $myemail = new PMProEmail(); + $myemail->sendRefundedEmail( $user, $order ); + + //send an email to the admin + $myemail = new PMProEmail(); + $myemail->sendRefundedAdminEmail( $user, $order ); + + $order->saveOrder(); + } + } + + return $success; + } + + /** + * Enable refund functionality for paystack. + * @since TBD. + */ + static function pmpro_allowed_refunds_gateways( $gateways ) { + $gateways[] = 'paystack'; + return $gateways; + } + + /** + * Change the confirmation message, as Paystack's webhook notification may take a few seconds. + * @since TBD + */ + public static function pmpro_confirmation_incomplete_message( $message, $pmpro_invoice ) { + + if ( $pmpro_invoice->gateway != 'paystack' ) { + return $message; + } + + $message .= ' ' . esc_html__( 'This may take a few seconds.', 'text-domain' ); + + return $message; + } + + /** + * Undocumented function + * + * @param string $interval The pmpro paystack + * @return string $interval The required interval for PayStack to recognize. + */ + function convert_interval_for_paystack( $interval ) { + + $interval = strtolower( $interval ); + + switch( $interval ) { + case 'day': + $interval = 'daily'; + break; + case 'week': + $interval = 'weekly'; + break; + case 'month': + $interval = 'monthly'; + break; + case 'year': + $interval = 'annually'; + break; + default: + $interval = 'monthly'; + } + + return $interval; + + } + + /** + * Convert Paystack's intervals for PMPro's format. + * + * @param string $interval The received Paystack interval (i.e. Weekly, Monthly etc ) + * @return string $interval The converted interval for PMPro. + */ + function convert_interval_for_pmpro( $interval ) { + + $interval = strtolower( $interval ); + + switch( $interval ) { + case 'daily': + $interval = 'Day'; + break; + case 'weekly': + $interval = 'Week'; + break; + case 'monthly': + $interval = 'Month'; + break; + case 'annually': + $interval = 'Year'; + break; + case 'quarterly': + $interval = 'Month'; + break; + case 'biannually': + $interval = 'Month'; + break; + default: + $interval = 'Month'; + } + + return $interval; + + } + + // Get Caller info for debugging. + function get_caller_info() { + $c = ''; + $file = ''; + $func = ''; + $class = ''; + $trace = debug_backtrace(); + if (isset($trace[2])) { + $file = $trace[1]['file']; + $func = $trace[2]['function']; + if ((substr($func, 0, 7) == 'include') || (substr($func, 0, 7) == 'require')) { + $func = ''; + } + } else if (isset($trace[1])) { + $file = $trace[1]['file']; + $func = ''; + } + if (isset($trace[3]['class'])) { + $class = $trace[3]['class']; + $func = $trace[3]['function']; + $file = $trace[2]['file']; + } else if (isset($trace[2]['class'])) { + $class = $trace[2]['class']; + $func = $trace[2]['function']; + $file = $trace[1]['file']; + } + if ($file != '') $file = basename($file); + $c = $file . ": "; + $c .= ($class != '') ? ":" . $class . "->" : ""; + $c .= ($func != '') ? $func . "(): " : ""; + return($c); + } + +} // End of class PMProGateway_Paystack + + +/// Probably needs to go elsewhere? +/** + * Create the log string for debugging purposes. + * + * @param string $s The error/information you want to log to the IPN log. + * @return string $logstr A formatted message for the logfile. + * + * @since TBD + */ +function pmpro_paystack_webhook_log( $s ) { + global $logstr; + $logstr .= "\t" . $s . "\n"; +} + +/** + * Write to the log file and exit. + * + * @since TBD + */ +function pmpro_paystack_webhook_exit() { + global $logstr; + + //for log + if ( $logstr ) { + $logstr = "Logged On: " . date_i18n( "m/d/Y H:i:s" ) . "\n" . $logstr . "\n-------------\n"; + + echo esc_html( $logstr ); + + //log or dont log? log in file or email? + //- dont log if constant is undefined or defined but false + //- log to file if constant is set to TRUE or 'log' + //- log to file if constant is defined to a valid email address + if ( defined( 'PMPRO_PAYSTACK_WEBHOOK_DEBUG' ) ) { + if( PMPRO_PAYSTACK_WEBHOOK_DEBUG === false ){ + //dont log here. false mean no. + //should avoid counterintuitive interpretation of false. + } elseif ( PMPRO_PAYSTACK_WEBHOOK_DEBUG === "log" ) { + //file + + $logfile = apply_filters( 'pmpro_paystack_webhook_log_file', PMPRO_PAYSTACK_DIR . "logs/ipn.txt" ); + + // Check if the dir exists, if not let's create it. + $logdir = dirname( $logfile ); + if ( ! file_exists( $logdir ) ) { + mkdir( $logdir, 0775 ); + } + + // If the log file doesn't exist let's create it. + if ( ! file_exists( $logfile ) ) { + // Create a blank logfile + file_put_contents( $logfile, "" ); + } + + $loghandle = fopen( $logfile, "a+" ); + fwrite( $loghandle, $logstr ); + fclose( $loghandle ); + } elseif ( is_email( PMPRO_PAYSTACK_WEBHOOK_DEBUG ) ) { + //email to specified address + wp_mail( PMPRO_PAYSTACK_WEBHOOK_DEBUG, get_option( "blogname" ) . " IPN Log", nl2br( esc_html( $logstr ) ) ); + } else { + //email to admin + wp_mail( get_option( "admin_email" ), get_option( "blogname" ) . " IPN Log", nl2br( esc_html( $logstr ) ) ); + } + } + } + + exit; + +} diff --git a/classes/paystack-tracker.php b/classes/paystack-tracker.php new file mode 100644 index 0000000..f09528d --- /dev/null +++ b/classes/paystack-tracker.php @@ -0,0 +1,42 @@ +plugin_name = $plugin; + $this->public_key = $pk; + } + + + + function log_transaction_success($trx_ref){ + //send reference to logger along with plugin name and public key + $url = "https://plugin-tracker.paystackintegrations.com/log/charge_success"; + + $fields = [ + 'plugin_name' => $this->plugin_name, + 'transaction_reference' => $trx_ref, + 'public_key' => $this->public_key + ]; + + $fields_string = http_build_query($fields); + + $ch = curl_init(); + + curl_setopt($ch,CURLOPT_URL, $url); + curl_setopt($ch,CURLOPT_POST, true); + curl_setopt($ch,CURLOPT_POSTFIELDS, $fields_string); + + curl_setopt($ch,CURLOPT_RETURNTRANSFER, true); + + //execute post + $result = curl_exec($ch); + // echo $result; + } +} + +?> diff --git a/paystack-gateway-paid-memberships-pro.php b/paystack-gateway-paid-memberships-pro.php new file mode 100644 index 0000000..959cbff --- /dev/null +++ b/paystack-gateway-paid-memberships-pro.php @@ -0,0 +1,124 @@ + 0 ? $set_y : $current_y; +$temp_m = $set_m > 0 ? $set_m : $current_m; +$temp_d = $set_d; + +// Add months. +if(!empty($add_months)) { +for($i = 0; $i < $add_months; $i++) { + // If "M1", only add months if current date of month has already passed. + if(0 == $i) { + if($temp_d < $current_d) { + $temp_m++; + $add_months--; + } + } else { + $temp_m++; + } + + // If we hit 13, reset to Jan of next year and subtract one of the years to add. + if($temp_m == 13) { + $temp_m = 1; + $temp_y++; + $add_years--; + } +} +} + +// Add years. +if(!empty($add_years)) { +for($i = 0; $i < $add_years; $i++) { + // If "Y1", only add years if current date has already passed. + if(0 == $i) { + $temp_date = strtotime(date("{$temp_y}-{$temp_m}-{$temp_d}")); + if($temp_date < $current_date) { + $temp_y++; + $add_years--; + } + } else { + $temp_y++; + } +} +} + +// Pad dates if necessary. +$temp_m = str_pad($temp_m, 2, '0', STR_PAD_LEFT); +$temp_d = str_pad($temp_d, 2, '0', STR_PAD_LEFT); + +// Put it all together. +$set_date = date("{$temp_y}-{$temp_m}-{$temp_d}"); + +// Make sure we use the right day of the month for dates > 28 +// From: http://stackoverflow.com/a/654378/1154321 +$dotm = pmpro_getMatches('/\-([0-3][0-9]$)/', $set_date, true); +if ( $temp_m == '02' && intval($dotm) > 28 || intval($dotm) > 30 ) { +$set_date = date('Y-m-t', strtotime(substr($set_date, 0, 8) . "01")); +} + + + +return $set_date; +} + diff --git a/services/paystack-webhook.php b/services/paystack-webhook.php new file mode 100644 index 0000000..b142324 --- /dev/null +++ b/services/paystack-webhook.php @@ -0,0 +1,439 @@ +event ){ +case 'subscription.create': + // This also runs from time to time? I think this is the first time it runs. + pmpro_paystack_renew_payment( $post_event ); + break; +case 'subscription.disable': /// Cancel the membership since it's done via the IPN. + pmpro_handle_subscription_cancellation_at_gateway( $subscription_id, 'paystack', $gateway_environment ); + pmpro_paystack_webhook_exit(); + break; +case 'charge.success': // This can change too. + $morder = new MemberOrder($post_event->data->reference); + $morder->getMembershipLevel(); + $morder->getUser(); + pmpro_paystack_complete_order( $post_event->data->reference, $morder ); + pmpro_paystack_confirm_subscription( $post_event->data->reference, $morder ); // This will be for recurring subscriptions/levels. + $pstk_logger = new pmpro_paystack_plugin_tracker( 'pm-pro', $public_key ); + $pstk_logger->log_transaction_success( $post_event->data->reference ); + pmpro_paystack_webhook_log( 'Charge success. Reference: ' . $post_event->data->reference ); + break; +case 'invoice.create': + pmpro_paystack_renew_payment($post_event); + break; +case 'invoice.update': + pmpro_paystack_renew_payment($post_event); + break; +} +http_response_code(200); +pmpro_paystack_webhook_exit(); + + +/** + * Complete the + * + * @param [type] $reference + * @param [type] $morder + * @return void + */ +function pmpro_paystack_complete_order( $reference, &$order ) { + + // Only run this if we got an order. + if ( empty( $order ) ) { + return false; + } + + // update order status and transaction ids + $order->payment_transaction_id = $reference; + + /// Change this. + if ( ! empty( $_POST['token'] ) ) { + $order->subscription_transaction_id = sanitize_text_field( $_POST['m_payment_id'] ); + } + + $order->saveOrder(); // Temporarily save the order before processing it. + + + // Change level and complete the order. + pmpro_pull_checkout_data_from_order( $order ); + return pmpro_complete_async_checkout( $order ); + +} + +// Confirm the subscription +function pmpro_paystack_confirm_subscription( $reference, $order ) { + global $wpdb, $current_user, $pmpro_invoice, $pmpro_currency,$gateway; + + if (empty($pmpro_invoice)) { + $morder = new MemberOrder($reference); + if (!empty($morder) && $morder->gateway == "paystack") $pmpro_invoice = $morder; + } + + if (!empty($pmpro_invoice) && $pmpro_invoice->gateway == "paystack" && isset($pmpro_invoice->total) && $pmpro_invoice->total > 0) { + $morder = $pmpro_invoice; + if ($morder->code == $reference ) { + + /// Use pmpro_getLevel instead of a DB query. + $pmpro_level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . (int)$morder->membership_id . "' LIMIT 1"); + + /// Don't need filters. + $pmpro_level = apply_filters("pmpro_checkout_level", $pmpro_level); + $startdate = apply_filters("pmpro_checkout_start_date", "'" . current_time("mysql") . "'", $morder->user_id, $pmpro_level); + + // The level object from the order that can be filtered when returning from Paystack and user is getting their membership level. + /// Deprecate this. + $morder->membership_level = apply_filters( 'pmpro_paystack_webhook_level', $morder->membership_level, $morder->user_id ); + + /// Fix this mode? + $mode = pmpro_getOption("gateway_environment"); + if ($mode == "sandbox") { + $key = pmpro_getOption("paystack_tsk"); + $pk = pmpro_getOption("paystack_tpk"); + } else { + $key = pmpro_getOption("paystack_lsk"); + $pk = pmpro_getOption("paystack_lpk"); + } + $paystack_url = 'https://api.paystack.co/transaction/verify/' . $reference; + $headers = array( + 'Authorization' => 'Bearer ' . $key + ); + $args = array( + 'headers' => $headers, + 'timeout' => 60 + ); + $request = wp_remote_get( $paystack_url, $args ); + + if (!is_wp_error($request) && 200 == wp_remote_retrieve_response_code($request) ) { + $paystack_response = json_decode(wp_remote_retrieve_body($request)); + + if ( 'success' == $paystack_response->data->status ) { + $customer_code = $paystack_response->data->customer->customer_code; + + //Add logger here + $pstk_logger = new pmpro_paystack_plugin_tracker('pm-pro',$pk); + $pstk_logger->log_transaction_success($reference); + + // Get level from the order. + $pmpro_level = $morder->getMembershipLevel(); + + // There's recurring settings, lets convert to Paystack intervals now. + if ( $pmpro_level->billing_amount > 0 ) { + + // Convert the PMPro cycle to match that of paystacks. + $pmpro_paystack = new PMProGateway_paystack(); + $interval = $pmpro_paystack->convert_interval_for_paystack( $pmpro_level->cycle_period ); + + // Biannual and quarterly conversion for special cases. + if ( $pmpro_level->cycle_number == 3 && $pmpro_level->cycle_period == 'Month' ) { + $interval = 'quarterly'; + } + + if ( $pmpro_level->cycle_number == 6 && $pmpro_level->cycle_period == 'Month' ) { + $interval = 'biannually'; + } + + $amount = $pmpro_level->billing_amount; + $koboamount = $amount*100; + //Create Plan + $paystack_url = 'https://api.paystack.co/plan'; + $subscription_url = 'https://api.paystack.co/subscription'; + $check_url = 'https://api.paystack.co/plan?amount='.$koboamount.'&interval='.$interval; + $headers = array( + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $key + ); + + $checkargs = array( + 'headers' => $headers, + 'timeout' => 60 + ); + // Check if plan exist + $checkrequest = wp_remote_get($check_url, $checkargs); + if (!is_wp_error($checkrequest)) { + $response = json_decode(wp_remote_retrieve_body($checkrequest)); + if ($response->meta->total >= 1) { + $plan = $response->data[0]; + $plancode = $plan->plan_code; + + } else { + //Create Plan + $body = array( + 'name' => '('.number_format($amount).') - '.$interval.' - ['.$pmpro_level->cycle_number.' - '.$pmpro_level->cycle_period.']' , + 'amount' => $koboamount, + 'interval' => $interval + ); + $args = array( + 'body' => json_encode($body), + 'headers' => $headers, + 'timeout' => 60 + ); + + $request = wp_remote_post($paystack_url, $args); + if (!is_wp_error($request)) { + $paystack_response = json_decode(wp_remote_retrieve_body($request)); + $plancode = $paystack_response->data->plan_code; + } + } + + } + + $subscription_delay = get_option( 'pmpro_subscription_delay_' . $pmpro_level->id, 0 ); + + $body = array( + 'customer' => $customer_code, + 'plan' => $plancode + ); + + if ( $subscription_delay ) { + if ( ! is_numeric( $subscription_delay ) ) { + $start_date = kkd_pmprosd_convert_date( $subscription_delay ); + } else { + $start_date = date( 'Y-m-d', strtotime( '+ ' . intval( $subscription_delay ) . ' Days', current_time( 'timestamp' ) ) ); + } + } else { + // $start_date = current_time( 'mysql' ); + $start_date = NULL; + } + + // If we are tweaking the start date via Subscription Delays Add On, let's set that to the subscription. + if ( ! empty( $start_date ) ) { + $body['start_date'] = apply_filters( 'pmpro_paystack_subscription_start_date', $start_date ); + } + + + $args = array( + 'body' => json_encode($body), + 'headers' => $headers, + 'timeout' => 60 + ); + + $request = wp_remote_post($subscription_url, $args); + if (!is_wp_error($request)) { + $paystack_response = json_decode(wp_remote_retrieve_body($request)); + if ( isset( $paystack_response->data->status ) && 'active' == $paystack_response->data->status ) { + $subscription_code = $paystack_response->data->subscription_code; + $token = $paystack_response->data->email_token; + $morder->subscription_transaction_id = $subscription_code; + $morder->subscription_token = $token; + $morder->saveOrder(); + } + } + } + + // get discount code (NOTE: but discount_code isn't set here. How to handle discount codes for PayPal Standard?) + $morder->getDiscountCode(); + if ( ! empty( $morder->discount_code ) ) { + // update membership level + $morder->getMembershipLevel( true ); + $discount_code_id = $morder->discount_code->id; + } else { + $discount_code_id = ''; + } + + // Get the expiration date. + if ( ! empty( $morder->membership_level->expiration_number ) ) { + $enddate = "'" . date_i18n( "Y-m-d", strtotime( "+ " . $morder->membership_level->expiration_number . " " . $morder->membership_level->expiration_period, current_time( "timestamp" ) ) ) . "'"; + } else { + $enddate = "NULL"; + } + + $custom_level = array( + 'user_id' => $morder->user_id, + 'membership_id' => $morder->membership_level->id, + 'code_id' => $discount_code_id, + 'initial_payment' => $morder->membership_level->initial_payment, + 'billing_amount' => $morder->membership_level->billing_amount, + 'cycle_number' => $morder->membership_level->cycle_number, + 'cycle_period' => $morder->membership_level->cycle_period, + 'billing_limit' => $morder->membership_level->billing_limit, + 'trial_amount' => $morder->membership_level->trial_amount, + 'trial_limit' => $morder->membership_level->trial_limit, + 'startdate' => $startdate, + 'enddate' => $enddate + ); + if ($morder->status != 'success') { + + if (pmpro_changeMembershipLevel($custom_level, $morder->user_id, 'changed')) { + $morder->membership_id = $morder->membership_level->id; + $morder->payment_transaction_id = $reference; + $morder->status = "success"; + $morder->saveOrder(); + } + + } + + if (!empty($morder)) { + $pmpro_invoice = new MemberOrder($morder->id); + } else { + $pmpro_invoice = null; + } + + $current_user->membership_level = $pmpro_level; //make sure they have the right level info + $current_user->membership_level->enddate = $enddate; + if ($current_user->ID) { + $current_user->membership_level = pmpro_getMembershipLevelForUser($current_user->ID); + // echo "interesting"; + } + + } + } + } + } +} + +// First stab at renewal payments. This is for recurring subs yo! +function pmpro_paystack_renew_payment( $post_event ) { + global $wp,$wpdb; + + if (isset($event->data->paid) && ($event->data->paid == 1)) { + + $amount = $event->data->subscription->amount/100; + $old_order = new MemberOrder(); + $subscription_code = $event->data->subscription->subscription_code; + $email = $event->data->customer->email; + $old_order->getLastMemberOrderBySubscriptionTransactionID($subscription_code); + + + if (empty($old_order)) { + pmpro_paystack_webhook_log( 'Could not find last order for subscription code: ' . $subscription_code ); + pmpro_paystack_webhook_exit(); + } + $user_id = $old_order->user_id; + $user = get_userdata($user_id); + + if (empty($user)) { + pmpro_paystack_webhook_log( 'Could not get user for renewal payment' ); + pmpro_paystack_webhook_exit(); + } + + $morder = new MemberOrder(); + + // Set the orders date to time it was paid. + if ( ! empty( $event->data->paid_at ) ) { + $morder->timestamp = strtotime( sanitize_text_field( $event->data->paid_at ) ); + } + + $morder->user_id = $old_order->user_id; + $morder->membership_id = $old_order->membership_id; + $morder->InitialPayment = $amount; //not the initial payment, but the order class is expecting this + $morder->PaymentAmount = $amount; + $morder->payment_transaction_id = $event->data->invoice_code; + $morder->subscription_transaction_id = $subscription_code; + + $morder->gateway = $old_order->gateway; + $morder->gateway_environment = $old_order->gateway_environment; + + $morder->Email = $email; + $pmpro_level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . (int)$morder->membership_id . "' LIMIT 1"); + $pmpro_level = apply_filters("pmpro_checkout_level", $pmpro_level); + $startdate = apply_filters( 'pmpro_checkout_start_date', "'" . current_time( 'mysql' ) . "'", $morder->user_id, $morder->membership_level ); + + // get discount code (NOTE: but discount_code isn't set here. How to handle discount codes for PayPal Standard?) + $morder->getDiscountCode(); + if ( ! empty( $morder->discount_code ) ) { + // update membership level + $morder->getMembershipLevel( true ); + $discount_code_id = $morder->discount_code->id; + } else { + $discount_code_id = ''; + } + + + //fix expiration date + if ( ! empty( $morder->membership_level->expiration_number ) ) { + $enddate = "'" . date_i18n( "Y-m-d", strtotime( "+ " . $morder->membership_level->expiration_number . " " . $morder->membership_level->expiration_period, current_time( "timestamp" ) ) ) . "'"; + } else { + $enddate = "NULL"; + } + + $custom_level = array( + 'user_id' => $morder->user_id, + 'membership_id' => $morder->memberhsip_level->id, + 'code_id' => $discount_code_id, + 'initial_payment' => $morder->memberhsip_level->initial_payment, + 'billing_amount' => $morder->memberhsip_level->billing_amount, + 'cycle_number' => $morder->memberhsip_level->cycle_number, + 'cycle_period' => $morder->memberhsip_level->cycle_period, + 'billing_limit' => $morder->memberhsip_level->billing_limit, + 'trial_amount' => $morder->memberhsip_level->trial_amount, + 'trial_limit' => $morder->memberhsip_level->trial_limit, + 'startdate' => $startdate, + 'enddate' => $enddate + ); + + //get CC info that is on file + $morder->expirationmonth = get_user_meta($user_id, "pmpro_ExpirationMonth", true); + $morder->expirationyear = get_user_meta($user_id, "pmpro_ExpirationYear", true); + $morder->ExpirationDate = $morder->expirationmonth . $morder->expirationyear; + $morder->ExpirationDate_YdashM = $morder->expirationyear . "-" . $morder->expirationmonth; + + + // Save entire order data to IPN Log - loop through the order object. + $order_data = array(); + foreach ( $morder as $key => $value ) { + $order_data[ $key ] = $value; + } + pmpro_paystack_webhook_log( 'Order data: ' . print_r( $order_data, true ) ); + + + //save + if ($morder->status != 'success') { + + $_REQUEST['cancel_membership'] = false; // Do NOT cancel gateway subscription + + if ( pmpro_changeMembershipLevel( $custom_level, $morder->user_id, 'changed' ) !== false ) { + $morder->status = "success"; + } + + } + + // Save the order before emailing the customer about it. + $morder->saveOrder(); + $morder->getMemberOrderByID( $morder->id ); + + //email the user their invoice + $pmproemail = new PMProEmail(); + $pmproemail->sendInvoiceEmail($user, $morder); + + do_action('pmpro_subscription_payment_completed', $morder); + pmpro_paystack_webhook_log( sprintf( 'Subscription payment completed for user with ID: %d. Order ID: %s', $user_id, $morder->code ) ); + pmpro_paystack_webhook_exit(); + } +} \ No newline at end of file From 208bf8cabb800468f3e6889c5fae2d0bb1b91f70 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 18 Oct 2024 15:26:24 +0200 Subject: [PATCH 68/91] Add back the cancel method. --- classes/class.pmprogateway_paystack.php | 64 +++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/classes/class.pmprogateway_paystack.php b/classes/class.pmprogateway_paystack.php index 354a84f..e1531c5 100644 --- a/classes/class.pmprogateway_paystack.php +++ b/classes/class.pmprogateway_paystack.php @@ -376,6 +376,70 @@ public function update_subscription_info( $subscription ) { } } + function cancel(&$order, $update_status = true ) + { + $backtrace = self::get_caller_info(); + $furtherbacktrace = wp_debug_backtrace_summary(); + + //no matter what happens below, we're going to cancel the order in our system + if ( $update_status ) { + $order->updateStatus( "cancelled" ); + } + + $mode = pmpro_getOption("gateway_environment"); + $code = $order->subscription_transaction_id; + if ($mode == 'sandbox') { + $key = pmpro_getOption("paystack_tsk"); + } else { + $key = pmpro_getOption("paystack_lsk"); + + } + + if ( $code != "") { + $paystack_url = 'https://api.paystack.co/subscription/' . $code; + + $headers = array( + 'Authorization' => 'Bearer ' . $key + ); + $args = array( + 'headers' => $headers, + 'timeout' => 60, + ); + + $request = wp_remote_get($paystack_url, $args); + if (!is_wp_error($request) && 200 == wp_remote_retrieve_response_code($request)) { + $paystack_response = json_decode(wp_remote_retrieve_body($request)); + if ('active' == $paystack_response->data->status && $code == $paystack_response->data->subscription_code && '1' == $paystack_response->status) { + + $paystack_url = 'https://api.paystack.co/subscription/disable'; + $headers = array( + 'Content-Type' => 'application/json', + 'Authorization' => "Bearer ".$key + ); + $body = array( + 'code' => $paystack_response->data->subscription_code, + 'token' => $paystack_response->data->email_token, + 'debug_trace'=> $backtrace . " ". $furtherbacktrace + ); + $args = array( + 'body' => json_encode($body), + 'headers' => $headers, + 'timeout' => 60, + ); + + $request = wp_remote_post($paystack_url, $args); + + if ( ! is_wp_error( $request ) ) { + return true; + } else { + return false; // There was an error cancelling for some reason. + } + } + } + } + return true; + } + /** * Allow refunds from within Paid Memberships Pro and Paystack. * @since TBD From 7ddf4402b7106188b7db320531024ad58117a193 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Tue, 22 Oct 2024 13:39:42 +0200 Subject: [PATCH 69/91] Refactored logic --- services/paystack-webhook.php | 104 ++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 50 deletions(-) diff --git a/services/paystack-webhook.php b/services/paystack-webhook.php index b142324..5be0d2e 100644 --- a/services/paystack-webhook.php +++ b/services/paystack-webhook.php @@ -37,12 +37,8 @@ pmpro_paystack_webhook_log( 'Event: ' . print_r( $post_event, true ) ); switch( $post_event->event ){ -case 'subscription.create': - // This also runs from time to time? I think this is the first time it runs. - pmpro_paystack_renew_payment( $post_event ); - break; -case 'subscription.disable': /// Cancel the membership since it's done via the IPN. - pmpro_handle_subscription_cancellation_at_gateway( $subscription_id, 'paystack', $gateway_environment ); +case 'subscription.disable': + pmpro_handle_subscription_cancellation_at_gateway( $post_event->data->subscription_code, 'paystack', $gateway_environment ); pmpro_paystack_webhook_exit(); break; case 'charge.success': // This can change too. @@ -56,10 +52,10 @@ pmpro_paystack_webhook_log( 'Charge success. Reference: ' . $post_event->data->reference ); break; case 'invoice.create': - pmpro_paystack_renew_payment($post_event); + pmpro_paystack_renew_payment( $post_event ); // Create the order as this webhook is sent first. break; -case 'invoice.update': - pmpro_paystack_renew_payment($post_event); +case 'invoice.update': /// Don't think we need this always. + pmpro_paystack_renew_payment($post_event); break; } http_response_code(200); @@ -67,7 +63,7 @@ /** - * Complete the + * Complete the PMPro order. * * @param [type] $reference * @param [type] $morder @@ -75,22 +71,35 @@ */ function pmpro_paystack_complete_order( $reference, &$order ) { + // No reference passed, let's bail. + if ( empty( $reference ) ) { + return false; + } + // Only run this if we got an order. - if ( empty( $order ) ) { + if ( !isset( $order->code ) ) { return false; } - // update order status and transaction ids - $order->payment_transaction_id = $reference; + // If not object let's bail. + if ( ! is_object( $order ) ) { + return false; + } - /// Change this. - if ( ! empty( $_POST['token'] ) ) { - $order->subscription_transaction_id = sanitize_text_field( $_POST['m_payment_id'] ); - } + // Reference is for another order + if ( $order->code != $reference ) { + return false; + } + + // update order status and transaction ids + $order->payment_transaction_id = $reference; $order->saveOrder(); // Temporarily save the order before processing it. - + if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { + return false; + } + // Change level and complete the order. pmpro_pull_checkout_data_from_order( $order ); return pmpro_complete_async_checkout( $order ); @@ -98,23 +107,32 @@ function pmpro_paystack_complete_order( $reference, &$order ) { } // Confirm the subscription -function pmpro_paystack_confirm_subscription( $reference, $order ) { +function pmpro_paystack_confirm_subscription( $post_event, $order ) { global $wpdb, $current_user, $pmpro_invoice, $pmpro_currency,$gateway; + $webhook_reference_id = $post_event->data->reference; + if (empty($pmpro_invoice)) { - $morder = new MemberOrder($reference); - if (!empty($morder) && $morder->gateway == "paystack") $pmpro_invoice = $morder; + $morder = new MemberOrder($webhook_reference_id); + if (!empty($morder) && $morder->gateway == "paystack") { + $pmpro_invoice = $morder; + } + } + + // Order is already confirmed, no need. + if ( $morder->status == 'success' ) { + return; } + // No user found lets bail then. if (!empty($pmpro_invoice) && $pmpro_invoice->gateway == "paystack" && isset($pmpro_invoice->total) && $pmpro_invoice->total > 0) { $morder = $pmpro_invoice; - if ($morder->code == $reference ) { + if ($morder->code == $webhook_reference_id ) { /// Use pmpro_getLevel instead of a DB query. $pmpro_level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . (int)$morder->membership_id . "' LIMIT 1"); /// Don't need filters. - $pmpro_level = apply_filters("pmpro_checkout_level", $pmpro_level); $startdate = apply_filters("pmpro_checkout_start_date", "'" . current_time("mysql") . "'", $morder->user_id, $pmpro_level); // The level object from the order that can be filtered when returning from Paystack and user is getting their membership level. @@ -130,7 +148,7 @@ function pmpro_paystack_confirm_subscription( $reference, $order ) { $key = pmpro_getOption("paystack_lsk"); $pk = pmpro_getOption("paystack_lpk"); } - $paystack_url = 'https://api.paystack.co/transaction/verify/' . $reference; + $paystack_url = 'https://api.paystack.co/transaction/verify/' . $webhook_reference_id; $headers = array( 'Authorization' => 'Bearer ' . $key ); @@ -148,7 +166,7 @@ function pmpro_paystack_confirm_subscription( $reference, $order ) { //Add logger here $pstk_logger = new pmpro_paystack_plugin_tracker('pm-pro',$pk); - $pstk_logger->log_transaction_success($reference); + $pstk_logger->log_transaction_success($webhook_reference_id); // Get level from the order. $pmpro_level = $morder->getMembershipLevel(); @@ -292,53 +310,42 @@ function pmpro_paystack_confirm_subscription( $reference, $order ) { if (pmpro_changeMembershipLevel($custom_level, $morder->user_id, 'changed')) { $morder->membership_id = $morder->membership_level->id; - $morder->payment_transaction_id = $reference; + $morder->payment_transaction_id = $webhook_reference_id; $morder->status = "success"; $morder->saveOrder(); } } - - if (!empty($morder)) { - $pmpro_invoice = new MemberOrder($morder->id); - } else { - $pmpro_invoice = null; - } - - $current_user->membership_level = $pmpro_level; //make sure they have the right level info - $current_user->membership_level->enddate = $enddate; - if ($current_user->ID) { - $current_user->membership_level = pmpro_getMembershipLevelForUser($current_user->ID); - // echo "interesting"; - } - } } } } } -// First stab at renewal payments. This is for recurring subs yo! +// First stab at renewal payments. This is for recurring subs yo! /// function pmpro_paystack_renew_payment( $post_event ) { global $wp,$wpdb; + + /// This is just for now. + $event = $post_event; if (isset($event->data->paid) && ($event->data->paid == 1)) { $amount = $event->data->subscription->amount/100; - $old_order = new MemberOrder(); $subscription_code = $event->data->subscription->subscription_code; $email = $event->data->customer->email; + $old_order = new MemberOrder(); $old_order->getLastMemberOrderBySubscriptionTransactionID($subscription_code); - if (empty($old_order)) { pmpro_paystack_webhook_log( 'Could not find last order for subscription code: ' . $subscription_code ); pmpro_paystack_webhook_exit(); } + $user_id = $old_order->user_id; $user = get_userdata($user_id); - if (empty($user)) { + if ( empty( $user ) ) { pmpro_paystack_webhook_log( 'Could not get user for renewal payment' ); pmpro_paystack_webhook_exit(); } @@ -352,9 +359,9 @@ function pmpro_paystack_renew_payment( $post_event ) { $morder->user_id = $old_order->user_id; $morder->membership_id = $old_order->membership_id; - $morder->InitialPayment = $amount; //not the initial payment, but the order class is expecting this - $morder->PaymentAmount = $amount; - $morder->payment_transaction_id = $event->data->invoice_code; + $morder->subtotal = $amount; //not the initial payment, but the order class is expecting this + $morder->total = $amount; + $morder->payment_transaction_id = ! empty( $event->data->invoice_code ) ? $event->data->invoice_code : $event->transaction->reference; /// invoice_code is only available in invoice.create and not charge.success (so change the reference) $morder->subscription_transaction_id = $subscription_code; $morder->gateway = $old_order->gateway; @@ -362,7 +369,6 @@ function pmpro_paystack_renew_payment( $post_event ) { $morder->Email = $email; $pmpro_level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . (int)$morder->membership_id . "' LIMIT 1"); - $pmpro_level = apply_filters("pmpro_checkout_level", $pmpro_level); $startdate = apply_filters( 'pmpro_checkout_start_date', "'" . current_time( 'mysql' ) . "'", $morder->user_id, $morder->membership_level ); // get discount code (NOTE: but discount_code isn't set here. How to handle discount codes for PayPal Standard?) @@ -375,7 +381,6 @@ function pmpro_paystack_renew_payment( $post_event ) { $discount_code_id = ''; } - //fix expiration date if ( ! empty( $morder->membership_level->expiration_number ) ) { $enddate = "'" . date_i18n( "Y-m-d", strtotime( "+ " . $morder->membership_level->expiration_number . " " . $morder->membership_level->expiration_period, current_time( "timestamp" ) ) ) . "'"; @@ -426,7 +431,6 @@ function pmpro_paystack_renew_payment( $post_event ) { // Save the order before emailing the customer about it. $morder->saveOrder(); - $morder->getMemberOrderByID( $morder->id ); //email the user their invoice $pmproemail = new PMProEmail(); From 7a86ef40c437bd63452832f57dc9480b41fba3a2 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Thu, 24 Oct 2024 14:36:27 +0200 Subject: [PATCH 70/91] Updated recurring payments webhook function. --- services/paystack-webhook.php | 115 ++++++++++------------------------ 1 file changed, 32 insertions(+), 83 deletions(-) diff --git a/services/paystack-webhook.php b/services/paystack-webhook.php index 5be0d2e..38d042f 100644 --- a/services/paystack-webhook.php +++ b/services/paystack-webhook.php @@ -41,7 +41,7 @@ pmpro_handle_subscription_cancellation_at_gateway( $post_event->data->subscription_code, 'paystack', $gateway_environment ); pmpro_paystack_webhook_exit(); break; -case 'charge.success': // This can change too. +case 'charge.success': // This runs for both recurring and initial checkouts. $morder = new MemberOrder($post_event->data->reference); $morder->getMembershipLevel(); $morder->getUser(); @@ -52,7 +52,7 @@ pmpro_paystack_webhook_log( 'Charge success. Reference: ' . $post_event->data->reference ); break; case 'invoice.create': - pmpro_paystack_renew_payment( $post_event ); // Create the order as this webhook is sent first. + pmpro_paystack_renew_payment( $post_event ); break; case 'invoice.update': /// Don't think we need this always. pmpro_paystack_renew_payment($post_event); @@ -86,6 +86,11 @@ function pmpro_paystack_complete_order( $reference, &$order ) { return false; } + // Order is not in token status, so we can just bail - no need to update anything. + if ( $order->status != 'token' ) { + return false; + } + // Reference is for another order if ( $order->code != $reference ) { return false; @@ -106,7 +111,7 @@ function pmpro_paystack_complete_order( $reference, &$order ) { } -// Confirm the subscription +// Confirm the subscription via plan etc. function pmpro_paystack_confirm_subscription( $post_event, $order ) { global $wpdb, $current_user, $pmpro_invoice, $pmpro_currency,$gateway; @@ -322,119 +327,63 @@ function pmpro_paystack_confirm_subscription( $post_event, $order ) { } } -// First stab at renewal payments. This is for recurring subs yo! /// +/** + * Handle the successful recurring payments. + * + * @param object $post_event The Paystack event object data. + * @return void + */ function pmpro_paystack_renew_payment( $post_event ) { - global $wp,$wpdb; - - /// This is just for now. - $event = $post_event; - - if (isset($event->data->paid) && ($event->data->paid == 1)) { - - $amount = $event->data->subscription->amount/100; - $subscription_code = $event->data->subscription->subscription_code; - $email = $event->data->customer->email; + if ( isset( $post_event->data->paid ) && ( $post_event->data->paid == 1 ) ) { + $amount = $post_event->data->subscription->amount/100; + $subscription_code = $post_event->data->subscription->subscription_code; + $email = $post_event->data->customer->email; $old_order = new MemberOrder(); - $old_order->getLastMemberOrderBySubscriptionTransactionID($subscription_code); + $old_order->getLastMemberOrderBySubscriptionTransactionID( $subscription_code ); - if (empty($old_order)) { + if ( empty( $old_order ) || empty( $old_order->id ) ) { pmpro_paystack_webhook_log( 'Could not find last order for subscription code: ' . $subscription_code ); pmpro_paystack_webhook_exit(); } $user_id = $old_order->user_id; - $user = get_userdata($user_id); + $user = get_userdata( $user_id ); if ( empty( $user ) ) { pmpro_paystack_webhook_log( 'Could not get user for renewal payment' ); pmpro_paystack_webhook_exit(); } + // Let's create the order. $morder = new MemberOrder(); - - // Set the orders date to time it was paid. - if ( ! empty( $event->data->paid_at ) ) { - $morder->timestamp = strtotime( sanitize_text_field( $event->data->paid_at ) ); - } - $morder->user_id = $old_order->user_id; $morder->membership_id = $old_order->membership_id; - $morder->subtotal = $amount; //not the initial payment, but the order class is expecting this + $morder->subtotal = $amount; $morder->total = $amount; - $morder->payment_transaction_id = ! empty( $event->data->invoice_code ) ? $event->data->invoice_code : $event->transaction->reference; /// invoice_code is only available in invoice.create and not charge.success (so change the reference) + $morder->payment_transaction_id = ! empty( $post_event->data->invoice_code ) ? $post_event->data->invoice_code : ''; $morder->subscription_transaction_id = $subscription_code; - + // Set the orders date to time it was paid. + if ( ! empty( $post_event->data->paid_at ) ) { + $morder->timestamp = strtotime( sanitize_text_field( $post_event->data->paid_at ) ); + } $morder->gateway = $old_order->gateway; $morder->gateway_environment = $old_order->gateway_environment; - $morder->Email = $email; - $pmpro_level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . (int)$morder->membership_id . "' LIMIT 1"); - $startdate = apply_filters( 'pmpro_checkout_start_date', "'" . current_time( 'mysql' ) . "'", $morder->user_id, $morder->membership_level ); - - // get discount code (NOTE: but discount_code isn't set here. How to handle discount codes for PayPal Standard?) - $morder->getDiscountCode(); - if ( ! empty( $morder->discount_code ) ) { - // update membership level - $morder->getMembershipLevel( true ); - $discount_code_id = $morder->discount_code->id; - } else { - $discount_code_id = ''; - } - - //fix expiration date - if ( ! empty( $morder->membership_level->expiration_number ) ) { - $enddate = "'" . date_i18n( "Y-m-d", strtotime( "+ " . $morder->membership_level->expiration_number . " " . $morder->membership_level->expiration_period, current_time( "timestamp" ) ) ) . "'"; - } else { - $enddate = "NULL"; - } - - $custom_level = array( - 'user_id' => $morder->user_id, - 'membership_id' => $morder->memberhsip_level->id, - 'code_id' => $discount_code_id, - 'initial_payment' => $morder->memberhsip_level->initial_payment, - 'billing_amount' => $morder->memberhsip_level->billing_amount, - 'cycle_number' => $morder->memberhsip_level->cycle_number, - 'cycle_period' => $morder->memberhsip_level->cycle_period, - 'billing_limit' => $morder->memberhsip_level->billing_limit, - 'trial_amount' => $morder->memberhsip_level->trial_amount, - 'trial_limit' => $morder->memberhsip_level->trial_limit, - 'startdate' => $startdate, - 'enddate' => $enddate - ); - - //get CC info that is on file - $morder->expirationmonth = get_user_meta($user_id, "pmpro_ExpirationMonth", true); - $morder->expirationyear = get_user_meta($user_id, "pmpro_ExpirationYear", true); - $morder->ExpirationDate = $morder->expirationmonth . $morder->expirationyear; - $morder->ExpirationDate_YdashM = $morder->expirationyear . "-" . $morder->expirationmonth; - - // Save entire order data to IPN Log - loop through the order object. $order_data = array(); foreach ( $morder as $key => $value ) { $order_data[ $key ] = $value; } - pmpro_paystack_webhook_log( 'Order data: ' . print_r( $order_data, true ) ); - - - //save - if ($morder->status != 'success') { - - $_REQUEST['cancel_membership'] = false; // Do NOT cancel gateway subscription - - if ( pmpro_changeMembershipLevel( $custom_level, $morder->user_id, 'changed' ) !== false ) { - $morder->status = "success"; - } - } + pmpro_paystack_webhook_log( 'Order data: ' . print_r( $order_data, true ) ); - // Save the order before emailing the customer about it. + $morder->status = 'success'; $morder->saveOrder(); + $morder->getMemberOrderByID($morder->id); //email the user their invoice $pmproemail = new PMProEmail(); - $pmproemail->sendInvoiceEmail($user, $morder); + $pmproemail->sendInvoiceEmail( $user, $morder ); do_action('pmpro_subscription_payment_completed', $morder); pmpro_paystack_webhook_log( sprintf( 'Subscription payment completed for user with ID: %d. Order ID: %s', $user_id, $morder->code ) ); From 0ea2024776a6464b4eeb54e1c03d055136343095 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 25 Oct 2024 09:51:36 +0200 Subject: [PATCH 71/91] Remove ajax call. We don't need this. --- services/paystack-webhook.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/services/paystack-webhook.php b/services/paystack-webhook.php index 38d042f..cbc1f28 100644 --- a/services/paystack-webhook.php +++ b/services/paystack-webhook.php @@ -101,10 +101,6 @@ function pmpro_paystack_complete_order( $reference, &$order ) { $order->payment_transaction_id = $reference; $order->saveOrder(); // Temporarily save the order before processing it. - if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { - return false; - } - // Change level and complete the order. pmpro_pull_checkout_data_from_order( $order ); return pmpro_complete_async_checkout( $order ); From 334b4d0d468f89df87869de50d7f1f2002ba0637 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 25 Oct 2024 11:03:17 +0200 Subject: [PATCH 72/91] Fix webhoook handler recurring subscriptions. --- services/paystack-webhook.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/services/paystack-webhook.php b/services/paystack-webhook.php index cbc1f28..a53b84a 100644 --- a/services/paystack-webhook.php +++ b/services/paystack-webhook.php @@ -46,7 +46,7 @@ $morder->getMembershipLevel(); $morder->getUser(); pmpro_paystack_complete_order( $post_event->data->reference, $morder ); - pmpro_paystack_confirm_subscription( $post_event->data->reference, $morder ); // This will be for recurring subscriptions/levels. + pmpro_paystack_confirm_subscription( $post_event, $morder ); // This will be for recurring subscriptions/levels. $pstk_logger = new pmpro_paystack_plugin_tracker( 'pm-pro', $public_key ); $pstk_logger->log_transaction_success( $post_event->data->reference ); pmpro_paystack_webhook_log( 'Charge success. Reference: ' . $post_event->data->reference ); @@ -120,11 +120,6 @@ function pmpro_paystack_confirm_subscription( $post_event, $order ) { } } - // Order is already confirmed, no need. - if ( $morder->status == 'success' ) { - return; - } - // No user found lets bail then. if (!empty($pmpro_invoice) && $pmpro_invoice->gateway == "paystack" && isset($pmpro_invoice->total) && $pmpro_invoice->total > 0) { $morder = $pmpro_invoice; From 2264caef3917948bfce925302ea65a6e5fab3b08 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 25 Oct 2024 11:16:09 +0200 Subject: [PATCH 73/91] Put language files back. --- .../paystack-gateway-paid-memberships-pro.mo | Bin 0 -> 559 bytes .../paystack-gateway-paid-memberships-pro.po | 164 ++++++++++++++++++ .../paystack-gateway-paid-memberships-pro.pot | 100 +++++++++++ 3 files changed, 264 insertions(+) create mode 100644 languages/paystack-gateway-paid-memberships-pro.mo create mode 100644 languages/paystack-gateway-paid-memberships-pro.po create mode 100644 languages/paystack-gateway-paid-memberships-pro.pot diff --git a/languages/paystack-gateway-paid-memberships-pro.mo b/languages/paystack-gateway-paid-memberships-pro.mo new file mode 100644 index 0000000000000000000000000000000000000000..3e27e17dd1df70fbe909dd44376cff27ddc0ac03 GIT binary patch literal 559 zcmbVJJx{|h5KR$NMrH;NY~_LzAxO=KC~YW_8WL3j3Y(j_siCnW#|8RB_XO*TU(X|O`jM)yQlj*Ex24d&DZKG(>g5bGTd-HKdXtE~fILGF#iMb|Nj!uDf)6MPdU$R5Lh38q%s k-6zl+hRAc=hQb*7vNR_XRIP1\n" +"Language-Team: Paid Memberships Pro \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"POT-Creation-Date: 2024-10-09T08:26:19+00:00\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"X-Generator: WP-CLI 2.11.0\n" +"X-Domain: paystack-gateway-paid-memberships-pro\n" + +#. Plugin Name of the plugin +#: class.pmprogateway_paystack.php +msgid "Paystack Gateway for Paid Memberships Pro" +msgstr "" + +#. Plugin URI of the plugin +#: class.pmprogateway_paystack.php +msgid "https://www.paidmembershipspro.com/add-ons/paystack-gateway/" +msgstr "" + +#. Description of the plugin +#: class.pmprogateway_paystack.php +msgid "Plugin to add Paystack payment gateway into Paid Memberships Pro" +msgstr "" + +#. Author of the plugin +msgid "Paystack, Paid Memberships Pro" +msgstr "" + +#. Author URI of the plugin +#: class.pmprogateway_paystack.php +msgid "https://www.paidmembershipspro.com" +msgstr "" + +#: class.pmprogateway_paystack.php:97 +#: class.pmprogateway_paystack.php:108 +#: class.pmprogateway_paystack.php:114 +#: class.pmprogateway_paystack.php:118 +msgid "Settings" +msgstr "" + +#: class.pmprogateway_paystack.php:111 +#: class.pmprogateway_paystack.php:122 +#: class.pmprogateway_paystack.php:128 +#: class.pmprogateway_paystack.php:132 +msgid "Check Out with Paystack" +msgstr "" + +#: class.pmprogateway_paystack.php:111 +#: class.pmprogateway_paystack.php:122 +#: class.pmprogateway_paystack.php:128 +#: class.pmprogateway_paystack.php:132 +msgid "Submit and Confirm" +msgstr "" + +#: class.pmprogateway_paystack.php:124 +#: class.pmprogateway_paystack.php:135 +#: class.pmprogateway_paystack.php:141 +#: class.pmprogateway_paystack.php:145 +msgid "Paystack" +msgstr "" + +#: class.pmprogateway_paystack.php:348 +#: class.pmprogateway_paystack.php:377 +#: class.pmprogateway_paystack.php:394 +#: class.pmprogateway_paystack.php:408 +#: class.pmprogateway_paystack.php:407 +msgid "Test Secret Key" +msgstr "" + +#: class.pmprogateway_paystack.php:356 +#: class.pmprogateway_paystack.php:385 +#: class.pmprogateway_paystack.php:402 +#: class.pmprogateway_paystack.php:416 +#: class.pmprogateway_paystack.php:415 +msgid "Test Public Key" +msgstr "" + +#: class.pmprogateway_paystack.php:364 +#: class.pmprogateway_paystack.php:393 +#: class.pmprogateway_paystack.php:410 +#: class.pmprogateway_paystack.php:424 +#: class.pmprogateway_paystack.php:423 +msgid "Live Secret Key" +msgstr "" + +#: class.pmprogateway_paystack.php:372 +#: class.pmprogateway_paystack.php:401 +#: class.pmprogateway_paystack.php:418 +#: class.pmprogateway_paystack.php:432 +#: class.pmprogateway_paystack.php:431 +msgid "Live Public Key" +msgstr "" + +#: class.pmprogateway_paystack.php:380 +#: class.pmprogateway_paystack.php:409 +#: class.pmprogateway_paystack.php:426 +#: class.pmprogateway_paystack.php:440 +#: class.pmprogateway_paystack.php:439 +msgid "Webhook" +msgstr "" + +#: class.pmprogateway_paystack.php:383 +#: class.pmprogateway_paystack.php:412 +#: class.pmprogateway_paystack.php:429 +#: class.pmprogateway_paystack.php:443 +#: class.pmprogateway_paystack.php:442 +msgid "To fully integrate with Paystack, be sure to use the following for your Webhook URL to" +msgstr "" + +#: class.pmprogateway_paystack.php:852 +#: class.pmprogateway_paystack.php:930 +#: class.pmprogateway_paystack.php:948 +#: class.pmprogateway_paystack.php:975 +#: class.pmprogateway_paystack.php:973 +#: class.pmprogateway_paystack.php:984 +msgid "Account" +msgstr "" + +#: class.pmprogateway_paystack.php:853 +#: class.pmprogateway_paystack.php:931 +#: class.pmprogateway_paystack.php:949 +#: class.pmprogateway_paystack.php:976 +#: class.pmprogateway_paystack.php:974 +#: class.pmprogateway_paystack.php:985 +msgid "Order" +msgstr "" + +#: class.pmprogateway_paystack.php:854 +#: class.pmprogateway_paystack.php:932 +#: class.pmprogateway_paystack.php:950 +#: class.pmprogateway_paystack.php:977 +#: class.pmprogateway_paystack.php:975 +#: class.pmprogateway_paystack.php:986 +msgid "Membership Level" +msgstr "" + +#: class.pmprogateway_paystack.php:855 +#: class.pmprogateway_paystack.php:933 +#: class.pmprogateway_paystack.php:951 +#: class.pmprogateway_paystack.php:978 +#: class.pmprogateway_paystack.php:976 +#: class.pmprogateway_paystack.php:987 +msgid "Amount Paid" +msgstr "" + +#. Author of the plugin +#: class.pmprogateway_paystack.php +msgid "Paid Memberships Pro, Paystack" +msgstr "" + +#: class.pmprogateway_paystack.php:1119 +#: class.pmprogateway_paystack.php:1140 +#: class.pmprogateway_paystack.php:1166 +#: class.pmprogateway_paystack.php:1164 +#: class.pmprogateway_paystack.php:1175 +msgid "There was an error communicating with Paystack. Please confirm your connectivity and API details and try again." +msgstr "" diff --git a/languages/paystack-gateway-paid-memberships-pro.pot b/languages/paystack-gateway-paid-memberships-pro.pot new file mode 100644 index 0000000..55eb0ea --- /dev/null +++ b/languages/paystack-gateway-paid-memberships-pro.pot @@ -0,0 +1,100 @@ +# Copyright (C) 2024 Paid Memberships Pro, Paystack +# This file is distributed under the GPLv2 or later. +msgid "" +msgstr "" +"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.8\n" +"Report-Msgid-Bugs-To: info@paidmembershipspro.com\n" +"Last-Translator: Paid Memberships Pro \n" +"Language-Team: Paid Memberships Pro \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"POT-Creation-Date: 2024-10-09T08:26:19+00:00\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"X-Generator: WP-CLI 2.11.0\n" +"X-Domain: paystack-gateway-paid-memberships-pro\n" + +#. Plugin Name of the plugin +#: class.pmprogateway_paystack.php +msgid "Paystack Gateway for Paid Memberships Pro" +msgstr "" + +#. Plugin URI of the plugin +#: class.pmprogateway_paystack.php +msgid "https://www.paidmembershipspro.com/add-ons/paystack-gateway/" +msgstr "" + +#. Description of the plugin +#: class.pmprogateway_paystack.php +msgid "Plugin to add Paystack payment gateway into Paid Memberships Pro" +msgstr "" + +#. Author of the plugin +#: class.pmprogateway_paystack.php +msgid "Paid Memberships Pro, Paystack" +msgstr "" + +#. Author URI of the plugin +#: class.pmprogateway_paystack.php +msgid "https://www.paidmembershipspro.com" +msgstr "" + +#: class.pmprogateway_paystack.php:118 +msgid "Settings" +msgstr "" + +#: class.pmprogateway_paystack.php:132 +msgid "Check Out with Paystack" +msgstr "" + +#: class.pmprogateway_paystack.php:132 +msgid "Submit and Confirm" +msgstr "" + +#: class.pmprogateway_paystack.php:145 +msgid "Paystack" +msgstr "" + +#: class.pmprogateway_paystack.php:407 +msgid "Test Secret Key" +msgstr "" + +#: class.pmprogateway_paystack.php:415 +msgid "Test Public Key" +msgstr "" + +#: class.pmprogateway_paystack.php:423 +msgid "Live Secret Key" +msgstr "" + +#: class.pmprogateway_paystack.php:431 +msgid "Live Public Key" +msgstr "" + +#: class.pmprogateway_paystack.php:439 +msgid "Webhook" +msgstr "" + +#: class.pmprogateway_paystack.php:442 +msgid "To fully integrate with Paystack, be sure to use the following for your Webhook URL to" +msgstr "" + +#: class.pmprogateway_paystack.php:984 +msgid "Account" +msgstr "" + +#: class.pmprogateway_paystack.php:985 +msgid "Order" +msgstr "" + +#: class.pmprogateway_paystack.php:986 +msgid "Membership Level" +msgstr "" + +#: class.pmprogateway_paystack.php:987 +msgid "Amount Paid" +msgstr "" + +#: class.pmprogateway_paystack.php:1175 +msgid "There was an error communicating with Paystack. Please confirm your connectivity and API details and try again." +msgstr "" From 56924871ce871c97b2adbc81e4ce5575cfed73df Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 25 Oct 2024 11:17:15 +0200 Subject: [PATCH 74/91] Put the Readme file back --- README.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..8f1cfe5 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +![](pmpro-paystack-banner.jpg) + +# [Paid Memberships Pro - Paystack Gateway](https://www.paidmembershipspro.com/add-ons/paystack-gateway/) # + +![WordPress Plugin Downloads](https://img.shields.io/wordpress/plugin/dy/paystack-gateway-paid-memberships-pro?style=flat-square) ![License](https://img.shields.io/badge/license-GPL--2.0%2B-red.svg?style=flat-square) + +### Welcome to the Paid Memberships Pro - Paystack Gateway GitHub Repository +Add Paystack as a gateway option for Paid Memberships Pro and accept payments from members around Africa. + +For more information please visit [paidmembershipspro.com/add-ons/paystack-gateway/](https://www.paidmembershipspro.com/add-ons/paystack-gateway/) + +## Installation ## +For detailed installation steps, visit the [documentation](https://www.paidmembershipspro.com/add-ons/paystack-gateway/) page. + +1. Download the current development ZIP file directly: `https://github.com/strangerstudios/paystack-gateway-paid-memberships-pro/archive/dev.zip` + +**Please ensure that once installing this version of the plugin to remove `-dev` from the plugin's folder name.** + +## Bugs ## +If you find an issue/bug, let us know by [creating a detailed GitHub issue](https://github.com/strangerstudios/paystack-gateway-paid-memberships-pro/issues/new). + +## Support ## +This is a developer's portal for Paid Memberships Pro - Paystack Gateway. We do not offer support on this channel. **Any support related questions should be directed to [paidmembershipspro.com/add-ons/paystack-gateway/](https://www.paidmembershipspro.com/add-ons/paystack-gateway/).** + +## Contributing to Paid Memberships Pro - Paystack Gateway ## +We encourage and welcome any contribution to Paid Memberships Pro - Paystack Gateway. Please read the [guidelines for contributing](https://github.com/strangerstudios/paid-memberships-pro/blob/dev/.github/CONTRIBUTING.md) to this repository. + +There are various **ways to the help development** of Paid Memberships Pro - Paystack Gateway: + +1. Report [bugs/issues](https://github.com/strangerstudios/paystack-gateway-paid-memberships-pro/issues/new) on GitHub. +2. Work on any issues by submitting a Pull Request. + +Here are some ways for **non-developers to contribute** to Paid Memberships Pro - Paystack Gateway: + +1. Translate Paid Memberships Pro - Paystack Gateway into your own [language](https://www.paidmembershipspro.com/paid-memberships-pro-in-your-language/). +2. [Purchase a paid membership](https://paidmembershipspro.com/pricing) to help fund ongoing development and bug fixes. \ No newline at end of file From 014145ef316a10ce2fbafe1bc3611f4b211d0c0f Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 25 Oct 2024 11:18:21 +0200 Subject: [PATCH 75/91] Put .org readme back --- readme.txt | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100755 readme.txt diff --git a/readme.txt b/readme.txt new file mode 100755 index 0000000..8aa0935 --- /dev/null +++ b/readme.txt @@ -0,0 +1,127 @@ +=== Paystack Gateway for Paid Membership Pro === +Contributors: paystack, kendysond, steveamaza, lukman008, andrewza, strangerstudios, paidmembershipspro +Donate link: https://paystack.com/demo +Tags: paid memberships pro, paystack, gateway, credit card, Naira +Requires at least: 5.2 +Tested up to: 6.6 +Stable tag: 1.7.8 +License: GPLv3 +License URI: http://www.gnu.org/licenses/gpl-3.0.html + +Pay with Paystack on Paid Membership Pro + +== Description == + +Paid Membership Pro is a complete member management and membership subscriptions plugin for WordPress. Paid Memberships Pro is designed for premium content sites, clubs/associations, subscription products, newsletters and more! + +The **Paystack Gateway for Paid Membership Pro** allows site owners from Nigeria, Ghana, Kenya and South Africa to accept payments from their customers via Paid Membership Pro. + +To be able to use the Paystack Gateway for Paid Membership Pro, you must [have an account on Paystack](https://dashboard.paystack.com) from which you will get Test and Live API keys to connect the plugin to your Paystack business. Here are some benefits of using Paystack! + += Intuitive Dashboard = +Use the Paystack dashboard to manage your customers, payments, and track your growth. + += Fraud Protection = +For Paystack, stopping fraud is top priority. We've used machine learning to minimize risks, reduce chargebacks and its associated costs. Paystack's fraud systems is built to learn. And so it's continually adapting to both local and international fraud. + +We screen every transaction by checking the IP, history, geolocation etc. to proactively identify fraudulent transactions. The entire network is used to prevent fraud. We learn from card and device fingerprints used to pay across different merchants. + += Multiple Channels = +We've done all the heavy lifting such that you can immediately start accepting payments across all channels. Allow your customers make payments via their credit/debit card, bank accounts, USSD and Mobile Money. + += Paystack Go! = +Track your business performance in the palm of your hand with Paystack Go! - This is a Progressive Web App that gives you access to your dashboard even when you are offline. You can easily look up transactions, track your businesses, and send invoices on the go. + +If your Paystack business has been activated, simply visit [go.paystack.com](https://go.paystack.com) on your mobile phone to use Paystack Go. + += Join our growing community = + +When you download Paystack, you join a community of more than ten thousand merchants, developers, and enthusiasts. We're one of the fastest-growing open source communities online, and no matter your skill level we'd love to have you! + +If you’re interested in contributing to Paystack plugins and libraries we’ve got more than 100 contributors, and there’s always room for more. Head over to the [Paystack GitHub Repository](https://github.com/paystackHQ/) to find out how you can pitch in. + +We also have a developer community on Slack where we share product announcements, private events and discuss contributions to open source library and plugins. Join the Payslack Community [here](https://payslack.slack.com/). + +== Installation == + += Minimum Requirements = + +* Confirm that your server can conclude a TLSv1.2 connection to Paystack's servers. More information about this requirement can be gleaned here: [TLS v1.2 requirement](https://developers.paystack.co/blog/tls-v12-requirement). +* Installed and activated [Paid Membership Pro Plugin] (https://www.paidmembershipspro.com) + += Manual installation = + +The manual installation method involves downloading our payment plugin and uploading it to your webserver via your favourite FTP application. The WordPress codex contains [instructions on how to do this here](https://codex.wordpress.org/Managing_Plugins#Manual_Plugin_Installation). + + +== Frequently Asked Questions == + += Where can I find help and documentation to understand Paystack? = + +You can find help and information on Paystack on our [Help Desk](https://paystack.com/help) + += Where can I get support or talk to other users? = + +If you get stuck, you can ask for help in the [Paystack Gateway for Paid Membership Pro Plugin Forum](https://wordpress.org/support/plugin/paystack-gateway-paid-memberships-pro). + += Paystack Gateway for Paid Membership Pro is awesome! Can I contribute? = + +Yes you can! Join in on our [GitHub repository](https://github.com/strangerstudios/paystack-gateway-paid-memberships-pro) + +== Screenshots == + +1. The slick Paystack settings panel. + +== Changelog == += 1.7.8 - 2024-10-09 = +* BUG FIX: Fixed an issue where free checkouts would cause a fatal error. + += 1.7.7 - 2024-07-19 = +* ENHANCEMENT: Added support for Paid Memberships Pro V3.1+ and used `pmpro_get_element_class` method on the frontend. (@andrewlimaza) +* BUG FIX: Fixed an issue where cancellations weren't working correctly in some cases/instances. (@andrewlimaza) +* BUG FIX: Fixed an issue where billing would bill later in the day at the start of the subscription. (@andrewlimaza) + += 1.7.6 - 2024-07-03 = +* ENHANCEMENT: Added webhook logging functionality to debug incoming webhook data. You may use the constant 'PMPRO_PAYSTACK_WEBHOOK_LOG' to enable this feature. Please delete logs and disable this constant after completing debugging. +* BUG FIX: Fixed an issue where the webhook handler was not correctly storing the order amount if the subscription amount changed. +* BUG FIX: Fixed an issue when renewal payments were being processed and failing to complete the order. + += 1.7.4 - 2024-04-24 = +* ENHANCEMENT: Added new filter `pmpro_paystack_webhook_level` to tweak the level given to members after checkout. This includes support for the Set Expiration Dates Add On. + += 1.7.3 - 2024-04-15 = +* SECURITY: Improved sanitization to output of translatable strings. +* ENHANCEMENT: Added functionality for refunds within Paid Memberships Pro. Supports full refunds only. +* BUG FIX: Fixed an issue where subscriptions weren't being linked inside Paid Memberships Pro correctly when confirming the membership. + += 1.7.2 - 2024-02-15 = +* ENHANCEMENT: Added support for Paid Memberships Pro 3.0+ subscriptions. +* BUG FIX: Fixed an issue where discount codes were not reflecting on Paid Memberships Pro side. + += 1.7.1 - 2023-09-13 = +* SECURITY: Improved security to the webhook handler, this now checks for the presence of the Paystack signature header before processing the request. +* ENHANCEMENT: Only allow card checkout for recurring subscriptions as other payment options don't allow subscriptions. +* BUG FIX: Fixed an issue where intervals weren't being set correctly. This now supports all intervals, for quarterly and biannually please use 3 month and 6 month in the recurring subscription fields respectively. +* BUG FIX: Fixed an issue where non-expiring memberships would obtain an expiration date incorrectly. +* REFACTOR: Minor improvements to the settings UI page to align with other Paid Memberships Pro gateways. + += 1.4 = +* Add quarterly subscription option for recurring payments set to 3 months or 90 days cycle period. + += 1.5 = +* Added plugin metrics tracker +* Add biannual subscription option for recurring payments set to 6 months or 180 days cycle period. + + += 1.6.0 = +* Add functionalty to delay subscriptions renewal using the PM Pro Subscription Delays addon. += 1.6.1 = +* BUG-FIX - Disabled non-recurring payment methods like USSD, QR for subscription plans etc. += 1.6.2 = +* Implement webhook to listen for cancelled subscriptions. += 1.6.3 = +* BUG FIX - Fix issue where setting webhook URL automatically cancels customer subscriptions. += 1.7.0 = +* BUG FIX - Fix issue where an 'Invalid Reference' error is being thrown when discount codes are used. +* BUG FIX - Fix issue where end date for subbscriptions is '1970-01-01'. +* Compatibility with WordPress 5.9 From 1c2faeb6b4e184909916a27440b7314a52631f67 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 25 Oct 2024 11:25:52 +0200 Subject: [PATCH 76/91] Remove DS_Store file --- .DS_Store | Bin 6148 -> 0 bytes .gitignore | 2 ++ 2 files changed, 2 insertions(+) delete mode 100644 .DS_Store create mode 100644 .gitignore diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 8372f54f8a9fde228b4f1b868d10fe190aac6a4e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKJ5Iwu5Su1a3B_cCejQd1ABFdqRoi2uNg#D~7=|l@V4X-hq^oN&q zHE%_#;jc2l&n}}wn$Q(pC(rNr<+Y^xTbqQO3aeMQAH+niv{o^0~^J(MVIOq3` zwm<9AlonJb)oJ&n9nsvw!`s2$>-{6|VP&1F{@Tg414B3h05h0_U>^SMfR+tF7tssC0x^;blvERs7)H`z zw=vE|^n#L3CRD~cv9gIL6ceh$ZbLX3C#a(`pbVr8Y{_Mh`~TVc^M9J8SIU4guu%+{ ztQZyp+*0hVt()UsYoQ}33&(lEWeO&t6(g2g@jf&N?3O1$7tssC0?{9VK!Xm-z^^j! E0r^HtE&u=k diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9bea433 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ + +.DS_Store From 5be3801ec47df5e39a734ab545ff471045a659ff Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 25 Oct 2024 11:26:36 +0200 Subject: [PATCH 77/91] Add gitignore to gitattributes --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index 4a511e2..2b6a80c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,5 @@ .gitattributes export-ignore +.gitignore export-ignore .github export-ignore CONTRIBUTING.md export-ignore README.md export-ignore From ca13889ec161f175db120803ec4f153242e9c2bf Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Wed, 30 Oct 2024 10:08:03 +0200 Subject: [PATCH 78/91] Fix plugin name. --- paystack-gateway-paid-memberships-pro.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paystack-gateway-paid-memberships-pro.php b/paystack-gateway-paid-memberships-pro.php index 959cbff..221f09b 100644 --- a/paystack-gateway-paid-memberships-pro.php +++ b/paystack-gateway-paid-memberships-pro.php @@ -1,6 +1,6 @@ Date: Wed, 30 Oct 2024 10:16:16 +0200 Subject: [PATCH 79/91] Remove required billing fields --- classes/class.pmprogateway_paystack.php | 1 + 1 file changed, 1 insertion(+) diff --git a/classes/class.pmprogateway_paystack.php b/classes/class.pmprogateway_paystack.php index e1531c5..f282bcc 100644 --- a/classes/class.pmprogateway_paystack.php +++ b/classes/class.pmprogateway_paystack.php @@ -42,6 +42,7 @@ public static function init() { add_filter( 'pmpro_include_billing_address_fields', '__return_false' ); add_filter( 'pmpro_include_payment_information_fields', '__return_false' ); add_filter( 'pmpro_billing_show_payment_method', '__return_false' ); + add_filter('pmpro_required_billing_fields', array( 'PMProGateway_Paystack', 'pmpro_required_billing_fields' ) ); // Refund functionality. add_filter( 'pmpro_allowed_refunds_gateways', array( 'PMProGateway_Paystack', 'pmpro_allowed_refunds_gateways' ) ); From 83551bafb23177458a75af459c5e0b027fed2f2c Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Wed, 6 Nov 2024 14:54:05 +0200 Subject: [PATCH 80/91] Version 1.8 bump FEATURE: Now updating the plugin from paidmembershipspro.com --- class.pmprogateway_paystack.php | 2 +- readme.txt | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/class.pmprogateway_paystack.php b/class.pmprogateway_paystack.php index a2ff84f..906d598 100755 --- a/class.pmprogateway_paystack.php +++ b/class.pmprogateway_paystack.php @@ -3,7 +3,7 @@ * Plugin Name: Paystack Gateway for Paid Memberships Pro * Plugin URI: https://www.paidmembershipspro.com/add-ons/paystack-gateway/ * Description: Plugin to add Paystack payment gateway into Paid Memberships Pro - * Version: 1.7.8 + * Version: 1.8 * Author: Paid Memberships Pro, Paystack * Author URI: https://www.paidmembershipspro.com * License: GPLv2 or later diff --git a/readme.txt b/readme.txt index 8aa0935..4018048 100755 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Donate link: https://paystack.com/demo Tags: paid memberships pro, paystack, gateway, credit card, Naira Requires at least: 5.2 Tested up to: 6.6 -Stable tag: 1.7.8 +Stable tag: 1.8 License: GPLv3 License URI: http://www.gnu.org/licenses/gpl-3.0.html @@ -73,6 +73,9 @@ Yes you can! Join in on our [GitHub repository](https://github.com/strangerstudi 1. The slick Paystack settings panel. == Changelog == += 1.8 - 2024-11-06 = +* FEATURE: Now updating the plugin from paidmembershipspro.com + = 1.7.8 - 2024-10-09 = * BUG FIX: Fixed an issue where free checkouts would cause a fatal error. From c5dc8267bd7f8dc4641cbe054c262e4e2524abd5 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Wed, 6 Nov 2024 14:56:38 +0200 Subject: [PATCH 81/91] rename core file for our updater. --- ...eway_paystack.php => paystack-gateway-paid-memberships-pro.php | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename class.pmprogateway_paystack.php => paystack-gateway-paid-memberships-pro.php (100%) diff --git a/class.pmprogateway_paystack.php b/paystack-gateway-paid-memberships-pro.php similarity index 100% rename from class.pmprogateway_paystack.php rename to paystack-gateway-paid-memberships-pro.php From a5afca0d7d73a5aa3b8821163e27f42f3f65ed16 Mon Sep 17 00:00:00 2001 From: WordPress POT/PO/MO Generator <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 6 Nov 2024 12:57:27 +0000 Subject: [PATCH 82/91] =?UTF-8?q?=F0=9F=94=84=20Regenerate=20translation?= =?UTF-8?q?=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../paystack-gateway-paid-memberships-pro.mo | Bin 559 -> 557 bytes .../paystack-gateway-paid-memberships-pro.po | 24 +++++++++- .../paystack-gateway-paid-memberships-pro.pot | 44 +++++++++--------- 3 files changed, 44 insertions(+), 24 deletions(-) diff --git a/languages/paystack-gateway-paid-memberships-pro.mo b/languages/paystack-gateway-paid-memberships-pro.mo index 3e27e17dd1df70fbe909dd44376cff27ddc0ac03..4eb2e0377d86a776ceb6e14158a67c911aaeb25b 100644 GIT binary patch delta 34 qcmZ3_vX*6n3e$haiE8c}^K}_{4RsC7LJWvYusv3KJvKL^XFt^Nl%rjJyWA29_ZP7FI@PR)&_7mov5j0H&)6HUIzs diff --git a/languages/paystack-gateway-paid-memberships-pro.po b/languages/paystack-gateway-paid-memberships-pro.po index 64446c6..d0ba481 100644 --- a/languages/paystack-gateway-paid-memberships-pro.po +++ b/languages/paystack-gateway-paid-memberships-pro.po @@ -2,30 +2,33 @@ # This file is distributed under the GPLv2 or later. msgid "" msgstr "" -"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.8\n" +"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.8\n" "Report-Msgid-Bugs-To: info@paidmembershipspro.com\n" "Last-Translator: Paid Memberships Pro \n" "Language-Team: Paid Memberships Pro \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2024-10-09T08:26:19+00:00\n" +"POT-Creation-Date: 2024-11-06T12:57:27+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "X-Generator: WP-CLI 2.11.0\n" "X-Domain: paystack-gateway-paid-memberships-pro\n" #. Plugin Name of the plugin #: class.pmprogateway_paystack.php +#: paystack-gateway-paid-memberships-pro.php msgid "Paystack Gateway for Paid Memberships Pro" msgstr "" #. Plugin URI of the plugin #: class.pmprogateway_paystack.php +#: paystack-gateway-paid-memberships-pro.php msgid "https://www.paidmembershipspro.com/add-ons/paystack-gateway/" msgstr "" #. Description of the plugin #: class.pmprogateway_paystack.php +#: paystack-gateway-paid-memberships-pro.php msgid "Plugin to add Paystack payment gateway into Paid Memberships Pro" msgstr "" @@ -35,6 +38,7 @@ msgstr "" #. Author URI of the plugin #: class.pmprogateway_paystack.php +#: paystack-gateway-paid-memberships-pro.php msgid "https://www.paidmembershipspro.com" msgstr "" @@ -42,6 +46,7 @@ msgstr "" #: class.pmprogateway_paystack.php:108 #: class.pmprogateway_paystack.php:114 #: class.pmprogateway_paystack.php:118 +#: paystack-gateway-paid-memberships-pro.php:118 msgid "Settings" msgstr "" @@ -49,6 +54,7 @@ msgstr "" #: class.pmprogateway_paystack.php:122 #: class.pmprogateway_paystack.php:128 #: class.pmprogateway_paystack.php:132 +#: paystack-gateway-paid-memberships-pro.php:132 msgid "Check Out with Paystack" msgstr "" @@ -56,6 +62,7 @@ msgstr "" #: class.pmprogateway_paystack.php:122 #: class.pmprogateway_paystack.php:128 #: class.pmprogateway_paystack.php:132 +#: paystack-gateway-paid-memberships-pro.php:132 msgid "Submit and Confirm" msgstr "" @@ -63,6 +70,7 @@ msgstr "" #: class.pmprogateway_paystack.php:135 #: class.pmprogateway_paystack.php:141 #: class.pmprogateway_paystack.php:145 +#: paystack-gateway-paid-memberships-pro.php:145 msgid "Paystack" msgstr "" @@ -71,6 +79,7 @@ msgstr "" #: class.pmprogateway_paystack.php:394 #: class.pmprogateway_paystack.php:408 #: class.pmprogateway_paystack.php:407 +#: paystack-gateway-paid-memberships-pro.php:407 msgid "Test Secret Key" msgstr "" @@ -79,6 +88,7 @@ msgstr "" #: class.pmprogateway_paystack.php:402 #: class.pmprogateway_paystack.php:416 #: class.pmprogateway_paystack.php:415 +#: paystack-gateway-paid-memberships-pro.php:415 msgid "Test Public Key" msgstr "" @@ -87,6 +97,7 @@ msgstr "" #: class.pmprogateway_paystack.php:410 #: class.pmprogateway_paystack.php:424 #: class.pmprogateway_paystack.php:423 +#: paystack-gateway-paid-memberships-pro.php:423 msgid "Live Secret Key" msgstr "" @@ -95,6 +106,7 @@ msgstr "" #: class.pmprogateway_paystack.php:418 #: class.pmprogateway_paystack.php:432 #: class.pmprogateway_paystack.php:431 +#: paystack-gateway-paid-memberships-pro.php:431 msgid "Live Public Key" msgstr "" @@ -103,6 +115,7 @@ msgstr "" #: class.pmprogateway_paystack.php:426 #: class.pmprogateway_paystack.php:440 #: class.pmprogateway_paystack.php:439 +#: paystack-gateway-paid-memberships-pro.php:439 msgid "Webhook" msgstr "" @@ -111,6 +124,7 @@ msgstr "" #: class.pmprogateway_paystack.php:429 #: class.pmprogateway_paystack.php:443 #: class.pmprogateway_paystack.php:442 +#: paystack-gateway-paid-memberships-pro.php:442 msgid "To fully integrate with Paystack, be sure to use the following for your Webhook URL to" msgstr "" @@ -120,6 +134,7 @@ msgstr "" #: class.pmprogateway_paystack.php:975 #: class.pmprogateway_paystack.php:973 #: class.pmprogateway_paystack.php:984 +#: paystack-gateway-paid-memberships-pro.php:984 msgid "Account" msgstr "" @@ -129,6 +144,7 @@ msgstr "" #: class.pmprogateway_paystack.php:976 #: class.pmprogateway_paystack.php:974 #: class.pmprogateway_paystack.php:985 +#: paystack-gateway-paid-memberships-pro.php:985 msgid "Order" msgstr "" @@ -138,6 +154,7 @@ msgstr "" #: class.pmprogateway_paystack.php:977 #: class.pmprogateway_paystack.php:975 #: class.pmprogateway_paystack.php:986 +#: paystack-gateway-paid-memberships-pro.php:986 msgid "Membership Level" msgstr "" @@ -147,11 +164,13 @@ msgstr "" #: class.pmprogateway_paystack.php:978 #: class.pmprogateway_paystack.php:976 #: class.pmprogateway_paystack.php:987 +#: paystack-gateway-paid-memberships-pro.php:987 msgid "Amount Paid" msgstr "" #. Author of the plugin #: class.pmprogateway_paystack.php +#: paystack-gateway-paid-memberships-pro.php msgid "Paid Memberships Pro, Paystack" msgstr "" @@ -160,5 +179,6 @@ msgstr "" #: class.pmprogateway_paystack.php:1166 #: class.pmprogateway_paystack.php:1164 #: class.pmprogateway_paystack.php:1175 +#: paystack-gateway-paid-memberships-pro.php:1175 msgid "There was an error communicating with Paystack. Please confirm your connectivity and API details and try again." msgstr "" diff --git a/languages/paystack-gateway-paid-memberships-pro.pot b/languages/paystack-gateway-paid-memberships-pro.pot index 55eb0ea..3631675 100644 --- a/languages/paystack-gateway-paid-memberships-pro.pot +++ b/languages/paystack-gateway-paid-memberships-pro.pot @@ -2,99 +2,99 @@ # This file is distributed under the GPLv2 or later. msgid "" msgstr "" -"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.8\n" +"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.8\n" "Report-Msgid-Bugs-To: info@paidmembershipspro.com\n" "Last-Translator: Paid Memberships Pro \n" "Language-Team: Paid Memberships Pro \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2024-10-09T08:26:19+00:00\n" +"POT-Creation-Date: 2024-11-06T12:57:27+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "X-Generator: WP-CLI 2.11.0\n" "X-Domain: paystack-gateway-paid-memberships-pro\n" #. Plugin Name of the plugin -#: class.pmprogateway_paystack.php +#: paystack-gateway-paid-memberships-pro.php msgid "Paystack Gateway for Paid Memberships Pro" msgstr "" #. Plugin URI of the plugin -#: class.pmprogateway_paystack.php +#: paystack-gateway-paid-memberships-pro.php msgid "https://www.paidmembershipspro.com/add-ons/paystack-gateway/" msgstr "" #. Description of the plugin -#: class.pmprogateway_paystack.php +#: paystack-gateway-paid-memberships-pro.php msgid "Plugin to add Paystack payment gateway into Paid Memberships Pro" msgstr "" #. Author of the plugin -#: class.pmprogateway_paystack.php +#: paystack-gateway-paid-memberships-pro.php msgid "Paid Memberships Pro, Paystack" msgstr "" #. Author URI of the plugin -#: class.pmprogateway_paystack.php +#: paystack-gateway-paid-memberships-pro.php msgid "https://www.paidmembershipspro.com" msgstr "" -#: class.pmprogateway_paystack.php:118 +#: paystack-gateway-paid-memberships-pro.php:118 msgid "Settings" msgstr "" -#: class.pmprogateway_paystack.php:132 +#: paystack-gateway-paid-memberships-pro.php:132 msgid "Check Out with Paystack" msgstr "" -#: class.pmprogateway_paystack.php:132 +#: paystack-gateway-paid-memberships-pro.php:132 msgid "Submit and Confirm" msgstr "" -#: class.pmprogateway_paystack.php:145 +#: paystack-gateway-paid-memberships-pro.php:145 msgid "Paystack" msgstr "" -#: class.pmprogateway_paystack.php:407 +#: paystack-gateway-paid-memberships-pro.php:407 msgid "Test Secret Key" msgstr "" -#: class.pmprogateway_paystack.php:415 +#: paystack-gateway-paid-memberships-pro.php:415 msgid "Test Public Key" msgstr "" -#: class.pmprogateway_paystack.php:423 +#: paystack-gateway-paid-memberships-pro.php:423 msgid "Live Secret Key" msgstr "" -#: class.pmprogateway_paystack.php:431 +#: paystack-gateway-paid-memberships-pro.php:431 msgid "Live Public Key" msgstr "" -#: class.pmprogateway_paystack.php:439 +#: paystack-gateway-paid-memberships-pro.php:439 msgid "Webhook" msgstr "" -#: class.pmprogateway_paystack.php:442 +#: paystack-gateway-paid-memberships-pro.php:442 msgid "To fully integrate with Paystack, be sure to use the following for your Webhook URL to" msgstr "" -#: class.pmprogateway_paystack.php:984 +#: paystack-gateway-paid-memberships-pro.php:984 msgid "Account" msgstr "" -#: class.pmprogateway_paystack.php:985 +#: paystack-gateway-paid-memberships-pro.php:985 msgid "Order" msgstr "" -#: class.pmprogateway_paystack.php:986 +#: paystack-gateway-paid-memberships-pro.php:986 msgid "Membership Level" msgstr "" -#: class.pmprogateway_paystack.php:987 +#: paystack-gateway-paid-memberships-pro.php:987 msgid "Amount Paid" msgstr "" -#: class.pmprogateway_paystack.php:1175 +#: paystack-gateway-paid-memberships-pro.php:1175 msgid "There was an error communicating with Paystack. Please confirm your connectivity and API details and try again." msgstr "" From 4f6628cba263f2807d68f66ac0dd77071ce84833 Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Thu, 10 Jul 2025 13:43:52 +0200 Subject: [PATCH 83/91] Added support for PMPro 3.5 --- classes/class.pmprogateway_paystack.php | 116 +++++++++++++++++++++++- services/paystack-webhook.php | 4 +- 2 files changed, 114 insertions(+), 6 deletions(-) diff --git a/classes/class.pmprogateway_paystack.php b/classes/class.pmprogateway_paystack.php index f282bcc..090b9b9 100644 --- a/classes/class.pmprogateway_paystack.php +++ b/classes/class.pmprogateway_paystack.php @@ -25,7 +25,7 @@ public static function init() { // Add fields to payment settings. add_filter( 'pmpro_payment_options', array( 'PMProGateway_Paystack', 'pmpro_payment_options' ) ); - add_filter( 'pmpro_payment_option_fields', array( 'PMProGateway_Paystack', 'pmpro_payment_option_fields' ), 10, 2 ); + add_filter( 'pmpro_payment_option_fields', array( 'PMProGateway_Paystack', 'pmpro_payment_option_fields' ), 10, 2 ); add_action( 'wp_ajax_pmpro_paystack_ipn', array( 'PMProGateway_Paystack', 'pmpro_paystack_ipn' ) ); add_action( 'wp_ajax_nopriv_pmpro_paystack_ipn', array( 'PMProGateway_Paystack', 'pmpro_paystack_ipn' ) ); @@ -42,7 +42,7 @@ public static function init() { add_filter( 'pmpro_include_billing_address_fields', '__return_false' ); add_filter( 'pmpro_include_payment_information_fields', '__return_false' ); add_filter( 'pmpro_billing_show_payment_method', '__return_false' ); - add_filter('pmpro_required_billing_fields', array( 'PMProGateway_Paystack', 'pmpro_required_billing_fields' ) ); + add_filter( 'pmpro_required_billing_fields', array( 'PMProGateway_Paystack', 'pmpro_required_billing_fields' ) ); // Refund functionality. add_filter( 'pmpro_allowed_refunds_gateways', array( 'PMProGateway_Paystack', 'pmpro_allowed_refunds_gateways' ) ); @@ -118,6 +118,7 @@ static function getGatewayOptions() { /** * Set payment options for payment settings page. + * This has been deprecated due to Paid Memberships Pro V3.5+. */ static function pmpro_payment_options( $options ) { //get Paystack options @@ -131,6 +132,8 @@ static function pmpro_payment_options( $options ) { /** * Display fields for Paystack options. + * This function is deprecated and only used for versions before PMPro 3.5 + * See the new callback `show_settings_fields` in the `PMProGateway_Paystack` class. */ static function pmpro_payment_option_fields( $values, $gateway ) { ?> @@ -178,6 +181,111 @@ static function pmpro_payment_option_fields( $values, $gateway ) { +

+ ' . esc_html__( 'Paystack documentation', 'paystack-gateway-paid-memberships-pro' ) . '' + ); + ?> +

+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + +


+
+
+
+ status ) { - $order->notes = trim( $order->notes.' '.sprintf( __('Admin: Order refund failed on %1$s for transaction ID %2$s by %3$s. Order may have already been refunded.', 'paid-memberships-pro' ), date_i18n('Y-m-d H:i:s'), $transaction_id, $current_user->display_name ) ); + $order->notes = trim( $order->notes.' '.sprintf( __('Admin: Order refund failed on %1$s for transaction ID %2$s by %3$s. Order may have already been refunded.', 'paystack-gateway-paid-memberships-pro' ), date_i18n('Y-m-d H:i:s'), $transaction_id, $current_user->display_name ) ); $order->saveOrder(); } else { // Set the order status to refunded and save it and return true @@ -505,7 +613,7 @@ public static function process_refund( $success, $order ) { $success = true; - $order->notes = trim( $order->notes.' '.sprintf( __('Admin: Order successfully refunded on %1$s for transaction ID %2$s by %3$s.', 'paid-memberships-pro' ), date_i18n('Y-m-d H:i:s'), $transaction_id, $current_user->display_name ) ); + $order->notes = trim( $order->notes.' '.sprintf( __('Admin: Order successfully refunded on %1$s for transaction ID %2$s by %3$s.', 'paystack-gateway-paid-memberships-pro' ), date_i18n('Y-m-d H:i:s'), $transaction_id, $current_user->display_name ) ); $user = get_user_by( 'id', $order->user_id ); //send an email to the member diff --git a/services/paystack-webhook.php b/services/paystack-webhook.php index a53b84a..5e3a4d6 100644 --- a/services/paystack-webhook.php +++ b/services/paystack-webhook.php @@ -209,7 +209,7 @@ function pmpro_paystack_confirm_subscription( $post_event, $order ) { } else { //Create Plan $body = array( - 'name' => '('.number_format($amount).') - '.$interval.' - ['.$pmpro_level->cycle_number.' - '.$pmpro_level->cycle_period.']' , + 'name' => apply_filters( 'pmpro_paystack_plan_description', substr( trim( $pmpro_level->name ) . " at " . trim( get_bloginfo( "name" ) ), 0, 127 ), $pmpro_level ), 'amount' => $koboamount, 'interval' => $interval ); @@ -380,4 +380,4 @@ function pmpro_paystack_renew_payment( $post_event ) { pmpro_paystack_webhook_log( sprintf( 'Subscription payment completed for user with ID: %d. Order ID: %s', $user_id, $morder->code ) ); pmpro_paystack_webhook_exit(); } -} \ No newline at end of file +} From 9a05bbaef084ccc2b66635f9b022780acf30d5cf Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 11 Jul 2025 15:03:56 +0200 Subject: [PATCH 84/91] Update version --- README.md | 3 +- paystack-gateway-paid-memberships-pro.php | 2 +- readme.txt | 13 +- style.css | 434 ---------------------- 4 files changed, 13 insertions(+), 439 deletions(-) delete mode 100644 style.css diff --git a/README.md b/README.md index 8f1cfe5..ac5ba37 100644 --- a/README.md +++ b/README.md @@ -33,4 +33,5 @@ There are various **ways to the help development** of Paid Memberships Pro - Pay Here are some ways for **non-developers to contribute** to Paid Memberships Pro - Paystack Gateway: 1. Translate Paid Memberships Pro - Paystack Gateway into your own [language](https://www.paidmembershipspro.com/paid-memberships-pro-in-your-language/). -2. [Purchase a paid membership](https://paidmembershipspro.com/pricing) to help fund ongoing development and bug fixes. \ No newline at end of file +2. [Purchase a paid membership](https://paidmembershipspro.com/pricing) to help fund ongoing development and bug fixes. +3. Leave an honest review for [Paid Memberships Pro - Paystack Gateway](https://www.paidmembershipspro.com/submit-testimonial/). \ No newline at end of file diff --git a/paystack-gateway-paid-memberships-pro.php b/paystack-gateway-paid-memberships-pro.php index 221f09b..49586f9 100644 --- a/paystack-gateway-paid-memberships-pro.php +++ b/paystack-gateway-paid-memberships-pro.php @@ -3,7 +3,7 @@ * Plugin Name: Paystack Gateway for Paid Memberships Pro * Plugin URI: https://www.paidmembershipspro.com/add-ons/paystack-gateway/ * Description: Plugin to add Paystack payment gateway into Paid Memberships Pro - * Version: 1.7.8 + * Version: 1.9 * Author: Paid Memberships Pro, Paystack * Author URI: https://www.paidmembershipspro.com * License: GPLv2 or later diff --git a/readme.txt b/readme.txt index 8aa0935..3708f3e 100755 --- a/readme.txt +++ b/readme.txt @@ -3,8 +3,8 @@ Contributors: paystack, kendysond, steveamaza, lukman008, andrewza, strangerstud Donate link: https://paystack.com/demo Tags: paid memberships pro, paystack, gateway, credit card, Naira Requires at least: 5.2 -Tested up to: 6.6 -Stable tag: 1.7.8 +Tested up to: 6.8 +Stable tag: 1.9 License: GPLv3 License URI: http://www.gnu.org/licenses/gpl-3.0.html @@ -62,7 +62,7 @@ You can find help and information on Paystack on our [Help Desk](https://paystac = Where can I get support or talk to other users? = -If you get stuck, you can ask for help in the [Paystack Gateway for Paid Membership Pro Plugin Forum](https://wordpress.org/support/plugin/paystack-gateway-paid-memberships-pro). +If you get stuck, you can ask for help in the [Paid Memberships Pro Support](https://www.paidmembershipspro.com/support). = Paystack Gateway for Paid Membership Pro is awesome! Can I contribute? = @@ -73,6 +73,13 @@ Yes you can! Join in on our [GitHub repository](https://github.com/strangerstudi 1. The slick Paystack settings panel. == Changelog == += 1.9 - 2025-07-11 = +* BUG FIX: Fixed an issue where the payment settings fields were not showing correctly in Paid Memberships Pro 3.5+ +* REFACTOR: Improved support for Paid Memberships Pro 3.0+ to better support new subscription features moving forward. + += 1.8 - 2024-11-06 = +* ENHANCEMENT: Serving updates from the paidmembershipspro.com update server. + = 1.7.8 - 2024-10-09 = * BUG FIX: Fixed an issue where free checkouts would cause a fatal error. diff --git a/style.css b/style.css deleted file mode 100644 index f688833..0000000 --- a/style.css +++ /dev/null @@ -1,434 +0,0 @@ -@charset "UTF-8"; -/* CSS Document */ -.pmpro_advanced_levels-genesis .pmpro_level.one-third, .pmpro_advanced_levels-genesis .pmpro_level.one-fourth, .pmpro_advanced_levels-gantry .pmpro_level.span4, .pmpro_advanced_levels-gantry .pmpro_level.span3, .pmpro_advanced_levels-woothemes .pmpro_level.threecol-one, .pmpro_advanced_levels-woothemes .pmpro_level.fourcol-one, .pmpro_advanced_levels-div .text-center, .pmpro_advanced_levels-compare_table th, .pmpro_advanced_levels-compare_table td {text-align: center; } -table.pmpro_advanced_levels-genesis .button, .pmpro_advanced_levels-genesis .pmpro_level.one-third .button, .pmpro_advanced_levels-genesis .pmpro_level.one-fourth .button, table.pmpro_advanced_levels-foundation .button, .pmpro_advanced_levels-foundation .pmpro_level.medium-4 .button, .pmpro_advanced_levels-foundation .pmpro_level.medium-3 .button, table.pmpro_advanced_levels-gantry .btn, table.pmpro_advanced_levels-woothemes .woo-sc-button, .pmpro_advanced_levels-woothemes .pmpro_level.threecol-one .woo-sc-button, .pmpro_advanced_levels-woothemes .pmpro_level.fourcol-one .woo-sc-button, table.pmpro_advanced_levels-twentyfourteen .button, .pmpro_advanced_levels-table .pmpro_btn, .text-center.pmpro_advanced_levels-div .pmpro_btn, table.pmpro_advanced_levels-bootstrap .btn, .pmpro_advanced_levels-compare_table .pmpro_btn, .pmpro_advanced_levels-compare_table_responsive .pmpro_btn, .pmpro_advanced_levels-compare_table_responsive .button {display: block; text-align: center; } -.pmpro_advanced_levels-compare_table {border: none; } -.pmpro_advanced_levels-compare_table th, .pmpro_advanced_levels-compare_table td {border-color: #d1d1d1; border-width: 0 1px 1px 0; padding: .75em 1em; vertical-align: middle; } -.pmpro_advanced_levels-compare_table thead th, .pmpro_advanced_levels-compare_table tfoot td {border-width: 0 1px 0 0; } -.pmpro_advanced_levels-compare_table tr th:last-child, .pmpro_advanced_levels-compare_table tr td:last-child {border-right: none; } -.pmpro_advanced_levels-compare_table thead tr th:nth-child(even), .pmpro_advanced_levels-compare_table tbody tr td:nth-child(even) {background: rgba(0,0,0,0.02); } -.pmpro_advanced_levels-compare_table thead tr th:first-child, .pmpro_advanced_levels-compare_table tfoot tr td:first-child {background: none; text-indent: -9999em; width: 20%; } -.pmpro_advanced_levels-compare_table thead tr:last-child th, .pmpro_advanced_levels-compare_table tbody tr:last-child td {border-bottom: .5rem solid #CCC; } -.pmpro_advanced_levels-compare_table tbody tr:nth-child(even) td {background: rgba(0,0,0,0.05); } -.pmpro_advanced_levels-compare_table tbody tr td:first-child {text-align: right; } -.pmpro_advanced_levels-compare_table tfoot td {padding: .75em 1em; vertical-align: middle; } -.pmpro_advanced_levels-compare_table thead th h2 {margin: 0; padding: .5em 0 0 0; } -.pmpro_advanced_levels-compare_table .pmpro_level-price {font-size: 1.6rem; padding-bottom: 0; padding-top: 0; } -.pmpro_advanced_levels-compare_table .pmpro_level-expiration {font-weight: normal; } -.pmpro_advanced_levels-compare_table .pmpro_level-compare-true, .pmpro_advanced_levels-compare_table_responsive .pmpro_level-compare-true {font-size: 2rem; line-height: 2rem; } -.pmpro_advanced_levels-compare_table .pmpro_level-compare-true:after, .pmpro_advanced_levels-compare_table_responsive .pmpro_level-compare-true:after {content: "\2713"; } -.pmpro_advanced_levels-compare_table .pmpro_level-compare-false, .pmpro_advanced_levels-compare_table_responsive .pmpro_level-compare-false {color: rgba(0,0,0,0.2); } -.pmpro_advanced_levels-compare_table .pmpro_level-compare-false:after, .pmpro_advanced_levels-compare_table_responsive .pmpro_level-compare-false:after {content: "\002D"; } - -/* Styles for Bootstrap Template */ -table.pmpro_advanced_levels-bootstrap h2 {margin-top: 0; } -.pmpro_advanced_levels-bootstrap .panel-heading h2 {margin: 0; padding: 0; } - -/* Styles for Genesis Template */ -table.pmpro_advanced_levels-genesis thead th, table.pmpro_advanced_levels-genesis tbody td {padding-right: 10px; padding-left: 10px; } - -/* Styles for Gantry Template */ -table.pmpro_advanced_levels-gantry h2 {border-bottom: none; box-shadow: none; padding-bottom: 0; } - -/* Styles for WooThemes Template */ -table.pmpro_advanced_levels-woothemes tbody td {vertical-align: middle; } - -/* Styles for Foundation Template */ -.pmpro_advanced_levels-foundation .panel .button {margin-bottom: 0; } -.pmpro_advanced_levels-foundation .panel h5.subheader {color: #6f6f6f; } -.pmpro_advanced_levels-foundation .pricing-table .price {font-size: 1rem; } -.pmpro_advanced_levels-foundation .pricing-table .price span {display: block; font-size: 2rem; } - -/* Styles for TwentyFourteen Template */ -.pmpro_advanced_levels-twentyfourteen .button, .pmpro_advanced_levels-twentyfourteen .button:hover {color: #FFF; } - -/* Styles for No Template */ -.pmpro_advanced_levels-div .post {padding: 1em; } - -.pmpro_advanced_levels-compare_table_responsive {display: none; text-align: center; } - -@media only screen and (max-width: 767px) { - .pmpro_advanced_levels-compare_table {display: none; } - .pmpro_advanced_levels-compare_table_responsive {display: block; } -} - -.pmpro_advanced_levels-div .row { - width: 100%; - margin-left: auto; - margin-right: auto; - margin-top: 0; - margin-bottom: 0; - max-width: 73.125em; } - .pmpro_advanced_levels-div .row:before, .pmpro_advanced_levels-div .row:after { - content: " "; - display: table; } - .pmpro_advanced_levels-div .row:after { - clear: both; } - .pmpro_advanced_levels-div .column, - .pmpro_advanced_levels-div .columns { - position: relative; - float: left; } - -.pmpro_advanced_levels-div [class*="column"] + [class*="column"]:last-child { - float: right; } - -.pmpro_advanced_levels-div [class*="column"] + [class*="column"].end { - float: left; } - -.pmpro_advanced_levels-div .small-12 { - width: 100%; } - -@media only screen and (min-width: 40.063em) { - .pmpro_advanced_levels-div .medium-1 { - width: 8.33333%; } - - .pmpro_advanced_levels-div .medium-2 { - width: 16.66667%; } - - .pmpro_advanced_levels-div .medium-3 { - width: 25%; } - - .pmpro_advanced_levels-div .medium-4 { - width: 33.33333%; } - - .pmpro_advanced_levels-div .medium-5 { - width: 41.66667%; } - - .pmpro_advanced_levels-div .medium-6 { - width: 50%; } - - .pmpro_advanced_levels-div .medium-7 { - width: 58.33333%; } - - .pmpro_advanced_levels-div .medium-8 { - width: 66.66667%; } - - .pmpro_advanced_levels-div .medium-9 { - width: 75%; } - - .pmpro_advanced_levels-div .medium-10 { - width: 83.33333%; } - - .pmpro_advanced_levels-div .medium-11 { - width: 91.66667%; } - - .pmpro_advanced_levels-div .medium-12 { - width: 100%; } -} - -/*-------------------------------------------------------------- -11.1 Paid Memberships Pro Integrated Styles ---------------------------------------------------------------*/ -#pmpro_levels {margin-top: 2rem; margin-bottom: 2rem; } -#pmpro_levels .medium-4, #pmpro_levels .medium-3 { - text-align: center; -} -#pmpro_levels .row .post { - padding: 0 1rem; -} -#pmpro_levels .post h2, .memberlite_signup h2, .pmpro_signup_form h2 { - background: #FAFAFA; - color: #2C3E50; - border-top: 1px solid #CCC; - border-bottom: 1px dotted #CCC; - padding: .5rem; - margin: 0 0 1rem 0; -} -#pmpro_levels .medium-4 .pmpro_btn, #pmpro_levels .medium-3 .pmpro_btn, .pmpro_levels-table .pmpro_btn, .pmpro_advanced_levels-compare_table .pmpro_btn, .pmpro_advanced_levels-compare_table_responsive .pmpro_btn { - display: block; -} -#pmpro_levels.pmpro_levels-table.pmpro_level-highlight, #pmpro_levels.pmpro_levels-div .pmpro_level-highlight, #pmpro_levels.pmpro_levels-2col .pmpro_level-highlight, #pmpro_levels.pmpro_levels-3col .pmpro_level-highlight, #pmpro_levels.pmpro_levels-4col .pmpro_level-highlight, .memberlite_signup, .pmpro_signup_form {padding: 1rem; background: #FFF; z-index: 100; border-top: .5rem solid #18BC9C; border-bottom: 1.5rem solid #18BC9C; border-left: 1px solid #CCC; border-right: 1px solid #CCC; } -#pmpro_levels.pmpro_levels-2col .pmpro_level-highlight, #pmpro_levels.pmpro_levels-3col .pmpro_level-highlight, #pmpro_levels.pmpro_levels-4col .pmpro_level-highlight {margin-top: -1.5rem; } - -#pmpro_levels_table td, #pmpro_levels.pmpro_levels-table td {vertical-align: middle; } - -#pmpro_levels.pmpro_levels-table .pmpro_level-highlight td:first-child {border-left: 15px solid #18BC9C } -#pmpro_levels.pmpro_levels-table .pmpro_level-highlight td:last-child {border-right: 15px solid #18BC9C } - -#pmpro_levels.pmpro_advanced_levels-compare_table {overflow: hidden; } -#pmpro_levels.pmpro_advanced_levels-compare_table th, #pmpro_levels.pmpro_advanced_levels-compare_table td {padding: 1rem 2rem; position: relative; text-align: center; } -#pmpro_levels.pmpro_advanced_levels-compare_table thead th {border: none; padding-top: 0; text-align: center; } -#pmpro_levels.pmpro_advanced_levels-compare_table thead th h2 {margin-bottom: 0; } -#pmpro_levels.pmpro_advanced_levels-compare_table thead th:first-child, #pmpro_levels.pmpro_advanced_levels-compare_table tfoot td:first-child {background: none; border: none; text-indent: -9999em; width: 20%; } -#pmpro_levels.pmpro_advanced_levels-compare_table thead tr th:nth-child(even) {background: rgba(0,0,0,0.1); } -#pmpro_levels.pmpro_advanced_levels-compare_table tbody td {vertical-align: middle; } -#pmpro_levels.pmpro_advanced_levels-compare_table tbody td:first-child {text-align: right; } -#pmpro_levels.pmpro_advanced_levels-compare_table tbody tr:last-child td {border-bottom: .5rem solid #CCC; } -#pmpro_levels.pmpro_advanced_levels-compare_table thead tr:last-child th {border-bottom: .5rem solid #CCC; } -#pmpro_levels.pmpro_advanced_levels-compare_table thead th.pmpro_level-highlight, #pmpro_levels.pmpro_advanced_levels-compare_table tbody td.pmpro_level-highlight, #pmpro_levels.pmpro_advanced_levels-compare_table tfoot td.pmpro_level-highlight { border-left: 1rem solid #18BC9C; border-right: 1rem solid #18BC9C; } -#pmpro_levels.pmpro_advanced_levels-compare_table thead tr:first-child th.pmpro_level-highlight { border-top: 1rem solid #18BC9C; } -#pmpro_levels.pmpro_advanced_levels-compare_table thead tr:last-child th.pmpro_level-highlight, #pmpro_levels.pmpro_advanced_levels-compare_table tbody tr td.pmpro_level-highlight, #pmpro_levels.pmpro_advanced_levels-compare_table tfoot tr td.pmpro_level-highlight { border-bottom: none; } -#pmpro_levels.pmpro_advanced_levels-compare_table tfoot tr:last-child td.pmpro_level-highlight {border-bottom: 1rem solid #18BC9C; } - -#pmpro_levels.pmpro_advanced_levels-compare_table_responsive {display: none; } -#pmpro_levels.pmpro_advanced_levels-compare_table_responsive {text-align: center; } -#pmpro_levels.pmpro_advanced_levels-compare_table_responsive .pmpro_level-highlight {border-top: .5rem solid #18BC9C; border-bottom: 1.5rem solid #18BC9C; border-left: 1px solid #CCC; border-right: 1px solid #CCC; } - -#pmpro_levels.pmpro_advanced_levels-compare_table:hover tbody tr:nth-child(even) td {background: none !important; } -#pmpro_levels.pmpro_advanced_levels-compare_table tbody tr:hover {background-color: rgba(252,248,227,0.8); } -#pmpro_levels.pmpro_advanced_levels-compare_table tbody td:hover::after, #pmpro_levels.pmpro_advanced_levels-compare_table tbody tr:nth-child(even) td:hover::after { - background-color: rgba(252,248,227,0.3); - content: ""; - height: 10000px; - left: 0; - position: absolute; - top: -5000px; - width: 100%; - z-index: -1; -} - -.pmpro_levels-3col .pmpro_level-price, .pmpro_levels-4col .pmpro_level-price {font-size: 24px; font-size: 2.4rem; } -.pmpro_levels-3col .pmpro_level-subprice, .pmpro_levels-4col .pmpro_level-subprice {display: block; font-size: 14px; font-size: 1.4rem; line-height: 2rem; } -.pmpro_levels-3col .pmpro_level-trialprice, .pmpro_levels-4col .pmpro_level-trialprice {display: block; font-size: 14px; font-size: 1.4rem; line-height: 2rem; margin: 1rem; } -.pmpro_levels-div .pmpro_level-subprice, .pmpro_levels-table .pmpro_level-subprice { } -.pmpro_levels-3col .pmpro_level-expiration, .pmpro_levels-4col .pmpro_level-expiration { } - -.pmpro_levels-div .pmpro_btn-select, .pmpro_levels-2col .pmpro_btn-select {margin-left: 3rem; } -.pmpro_levels-div .pmpro_level-price, .pmpro_levels-2col .pmpro_level-price, .pmpro_levels-div .pmpro_level-expiration, .pmpro_levels-2col .pmpro_level-expiration {display: inline; margin: 0; } - -.memberlite_signup-fixed {position: fixed; top: 0; } - -.pmpro_asterisk {color: #C00; } -.pmpro_asterisk abbr {border: none; } -form.pmpro_form table {margin: 0 0 2.5rem; } -form.pmpro_form #pmpro_level_cost {margin: 0; } -form.pmpro_form p {margin: 1rem 0; } -form.pmpro_form label {font-weight: 400; } -form.pmpro_form span.pmpro_thead-name {width: 35%; } -form.pmpro_form span.pmpro_thead-msg {font-family: 'Lato', sans-serif; font-weight: normal; width: 65%; } -form.pmpro_form .pmpro_submit {padding: 0 1rem; } -form.pmpro_form textarea {width:75%; } -form.pmpro_form .pmpro_checkout { - padding-top: 0; -} -form.pmpro_form .pmpro_checkout h2 { - background: #EFEFEF; - border-top: 1px solid #CCC; - border-bottom: 1px solid #CCC; - font-family: 'Lato', sans-serif; - font-size: 16px; - font-size: 1.6rem; - font-weight: 700; - line-height: 2.6rem; - margin: 0; - padding: 1rem; -} -form.pmpro_form div.pmpro_checkout h2:after { - clear: both; - content: ''; - display: table; -} -form.pmpro_form .pmpro_checkout .pmpro_checkout-fields { - padding: 1rem; -} -form.pmpro_form .pmpro_checkout #other_discount_code_p, form.pmpro_form .pmpro_checkout #other_discount_code_div { - border-top: 1px dotted #CCC; - margin: 1rem 0 0 0; - padding: 1rem 0 0 0; -} -form.pmpro_form .pmpro_checkout #other_discount_code_div input[type=button], #discount_code_button {padding-top: 3px; padding-bottom: 3px; } - -#pmpro_account .pmpro_box {padding: 3rem 0 0 0; margin: 0 0 3rem 0; border-bottom: none; border-top: 1px dotted #CCC; } -#pmpro_account #pmpro_account-membership .pmpro_actionlinks {text-align: center; } -#pmpro_account .pmpro_actionlinks a {text-decoration: underline; } -#pmpro_account #pmpro_account-membership table tbody tr td .pmpro_actionlinks {text-align: left; } - -#pmpro_account .pmpro_box h3 {margin: 0 0 2rem 0; } -#pmpro_account #pmpro_account-profile ul {margin-top: 1rem; margin-bottom: 1rem; } - -.pmpro_member_directory h3 {margin-top: 0; } - -/*-------------------------------------------------------------- -11.2 Theme My Login: https://wordpress.org/plugins/theme-my-login/ ---------------------------------------------------------------*/ -#your-profile label {display: inline-block; } -#your-profile .form-table th {width: 250px; } -#your-profile h3 { - background: #EFEFEF; - border-top: 1px solid #CCC; - border-bottom: 1px solid #CCC; - margin: 0; - padding: 1rem; -} -#your-profile p {margin-bottom: .5rem; } -#your-profile p.submit {text-align: center; } -#secondary .widget_theme_my_login .widget-title {margin-bottom: .5em; } -#secondary .widget_theme_my_login ul li { - border: none; - padding: 0; -} -#secondary .tml {max-width: 100%; margin: 0 .5em; } -#secondary .tml p {margin-bottom: 1rem; } -#secondary .tml .input {margin-top: 0; } -.tml-user-avatar { - float: left; - width: 80px; - height: 80px; - border-radius: 50%; - margin: 0 10px 0 0; -} -.tml-user-avatar img { - border-radius: 50%; - transition: transform 0.5s ease; - transform: scale(0.8); -} -.tml .tml-action-links { - border-top: 1px dotted #AAA; - list-style: none; - margin: 1rem 0 0 0; - padding: 1rem 0 0 0; - width: 100%; -} -.tml .tml-action-links li { - display:inline-block; -} -#primary .tml-action-links { - background: #FAFAFA; - border-bottom: 1px dotted #CCC; - color: #AAA; - font-style: italic; - padding: 1rem; -} -#primary .tml-action-links a { - border-bottom: 1px solid transparent; - color: #777; - text-decoration: none; -} -#primary .tml-action-links a:hover {border-bottom: 1px dotted #777; } -#secondary .tml-action-links {text-align: center; } -ul.tml-user-links {list-style: none; margin-bottom: 0; } - -#primary .medium-4 .login input[type="text"], #primary .medium-4 .login input[type="password"] {width: 100%; } -#primary .medium-4 .login #pass-strength-result {width: 100%; } -#primary .medium-4 .login input[type="submit"] {display: block; width: 100%; } -#primary .medium-4 .tml-action-links {text-align: center; } - - -/*-------------------------------------------------------------- -4.1 Buttons ---------------------------------------------------------------*/ -button, -input, -select, -textarea, -a.comment-reply-link, -a.pmpro_btn, -#main div.em-search-main button.em-search-submit { - font-size: 100%; /* Corrects font size not being inherited in all browsers */ - margin: 0; /* Addresses margins set differently in IE6/7, F3/4, S5, Chrome */ - vertical-align: baseline; /* Improves appearance and consistency in all browsers */ -} -button, -input[type="button"], -input[type="reset"], -input[type="submit"], -.btn, -.btn:link, -a.comment-reply-link, -a.pmpro_btn, -.pmpro_content_message a, -.pmpro_content_message a:link, -input[type="submit"].pmpro_btn, -#main div.em-search-main button.em-search-submit { - background: #95a5a6; - border: none; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - box-shadow: none; - color: #FFF !important; - cursor: pointer; /* Improves usability and consistency of cursor style between image-type 'input' and others */ - /* -webkit-appearance: button; Corrects inability to style clickable 'input' types in iOS */ - font-family: 'Lato', sans-serif; - font-size: 16px; - font-size: 1.6rem; - font-style: normal; - font-weight: normal; - display: inline-block; - /*padding: 1rem 1.5rem;*/ - text-align: center; - text-decoration: none; - text-shadow: none; - -webkit-transform: translateZ(0); - transform: translateZ(0); - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - -moz-osx-font-smoothing: grayscale; - overflow: hidden; - -webkit-transition-duration: 0.3s; - transition-duration: 0.3s; - -webkit-transition-property: color, background-color; - transition-property: color, background-color; -} -#main div.em-search-main button.em-search-submit {padding-bottom: .5rem; } - -a.pmpro_btn, -a.pmpro_btn:link, -a.pmpro_btn:visited, -input[type="submit"].pmpro_btn { - background: #F39C12; - border: none; - color: #FFF; - font-size: 18px; - font-size: 1.8rem; - padding: 0.4rem 1rem; - margin-top: 1em; - text-shadow: none; -} -button:hover, -input[type="button"]:hover, -input[type="reset"]:hover, -input[type="submit"]:hover, -button:focus, -input[type="button"]:focus, -input[type="reset"]:focus, -input[type="submit"]:focus, -button:active, -input[type="button"]:active, -input[type="reset"]:active, -input[type="submit"]:active, -.btn:hover, -.btn:active, -.btn:focus, -a.comment-reply-link:focus, -a.comment-reply-link:active, -a.comment-reply-link:hover, -a.pmpro_btn:active, -#main div.em-search-main button.em-search-submit:hover { - background: #798d8f; - color: #FFF; -} - -a.pmpro_btn:hover, input[type="submit"].pmpro_btn:hover { - background: #d90000; - border: none; - text-shadow: none; -} - -.btn_info, .btn_info:link {background-color: #5bc0de; } -.btn_success, .btn_success:link {background-color: #5cb85c; } -.btn_error, .btn_error:link {background-color: #d9534f; } -.btn_alert, .btn_alert:link {background-color: #f0ad4e; } -.btn_info:hover {background-color: #31b0d5; } -.btn_success:hover {background-color: #449d44; } -.btn_error:hover {background-color: #c9302c; } -.btn_alert:hover {background-color: #ec971f; } - -.btn_link { - cursor: pointer; /* Improves usability and consistency of cursor style between image-type 'input' and others */ - display: inline-block; - /* -webkit-appearance: button; Corrects inability to style clickable 'input' types in iOS */ - font-family: 'Lato', sans-serif; - font-style: normal; - font-weight: 400; - overflow: hidden; - padding: 1rem 1.5rem; - text-decoration: none; - text-shadow: none; -} -.btn_link:hover {text-decoration: underline; } -.btn_primary, .btn_primary:link {background: #2C3E50; } -.btn_secondary, .btn_secondary:link {background: #18BC9C; } -.btn_action, .btn_action:link {background: #F39C12; } -.btn_block, .btn_block:link {display: block; width: 100%; } -.btn .fa {padding: 0 .5rem; } From 8dbf2ad0a7de108b958750d3c0f347617c17a90f Mon Sep 17 00:00:00 2001 From: WordPress POT/PO/MO Generator <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 13:06:32 +0000 Subject: [PATCH 85/91] =?UTF-8?q?=F0=9F=94=84=20Regenerate=20translation?= =?UTF-8?q?=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../paystack-gateway-paid-memberships-pro.mo | Bin 559 -> 557 bytes .../paystack-gateway-paid-memberships-pro.po | 51 ++++++++++- .../paystack-gateway-paid-memberships-pro.pot | 82 +++++++++--------- 3 files changed, 89 insertions(+), 44 deletions(-) diff --git a/languages/paystack-gateway-paid-memberships-pro.mo b/languages/paystack-gateway-paid-memberships-pro.mo index 3e27e17dd1df70fbe909dd44376cff27ddc0ac03..5d4fc4936d380dfa7b0aa28e475825d445bb88ae 100644 GIT binary patch delta 45 zcmZ3_vX*6n3e$haiE8eQmKzH+83jyr4a{{74MPl#tqjbpjEyF*WDI09n*5v51OO!I B41oXu delta 47 zcmZ3>vYusv3KJvKL^XG2b3KcVd76v@Cc1_Ox(1dZ1{PLEW>$ullb16FG8#_)$!G!q D6Bi5a diff --git a/languages/paystack-gateway-paid-memberships-pro.po b/languages/paystack-gateway-paid-memberships-pro.po index 64446c6..cdb394b 100644 --- a/languages/paystack-gateway-paid-memberships-pro.po +++ b/languages/paystack-gateway-paid-memberships-pro.po @@ -1,31 +1,34 @@ -# Copyright (C) 2024 Paid Memberships Pro, Paystack +# Copyright (C) 2025 Paid Memberships Pro, Paystack # This file is distributed under the GPLv2 or later. msgid "" msgstr "" -"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.8\n" +"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.9\n" "Report-Msgid-Bugs-To: info@paidmembershipspro.com\n" "Last-Translator: Paid Memberships Pro \n" "Language-Team: Paid Memberships Pro \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2024-10-09T08:26:19+00:00\n" +"POT-Creation-Date: 2025-07-11T13:06:32+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"X-Generator: WP-CLI 2.11.0\n" +"X-Generator: WP-CLI 2.12.0\n" "X-Domain: paystack-gateway-paid-memberships-pro\n" #. Plugin Name of the plugin #: class.pmprogateway_paystack.php +#: paystack-gateway-paid-memberships-pro.php msgid "Paystack Gateway for Paid Memberships Pro" msgstr "" #. Plugin URI of the plugin #: class.pmprogateway_paystack.php +#: paystack-gateway-paid-memberships-pro.php msgid "https://www.paidmembershipspro.com/add-ons/paystack-gateway/" msgstr "" #. Description of the plugin #: class.pmprogateway_paystack.php +#: paystack-gateway-paid-memberships-pro.php msgid "Plugin to add Paystack payment gateway into Paid Memberships Pro" msgstr "" @@ -35,6 +38,7 @@ msgstr "" #. Author URI of the plugin #: class.pmprogateway_paystack.php +#: paystack-gateway-paid-memberships-pro.php msgid "https://www.paidmembershipspro.com" msgstr "" @@ -42,6 +46,7 @@ msgstr "" #: class.pmprogateway_paystack.php:108 #: class.pmprogateway_paystack.php:114 #: class.pmprogateway_paystack.php:118 +#: classes/class.pmprogateway_paystack.php:205 msgid "Settings" msgstr "" @@ -71,6 +76,8 @@ msgstr "" #: class.pmprogateway_paystack.php:394 #: class.pmprogateway_paystack.php:408 #: class.pmprogateway_paystack.php:407 +#: classes/class.pmprogateway_paystack.php:142 +#: classes/class.pmprogateway_paystack.php:213 msgid "Test Secret Key" msgstr "" @@ -79,6 +86,8 @@ msgstr "" #: class.pmprogateway_paystack.php:402 #: class.pmprogateway_paystack.php:416 #: class.pmprogateway_paystack.php:415 +#: classes/class.pmprogateway_paystack.php:150 +#: classes/class.pmprogateway_paystack.php:221 msgid "Test Public Key" msgstr "" @@ -87,6 +96,8 @@ msgstr "" #: class.pmprogateway_paystack.php:410 #: class.pmprogateway_paystack.php:424 #: class.pmprogateway_paystack.php:423 +#: classes/class.pmprogateway_paystack.php:158 +#: classes/class.pmprogateway_paystack.php:229 msgid "Live Secret Key" msgstr "" @@ -95,6 +106,8 @@ msgstr "" #: class.pmprogateway_paystack.php:418 #: class.pmprogateway_paystack.php:432 #: class.pmprogateway_paystack.php:431 +#: classes/class.pmprogateway_paystack.php:166 +#: classes/class.pmprogateway_paystack.php:237 msgid "Live Public Key" msgstr "" @@ -103,6 +116,8 @@ msgstr "" #: class.pmprogateway_paystack.php:426 #: class.pmprogateway_paystack.php:440 #: class.pmprogateway_paystack.php:439 +#: classes/class.pmprogateway_paystack.php:174 +#: classes/class.pmprogateway_paystack.php:245 msgid "Webhook" msgstr "" @@ -111,6 +126,8 @@ msgstr "" #: class.pmprogateway_paystack.php:429 #: class.pmprogateway_paystack.php:443 #: class.pmprogateway_paystack.php:442 +#: classes/class.pmprogateway_paystack.php:177 +#: classes/class.pmprogateway_paystack.php:248 msgid "To fully integrate with Paystack, be sure to use the following for your Webhook URL to" msgstr "" @@ -152,6 +169,7 @@ msgstr "" #. Author of the plugin #: class.pmprogateway_paystack.php +#: paystack-gateway-paid-memberships-pro.php msgid "Paid Memberships Pro, Paystack" msgstr "" @@ -160,5 +178,30 @@ msgstr "" #: class.pmprogateway_paystack.php:1166 #: class.pmprogateway_paystack.php:1164 #: class.pmprogateway_paystack.php:1175 +#: classes/class.pmprogateway_paystack.php:484 msgid "There was an error communicating with Paystack. Please confirm your connectivity and API details and try again." msgstr "" + +#. translators: %s: URL to the Paystack gateway documentation. +#: classes/class.pmprogateway_paystack.php:196 +#, php-format +msgid "For detailed setup instructions, please visit our %s." +msgstr "" + +#: classes/class.pmprogateway_paystack.php:197 +msgid "Paystack documentation" +msgstr "" + +#: classes/class.pmprogateway_paystack.php:266 +msgid "With Paystack, members can pay using credit or debit cards, bank transfers, USSD, mobile money, QR codes, Apple Pay and more. Paystack is accepted worldwide and offers multi-currency support across numerous African countries." +msgstr "" + +#: classes/class.pmprogateway_paystack.php:608 +#, php-format +msgid "Admin: Order refund failed on %1$s for transaction ID %2$s by %3$s. Order may have already been refunded." +msgstr "" + +#: classes/class.pmprogateway_paystack.php:616 +#, php-format +msgid "Admin: Order successfully refunded on %1$s for transaction ID %2$s by %3$s." +msgstr "" diff --git a/languages/paystack-gateway-paid-memberships-pro.pot b/languages/paystack-gateway-paid-memberships-pro.pot index 55eb0ea..e90dc79 100644 --- a/languages/paystack-gateway-paid-memberships-pro.pot +++ b/languages/paystack-gateway-paid-memberships-pro.pot @@ -1,100 +1,102 @@ -# Copyright (C) 2024 Paid Memberships Pro, Paystack +# Copyright (C) 2025 Paid Memberships Pro, Paystack # This file is distributed under the GPLv2 or later. msgid "" msgstr "" -"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.7.8\n" +"Project-Id-Version: Paystack Gateway for Paid Memberships Pro 1.9\n" "Report-Msgid-Bugs-To: info@paidmembershipspro.com\n" "Last-Translator: Paid Memberships Pro \n" "Language-Team: Paid Memberships Pro \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2024-10-09T08:26:19+00:00\n" +"POT-Creation-Date: 2025-07-11T13:06:32+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"X-Generator: WP-CLI 2.11.0\n" +"X-Generator: WP-CLI 2.12.0\n" "X-Domain: paystack-gateway-paid-memberships-pro\n" #. Plugin Name of the plugin -#: class.pmprogateway_paystack.php +#: paystack-gateway-paid-memberships-pro.php msgid "Paystack Gateway for Paid Memberships Pro" msgstr "" #. Plugin URI of the plugin -#: class.pmprogateway_paystack.php +#: paystack-gateway-paid-memberships-pro.php msgid "https://www.paidmembershipspro.com/add-ons/paystack-gateway/" msgstr "" #. Description of the plugin -#: class.pmprogateway_paystack.php +#: paystack-gateway-paid-memberships-pro.php msgid "Plugin to add Paystack payment gateway into Paid Memberships Pro" msgstr "" #. Author of the plugin -#: class.pmprogateway_paystack.php +#: paystack-gateway-paid-memberships-pro.php msgid "Paid Memberships Pro, Paystack" msgstr "" #. Author URI of the plugin -#: class.pmprogateway_paystack.php +#: paystack-gateway-paid-memberships-pro.php msgid "https://www.paidmembershipspro.com" msgstr "" -#: class.pmprogateway_paystack.php:118 -msgid "Settings" -msgstr "" - -#: class.pmprogateway_paystack.php:132 -msgid "Check Out with Paystack" -msgstr "" - -#: class.pmprogateway_paystack.php:132 -msgid "Submit and Confirm" -msgstr "" - -#: class.pmprogateway_paystack.php:145 -msgid "Paystack" -msgstr "" - -#: class.pmprogateway_paystack.php:407 +#: classes/class.pmprogateway_paystack.php:142 +#: classes/class.pmprogateway_paystack.php:213 msgid "Test Secret Key" msgstr "" -#: class.pmprogateway_paystack.php:415 +#: classes/class.pmprogateway_paystack.php:150 +#: classes/class.pmprogateway_paystack.php:221 msgid "Test Public Key" msgstr "" -#: class.pmprogateway_paystack.php:423 +#: classes/class.pmprogateway_paystack.php:158 +#: classes/class.pmprogateway_paystack.php:229 msgid "Live Secret Key" msgstr "" -#: class.pmprogateway_paystack.php:431 +#: classes/class.pmprogateway_paystack.php:166 +#: classes/class.pmprogateway_paystack.php:237 msgid "Live Public Key" msgstr "" -#: class.pmprogateway_paystack.php:439 +#: classes/class.pmprogateway_paystack.php:174 +#: classes/class.pmprogateway_paystack.php:245 msgid "Webhook" msgstr "" -#: class.pmprogateway_paystack.php:442 +#: classes/class.pmprogateway_paystack.php:177 +#: classes/class.pmprogateway_paystack.php:248 msgid "To fully integrate with Paystack, be sure to use the following for your Webhook URL to" msgstr "" -#: class.pmprogateway_paystack.php:984 -msgid "Account" +#. translators: %s: URL to the Paystack gateway documentation. +#: classes/class.pmprogateway_paystack.php:196 +#, php-format +msgid "For detailed setup instructions, please visit our %s." msgstr "" -#: class.pmprogateway_paystack.php:985 -msgid "Order" +#: classes/class.pmprogateway_paystack.php:197 +msgid "Paystack documentation" msgstr "" -#: class.pmprogateway_paystack.php:986 -msgid "Membership Level" +#: classes/class.pmprogateway_paystack.php:205 +msgid "Settings" msgstr "" -#: class.pmprogateway_paystack.php:987 -msgid "Amount Paid" +#: classes/class.pmprogateway_paystack.php:266 +msgid "With Paystack, members can pay using credit or debit cards, bank transfers, USSD, mobile money, QR codes, Apple Pay and more. Paystack is accepted worldwide and offers multi-currency support across numerous African countries." msgstr "" -#: class.pmprogateway_paystack.php:1175 +#: classes/class.pmprogateway_paystack.php:484 msgid "There was an error communicating with Paystack. Please confirm your connectivity and API details and try again." msgstr "" + +#: classes/class.pmprogateway_paystack.php:608 +#, php-format +msgid "Admin: Order refund failed on %1$s for transaction ID %2$s by %3$s. Order may have already been refunded." +msgstr "" + +#: classes/class.pmprogateway_paystack.php:616 +#, php-format +msgid "Admin: Order successfully refunded on %1$s for transaction ID %2$s by %3$s." +msgstr "" From 0144a938ebfcd0078edd25bb154c883649c1842a Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 11 Jul 2025 15:19:46 +0200 Subject: [PATCH 86/91] Fix some comments --- services/paystack-webhook.php | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/services/paystack-webhook.php b/services/paystack-webhook.php index 5e3a4d6..29b4955 100644 --- a/services/paystack-webhook.php +++ b/services/paystack-webhook.php @@ -1,8 +1,7 @@ code == $webhook_reference_id ) { - /// Use pmpro_getLevel instead of a DB query. + // TODO: Use pmpro_getLevel instead of a DB query. $pmpro_level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . (int)$morder->membership_id . "' LIMIT 1"); - - /// Don't need filters. + + // TODO: Move to pmpro_calculate_start_date. $startdate = apply_filters("pmpro_checkout_start_date", "'" . current_time("mysql") . "'", $morder->user_id, $pmpro_level); // The level object from the order that can be filtered when returning from Paystack and user is getting their membership level. - /// Deprecate this. + // TODO: Deprecate this. $morder->membership_level = apply_filters( 'pmpro_paystack_webhook_level', $morder->membership_level, $morder->user_id ); - /// Fix this mode? + // Get the mode of the environment so we know which keys to use. $mode = pmpro_getOption("gateway_environment"); if ($mode == "sandbox") { $key = pmpro_getOption("paystack_tsk"); @@ -380,4 +379,4 @@ function pmpro_paystack_renew_payment( $post_event ) { pmpro_paystack_webhook_log( sprintf( 'Subscription payment completed for user with ID: %d. Order ID: %s', $user_id, $morder->code ) ); pmpro_paystack_webhook_exit(); } -} +} \ No newline at end of file From 263ab7d346117a1e81144b4aa514be3395db564d Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Fri, 11 Jul 2025 15:20:44 +0200 Subject: [PATCH 87/91] Fix comment. --- services/paystack-webhook.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/paystack-webhook.php b/services/paystack-webhook.php index 29b4955..394e680 100644 --- a/services/paystack-webhook.php +++ b/services/paystack-webhook.php @@ -127,7 +127,7 @@ function pmpro_paystack_confirm_subscription( $post_event, $order ) { // TODO: Use pmpro_getLevel instead of a DB query. $pmpro_level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . (int)$morder->membership_id . "' LIMIT 1"); - // TODO: Move to pmpro_calculate_start_date. + // TODO: Move to pmpro_calculate_profile_start_date. $startdate = apply_filters("pmpro_checkout_start_date", "'" . current_time("mysql") . "'", $morder->user_id, $pmpro_level); // The level object from the order that can be filtered when returning from Paystack and user is getting their membership level. From cae3e67b04d54c1e53b0e4481c599a59909ae27a Mon Sep 17 00:00:00 2001 From: Andrew Lima Date: Tue, 7 Oct 2025 16:29:55 +0200 Subject: [PATCH 88/91] Mark PMPro as ready * Removes warning that gateway isn't setup when Paystack is active. Resolves: #13 --- classes/class.pmprogateway_paystack.php | 62 +++++++++++++------------ 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/classes/class.pmprogateway_paystack.php b/classes/class.pmprogateway_paystack.php index 090b9b9..9ad5eb7 100644 --- a/classes/class.pmprogateway_paystack.php +++ b/classes/class.pmprogateway_paystack.php @@ -9,47 +9,49 @@ class PMProGateway_paystack extends PMProGateway { - function __construct( $gateway = NULL ) { - $this->gateway = $gateway; - return $this->gateway; - } + function __construct( $gateway = NULL ) { + $this->gateway = $gateway; + return $this->gateway; + } - /** - * Run on WP init. - * This method will run all the necessary gateway hooks that are needed. - */ - public static function init() { + /** + * Run on WP init. + * This method will run all the necessary gateway hooks that are needed. + */ + public static function init() { - // Make sure Paystack is a gateway option. - add_filter( 'pmpro_gateways', array( 'PMProGateway_paystack', 'pmpro_gateways' ) ); + // Make sure Paystack is a gateway option. + add_filter( 'pmpro_gateways', array( 'PMProGateway_paystack', 'pmpro_gateways' ) ); - // Add fields to payment settings. - add_filter( 'pmpro_payment_options', array( 'PMProGateway_Paystack', 'pmpro_payment_options' ) ); + // Add fields to payment settings. + add_filter( 'pmpro_payment_options', array( 'PMProGateway_Paystack', 'pmpro_payment_options' ) ); add_filter( 'pmpro_payment_option_fields', array( 'PMProGateway_Paystack', 'pmpro_payment_option_fields' ), 10, 2 ); - add_action( 'wp_ajax_pmpro_paystack_ipn', array( 'PMProGateway_Paystack', 'pmpro_paystack_ipn' ) ); - add_action( 'wp_ajax_nopriv_pmpro_paystack_ipn', array( 'PMProGateway_Paystack', 'pmpro_paystack_ipn' ) ); + add_action( 'wp_ajax_pmpro_paystack_ipn', array( 'PMProGateway_Paystack', 'pmpro_paystack_ipn' ) ); + add_action( 'wp_ajax_nopriv_pmpro_paystack_ipn', array( 'PMProGateway_Paystack', 'pmpro_paystack_ipn' ) ); - // Keeping the deprecated action for backwards compatibility. - add_action( 'wp_ajax_kkd_pmpro_paystack_ipn', array( 'PMProGateway_Paystack', 'kkd_pmpro_paystack_ipn' ) ); - add_action( 'wp_ajax_nopriv_kkd_pmpro_paystack_ipn', array( 'PMProGateway_Paystack', 'kkd_pmpro_paystack_ipn' ) ); + // Keeping the deprecated action for backwards compatibility. + add_action( 'wp_ajax_kkd_pmpro_paystack_ipn', array( 'PMProGateway_Paystack', 'kkd_pmpro_paystack_ipn' ) ); + add_action( 'wp_ajax_nopriv_kkd_pmpro_paystack_ipn', array( 'PMProGateway_Paystack', 'kkd_pmpro_paystack_ipn' ) ); - // Adjust the confirmation message when waiting for Paystack to process the payment. - add_filter( 'pmpro_confirmation_payment_incomplete_message', array( 'PMProGateway_Paystack', 'pmpro_confirmation_incomplete_message' ), 10, 2 ); + // Adjust the confirmation message when waiting for Paystack to process the payment. + add_filter( 'pmpro_confirmation_payment_incomplete_message', array( 'PMProGateway_Paystack', 'pmpro_confirmation_incomplete_message' ), 10, 2 ); - //code to add at checkout - $gateway = pmpro_getGateway(); - if ( $gateway == 'paystack' ) { - add_filter( 'pmpro_include_billing_address_fields', '__return_false' ); + $gateway = pmpro_getGateway(); + if ( $gateway == 'paystack' ) { + global $pmpro_gateway_ready; + $pmpro_gateway_ready = true; + + add_filter( 'pmpro_include_billing_address_fields', '__return_false' ); add_filter( 'pmpro_include_payment_information_fields', '__return_false' ); add_filter( 'pmpro_billing_show_payment_method', '__return_false' ); - add_filter( 'pmpro_required_billing_fields', array( 'PMProGateway_Paystack', 'pmpro_required_billing_fields' ) ); + add_filter( 'pmpro_required_billing_fields', array( 'PMProGateway_Paystack', 'pmpro_required_billing_fields' ) ); - // Refund functionality. - add_filter( 'pmpro_allowed_refunds_gateways', array( 'PMProGateway_Paystack', 'pmpro_allowed_refunds_gateways' ) ); - add_filter( 'pmpro_process_refund_paystack', array( 'PMProGateway_Paystack', 'process_refund' ), 10, 2 ); - } + // Refund functionality. + add_filter( 'pmpro_allowed_refunds_gateways', array( 'PMProGateway_Paystack', 'pmpro_allowed_refunds_gateways' ) ); + add_filter( 'pmpro_process_refund_paystack', array( 'PMProGateway_Paystack', 'process_refund' ), 10, 2 ); + } - } + } /** * Add Paystack to the gateway list for PMPro From affbb431d4bec5eba21745abd8e28bc7c983dbb1 Mon Sep 17 00:00:00 2001 From: DAnn2012 Date: Thu, 30 Oct 2025 18:36:36 +0100 Subject: [PATCH 89/91] Update class.pmprogateway_paystack.php --- classes/class.pmprogateway_paystack.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/class.pmprogateway_paystack.php b/classes/class.pmprogateway_paystack.php index 090b9b9..f31855d 100644 --- a/classes/class.pmprogateway_paystack.php +++ b/classes/class.pmprogateway_paystack.php @@ -450,7 +450,7 @@ public function update_subscription_info( $subscription ) { if ( 200 !== wp_remote_retrieve_response_code( $request ) ) { // Throw an error here from the API - return esc_html__( sprintf( 'Paystack error: %s', $response->message ), 'paystack-gateway-paid-memberships-pro' ); + return sprintf( esc_html__( 'Paystack error: %s', 'paystack-gateway-paid-memberships-pro' ), $response->message ); } $update_array = array(); From 7ba687b44d7664d8a1f164731098836e3a8562c3 Mon Sep 17 00:00:00 2001 From: WordPress POT/PO/MO Generator <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 7 Nov 2025 06:49:13 +0000 Subject: [PATCH 90/91] =?UTF-8?q?=F0=9F=94=84=20Regenerate=20translation?= =?UTF-8?q?=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../paystack-gateway-paid-memberships-pro.mo | Bin 557 -> 557 bytes .../paystack-gateway-paid-memberships-pro.po | 26 +++++++++- .../paystack-gateway-paid-memberships-pro.pot | 45 ++++++++++-------- 3 files changed, 50 insertions(+), 21 deletions(-) diff --git a/languages/paystack-gateway-paid-memberships-pro.mo b/languages/paystack-gateway-paid-memberships-pro.mo index 5d4fc4936d380dfa7b0aa28e475825d445bb88ae..39f2451518e05e12a9e2090c2cfcc13abd3cd6fb 100644 GIT binary patch delta 25 gcmZ3>vX*7TOh#TqLtO*&5CbzS6H6<@$;%kq0bgzhJpcdz delta 25 gcmZ3>vX*7TOh#S Date: Fri, 7 Nov 2025 10:53:59 +0200 Subject: [PATCH 91/91] version bnump 1.9.1 --- paystack-gateway-paid-memberships-pro.php | 2 +- readme.txt | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/paystack-gateway-paid-memberships-pro.php b/paystack-gateway-paid-memberships-pro.php index 49586f9..0942b56 100755 --- a/paystack-gateway-paid-memberships-pro.php +++ b/paystack-gateway-paid-memberships-pro.php @@ -3,7 +3,7 @@ * Plugin Name: Paystack Gateway for Paid Memberships Pro * Plugin URI: https://www.paidmembershipspro.com/add-ons/paystack-gateway/ * Description: Plugin to add Paystack payment gateway into Paid Memberships Pro - * Version: 1.9 + * Version: 1.9.1 * Author: Paid Memberships Pro, Paystack * Author URI: https://www.paidmembershipspro.com * License: GPLv2 or later diff --git a/readme.txt b/readme.txt index 3708f3e..692c534 100755 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Donate link: https://paystack.com/demo Tags: paid memberships pro, paystack, gateway, credit card, Naira Requires at least: 5.2 Tested up to: 6.8 -Stable tag: 1.9 +Stable tag: 1.9.1 License: GPLv3 License URI: http://www.gnu.org/licenses/gpl-3.0.html @@ -73,6 +73,10 @@ Yes you can! Join in on our [GitHub repository](https://github.com/strangerstudi 1. The slick Paystack settings panel. == Changelog == += 1.9.1 - 2025-11-07 = +* BUG FIX: Fixed an issue where PMPro would show a warning that the gateway is not configured correctly when Paystack is set as the primary gateway. #14 (@andrewlimaza) +* BUG FIX: Fixed an issue with error output when syncing a subscription failed in the WordPress admin. #15 (@DAnn2012) + = 1.9 - 2025-07-11 = * BUG FIX: Fixed an issue where the payment settings fields were not showing correctly in Paid Memberships Pro 3.5+ * REFACTOR: Improved support for Paid Memberships Pro 3.0+ to better support new subscription features moving forward.