Massive problem – Partial Refunded commission withdrawable by vendors repeatedly

WCMp Core

Massive problem – Partial Refunded commission withdrawable by vendors repeatedly Open

Viewing 22 reply threads
  • Author
    Posts
    • #92297
      admin24
      Participant

      Hot for everyone: Vendors are able to withdraw refunded orders over and over again at the expense of the admin. Go to Commissions section and filter by partial refunded, open each one, and check the right side for multiple payouts. I just found this twice in my order history and confirmed with a vendor in both our Paypal accounts that it’s really happening.

      Sample case:
      – Order placed with 3 vendors.
      – Admin refunds all items from 1 of the 3 vendors in the parent order, does not touch other 2’s items.
      – Commission for remaining two vendors is set to partial refunded somehow.
      – Sub-Orders for remaining two vendors show a refund, but of $0.
      – Both vendors see commission ready for withdrawal and withdraws commission.
      – This commission is still in the withdraw list and he can withdraw over and over again.

      Sample case 2:
      – Order placed with 4 vendors.
      – Admin refunds 1 of 5 items from one vendor.
      – Same as above, but ONLY the vendor whose order had a refund in it can be repeatedly withdrawn.

    • #92298
      admin24
      Participant
      This reply has been marked as private.
    • #92299
      admin24
      Participant
      This reply has been marked as private.
    • #92301
      admin24
      Participant

      Holy crap this is way worse than I thought. It’s even for orders with a single vendor.

    • #92303
      admin24
      Participant

      Oh man, marking commissions as paid IS REPAYING THEM IN PAYPAL

    • #92371
      Michael Earley
      Participant

      This just happened to me, and empted my paypal completely. I’m out by hundreds, if not thousands of dollars as a result.

    • #92372
      Michael Earley
      Participant

      I just had some transactions paid 3, sometimes 4 times this morning. My account just got emptied.

    • #92378

      Please disable the cron as of now to stop this further.

      Tomorrow someone from our team will check it with urgent priority.

    • #92388
      admin24
      Participant

      Yeah this is absolutely tragic. Why would changing the commission status to paid send a payout?? I am also out hundreds of dollars and at a minimum I am facing an accounting nightmare for the erroneous payouts I will be able to get back.

    • #92392

      @admin24, Let me help you out with the automatic and manual payment.

      If a vendor has selected their payment mode to either Stripe/PayPal (these are automatic payment mode). Then when an admin will mark the commission as paid, this will ask admin’s PayPal/Stripe to pay the vendor. This is our default flow.

      Now, if the payment mode is Bank Transfer (which is manual payment), then it will only mark that commission as paid.

    • #92447
      admin24
      Participant

      The status was “partial refunded” and I was trying to mark them paid so that the vendor would stop withdrawing them. Marking the status as paid should not trigger the payouts system to actually send the money… we’re just changing the status of the commission. That doesn’t make sense at all. One of these paid a suspended vendor who I have no contact with whatsoever due to legal issues… there is no way for me to recover that lost money. So again that doesn’t make sense that a suspended vendor would get a payment, especially just for changing the status of a commission.

      But that shouldn’t be the focus right now. Vendors withdrawing over and over again is a critical coding error and you guys should be able to recreate it easily and fix it. I sent you plenty of information and a video. What’s the status???

    • #92481

      @admin24, as I can see you are refunding the order from main order. For now, the main order is not synced with suborder from backend, which we will fix in our next update.

      Now if you refund from suborder, you can see the refunded order has set commission as 0. Also, this only affects the individual order of vendor, hence if an order has a product from multiple vendors, managing the order vis suborder is better: https://www.loom.com/share/b0f876d1a9064b4882da1f0a18188e0b

    • #92560
      admin24
      Participant

      So that’s not how this works. The API for PayPal will not work to refund orders from within the suborder. So yeah sure, if you’re doing “refund manually” and doing it outside the platform you can do it your way, but in reality that’s not how this works at all.

      It’s now three days later and still waiting for this to be checked “with urgent priority.”

    • #92569

      @admin24, because for now, when the main order is reflecting the suborder doesn’t get updated, hence the commission amount remains the same.

      When this issue will be added in our plugin: https://github.com/wcmarketplace/dc-woocommerce-multi-vendor/issues/163, then when admin will refund the order, that will update both suborder and commission amount. Hence the vendor’s commission will be also updated as per the new amount after the refund.

    • #92574
      admin24
      Participant

      This makes no sense and does not explain why an error in your code can cause an entire marketplace to be destroyed. Frankly I am amazed at this response. This is a massive problem that you caused. We are out thousands of dollars. How can you expect anyone to want to be a part of your system if this happens and this is how you try to address it? How do I get my money back? Sorry but this is totally unacceptable.

    • #92578

      @admin24, as shared in the above thread, this issue will be fixed in our next update.
      I shared the GitHub link too: https://github.com/wcmarketplace/dc-woocommerce-multi-vendor/issues/163

      The update will be released next week.

      For the money back from the vendor, I understand that will be a difficult task for you. For the time being, you need to check which order is refunded and check the commission of that vendor. Then manually ask for a refund from PayPal.

    • #92884
      admin24
      Participant

      I read that and it is not the same thing at all as what we are talking about. Unfortunately this makes me want to do everything I can to tell everyone to avoid your plugin as a result of this error and your response to it. This is a devastating error in both your code and judgement, but conveniently it only monetarily affects us. It’s totally flippant and disrespectful. How can you do business this way? Especially for those of us who bought full packages to your plugins???

    • #92889

      @admin24, as you can see in our end, we didn’t face any such issue. i.e.

      Sample case:
      – Order placed with 3 vendors.
      – Admin refunds all items from 1 of the 3 vendors in the parent order, does not touch other 2’s items.
      – Sub-Orders for that vendors show a refund
      – The commission is getting updated too
      – This commission is removed from the withdraw list hence he can’t withdraw that money
      Here is the video: https://www.loom.com/share/8f46c20ca581478fbf6a76743dfedb70

      Can you please remove all the custom codes, and deactivated all the plugins except WooCommerce, WCMp and addons and check again. You would not face this issue, as you reported.

      We will check the partial_refund status and other sub order’s update issue. Also, to sync the suborder amount with parent order amount, you may use this code :

      add_action( 'init' , 'refund_order_to_parent' );
      function refund_order_to_parent(){
        global $WCMp;
        remove_action('wp_ajax_wcmp_do_refund', array($WCMp->ajax, 'wcmp_do_refund'));
        add_action( 'wp_ajax_wcmp_do_refund', 'refund_vendor_ro_parent' );
      }
      function refund_vendor_ro_parent(){
         ob_start();
      
              check_ajax_referer('wcmp-order-item', 'security');
             
              $order_id = absint($_POST['order_id']);
              $refund_amount = wc_format_decimal(sanitize_text_field(wp_unslash($_POST['refund_amount'])), wc_get_price_decimals());
              $refunded_amount = wc_format_decimal(sanitize_text_field(wp_unslash($_POST['refunded_amount'])), wc_get_price_decimals());
              $refund_reason = sanitize_text_field($_POST['refund_reason']);
              $line_item_qtys = json_decode(sanitize_text_field(wp_unslash($_POST['line_item_qtys'])), true);
              $line_item_totals = json_decode(sanitize_text_field(wp_unslash($_POST['line_item_totals'])), true);
              $line_item_tax_totals = json_decode(sanitize_text_field(wp_unslash($_POST['line_item_tax_totals'])), true);
              $api_refund = 'true' === $_POST['api_refund'];
              $restock_refunded_items = 'true' === $_POST['restock_refunded_items'];
              $refund = false;
              $response_data = array();
      
              try {
                  $order = wc_get_order($order_id);
                 
                  $parent_order_id = wp_get_post_parent_id($order_id);
                  $parent_order = wc_get_order( $parent_order_id );
                  $parent_items_ids = array_keys($parent_order->get_items( array( 'line_item', 'fee', 'shipping' ) ));
                 
                  $order_items = $order->get_items();
                  $max_refund = wc_format_decimal($order->get_total() - $order->get_total_refunded(), wc_get_price_decimals());
      
                  if (!$refund_amount || $max_refund < $refund_amount || 0 > $refund_amount) {
                      throw new exception(__('Invalid refund amount', 'woocommerce'));
                  }
      
                  if ($refunded_amount !== wc_format_decimal($order->get_total_refunded(), wc_get_price_decimals())) {
                      throw new exception(__('Error processing refund. Please try again.', 'woocommerce'));
                  }
      
                  // Prepare line items which we are refunding.
                  $line_items = array();
                 
                  $parent_line_items = array();
                 
                  $item_ids = array_unique(array_merge(array_keys($line_item_qtys, $line_item_totals)));
      
                  foreach ($item_ids as $item_id) {
                      $line_items[$item_id] = array(
                          'qty' => 0,
                          'refund_total' => 0,
                          'refund_tax' => array(),
                      );
      
                      $parent_item_id = $WCMp->order->get_vendor_parent_order_item_id($item_id);
                      if( $parent_item_id && in_array($parent_item_id, $parent_items_ids) ){
                          $parent_line_items[$parent_item_id] = array(
                              'qty' => 0,
                              'refund_total' => 0,
                              'refund_tax' => array(),
                          );
                      }
                  }
                  foreach ($line_item_qtys as $item_id => $qty) {
                      $line_items[$item_id]['qty'] = max($qty, 0);
                      $parent_item_id = $WCMp->order->get_vendor_parent_order_item_id($item_id);
                      if( $parent_item_id && in_array($parent_item_id, $parent_items_ids) ){
                          $parent_line_items[$parent_item_id]['qty'] = max($qty, 0);
                      }
                  }
                  foreach ($line_item_totals as $item_id => $total) {
                      $line_items[$item_id]['refund_total'] = wc_format_decimal($total);
                      $parent_item_id = $WCMp->order->get_vendor_parent_order_item_id($item_id);
                      if( $parent_item_id && in_array($parent_item_id, $parent_items_ids) ){
                          $parent_line_items[$parent_item_id]['refund_total'] = wc_format_decimal($total);
                      }
                  }
                  foreach ($line_item_tax_totals as $item_id => $tax_totals) {
                      $line_items[$item_id]['refund_tax'] = array_filter(array_map('wc_format_decimal', $tax_totals));
                      $parent_item_id = $WCMp->order->get_vendor_parent_order_item_id($item_id);
                      if( $parent_item_id && in_array($parent_item_id, $parent_items_ids) ){
                          $parent_line_items[$parent_item_id]['refund_tax'] = array_filter(array_map('wc_format_decimal', $tax_totals));
                      }
                  }
      
                  // Create the refund object.
                  $refund = wc_create_refund(
                          array(
                              'amount' => $refund_amount,
                              'reason' => $refund_reason,
                              'order_id' => $order_id,
                              'line_items' => $line_items,
                              'refund_payment' => $api_refund,
                              'restock_items' => $restock_refunded_items,
                          )
                  );
      
                  if( $parent_line_items ){
                      $parent_refund = wc_create_refund(
                              array(
                                  'amount' => $refund_amount,
                                  'reason' => $refund_reason,
                                  'order_id' => $parent_order_id,
                                  'line_items' => $parent_line_items,
                                  'refund_payment' => $api_refund,
                                  'restock_items' => $restock_refunded_items,
                              )
                      );
                  }
                 
                  if (is_wp_error($refund)) {
                      throw new Exception($refund->get_error_message());
                  }
                 
                  if (is_wp_error($parent_refund)) {
                      throw new Exception($parent_refund->get_error_message());
                  }
      
                  do_action( 'wcmp_order_refunded', $order_id, $refund->get_id() );
      
                  if (did_action('woocommerce_order_fully_refunded')) {
                      $response_data['status'] = 'fully_refunded';
                  }
      
                  wp_send_json_success($response_data);
                  die;
              } catch (Exception $e) {
                  wp_send_json_error(array('error' => $e->getMessage()));
                  die;
              }
      }
      Copy
    • #93347
      admin24
      Participant

      After 1:43 in your video, go into the OTHER vendor’s dashboards where it says the commission status is partial refunded and withdraw the commission… this is where that vendor can withdraw over and over again and it stays in his/her list. I’ve confirmed this as you described with everything disabled.

      Sample case:
      – Order placed with 3 vendors.
      – Admin refunds all items from 1 of the 3 vendors in the parent order, does not touch other 2’s items.
      – Sub-Orders for that vendors show a refund
      – The commission is getting updated too
      – This commission is removed from the withdraw list hence he can’t withdraw that money
      – Now go to the OTHER vendors who have the “partial refunded” commission and withdraw the commission, repeatedly.

      Then maybe try again with just one vendor, but don’t totally refund the order. I think it will duplicate then but I have not been able to test this case.

    • #95484
      admin24
      Participant

      Any update? This is a big deal…

    • #95568

      @admin24, I will be able to give you an update by tomorrow.

    • #95674

      @admin24, we have added this fix on our end, this will be released with our next update.

      If you want this urgently, we can share the plugin file with you manually.

    • #95972

      @admin24, we have released this fix in our current update version 3.5.0

Viewing 22 reply threads

Please LOGIN to reply to this topic

COVID-19 Outbreak: WCMp team is supporting business affected by coronavirus

Read the Message Buy at Slashed Price