/**
  * Include the PayPal payment meta data required to process automatic recurring payments so that store managers can
  * manually set up automatic recurring payments for a customer via the Edit Subscription screen.
  *
  * @param array $payment_meta associative array of meta data required for automatic payments
  * @param WC_Subscription $subscription An instance of a subscription object
  * @return array
  * @since 2.0
  */
 public static function add_payment_meta_details($payment_meta, $subscription)
 {
     if (WCS_PayPal::are_reference_transactions_enabled()) {
         $payment_meta['paypal'] = array('post_meta' => array('_paypal_subscription_id' => array('value' => get_post_meta($subscription->id, '_paypal_subscription_id', true), 'label' => 'PayPal Billing Agreement ID')));
     }
     return $payment_meta;
 }
 /**
  * Allow items on PayPal Standard Subscriptions to be switch when the PayPal account supports Reference Transactions
  *
  * Because PayPal Standard does not support recurring amount or date changes, items can not be switched when the subscription is using a
  * profile ID for PayPal Standard. However, PayPal Reference Transactions do allow these to be updated and because switching uses the checkout
  * process, we can migrate a subscription from PayPal Standard to Reference Transactions when the customer switches, so we will allow that.
  *
  * @since 2.0
  */
 public static function can_item_be_switched($item_can_be_switch, $item, $subscription)
 {
     if (false === $item_can_be_switch && 'paypal' === $subscription->payment_method && WCS_PayPal::are_reference_transactions_enabled()) {
         $is_billing_agreement = wcs_is_paypal_profile_a(wcs_get_paypal_id($subscription->id), 'billing_agreement');
         if ('line_item' == $item['type'] && wcs_is_product_switchable_type($item['product_id'])) {
             $is_product_switchable = true;
         } else {
             $is_product_switchable = false;
         }
         if ($subscription->has_status('active') && 0 !== $subscription->get_date('last_payment')) {
             $is_subscription_switchable = true;
         } else {
             $is_subscription_switchable = false;
         }
         // If the only reason the subscription isn't switchable is because the PayPal profile ID is not a billing agreement, allow it to be switched
         if (false === $is_billing_agreement && $is_product_switchable && $is_subscription_switchable) {
             $item_can_be_switch = true;
         }
     }
     return $item_can_be_switch;
 }
 /**
  * Add additional feature support at the subscription level instead of just the gateway level because some subscriptions may have been
  * setup with PayPal Standard while others may have been setup with Billing Agreements to use with Reference Transactions.
  *
  * @since 2.0
  */
 public static function add_feature_support_for_subscription($is_supported, $feature, $subscription)
 {
     if ('paypal' === $subscription->payment_method && WCS_PayPal::are_credentials_set()) {
         $paypal_profile_id = wcs_get_paypal_id($subscription->id);
         $is_billing_agreement = wcs_is_paypal_profile_a($paypal_profile_id, 'billing_agreement');
         if ('gateway_scheduled_payments' === $feature && $is_billing_agreement) {
             $is_supported = false;
         } elseif (in_array($feature, self::$standard_supported_features)) {
             if (wcs_is_paypal_profile_a($paypal_profile_id, 'out_of_date_id')) {
                 $is_supported = false;
             } else {
                 $is_supported = true;
             }
         } elseif (in_array($feature, self::$reference_transaction_supported_features)) {
             if ($is_billing_agreement) {
                 $is_supported = true;
             } else {
                 $is_supported = false;
             }
         }
     }
     return $is_supported;
 }
 /**
  * Checks a set of args and derives an Order ID with backward compatibility for WC < 1.7 where 'custom' was the Order ID.
  *
  * @since 2.0
  */
 public static function get_order_id_and_key($args, $order_type = 'shop_order')
 {
     $order_id = $order_key = '';
     if (isset($args['subscr_id'])) {
         // PayPal Standard IPN message
         $subscription_id = $args['subscr_id'];
     } elseif (isset($args['recurring_payment_id'])) {
         // PayPal Express Checkout IPN, most likely 'recurring_payment_suspended_due_to_max_failed_payment', for a PayPal Standard Subscription
         $subscription_id = $args['recurring_payment_id'];
     } else {
         $subscription_id = '';
     }
     // First try and get the order ID by the subscription ID
     if (!empty($subscription_id)) {
         $posts = get_posts(array('numberposts' => 1, 'orderby' => 'ID', 'order' => 'ASC', 'meta_key' => '_paypal_subscription_id', 'meta_value' => $subscription_id, 'post_type' => $order_type, 'post_status' => 'any', 'suppress_filters' => true));
         if (!empty($posts)) {
             $order_id = $posts[0]->ID;
             $order_key = get_post_meta($order_id, '_order_key', true);
         }
     }
     // Couldn't find the order ID by subscr_id, so it's either not set on the order yet or the $args doesn't have a subscr_id, either way, let's get it from the args
     if (empty($order_id) && isset($args['custom'])) {
         // WC < 1.6.5
         if (is_numeric($args['custom']) && 'shop_order' == $order_type) {
             $order_id = $args['custom'];
             $order_key = $args['invoice'];
         } else {
             $order_details = json_decode($args['custom']);
             if (is_object($order_details)) {
                 // WC 2.3.11+ converted the custom value to JSON, if we have an object, we've got valid JSON
                 if ('shop_order' == $order_type) {
                     $order_id = $order_details->order_id;
                     $order_key = $order_details->order_key;
                 } elseif (isset($order_details->subscription_id)) {
                     // Subscription created with Subscriptions 2.0+
                     $order_id = $order_details->subscription_id;
                     $order_key = $order_details->subscription_key;
                 } else {
                     // Subscription created with Subscriptions < 2.0
                     $subscriptions = wcs_get_subscriptions_for_order($order_details->order_id, array('order_type' => array('parent')));
                     if (!empty($subscriptions)) {
                         $subscription = array_pop($subscriptions);
                         $order_id = $subscription->id;
                         $order_key = $subscription->order_key;
                     }
                 }
             } elseif (preg_match('/^a:2:{/', $args['custom']) && !preg_match('/[CO]:\\+?[0-9]+:"/', $args['custom']) && ($order_details = maybe_unserialize($args['custom']))) {
                 // WC 2.0 - WC 2.3.11, only allow serialized data in the expected format, do not allow objects or anything nasty to sneak in
                 if ('shop_order' == $order_type) {
                     $order_id = $order_details[0];
                     $order_key = $order_details[1];
                 } else {
                     // Subscription, but we didn't have the subscription data in old, serialized value, so we need to pull it based on the order
                     $subscriptions = wcs_get_subscriptions_for_order($order_details[0], array('order_type' => array('parent')));
                     if (!empty($subscriptions)) {
                         $subscription = array_pop($subscriptions);
                         $order_id = $subscription->id;
                         $order_key = $subscription->order_key;
                     }
                 }
             } else {
                 // WC 1.6.5 - WC 2.0 or invalid data
                 $order_id = str_replace(WCS_PayPal::get_option('invoice_prefix'), '', $args['invoice']);
                 $order_key = $args['custom'];
             }
         }
     }
     return array('order_id' => (int) $order_id, 'order_key' => $order_key);
 }
 /**
  * Do not allow subscriptions to be switched using PayPal Standard as the payment method
  *
  * @since 2.0.16
  */
 public static function get_available_payment_gateways($available_gateways)
 {
     if (WC_Subscriptions_Switcher::cart_contains_switches() || isset($_GET['order_id']) && wcs_order_contains_switch($_GET['order_id'])) {
         foreach ($available_gateways as $gateway_id => $gateway) {
             if ('paypal' == $gateway_id && false == WCS_PayPal::are_reference_transactions_enabled()) {
                 unset($available_gateways[$gateway_id]);
             }
         }
     }
     return $available_gateways;
 }
 /**
  * Get PayPal Args for passing to PP
  *
  * Based on the HTML Variables documented here: https://developer.paypal.com/webapps/developer/docs/classic/paypal-payments-standard/integration-guide/Appx_websitestandard_htmlvariables/#id08A6HI00JQU
  *
  * @param WC_Order $order
  * @return array
  */
 public static function get_paypal_args($paypal_args, $order)
 {
     $is_payment_change = WC_Subscriptions_Change_Payment_Gateway::$is_request_to_change_payment;
     $order_contains_failed_renewal = false;
     // Payment method changes act on the subscription not the original order
     if ($is_payment_change) {
         $subscriptions = array(wcs_get_subscription($order->id));
         $subscription = array_pop($subscriptions);
         $order = $subscription->order;
         // We need the subscription's total
         remove_filter('woocommerce_order_amount_total', 'WC_Subscriptions_Change_Payment_Gateway::maybe_zero_total', 11, 2);
     } else {
         // Otherwise the order is the $order
         if ($cart_item = wcs_cart_contains_failed_renewal_order_payment() || false !== WC_Subscriptions_Renewal_Order::get_failed_order_replaced_by($order->id)) {
             $subscriptions = wcs_get_subscriptions_for_renewal_order($order);
             $order_contains_failed_renewal = true;
         } else {
             $subscriptions = wcs_get_subscriptions_for_order($order);
         }
         // Only one subscription allowed per order with PayPal
         $subscription = array_pop($subscriptions);
     }
     if ($order_contains_failed_renewal || !empty($subscription) && $subscription->get_total() > 0 && 'yes' !== get_option(WC_Subscriptions_Admin::$option_prefix . '_turn_off_automatic_payments', 'no')) {
         // It's a subscription
         $paypal_args['cmd'] = '_xclick-subscriptions';
         // Store the subscription ID in the args sent to PayPal so we can access them later
         $paypal_args['custom'] = wcs_json_encode(array('order_id' => $order->id, 'order_key' => $order->order_key, 'subscription_id' => $subscription->id, 'subscription_key' => $subscription->order_key));
         foreach ($subscription->get_items() as $item) {
             if ($item['qty'] > 1) {
                 $item_names[] = $item['qty'] . ' x ' . wcs_get_paypal_item_name($item['name']);
             } elseif ($item['qty'] > 0) {
                 $item_names[] = wcs_get_paypal_item_name($item['name']);
             }
         }
         // translators: 1$: subscription ID, 2$: order ID, 3$: names of items, comma separated
         $paypal_args['item_name'] = wcs_get_paypal_item_name(sprintf(_x('Subscription %1$s (Order %2$s) - %3$s', 'item name sent to paypal', 'woocommerce-subscriptions'), $subscription->get_order_number(), $order->get_order_number(), implode(', ', $item_names)));
         $unconverted_periods = array('billing_period' => $subscription->billing_period, 'trial_period' => $subscription->trial_period);
         $converted_periods = array();
         // Convert period strings into PayPay's format
         foreach ($unconverted_periods as $key => $period) {
             switch (strtolower($period)) {
                 case 'day':
                     $converted_periods[$key] = 'D';
                     break;
                 case 'week':
                     $converted_periods[$key] = 'W';
                     break;
                 case 'year':
                     $converted_periods[$key] = 'Y';
                     break;
                 case 'month':
                 default:
                     $converted_periods[$key] = 'M';
                     break;
             }
         }
         $price_per_period = $subscription->get_total();
         $subscription_interval = $subscription->billing_interval;
         $start_timestamp = $subscription->get_time('start');
         $trial_end_timestamp = $subscription->get_time('trial_end');
         $next_payment_timestamp = $subscription->get_time('next_payment');
         $is_synced_subscription = WC_Subscriptions_Synchroniser::subscription_contains_synced_product($subscription->id);
         if ($is_synced_subscription) {
             $length_from_timestamp = $next_payment_timestamp;
         } elseif ($trial_end_timestamp > 0) {
             $length_from_timestamp = $trial_end_timestamp;
         } else {
             $length_from_timestamp = $start_timestamp;
         }
         $subscription_length = wcs_estimate_periods_between($length_from_timestamp, $subscription->get_time('end'), $subscription->billing_period);
         $subscription_installments = $subscription_length / $subscription_interval;
         $initial_payment = $is_payment_change ? 0 : $order->get_total();
         if ($order_contains_failed_renewal || $is_payment_change) {
             if ($is_payment_change) {
                 // Add a nonce to the order ID to avoid "This invoice has already been paid" error when changing payment method to PayPal when it was previously PayPal
                 $suffix = '-wcscpm-' . wp_create_nonce();
             } else {
                 // Failed renewal order, append a descriptor and renewal order's ID
                 $suffix = '-wcsfrp-' . $order->id;
             }
             // Change the 'invoice' and the 'custom' values to be for the original order (if there is one)
             if (false === $subscription->order) {
                 // No original order so we need to use the subscriptions values instead
                 $order_number = ltrim($subscription->get_order_number(), _x('#', 'hash before the order number. Used as a character to remove from the actual order number', 'woocommerce-subscriptions')) . '-subscription';
                 $order_id_key = array('order_id' => $subscription->id, 'order_key' => $subscription->order_key);
             } else {
                 $order_number = ltrim($subscription->order->get_order_number(), _x('#', 'hash before the order number. Used as a character to remove from the actual order number', 'woocommerce-subscriptions'));
                 $order_id_key = array('order_id' => $subscription->order->id, 'order_key' => $subscription->order->order_key);
             }
             $order_details = false !== $subscription->order ? $subscription->order : $subscription;
             // Set the invoice details to the original order's invoice but also append a special string and this renewal orders ID so that we can match it up as a failed renewal order payment later
             $paypal_args['invoice'] = WCS_PayPal::get_option('invoice_prefix') . $order_number . $suffix;
             $paypal_args['custom'] = wcs_json_encode(array_merge($order_id_key, array('subscription_id' => $subscription->id, 'subscription_key' => $subscription->order_key)));
         }
         if ($order_contains_failed_renewal) {
             $subscription_trial_length = 0;
             $subscription_installments = max($subscription_installments - $subscription->get_completed_payment_count(), 0);
             // If we're changing the payment date or switching subs, we need to set the trial period to the next payment date & installments to be the number of installments left
         } elseif ($is_payment_change || $is_synced_subscription) {
             $next_payment_timestamp = $subscription->get_time('next_payment');
             // When the subscription is on hold
             if (false != $next_payment_timestamp && !empty($next_payment_timestamp)) {
                 $trial_until = wcs_calculate_paypal_trial_periods_until($next_payment_timestamp);
                 $subscription_trial_length = $trial_until['first_trial_length'];
                 $converted_periods['trial_period'] = $trial_until['first_trial_period'];
                 $second_trial_length = $trial_until['second_trial_length'];
                 $second_trial_period = $trial_until['second_trial_period'];
             } else {
                 $subscription_trial_length = 0;
             }
             // If this is a payment change, we need to account for completed payments on the number of installments owing
             if ($is_payment_change && $subscription_length > 0) {
                 $subscription_installments = max($subscription_installments - $subscription->get_completed_payment_count(), 0);
             }
         } else {
             $subscription_trial_length = wcs_estimate_periods_between($start_timestamp, $trial_end_timestamp, $subscription->trial_period);
         }
         if ($subscription_trial_length > 0) {
             // Specify a free trial period
             $paypal_args['a1'] = $initial_payment > 0 ? $initial_payment : 0;
             // Trial period length
             $paypal_args['p1'] = $subscription_trial_length;
             // Trial period
             $paypal_args['t1'] = $converted_periods['trial_period'];
             // We need to use a second trial period before we have more than 90 days until the next payment
             if (isset($second_trial_length) && $second_trial_length > 0) {
                 $paypal_args['a2'] = 0.01;
                 // Alas, although it's undocumented, PayPal appears to require a non-zero value in order to allow a second trial period
                 $paypal_args['p2'] = $second_trial_length;
                 $paypal_args['t2'] = $second_trial_period;
             }
         } elseif ($initial_payment != $price_per_period) {
             // No trial period, but initial amount includes a sign-up fee and/or other items, so charge it as a separate period
             if (1 == $subscription_installments) {
                 $param_number = 3;
             } else {
                 $param_number = 1;
             }
             $paypal_args['a' . $param_number] = $initial_payment;
             // Sign Up interval
             $paypal_args['p' . $param_number] = $subscription_interval;
             // Sign Up unit of duration
             $paypal_args['t' . $param_number] = $converted_periods['billing_period'];
         }
         // We have a recurring payment
         if (!isset($param_number) || 1 == $param_number) {
             // Subscription price
             $paypal_args['a3'] = $price_per_period;
             // Subscription duration
             $paypal_args['p3'] = $subscription_interval;
             // Subscription period
             $paypal_args['t3'] = $converted_periods['billing_period'];
         }
         // Recurring payments
         if (1 == $subscription_installments || $initial_payment != $price_per_period && 0 == $subscription_trial_length && 2 == $subscription_installments) {
             // Non-recurring payments
             $paypal_args['src'] = 0;
         } else {
             $paypal_args['src'] = 1;
             if ($subscription_installments > 0) {
                 // An initial period is being used to charge a sign-up fee
                 if ($initial_payment != $price_per_period && 0 == $subscription_trial_length) {
                     $subscription_installments--;
                 }
                 $paypal_args['srt'] = $subscription_installments;
             }
         }
         // Don't reattempt failed payments, instead let Subscriptions handle the failed payment
         $paypal_args['sra'] = 0;
         // Force return URL so that order description & instructions display
         $paypal_args['rm'] = 2;
         // Reattach the filter we removed earlier
         if ($is_payment_change) {
             add_filter('woocommerce_order_amount_total', 'WC_Subscriptions_Change_Payment_Gateway::maybe_zero_total', 11, 2);
         }
     }
     return $paypal_args;
 }
 /**
  * Instantiate our custom PayPal class
  *
  * @since 2.0
  */
 public static function init_paypal()
 {
     require_once 'paypal/class-wcs-paypal.php';
     WCS_PayPal::init();
 }
예제 #8
0
 /**
  * Return the default WC PayPal gateway's settings.
  *
  * @since 2.0
  */
 protected static function get_options()
 {
     self::$paypal_settings = get_option('woocommerce_paypal_settings');
     return self::$paypal_settings;
 }
 /**
  * Remove the invalid credentials error flag whenever a new set of API credentials are saved.
  *
  * @since 2.0
  */
 protected static function maybe_update_credentials_error_flag()
 {
     // Check if the API credentials are being saved - we can't do this on the 'woocommerce_update_options_payment_gateways_paypal' hook because it is triggered after 'admin_notices'
     if (!empty($_REQUEST['_wpnonce']) && wp_verify_nonce($_REQUEST['_wpnonce'], 'woocommerce-settings') && isset($_POST['woocommerce_paypal_api_username']) || isset($_POST['woocommerce_paypal_api_password']) || isset($_POST['woocommerce_paypal_api_signature'])) {
         $credentials_updated = false;
         if (isset($_POST['woocommerce_paypal_api_username']) && WCS_PayPal::get_option('api_username') != $_POST['woocommerce_paypal_api_username']) {
             $credentials_updated = true;
         } elseif (isset($_POST['woocommerce_paypal_api_password']) && WCS_PayPal::get_option('api_password') != $_POST['woocommerce_paypal_api_password']) {
             $credentials_updated = true;
         } elseif (isset($_POST['woocommerce_paypal_api_signature']) && WCS_PayPal::get_option('api_signature') != $_POST['woocommerce_paypal_api_signature']) {
             $credentials_updated = true;
         }
         if ($credentials_updated) {
             delete_option('wcs_paypal_credentials_error');
         }
     }
     do_action('wcs_paypal_admin_update_credentials');
 }
 /**
  * Performs an Express Checkout NVP API operation as passed in $api_method.
  *
  * Although the PayPal Standard API provides no facility for cancelling a subscription, the PayPal
  * Express Checkout NVP API can be used.
  *
  * @since 1.1
  */
 public static function change_subscription_status($profile_id, $new_status, $order = null)
 {
     _deprecated_function(__METHOD__, '2.0', 'WCS_PayPal::get_api()->manage_recurring_payments_profile_status()');
     return WCS_PayPal::get_api()->manage_recurring_payments_profile_status($profile_id, $new_status, $order);
 }
 /**
  * Supposed to return the main gatewya plugin class, but we don't have one of those
  *
  * @see \WCS_SV_API_Base::get_plugin()
  * @return object
  * @since 2.0
  */
 protected function get_plugin()
 {
     return WCS_PayPal::instance();
 }
 /**
  * If changing a subscriptions payment method from and to PayPal, the cancelled subscription hook was removed in
  * @see self::maybe_remove_cancelled_subscription_hook() so we want to add it again for other subscriptions.
  *
  * @since 2.0
  */
 public static function maybe_reattach_subscription_cancelled_callback($subscription, $new_payment_method, $old_payment_method)
 {
     if ('paypal' == $new_payment_method && 'paypal' == $old_payment_method && !WCS_PayPal::are_reference_transactions_enabled()) {
         add_action('woocommerce_subscription_cancelled_paypal', 'WCS_PayPal_Status_Manager::cancel_subscription');
     }
 }
 /**
  * Set up the payment details for a DoExpressCheckoutPayment or DoReferenceTransaction request
  *
  * @since 2.0.9
  * @param WC_Order $order order object
  * @param string $type the type of transaction for the payment
  * @param bool $use_deprecated_params whether to use deprecated PayPal NVP parameters (required for DoReferenceTransaction API calls)
  */
 protected function add_payment_details_parameters(WC_Order $order, $type, $use_deprecated_params = false)
 {
     $calculated_total = 0;
     $order_subtotal = 0;
     $item_count = 0;
     $order_items = array();
     // add line items
     foreach ($order->get_items() as $item) {
         $product = new WC_Product($item['product_id']);
         $order_items[] = array('NAME' => wcs_get_paypal_item_name($product->get_title()), 'DESC' => $this->get_item_description($item, $product), 'AMT' => $this->round($order->get_item_subtotal($item)), 'QTY' => !empty($item['qty']) ? absint($item['qty']) : 1, 'ITEMURL' => $product->get_permalink());
         $order_subtotal += $item['line_total'];
     }
     // add fees
     foreach ($order->get_fees() as $fee) {
         $order_items[] = array('NAME' => wcs_get_paypal_item_name($fee['name']), 'AMT' => $this->round($fee['line_total']), 'QTY' => 1);
         $order_subtotal += $fee['line_total'];
     }
     // add discounts
     if ($order->get_total_discount() > 0) {
         $order_items[] = array('NAME' => __('Total Discount', 'woocommerce-subscriptions'), 'QTY' => 1, 'AMT' => -$this->round($order->get_total_discount()));
     }
     if ($this->skip_line_items($order)) {
         $total_amount = $this->round($order->get_total());
         // calculate the total as PayPal would
         $calculated_total += $this->round($order_subtotal + $order->get_cart_tax()) + $this->round($order->get_total_shipping() + $order->get_shipping_tax());
         // offset the discrepency between the WooCommerce cart total and PayPal's calculated total by adjusting the order subtotal
         if ($total_amount !== $calculated_total) {
             $order_subtotal = $order_subtotal - ($calculated_total - $total_amount);
         }
         $item_names = array();
         foreach ($order_items as $item) {
             $item_names[] = sprintf('%1$s x %2$s', $item['NAME'], $item['QTY']);
         }
         // add a single item for the entire order
         $this->add_line_item_parameters(array('NAME' => sprintf(__('%s - Order', 'woocommerce-subscriptions'), get_option('blogname')), 'DESC' => wcs_get_paypal_item_name(implode(', ', $item_names)), 'AMT' => $this->round($order_subtotal + $order->get_cart_tax()), 'QTY' => 1), 0, $use_deprecated_params);
         // add order-level parameters
         //  - Do not sent the TAXAMT due to rounding errors
         if ($use_deprecated_params) {
             $this->add_parameters(array('AMT' => $total_amount, 'CURRENCYCODE' => $order->get_order_currency(), 'ITEMAMT' => $this->round($order_subtotal + $order->get_cart_tax()), 'SHIPPINGAMT' => $this->round($order->get_total_shipping() + $order->get_shipping_tax()), 'INVNUM' => WCS_PayPal::get_option('invoice_prefix') . wcs_str_to_ascii(ltrim($order->get_order_number(), _x('#', 'hash before the order number. Used as a character to remove from the actual order number', 'woocommerce-subscriptions'))), 'PAYMENTACTION' => $type, 'PAYMENTREQUESTID' => $order->id, 'CUSTOM' => json_encode(array('order_id' => $order->id, 'order_key' => $order->order_key))));
         } else {
             $this->add_payment_parameters(array('AMT' => $total_amount, 'CURRENCYCODE' => $order->get_order_currency(), 'ITEMAMT' => $this->round($order_subtotal + $order->get_cart_tax()), 'SHIPPINGAMT' => $this->round($order->get_total_shipping() + $order->get_shipping_tax()), 'INVNUM' => WCS_PayPal::get_option('invoice_prefix') . wcs_str_to_ascii(ltrim($order->get_order_number(), _x('#', 'hash before the order number. Used as a character to remove from the actual order number', 'woocommerce-subscriptions'))), 'PAYMENTACTION' => $type, 'PAYMENTREQUESTID' => $order->id, 'CUSTOM' => json_encode(array('order_id' => $order->id, 'order_key' => $order->order_key))));
         }
     } else {
         // add individual order items
         foreach ($order_items as $item) {
             $this->add_line_item_parameters($item, $item_count++, $use_deprecated_params);
             $calculated_total += $this->round($item['AMT'] * $item['QTY']);
         }
         // add shipping and tax to calculated total
         $calculated_total += $this->round($order->get_total_shipping()) + $this->round($order->get_total_tax());
         $total_amount = $this->round($order->get_total());
         // add order-level parameters
         if ($use_deprecated_params) {
             $this->add_parameters(array('AMT' => $total_amount, 'CURRENCYCODE' => $order->get_order_currency(), 'ITEMAMT' => $this->round($order_subtotal), 'SHIPPINGAMT' => $this->round($order->get_total_shipping()), 'TAXAMT' => $this->round($order->get_total_tax()), 'INVNUM' => WCS_PayPal::get_option('invoice_prefix') . wcs_str_to_ascii(ltrim($order->get_order_number(), _x('#', 'hash before the order number. Used as a character to remove from the actual order number', 'woocommerce-subscriptions'))), 'PAYMENTACTION' => $type, 'PAYMENTREQUESTID' => $order->id, 'CUSTOM' => json_encode(array('order_id' => $order->id, 'order_key' => $order->order_key))));
         } else {
             $this->add_payment_parameters(array('AMT' => $total_amount, 'CURRENCYCODE' => $order->get_order_currency(), 'ITEMAMT' => $this->round($order_subtotal), 'SHIPPINGAMT' => $this->round($order->get_total_shipping()), 'TAXAMT' => $this->round($order->get_total_tax()), 'INVNUM' => WCS_PayPal::get_option('invoice_prefix') . wcs_str_to_ascii(ltrim($order->get_order_number(), _x('#', 'hash before the order number. Used as a character to remove from the actual order number', 'woocommerce-subscriptions'))), 'PAYMENTACTION' => $type, 'PAYMENTREQUESTID' => $order->id, 'CUSTOM' => json_encode(array('order_id' => $order->id, 'order_key' => $order->order_key))));
         }
         // offset the discrepency between the WooCommerce cart total and PayPal's calculated total by adjusting the cost of the first item
         if ($total_amount !== $calculated_total) {
             $this->parameters['L_PAYMENTREQUEST_0_AMT0'] = $this->parameters['L_PAYMENTREQUEST_0_AMT0'] - ($calculated_total - $total_amount);
         }
     }
 }