Plugin Directory

Changeset 3305853


Ignore:
Timestamp:
06/03/2025 02:34:27 PM (10 months ago)
Author:
webimpian
Message:

Fix bug direct debit

Location:
bayarcash-wc
Files:
378 added
3 edited

Legend:

Unmodified
Added
Removed
  • bayarcash-wc/trunk/bayarcash-wc.php

    r3295064 r3305853  
    1313 * Plugin Name:         Bayarcash WC
    1414 * Plugin URI:          https://bayarcash.com/
    15  * Version:             4.3.8
     15 * Version:             4.3.9
    1616 * Description:         Accept payment from Malaysia. Bayarcash support FPX, Direct Debit, DuitNow OBW & DuitNow QR payment channels.
    1717 * Author:              Web Impian
  • bayarcash-wc/trunk/includes/src/Gateway/DirectDebitGateway.php

    r3251032 r3305853  
    66use JetBrains\PhpStorm\NoReturn;
    77use WC_Abstract_Order;
     8use WC_Data_Exception;
    89use WC_Order;
    910use WC_Order_Refund;
     
    2930    }
    3031
    31     protected function get_payment_descriptions(): array {
    32         return [
    33             'description' => 'Recurring payment via online banking Maybank2u, CIMB Clicks, Bank Islam GO and more banks from Malaysia.',
    34             'method_description' => 'Allow customers to pay with Direct Debit.',
    35         ];
    36     }
     32    protected function get_payment_descriptions(): array {
     33        return [
     34            'description' => 'Recurring payment via online banking Maybank2u, CIMB Clicks, Bank Islam GO and more banks from Malaysia.',
     35            'method_description' => 'Allow customers to pay with Direct Debit.',
     36        ];
     37    }
    3738
    3839    public function is_available(): bool
     
    104105
    105106    public function add_identification_fields($checkout): void {
    106         // Check if subscriptions feature exists
    107107        if (!$this->has_subscriptions()) {
    108108            return;
    109109        }
    110110
    111         // Get DirectDebit settings and check if enabled
    112111        $directdebit_settings = get_option('woocommerce_directdebit-wc_settings', []);
    113112        if (empty($directdebit_settings['enabled']) || $directdebit_settings['enabled'] !== 'yes') {
     
    115114        }
    116115
    117         // Check if FunnelKit or FunnelKit Pro is active
    118116        if (class_exists('WFFN_Core') || class_exists('WFFN_Pro_Core')) {
    119             return; // Exit if either FunnelKit or FunnelKit Pro is active
    120         }
    121 
    122         // Register AJAX handlers
     117            return;
     118        }
     119
    123120        add_action('wp_ajax_update_directdebit_fields', [$this, 'handle_directdebit_fields_update']);
    124121        add_action('wp_ajax_nopriv_update_directdebit_fields', [$this, 'handle_directdebit_fields_update']);
    125122
    126         // Add JavaScript for payment method handling
    127123        add_action('wp_footer', [$this, 'add_payment_method_script']);
    128124
     
    148144        if (!is_checkout()) return;
    149145        ?>
    150         <script type="text/javascript">
     146        <script type="text/javascript">
    151147            jQuery(function($) {
    152                 // Function to toggle identification fields
    153148                function toggleIdentificationFields(show) {
    154149                    const fields = $('#bayarcash_identification_type, #bayarcash_identification_id').closest('.form-row');
     
    162157                }
    163158
    164                 // Initial state check
    165159                const initialPaymentMethod = $('input[name="payment_method"]:checked').val();
    166160                toggleIdentificationFields(initialPaymentMethod === 'directdebit-wc');
    167161
    168                 // Listen for payment method changes
    169162                $('form.checkout').on('change', 'input[name="payment_method"]', function(e) {
    170163                    const selectedMethod = $(this).val();
     
    191184                });
    192185
    193                 // Handle updated_checkout event
    194186                $(document.body).on('updated_checkout', function() {
    195187                    const currentMethod = $('input[name="payment_method"]:checked').val();
     
    198190                });
    199191            });
    200         </script>
     192        </script>
    201193        <?php
    202194    }
     
    295287            $settings = $this->get_payment_settings($this->id);
    296288
    297             // Validate subscription period and interval
    298289            $this->validate_subscription_period($order);
    299290            $this->validate_subscription_interval($order);
    300291
    301             // Initialize the Bayarcash SDK
    302292            $this->initialize_bayarcash_sdk($settings);
    303293
     
    318308            $this->log('Detailed Validation Errors: ' . print_r($errors, true));
    319309
    320             // Properly extract error messages from the nested array structure
    321310            $error_messages = [];
    322311            if (is_array($errors)) {
     
    332321            }
    333322
    334             // If we successfully extracted error messages, use them; otherwise, use the general message
    335323            if (!empty($error_messages)) {
    336324                $detailed_error = implode('. ', $error_messages);
     
    348336                wc_add_notice(__('Only weekly and monthly subscriptions are supported.', 'bayarcash-wc'), 'error');
    349337            } else if (str_contains($e->getMessage(), 'Current billing schedule:')) {
    350                 // Pass through the full error message with the billing schedule details
    351338                wc_add_notice($e->getMessage(), 'error');
    352339            } else {
     
    359346    }
    360347
    361     /**
    362      * @throws Exception
    363      */
    364348    private function validate_subscription_period(\WC_Abstract_Order $order): void {
    365349        foreach ($order->get_items() as $item) {
     
    374358    }
    375359
    376     /**
    377      * @throws Exception
    378      */
    379360    private function validate_subscription_interval(\WC_Abstract_Order $order): void {
    380361        foreach ($order->get_items() as $item) {
     
    397378        }
    398379    }
    399     /**
    400      * @throws Exception
    401      */
     380
    402381    private function validate_request(): void
    403382    {
     
    416395    }
    417396
    418     /**
    419      * @return bool|WC_Order|WC_Order_Refund
    420      * @throws Exception
    421      */
    422397    private function get_and_validate_order()
    423398    {
     
    448423        $data_dec_arr = explode(',', $this->data_dec->data);
    449424
    450         // Get expiry date from subscription product
    451425        $expiry_date = $this->get_subscription_expiry_date($order);
    452426
     
    468442        ];
    469443
    470         // Add expiry_date if it exists
    471444        if ($expiry_date) {
    472445            $args['expiry_date'] = $expiry_date->format('Y-m-d');
     
    480453    }
    481454
    482     /**
    483      * Get the subscription expiry date based on product settings
    484      *
    485      * @param WC_Abstract_Order $order
    486      *
    487      * @return ?DateTime
    488      */
    489455    private function get_subscription_expiry_date( WC_Abstract_Order $order ): ?DateTime
    490456    {
     
    496462            }
    497463
    498             // Get subscription length and period
    499464            $length = \WC_Subscriptions_Product::get_length($product);
    500465            $period = \WC_Subscriptions_Product::get_period($product);
    501466
    502             // If no length specified or invalid period, subscription doesn't expire
    503467            if (empty($length) || !in_array($period, ['week', 'month'])) {
    504468                return null;
    505469            }
    506470
    507             // Create DateTime object for current time
    508471            $expiry_date = new DateTime();
    509472
    510             // Add subscription length based on period
    511473            switch ($period) {
    512474                case 'week':
     
    533495            $product = $item->get_product();
    534496            if ($product && $this->is_subscription_product($product)) {
    535                 // Use the fully qualified class name
    536497                return \WC_Subscriptions_Product::get_period($product);
    537498            }
     
    562523    }
    563524
    564     /**
    565      * @throws Exception
    566      */
    567525    private function create_fpx_direct_debit_enrollment(array $args, array $settings): object
    568526    {
     
    586544
    587545    public function process_callback(): void {
    588         //$this->log('Starting Direct Debit callback processing');
     546        $this->log('Starting Direct Debit callback processing');
    589547
    590548        $json_data = file_get_contents('php://input');
    591549        $data = json_decode($json_data, true);
    592550
    593         //$this->log('Decoded JSON data: ' . print_r($data, true));
    594 
    595551        if (is_array($data)) {
    596552            $_POST = array_merge($_POST, $data);
     
    599555        $response_data = $_POST;
    600556
    601         if (isset($response_data['record_type'])) {
     557        if (isset($response_data['record_type']) && $response_data['record_type'] !== 'transaction_receipt') {
    602558            $this->log('Processing record type: ' . $response_data['record_type']);
    603             // $this->log('Decoded JSON data: ' . print_r($response_data, true));
    604         }
    605 
    606         // Handle bank_approval record type
     559        }
     560
    607561        if (isset($response_data['record_type']) && $response_data['record_type'] === 'bank_approval') {
    608562            $this->handle_bank_approval($response_data);
     
    610564        }
    611565
    612         // Handle transaction record type (recurring subscription)
    613566        if (isset($response_data['record_type']) && $response_data['record_type'] === 'transaction') {
    614567            $this->handle_return_callback($response_data);
     568            return;
     569        }
     570
     571        if (isset($response_data['record_type']) && $response_data['record_type'] === 'authorization') {
     572            $this->handle_authorization_callback($response_data);
    615573            return;
    616574        }
     
    656614    }
    657615
    658     /**
    659      * @throws Exception
    660      */
    661616    private function handle_bank_approval($data): void {
    662617        $this->log('Processing bank approval callback');
     
    694649        $this->log('Added order note: ' . $note);
    695650
    696         // Handle subscription cancellation
    697651        if (isset($data['application_type']) && $data['application_type'] === '03') {
    698652            $this->log('Handling subscription cancellation approval');
    699653            $this->handle_subscription_cancellation_approval($order, $data);
    700654        } else {
    701             if ($approval_status === 5) { // Approved
     655            if ($approval_status === 5) {
    702656                $this->log('Order approved, processing subscription activation');
    703657                $this->process_subscription_activation($order, $data);
    704             } elseif ($approval_status === 6) { // Rejected
     658            } elseif ($approval_status === 6) {
    705659                $this->log('Order rejected, updating status to failed');
    706660                $order->update_status('failed', 'Bank approval rejected.');
     
    709663        }
    710664
    711         // Store information for future reference
    712665        $order->update_meta_data('bayarcash_mandate_id', $data['mandate_id']);
    713666        $order->update_meta_data('bayarcash_mandate_reference', $data['mandate_reference_number']);
     
    724677        foreach ($subscriptions as $subscription) {
    725678            try {
    726                 // Ensure the subscription can be activated
    727679                if (!apply_filters('woocommerce_can_subscription_be_updated_to', true, $subscription, 'active')) {
    728680                    throw new Exception('Subscription cannot be activated due to status restrictions.');
    729681                }
    730682
    731                 // Update subscription status
    732683                $subscription->update_status('active', 'Subscription activated after bank approval.');
    733684
    734                 // Set payment method
    735685                $subscription->set_payment_method($this);
    736686
    737                 // Set next payment date
    738687                $next_payment = $subscription->calculate_date('next_payment');
    739688                if ($next_payment) {
     
    744693                $subscription->save();
    745694
    746                 // Trigger the activation action
    747695                do_action('woocommerce_subscription_status_active', $subscription);
    748696
     
    754702        }
    755703
    756         // Complete the parent order
    757         $order->update_status('completed', 'Order completed and subscription(s) activated after bank approval.');
     704        $order->update_status('completed', 'Order completed and subscription(s) activated after bank approval. Awaiting 1st deduction.');
    758705        $order->payment_complete($data['mandate_reference_number']);
    759706
    760         // Trigger the subscriptions created action
    761707        do_action('subscriptions_created_for_order', $order);
    762708    }
     
    790736    }
    791737
     738    private function handle_authorization_callback($data): void {
     739        $this->log('Processing authorization callback');
     740
     741        if (!isset($data['order_number'])) {
     742            $this->log('Invalid authorization data: missing order_number');
     743            wp_send_json_error('Invalid authorization data: missing order_number', 400);
     744            return;
     745        }
     746
     747        $order_number = $data['order_number'];
     748        $order = wc_get_order($order_number);
     749
     750        if (!$order) {
     751            $this->log('Invalid order number in authorization: ' . $order_number);
     752            wp_send_json_error('Invalid order number', 404);
     753            return;
     754        }
     755
     756        $status = isset($data['status']) ? intval($data['status']) : 0;
     757        $status_description = $data['status_description'] ?? 'Unknown';
     758
     759        $note = sprintf(
     760            'Direct Debit Authorization received. Status: %s (%d). Transaction ID: %s, Amount: %s, Date: %s',
     761            $status_description,
     762            $status,
     763            $data['transaction_id'] ?? 'N/A',
     764            $data['amount'] ?? 'N/A',
     765            $data['datetime'] ?? 'N/A'
     766        );
     767
     768        $order->add_order_note($note);
     769
     770        if ($status === 3) {
     771            $this->log('Authorization approved for order: ' . $order_number);
     772        } else {
     773            $this->log('Authorization failed for order: ' . $order_number);
     774            $order->update_status('failed', 'Direct Debit authorization failed.');
     775        }
     776
     777        $order->save();
     778        $this->log('Authorization callback processed successfully for Order No.: ' . $order_number);
     779
     780        wp_send_json_success('Authorization processed');
     781    }
     782
    792783    private function handle_success_callback($order): void {
    793784        $this->log('Processing Direct Debit success callback');
     
    828819            $this->log('Response data: ' . print_r($response_data, true));
    829820
    830             // Update only the parent order status
    831             $order->update_status('on-hold', 'Direct Debit enrollment successful, awaiting first payment.');
    832 
    833             // If this is a subscription, add a note to the subscription
     821            $order->update_status('on-hold', 'Direct Debit enrollment successful, Awaiting bank approval for mandate.');
     822
    834823            $subscriptions = wcs_get_subscriptions_for_order($order);
    835824            foreach ($subscriptions as $subscription) {
     
    868857    private function handle_return_callback($data): void {
    869858        $this->log('Processing Direct Debit return callback');
    870         //$this->log('Callback Return data: ' . print_r($data, true));
    871859
    872860        if (!isset($data['record_type'])) {
     
    888876    private function handle_transaction_callback($data): void {
    889877        $this->log('Processing transaction callback');
    890         //$this->log('Transaction data: ' . print_r($data, true));
    891878
    892879        $required_fields = ['mandate_reference_number', 'status', 'amount', 'transaction_id', 'datetime', 'cycle'];
     
    900887
    901888        $parent_order_id = $data['mandate_reference_number'];
    902         $this->log('Parent order ID: ' . $parent_order_id);
     889        $cycle = $data['cycle'];
     890        $this->log('Parent order ID: ' . $parent_order_id . ', Cycle: ' . $cycle);
    903891
    904892        $parent_order = wc_get_order($parent_order_id);
     
    927915                if ($data['cycle'] == '1') {
    928916                    $this->process_first_payment($subscription, $parent_order, $data);
     917
     918                    $parent_order->update_meta_data('bayarcash_cycle_' . $cycle, $data['transaction_id']);
     919                    $parent_order->save();
     920                    $this->log('Added cycle meta to parent order: bayarcash_cycle_' . $cycle);
    929921                } else {
    930922                    $renewal_order = $this->get_or_create_renewal_order($subscription, $data);
    931923                    $this->log('Using renewal order: ' . $renewal_order->get_id());
    932924
     925                    if (in_array($renewal_order->get_status(), ['completed', 'failed'])) {
     926                        $this->log('Order already processed with status: ' . $renewal_order->get_status() . '. Skipping renewal process.');
     927                        continue;
     928                    }
     929
     930                    $renewal_order->update_meta_data('bayarcash_cycle', $cycle);
     931                    $renewal_order->update_meta_data('bayarcash_cycle_' . $cycle, $data['transaction_id']);
     932                    $renewal_order->save();
     933                    $this->log('Added cycle meta to renewal order: bayarcash_cycle_' . $cycle);
     934
    933935                    if ($data['amount'] != $subscription->get_total()) {
    934936                        $this->log('Amount mismatch. Expected: ' . $subscription->get_total() . ', Received: ' . $data['amount']);
    935                         // Decide how to handle this situation (e.g., partial payment, overpayment)
    936937                    }
    937938
    938                     if ($data['status'] == '3') { // Successful transaction
     939                    if ($data['status'] == '3') {
    939940                        $this->log('Processing successful renewal');
    940941                        $this->process_successful_renewal($subscription, $renewal_order, $data);
     
    969970
    970971        $order->add_order_note($note);
    971         //$this->log('Added note to order ' . $order->get_id() . ': ' . $note);
    972972    }
    973973
     
    975975        $this->log('Processing first payment for subscription: ' . $subscription->get_id());
    976976
    977         // Set parent order details
    978977        $parent_order->set_payment_method($this);
    979978        $parent_order->set_transaction_id($data['transaction_id']);
     
    982981        $this->add_transaction_note($parent_order, $data, true);
    983982
    984         if ($data['status'] == '3') { // Successful transaction
    985             // Complete the parent order and mark as 'completed'
     983        if ($data['status'] == '3') {
    986984            $parent_order->payment_complete();
    987985            $parent_order->update_status('completed');
     
    989987            $this->log('Parent order payment completed, marked as completed, and saved. Order ID: ' . $parent_order->get_id());
    990988
    991             // Activate the subscription
    992989            $subscription->update_status('active');
    993990            $subscription->save();
     
    997994            $this->log('Successful first payment processed for subscription: ' . $subscription->get_id());
    998995        } else {
    999             // Mark the parent order as failed
    1000996            $parent_order->update_status('failed');
    1001997            $parent_order->save();
    1002998            $this->log('Parent order marked as failed. Order ID: ' . $parent_order->get_id());
    1003999
    1004             // Update subscription
    10051000            WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($parent_order);
    10061001
     
    10091004    }
    10101005
    1011     /**
    1012      * @throws \WC_Data_Exception
    1013      */
    10141006    private function get_or_create_renewal_order($subscription, $data)
    10151007    {
    10161008        $parent_order_id = $subscription->get_parent_id();
    1017         $this->log("Searching for renewal order. Subscription ID: {$subscription->get_id()}, Parent Order ID: {$parent_order_id}");
    1018 
    1019         $existing_orders = wc_get_orders(array(
    1020             'type' => 'shop_order',
    1021             'status' => array('pending'),
    1022             'limit' => 1,
    1023             'orderby' => 'date',
    1024             'order' => 'DESC',
    1025             'meta_query' => array(
    1026                 'relation' => 'AND',
    1027                 array(
    1028                     'key' => '_subscription_renewal',
    1029                     'value' => $parent_order_id,
    1030                     'compare' => '=',
     1009        $subscription_id = $subscription->get_id();
     1010
     1011        $cycle = $data['cycle'];
     1012        $this->log("Searching for renewal order. Subscription ID: {$subscription->get_id()}, Parent Order ID: {$parent_order_id}, Cycle: {$cycle}");
     1013
     1014        $hpos_enabled = $this->is_hpos_enabled();
     1015
     1016        if ($hpos_enabled) {
     1017            $existing_orders = wc_get_orders(array(
     1018                'type' => 'shop_order',
     1019                'status' => array('pending', 'processing', 'completed', 'failed'),
     1020                'limit' => 1,
     1021                'orderby' => 'date',
     1022                'order' => 'DESC',
     1023                'meta_query' => array(
     1024                    'relation' => 'AND',
     1025                    array(
     1026                        'key' => '_subscription_renewal',
     1027                        'value' => $subscription_id,
     1028                        'compare' => '=',
     1029                    ),
     1030                    array(
     1031                        'key' => 'bayarcash_cycle',
     1032                        'value' => $cycle,
     1033                        'compare' => '=',
     1034                    ),
    10311035                ),
    1032                 array(
    1033                     'key' => '_subscription_renewal_subscription_id',
    1034                     'value' => $subscription->get_id(),
    1035                     'compare' => '=',
     1036            ));
     1037        } else {
     1038            $posts = get_posts(array(
     1039                'post_type' => 'shop_order',
     1040                'post_status' => array('wc-pending', 'wc-processing', 'wc-completed', 'wc-failed'),
     1041                'numberposts' => 1,
     1042                'orderby' => 'date',
     1043                'order' => 'DESC',
     1044                'meta_query' => array(
     1045                    'relation' => 'AND',
     1046                    array(
     1047                        'key' => '_subscription_renewal',
     1048                        'value' => $subscription_id,
     1049                        'compare' => '=',
     1050                    ),
     1051                    array(
     1052                        'key' => 'bayarcash_cycle',
     1053                        'value' => $cycle,
     1054                        'compare' => '=',
     1055                    ),
    10361056                ),
    1037             ),
    1038         ));
     1057            ));
     1058
     1059            $existing_orders = array();
     1060            if (!empty($posts)) {
     1061                foreach ($posts as $post) {
     1062                    $order = wc_get_order($post->ID);
     1063                    if ($order) {
     1064                        $existing_orders[] = $order;
     1065                    }
     1066                }
     1067            }
     1068        }
    10391069
    10401070        if (!empty($existing_orders)) {
    10411071            $renewal_order = reset($existing_orders);
    1042             $this->log('Using existing renewal order: ' . $renewal_order->get_id());
    1043 
    1044             // Check if the order total matches the expected amount
    1045             if (abs($renewal_order->get_total() - $data['amount']) > 0.01) {
    1046                 $this->log("Updating existing order total from {$renewal_order->get_total()} to {$data['amount']}");
     1072            $this->log('Found existing order for cycle ' . $cycle . ': ' . $renewal_order->get_id() . ' (Status: ' . $renewal_order->get_status() . ')');
     1073
     1074            $renewal_meta = $renewal_order->get_meta('_subscription_renewal');
     1075            $order_cycle = $renewal_order->get_meta('bayarcash_cycle');
     1076
     1077            if ($renewal_meta != $subscription_id || $order_cycle != $cycle) {
     1078                $this->log('Found order is not a valid renewal order for this cycle, creating new one');
     1079                $renewal_order = wcs_create_renewal_order($subscription);
     1080                $this->log('Created new renewal order: ' . $renewal_order->get_id());
     1081
    10471082                $renewal_order->set_total($data['amount']);
     1083                $renewal_order->update_meta_data('bayarcash_cycle', $cycle);
    10481084                $renewal_order->save();
     1085            } else {
     1086                if (abs($renewal_order->get_total() - $data['amount']) > 0.01) {
     1087                    $this->log("Updating existing order total from {$renewal_order->get_total()} to {$data['amount']}");
     1088                    $renewal_order->set_total($data['amount']);
     1089                    $renewal_order->save();
     1090                }
     1091
     1092                if (in_array($renewal_order->get_status(), ['completed', 'failed'])) {
     1093                    $this->log('Order for cycle ' . $cycle . ' already processed with status: ' . $renewal_order->get_status());
     1094                    return $renewal_order;
     1095                }
    10491096            }
    10501097        } else {
     
    10521099            $this->log('Created new renewal order: ' . $renewal_order->get_id());
    10531100
    1054             // Set the order total
    10551101            $renewal_order->set_total($data['amount']);
     1102            $renewal_order->update_meta_data('bayarcash_cycle', $cycle);
    10561103            $renewal_order->save();
    10571104        }
    10581105
    10591106        return $renewal_order;
     1107    }
     1108
     1109    private function is_hpos_enabled(): bool
     1110    {
     1111        if (class_exists('\Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController')) {
     1112            try {
     1113                return wc_get_container()
     1114                    ->get(\Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController::class)
     1115                    ->custom_orders_table_usage_is_enabled();
     1116            } catch (Exception $e) {
     1117                $this->log('Error checking HPOS status: ' . $e->getMessage());
     1118            }
     1119        }
     1120
     1121        return false;
    10601122    }
    10611123
     
    10631125        $this->log('Processing successful renewal for subscription: ' . $subscription->get_id());
    10641126
    1065         // Set renewal order details
    10661127        $renewal_order->set_payment_method($this);
    10671128        $renewal_order->set_transaction_id($data['transaction_id']);
     
    10701131        $this->add_transaction_note($renewal_order, $data);
    10711132
    1072         // Complete the renewal order and mark as 'completed'
    10731133        $renewal_order->payment_complete();
    10741134        $renewal_order->update_status('completed');
     
    10761136        $this->log('Renewal order payment completed, marked as completed, and saved. Order ID: ' . $renewal_order->get_id());
    10771137
    1078         // Use WC_Subscriptions_Manager to process the payment
    10791138        WC_Subscriptions_Manager::process_subscription_payments_on_order($renewal_order);
    10801139
     
    10851144        $this->log('Processing failed renewal for subscription: ' . $subscription->get_id());
    10861145
    1087         // Set renewal order details
    10881146        $renewal_order->set_payment_method($this);
    10891147        $renewal_order->set_transaction_id($data['transaction_id']);
     
    10911149        $this->add_transaction_note($renewal_order, $data);
    10921150
    1093         // Use WC_Subscriptions_Manager to process the failed payment
    10941151        WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($renewal_order);
    10951152
     
    11491206            }
    11501207
    1151             // Get the parent order
    11521208            $parent_order_id = $subscription->get_parent_id();
    11531209            $parent_order = wc_get_order($parent_order_id);
     
    11571213            }
    11581214
    1159             // Get the mandate ID from the parent order's metadata
    11601215            $mandate_id = $parent_order->get_meta('bayarcash_mandate_id');
    11611216
     
    11741229            $this->log('Cancellation request successful. Redirect URL: ' . $response->url);
    11751230
    1176             // Store cancellation details
    11771231            $cancellation_data = [
    11781232                'order_number' => $parent_order_id,
     
    11861240            ];
    11871241
    1188             // Store cancellation data in subscription meta
    11891242            $subscription->update_meta_data('bayarcash_cancellation_data', $cancellation_data);
    11901243            $subscription->save();
    11911244
    1192             // Determine if the current user is an admin
    11931245            $is_admin = current_user_can('manage_options');
    11941246
    1195             // Check if we're on the subscriptions or view-subscription page
    11961247            $current_url = home_url(add_query_arg(array(), $GLOBALS['wp']->request));
    11971248            $is_subscriptions_page = ( str_contains( $current_url, '/my-account/subscriptions' ) );
    11981249            $is_view_subscription_page = ( str_contains( $current_url, '/my-account/view-subscription' ) );
    11991250
    1200             // Treat admin as normal user if on subscriptions or view-subscription page
    12011251            $treat_as_user = !$is_admin || $is_subscriptions_page || $is_view_subscription_page;
    12021252
    1203             // Handle AJAX requests or treat as AJAX for specific pages
    12041253            if (wp_doing_ajax() || $treat_as_user) {
    12051254                wp_send_json_success(['redirect_url' => $response->url]);
    12061255            }
    1207             // Handle non-AJAX requests for admin
    12081256            else {
    1209                 // For admin users, set a transient to show an admin notice
    12101257                set_transient('bayarcash_admin_notice', 'Cancellation request sent successfully.', 45);
    12111258                wp_safe_redirect(wp_get_referer() ?: admin_url());
     
    12211268                wp_send_json_error(['message' => $error_message]);
    12221269            } else {
    1223                 // For admin users, set a transient to show an admin notice
    12241270                set_transient('bayarcash_admin_notice', $error_message, 45);
    12251271                wp_safe_redirect(wp_get_referer() ?: admin_url());
     
    12361282        }
    12371283    }
    1238     /**
    1239      * @throws Exception
    1240      */
     1284
    12411285    private function handle_subscription_cancellation_approval($order, $data): void {
    12421286        $this->log('Handling subscription cancellation approval for Order No.: ' . $order->get_id());
    12431287
    1244         // Find the related subscription
    12451288        $subscriptions = wcs_get_subscriptions_for_order($order, array('order_type' => 'any'));
    12461289
     
    12531296            $this->log('Processing cancellation for Subscription ID: ' . $subscription->get_id());
    12541297
    1255             // Check if the subscription is already cancelled
    12561298            if ($subscription->get_status() === 'cancelled') {
    12571299                $this->log('Subscription ' . $subscription->get_id() . ' is already cancelled.');
     
    12591301            }
    12601302
    1261             // Prepare the cancellation note
    12621303            $note = sprintf(
    12631304                'Subscription cancelled. Bank approval received. Mandate ID: %s, Reference: %s, Date: %s',
     
    12671308            );
    12681309
    1269             // Add the note to the subscription
    12701310            $subscription->add_order_note($note);
    12711311
    1272             // Update subscription status
    12731312            $subscription->update_status('cancelled', 'Subscription cancelled due to bank approval of cancellation request.');
    12741313
    1275             // Remove scheduled actions for this subscription
    12761314            WC_Subscriptions_Manager::remove_all_subscription_scheduled_actions($subscription->get_id());
    12771315
    1278             // Trigger the cancellation action
    12791316            do_action('woocommerce_subscription_status_cancelled', $subscription);
    12801317
    1281             // Force save the subscription to ensure notes are persisted
    12821318            $subscription->save();
    12831319
     
    12851321        }
    12861322
    1287         // Update the parent order status if necessary
    12881323        if ($order->get_status() !== 'cancelled') {
    12891324            $order->update_status('cancelled', 'Order cancelled due to subscription cancellation approval.');
  • bayarcash-wc/trunk/readme.txt

    r3295064 r3305853  
    55Tested up to: 6.8
    66Requires PHP: 7.4
    7 Stable tag: 4.3.8
     7Stable tag: 4.3.9
    88License: GPLv3
    99License URI: https://www.gnu.org/licenses/gpl-3.0.txt
     
    8989== Changelog ==
    9090
     91= 4.3.9 =
     92* Fixed Direct Debit not detecting 2nd, 3rd, and subsequent deductions
     93* Improved duplicate processing prevention for renewal orders
     94
    9195= 4.3.8 =
    9296* Fixed Vue.js compatibility issues with certain themes and plugins
Note: See TracChangeset for help on using the changeset viewer.