/** * When a PayPal IPN messaged is received for a subscription transaction, * check the transaction details and * * @since 1.0 */ public static function process_paypal_ipn_request($transaction_details) { global $wpdb; $transaction_details = stripslashes_deep($transaction_details); if (!in_array($transaction_details['txn_type'], array('subscr_signup', 'subscr_payment', 'subscr_cancel', 'subscr_eot', 'subscr_failed', 'subscr_modify'))) { return; } if (empty($transaction_details['custom']) || empty($transaction_details['invoice'])) { return; } // Get the $order_id & $order_key with backward compatibility extract(self::get_order_id_and_key($transaction_details)); $transaction_details['txn_type'] = strtolower($transaction_details['txn_type']); if (self::$debug) { self::$log->add('paypal', 'Subscription Transaction Type: ' . $transaction_details['txn_type']); } if (self::$debug) { self::$log->add('paypal', 'Subscription transaction details: ' . print_r($transaction_details, true)); } $order = new WC_Order($order_id); // We have an invalid $order_id, probably because invoice_prefix has changed since the subscription was first created, so get the order by order key if (!isset($order->id)) { $order_id = function_exists('woocommerce_get_order_id_by_order_key') ? woocommerce_get_order_id_by_order_key($order_key) : $wpdb->get_var("SELECT post_id FROM {$wpdb->prefix}postmeta WHERE meta_key = '_order_key' AND meta_value = '{$order_key}'"); $order = new WC_Order($order_id); } if ($order->order_key !== $order_key) { if (self::$debug) { self::$log->add('paypal', 'Subscription IPN Error: Order Key does not match invoice.'); } return; } if ('paypal' != $order->recurring_payment_method) { if (self::$debug) { self::$log->add('paypal', 'IPN ignored, recurring payment method has changed.'); } return; } if (isset($transaction_details['ipn_track_id'])) { // Make sure the IPN request has not already been handled $handled_ipn_requests = get_post_meta($order_id, '_paypal_ipn_tracking_ids', true); if (empty($handled_ipn_requests)) { $handled_ipn_requests = array(); } // The 'ipn_track_id' is not a unique ID and is shared between different transaction types, so create a unique ID by prepending the transaction type $transaction_id = $transaction_details['txn_type'] . '_' . $transaction_details['ipn_track_id']; if (in_array($transaction_id, $handled_ipn_requests)) { if (self::$debug) { self::$log->add('paypal', 'Subscription IPN Error: This IPN message has already been correctly handled.'); } return; } } if (isset($transaction_details['subscr_id'])) { update_post_meta($order_id, 'PayPal Subscriber ID', $transaction_details['subscr_id']); } // Get the subscription this IPN message relates to $subscriptions_in_order = WC_Subscriptions_Order::get_recurring_items($order); $subscription_item = array_pop($subscriptions_in_order); $subscription_key = WC_Subscriptions_Manager::get_subscription_key($order->id, WC_Subscriptions_Order::get_items_product_id($subscription_item)); $subscription = WC_Subscriptions_Manager::get_subscription($subscription_key); $is_first_payment = empty($subscription['completed_payments']) ? true : false; if ('switched' === $subscription['status']) { if (self::$debug) { self::$log->add('paypal', 'IPN ignored, subscription has been switched.'); } return; } switch ($transaction_details['txn_type']) { case 'subscr_signup': // Store PayPal Details update_post_meta($order_id, 'Payer PayPal address', $transaction_details['payer_email']); update_post_meta($order_id, 'Payer PayPal first name', $transaction_details['first_name']); update_post_meta($order_id, 'Payer PayPal last name', $transaction_details['last_name']); $default_invoice_string = self::$paypal_settings['invoice_prefix'] . ltrim($order->get_order_number(), '#'); // If the invoice ID doesn't match the default invoice ID and contains the string '-wcscpm-', the IPN is for a subscription payment method change if ($default_invoice_string != $transaction_details['invoice'] && false !== strpos($transaction_details['invoice'], '-wcscpm-')) { $is_payment_change = true; } else { $is_payment_change = false; } $switched_subscription_key = get_post_meta($order_id, '_switched_subscription_key', true); $no_initial_payment = 0 == WC_Subscriptions_Order::get_total_initial_payment($order) && WC_Subscriptions_Order::get_subscription_trial_length($order) > 0 ? true : false; // When there is a free trial & no initial payment amount, we need to mark the order as paid and activate the subscription if (!$is_payment_change && (!empty($switched_subscription_key) || $no_initial_payment)) { $order->payment_complete(); WC_Subscriptions_Manager::activate_subscriptions_for_order($order); } // Payment completed if ($is_payment_change) { $order->add_order_note(__('IPN subscription payment method changed.', WC_Subscriptions::$text_domain)); } else { $order->add_order_note(__('IPN subscription sign up completed.', WC_Subscriptions::$text_domain)); } if (self::$debug) { if ($is_payment_change) { self::$log->add('paypal', 'IPN subscription payment method changed for order ' . $order_id); } else { self::$log->add('paypal', 'IPN subscription sign up completed for order ' . $order_id); } } break; case 'subscr_payment': if ('completed' == strtolower($transaction_details['payment_status'])) { // Store PayPal Details update_post_meta($order_id, 'PayPal Transaction ID', $transaction_details['txn_id']); update_post_meta($order_id, 'Payer PayPal first name', $transaction_details['first_name']); update_post_meta($order_id, 'Payer PayPal last name', $transaction_details['last_name']); update_post_meta($order_id, 'PayPal Payment type', $transaction_details['payment_type']); // Subscription Payment completed $order->add_order_note(__('IPN subscription payment completed.', WC_Subscriptions::$text_domain)); if (self::$debug) { self::$log->add('paypal', 'IPN subscription payment completed for order ' . $order_id); } // First payment on order, process payment & activate subscription if ($is_first_payment) { $order->payment_complete(); WC_Subscriptions_Manager::activate_subscriptions_for_order($order); } else { // We don't need to reactivate the subscription because Subs didn't suspend it remove_action('reactivated_subscription_paypal', __CLASS__ . '::reactivate_subscription_with_paypal', 10, 2); WC_Subscriptions_Manager::process_subscription_payments_on_order($order); add_action('reactivated_subscription_paypal', __CLASS__ . '::reactivate_subscription_with_paypal', 10, 2); } } elseif ('failed' == strtolower($transaction_details['payment_status'])) { // Subscription Payment completed $order->add_order_note(__('IPN subscription payment failed.', WC_Subscriptions::$text_domain)); if (self::$debug) { self::$log->add('paypal', 'IPN subscription payment failed for order ' . $order_id); } // First payment on order, don't generate a renewal order if ($is_first_payment) { remove_action('processed_subscription_payment_failure', 'WC_Subscriptions_Renewal_Order::generate_failed_payment_renewal_order', 10, 2); } WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order); } else { if (self::$debug) { self::$log->add('paypal', 'IPN subscription payment notification received for order ' . $order_id . ' with status ' . $transaction_details['payment_status']); } } break; case 'subscr_cancel': if ('true' == get_post_meta($order_id, '_wcs_changing_payment_from_paypal_to_paypal', true)) { // The flag has served its purpose delete_post_meta($order_id, '_wcs_changing_payment_from_paypal_to_paypal'); if (self::$debug) { self::$log->add('paypal', 'IPN subscription cancellation request ignored as changing PayPal to PayPal, for order ' . $order_id); } } else { WC_Subscriptions_Manager::cancel_subscriptions_for_order($order); // Subscription Cancellation Completed $order->add_order_note(__('IPN subscription cancelled for order.', WC_Subscriptions::$text_domain)); if (self::$debug) { self::$log->add('paypal', 'IPN subscription cancelled for order ' . $order_id); } } break; case 'subscr_eot': // Subscription ended, either due to failed payments or expiration $subscription_length = WC_Subscriptions_Order::get_subscription_length($order); // PayPal fires the 'subscr_eot' notice immediately if a subscription is only for one billing period, so ignore the request when we only have one billing period if (1 != $subscription_length && $subscription_length != WC_Subscriptions_Order::get_subscription_interval($order)) { if (self::$debug) { self::$log->add('paypal', 'IPN subscription end-of-term for order ' . $order_id); } // Record subscription ended $order->add_order_note(__('IPN subscription end-of-term for order.', WC_Subscriptions::$text_domain)); // Ended due to failed payments so cancel the subscription if (gmdate('U') + 24 * 60 * 60 < strtotime(WC_Subscriptions_Manager::get_subscription_expiration_date(WC_Subscriptions_Manager::get_subscription_key($order->id), $order->customer_user))) { WC_Subscriptions_Manager::cancel_subscriptions_for_order($order); } else { WC_Subscriptions_Manager::expire_subscriptions_for_order($order); } } break; case 'subscr_failed': // Subscription sign up failed if (self::$debug) { self::$log->add('paypal', 'IPN subscription payment failure for order ' . $order_id); } // Subscription Payment completed $order->add_order_note(__('IPN subscription payment failure.', WC_Subscriptions::$text_domain)); // First payment on order, don't generate a renewal order if ($is_first_payment) { remove_action('processed_subscription_payment_failure', 'WC_Subscriptions_Renewal_Order::generate_failed_payment_renewal_order', 10, 2); } WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order_id); break; } // Store the transaction ID to avoid handling requests duplicated by PayPal if (isset($transaction_details['ipn_track_id'])) { $handled_ipn_requests[] = $transaction_id; update_post_meta($order_id, '_paypal_ipn_tracking_ids', $handled_ipn_requests); } // Prevent default IPN handling for subscription txn_types exit; }
/** * When a PayPal IPN messaged is received for a subscription transaction, * check the transaction details and * * @since 1.0 */ public static function process_paypal_ipn_request($transaction_details) { global $wpdb; $transaction_details = stripslashes_deep($transaction_details); if (!in_array($transaction_details['txn_type'], array('subscr_signup', 'subscr_payment', 'subscr_cancel', 'subscr_eot', 'subscr_failed', 'subscr_modify'))) { return; } // Get the $order_id & $order_key with backward compatibility extract(self::get_order_id_and_key($transaction_details)); $transaction_details['txn_type'] = strtolower($transaction_details['txn_type']); if (self::$debug) { self::$log->add('paypal', 'Subscription Transaction Type: ' . $transaction_details['txn_type']); } if (self::$debug) { self::$log->add('paypal', 'Subscription transaction details: ' . print_r($transaction_details, true)); } $order = new WC_Order($order_id); // We have an invalid $order_id, probably because invoice_prefix has changed since the subscription was first created, so get the order by order key if (!isset($order->id)) { $order_id = function_exists('woocommerce_get_order_id_by_order_key') ? woocommerce_get_order_id_by_order_key($order_key) : $wpdb->get_var("SELECT post_id FROM {$wpdb->prefix}postmeta WHERE meta_key = '_order_key' AND meta_value = '{$order_key}'"); $order = new WC_Order($order_id); } if ($order->order_key !== $order_key) { if (self::$debug) { self::$log->add('paypal', 'Subscription IPN Error: Order Key does not match invoice.'); } exit; } if ('paypal' != $order->recurring_payment_method) { if (self::$debug) { self::$log->add('paypal', 'IPN ignored, recurring payment method has changed.'); } exit; } if (isset($transaction_details['ipn_track_id'])) { // Make sure the IPN request has not already been handled $handled_ipn_requests = get_post_meta($order_id, '_paypal_ipn_tracking_ids', true); if (empty($handled_ipn_requests)) { $handled_ipn_requests = array(); } // The 'ipn_track_id' is not a unique ID and is shared between different transaction types, so create a unique ID by prepending the transaction type $ipn_id = $transaction_details['txn_type'] . '_' . $transaction_details['ipn_track_id']; if (in_array($ipn_id, $handled_ipn_requests)) { if (self::$debug) { self::$log->add('paypal', 'Subscription IPN Error: This IPN message has already been correctly handled.'); } exit; } } if (isset($transaction_details['txn_id'])) { // Make sure the IPN request has not already been handled $handled_transactions = get_post_meta($order_id, '_paypal_transaction_ids', true); if (empty($handled_transactions)) { $handled_transactions = array(); } $transaction_id = $transaction_details['txn_id']; if (isset($transaction_details['txn_type'])) { $transaction_id .= '_' . $transaction_details['txn_type']; } // The same transaction ID is used for different payment statuses, so make sure we handle it only once. See: http://stackoverflow.com/questions/9240235/paypal-ipn-unique-identifier if (isset($transaction_details['payment_status'])) { $transaction_id .= '_' . $transaction_details['payment_status']; } if (in_array($transaction_id, $handled_transactions)) { if (self::$debug) { self::$log->add('paypal', 'Subscription IPN Error: This transaction has already been correctly handled.'); } exit; } } // Save the profile ID if it's not a cancellation/expiration request if (isset($transaction_details['subscr_id']) && !in_array($transaction_details['txn_type'], array('subscr_cancel', 'subscr_eot'))) { update_post_meta($order_id, 'PayPal Subscriber ID', $transaction_details['subscr_id']); } // Get the subscription this IPN message relates to $subscriptions_in_order = WC_Subscriptions_Order::get_recurring_items($order); $subscription_item = array_pop($subscriptions_in_order); $product_id = WC_Subscriptions_Order::get_items_product_id($subscription_item); $subscription_key = WC_Subscriptions_Manager::get_subscription_key($order->id, $product_id); $subscription = WC_Subscriptions_Manager::get_subscription($subscription_key); $is_first_payment = empty($subscription['completed_payments']) ? true : false; if ('switched' === $subscription['status']) { if (self::$debug) { self::$log->add('paypal', 'IPN ignored, subscription has been switched.'); } exit; } switch ($transaction_details['txn_type']) { case 'subscr_signup': // Store PayPal Details update_post_meta($order_id, 'Payer PayPal address', $transaction_details['payer_email']); update_post_meta($order_id, 'Payer PayPal first name', $transaction_details['first_name']); update_post_meta($order_id, 'Payer PayPal last name', $transaction_details['last_name']); $default_invoice_string = self::$paypal_settings['invoice_prefix'] . ltrim($order->get_order_number(), '#'); // If the invoice ID doesn't match the default invoice ID and contains the string '-wcscpm-', the IPN is for a subscription payment method change if ($default_invoice_string != $transaction_details['invoice'] && false !== strpos($transaction_details['invoice'], '-wcscpm-')) { $is_payment_change = true; } else { $is_payment_change = false; } $switched_subscription_key = get_post_meta($order_id, '_switched_subscription_key', true); $no_initial_payment = 0 == WC_Subscriptions_Order::get_total_initial_payment($order) && WC_Subscriptions_Order::get_subscription_trial_length($order) > 0 ? true : false; // When there is a free trial & no initial payment amount, we need to mark the order as paid and activate the subscription if (!$is_payment_change && (!empty($switched_subscription_key) || $no_initial_payment)) { $order->payment_complete(); } // Payment completed if ($is_payment_change) { $old_payment_method = get_post_meta($order->id, '_old_recurring_payment_method', true); // We need to cancel the subscription now that the method has been changed successfully if ('paypal' == $old_payment_method) { $profile_id = get_post_meta($order->id, '_old_paypal_subscriber_id', true); self::cancel_subscription_with_paypal($order, $product_id, $profile_id); } $order->add_order_note(__('IPN subscription payment method changed.', 'woocommerce-subscriptions')); } else { $order->add_order_note(__('IPN subscription sign up completed.', 'woocommerce-subscriptions')); } if (self::$debug) { if ($is_payment_change) { self::$log->add('paypal', 'IPN subscription payment method changed for order ' . $order_id); } else { self::$log->add('paypal', 'IPN subscription sign up completed for order ' . $order_id); } } break; case 'subscr_payment': if ('completed' == strtolower($transaction_details['payment_status'])) { // Store PayPal Details update_post_meta($order_id, 'PayPal Transaction ID', $transaction_details['txn_id']); update_post_meta($order_id, 'Payer PayPal first name', $transaction_details['first_name']); update_post_meta($order_id, 'Payer PayPal last name', $transaction_details['last_name']); update_post_meta($order_id, 'PayPal Payment type', $transaction_details['payment_type']); // Subscription Payment completed $order->add_order_note(__('IPN subscription payment completed.', 'woocommerce-subscriptions')); if (self::$debug) { self::$log->add('paypal', 'IPN subscription payment completed for order ' . $order_id); } // First payment on order, process payment & activate subscription if ($is_first_payment) { $order->payment_complete(); WC_Subscriptions_Manager::activate_subscriptions_for_order($order); // Ignore the first IPN message if the PDT should have handled it (if it didn't handle it, it will have been dealt with as first payment), but set a flag to make sure we only ignore it once } elseif (count($subscription['completed_payments']) == 1 && !empty(self::$paypal_settings['identity_token']) && 'true' != get_post_meta($order_id, '_paypal_first_ipn_ignored_for_pdt', true)) { if (self::$debug) { self::$log->add('paypal', 'IPN subscription payment ignored for order ' . $order_id . ' due to PDT previously handling the payment.'); } update_post_meta($order_id, '_paypal_first_ipn_ignored_for_pdt', 'true'); // Process the payment if the subscription is active } elseif (!in_array($subscription['status'], array('cancelled', 'expired', 'switched', 'trash'))) { // We don't need to reactivate the subscription because Subs didn't suspend it remove_action('reactivated_subscription_paypal', __CLASS__ . '::reactivate_subscription_with_paypal', 10, 2); WC_Subscriptions_Manager::process_subscription_payments_on_order($order); // Make sure the next payment date is sync with when PayPal processes the payments WC_Subscriptions_Manager::set_next_payment_date($subscription_key, $order->customer_user); add_action('reactivated_subscription_paypal', __CLASS__ . '::reactivate_subscription_with_paypal', 10, 2); } } elseif ('failed' == strtolower($transaction_details['payment_status'])) { // Subscription Payment completed $order->add_order_note(__('IPN subscription payment failed.', 'woocommerce-subscriptions')); if (self::$debug) { self::$log->add('paypal', 'IPN subscription payment failed for order ' . $order_id); } // First payment on order, don't generate a renewal order if ($is_first_payment) { remove_action('processed_subscription_payment_failure', 'WC_Subscriptions_Renewal_Order::generate_failed_payment_renewal_order', 10, 2); } WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order); } else { if (self::$debug) { self::$log->add('paypal', 'IPN subscription payment notification received for order ' . $order_id . ' with status ' . $transaction_details['payment_status']); } } break; case 'subscr_cancel': // Make sure the subscription hasn't been linked to a new payment method if ($transaction_details['subscr_id'] != self::get_subscriptions_paypal_id($order)) { if (self::$debug) { self::$log->add('paypal', 'IPN subscription cancellation request ignored - new PayPal Profile ID linked to this subscription, for order ' . $order_id); } } else { WC_Subscriptions_Manager::cancel_subscriptions_for_order($order); // Subscription Cancellation Completed $order->add_order_note(__('IPN subscription cancelled for order.', 'woocommerce-subscriptions')); if (self::$debug) { self::$log->add('paypal', 'IPN subscription cancelled for order ' . $order_id); } } break; case 'subscr_eot': // Subscription ended, either due to failed payments or expiration if (self::$debug) { self::$log->add('paypal', 'IPN EOT request ignored for order ' . $order_id); } break; case 'subscr_failed': // Subscription sign up failed if (self::$debug) { self::$log->add('paypal', 'IPN subscription payment failure for order ' . $order_id); } // Subscription Payment completed $order->add_order_note(__('IPN subscription payment failure.', 'woocommerce-subscriptions')); // First payment on order, don't generate a renewal order if ($is_first_payment) { remove_action('processed_subscription_payment_failure', 'WC_Subscriptions_Renewal_Order::generate_failed_payment_renewal_order', 10, 2); } WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order); break; } // Store the transaction IDs to avoid handling requests duplicated by PayPal if (isset($transaction_details['ipn_track_id'])) { $handled_ipn_requests[] = $ipn_id; update_post_meta($order_id, '_paypal_ipn_tracking_ids', $handled_ipn_requests); } if (isset($transaction_details['txn_id'])) { $handled_transactions[] = $transaction_id; update_post_meta($order_id, '_paypal_transaction_ids', $handled_transactions); } // Prevent default IPN handling for subscription txn_types exit; }
/** * When a PayPal IPN messaged is received for a subscription transaction, * check the transaction details and * * @since 1.0 */ public static function process_paypal_ipn_request($transaction_details) { if (!in_array($transaction_details['txn_type'], array('subscr_signup', 'subscr_payment', 'subscr_cancel', 'subscr_eot', 'subscr_failed', 'subscr_modify'))) { return; } if (empty($transaction_details['custom']) || empty($transaction_details['invoice'])) { return; } // Get the $order_id & $order_key with backward compatibility extract(self::get_order_id_and_key($transaction_details)); $transaction_details['txn_type'] = strtolower($transaction_details['txn_type']); if (self::$debug) { self::$log->add('paypal', 'Subscription Transaction Type: ' . $transaction_details['txn_type']); } if (self::$debug) { self::$log->add('paypal', 'Subscription transaction details: ' . print_r($transaction_details, true)); } $order = new WC_Order($order_id); // We have an invalid $order_id, probably because invoice_prefix has changed since the subscription was first created, so get the if (!isset($order->id)) { $order_id = function_exists('woocommerce_get_order_id_by_order_key') ? woocommerce_get_order_id_by_order_key($order_key) : $wpdb->get_var("SELECT post_id FROM {$wpdb->prefix}postmeta WHERE meta_key = '_order_key' AND meta_value = '{$order_key}'"); $order = new WC_Order($order_id); } if ($order->order_key !== $order_key) { if (self::$debug) { self::$log->add('paypal', 'Subscription IPN Error: Order Key does not match invoice.'); } return; } switch ($transaction_details['txn_type']) { case 'subscr_signup': // Store PayPal Details update_post_meta($order_id, 'Payer PayPal address', $transaction_details['payer_email']); update_post_meta($order_id, 'Payer PayPal first name', $transaction_details['first_name']); update_post_meta($order_id, 'Payer PayPal last name', $transaction_details['last_name']); update_post_meta($order_id, 'PayPal Subscriber ID', $transaction_details['subscr_id']); // Payment completed $order->add_order_note(__('IPN subscription sign up completed.', WC_Subscriptions::$text_domain)); if (self::$debug) { self::$log->add('paypal', 'IPN subscription sign up completed for order ' . $order_id); } // When there is a free trial & no initial payment amount, we need to mark the order as paid and activate the subscription if (0 == WC_Subscriptions_Order::get_total_initial_payment($order) && WC_Subscriptions_Order::get_subscription_trial_length($order) > 0) { $order->payment_complete(); WC_Subscriptions_Manager::activate_subscriptions_for_order($order); } break; case 'subscr_payment': if ('completed' == strtolower($transaction_details['payment_status'])) { // Store PayPal Details update_post_meta($order_id, 'PayPal Transaction ID', $transaction_details['txn_id']); update_post_meta($order_id, 'Payer PayPal first name', $transaction_details['first_name']); update_post_meta($order_id, 'Payer PayPal last name', $transaction_details['last_name']); update_post_meta($order_id, 'PayPal Payment type', $transaction_details['payment_type']); // Subscription Payment completed $order->add_order_note(__('IPN subscription payment completed.', WC_Subscriptions::$text_domain)); if (self::$debug) { self::$log->add('paypal', 'IPN subscription payment completed for order ' . $order_id); } $subscriptions_in_order = WC_Subscriptions_Order::get_recurring_items($order); $subscription_item = array_pop($subscriptions_in_order); $subscription_key = WC_Subscriptions_Manager::get_subscription_key($order->id, $subscription_item['id']); $subscription = WC_Subscriptions_Manager::get_subscription($subscription_key, $order->customer_user); // First payment on order, process payment & activate subscription if (empty($subscription['completed_payments'])) { $order->payment_complete(); WC_Subscriptions_Manager::activate_subscriptions_for_order($order); } else { WC_Subscriptions_Manager::process_subscription_payments_on_order($order); } } elseif ('failed' == strtolower($transaction_details['payment_status'])) { // Subscription Payment completed $order->add_order_note(__('IPN subscription payment failed.', WC_Subscriptions::$text_domain)); if (self::$debug) { self::$log->add('paypal', 'IPN subscription payment failed for order ' . $order_id); } WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order); } else { if (self::$debug) { self::$log->add('paypal', 'IPN subscription payment notification received for order ' . $order_id . ' with status ' . $transaction_details['payment_status']); } } break; case 'subscr_cancel': if (self::$debug) { self::$log->add('paypal', 'IPN subscription cancelled for order ' . $order_id); } // Subscription Payment completed $order->add_order_note(__('IPN subscription cancelled for order.', WC_Subscriptions::$text_domain)); WC_Subscriptions_Manager::cancel_subscriptions_for_order($order); break; case 'subscr_eot': // Subscription ended, either due to failed payments or expiration // PayPal fires the 'subscr_eot' notice immediately if a subscription is only for one billing period, so ignore the request when we only have one billing period if (1 != WC_Subscriptions_Order::get_subscription_length($order)) { if (self::$debug) { self::$log->add('paypal', 'IPN subscription end-of-term for order ' . $order_id); } // Record subscription ended $order->add_order_note(__('IPN subscription end-of-term for order.', WC_Subscriptions::$text_domain)); // Ended due to failed payments so cancel the subscription if (time() < strtotime(WC_Subscriptions_Manager::get_subscription_expiration_date(WC_Subscriptions_Manager::get_subscription_key($order->id), $order->customer_user))) { WC_Subscriptions_Manager::cancel_subscriptions_for_order($order); } else { WC_Subscriptions_Manager::expire_subscriptions_for_order($order); } } break; case 'subscr_failed': // Subscription sign up failed if (self::$debug) { self::$log->add('paypal', 'IPN subscription sign up failure for order ' . $order_id); } // Subscription Payment completed $order->add_order_note(__('IPN subscription sign up failure.', WC_Subscriptions::$text_domain)); WC_Subscriptions_Manager::failed_subscription_sign_ups_for_order($order); break; } // Prevent default IPN handling for subscription txn_types exit; }
/** * When a PayPal IPN messaged is received for a subscription transaction, * check the transaction details and * * @since 1.0 */ public static function process_paypal_ipn_request($transaction_details) { if (!in_array($transaction_details['txn_type'], array('subscr_signup', 'subscr_payment', 'subscr_cancel', 'subscr_eot', 'subscr_failed', 'subscr_modify'))) { return; } if (empty($transaction_details['custom']) || empty($transaction_details['invoice'])) { return; } // Get the $order_id & $order_key with backward compatibility extract(self::get_order_id_and_key($transaction_details)); $transaction_details['txn_type'] = strtolower($transaction_details['txn_type']); if (self::$debug) { self::$log->add('paypal', 'Subscription Transaction Type: ' . $transaction_details['txn_type']); } if (self::$debug && 'subscr_modify' == $transaction_details['txn_type']) { self::$log->add('paypal', 'Subscription subscr_modify details: ' . print_r($transaction_details, true)); } $order = new WC_Order($order_id); if ($order->order_key !== $order_key) { if (self::$debug) { self::$log->add('paypal', 'Subscription IPN Error: Order Key does not match invoice.'); } return; } switch ($transaction_details['txn_type']) { case 'subscr_signup': // Store PayPal Details update_post_meta($order_id, 'Payer PayPal address', $transaction_details['payer_email']); update_post_meta($order_id, 'Payer PayPal first name', $transaction_details['first_name']); update_post_meta($order_id, 'Payer PayPal last name', $transaction_details['last_name']); update_post_meta($order_id, 'PayPal Subscriber ID', $transaction_details['subscr_id']); // Payment completed if (!in_array($order->status, array('completed', 'processing'))) { $order->add_order_note(__('IPN subscription sign up completed.', WC_Subscriptions::$text_domain)); if (self::$debug) { self::$log->add('paypal', 'IPN subscription sign up completed for order ' . $order_id); } $order->payment_complete(); WC_Subscriptions_Manager::activate_subscriptions_for_order($order); } break; case 'subscr_payment': if ('completed' == strtolower($transaction_details['payment_status'])) { // Store PayPal Details update_post_meta($order_id, 'Transaction ID', $transaction_details['txn_id']); update_post_meta($order_id, 'Payer first name', $transaction_details['first_name']); update_post_meta($order_id, 'Payer last name', $transaction_details['last_name']); update_post_meta($order_id, 'Payment type', $transaction_details['payment_type']); // Subscription Payment completed $order->add_order_note(__('IPN subscription payment completed.', WC_Subscriptions::$text_domain)); if (self::$debug) { self::$log->add('paypal', 'IPN subscription payment completed for order ' . $order_id); } WC_Subscriptions_Manager::process_subscription_payments_on_order($order); } elseif ('failed' == strtolower($transaction_details['payment_status'])) { // Subscription Payment completed $order->add_order_note(__('IPN subscription payment failed.', WC_Subscriptions::$text_domain)); if (self::$debug) { self::$log->add('paypal', 'IPN subscription payment failed for order ' . $order_id); } WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order); } else { if (self::$debug) { self::$log->add('paypal', 'IPN subscription payment notification received for order ' . $order_id . ' with status ' . $transaction_details['payment_status']); } } break; case 'subscr_cancel': if (self::$debug) { self::$log->add('paypal', 'IPN subscription cancelled for order ' . $order_id); } // Subscription Payment completed $order->add_order_note(__('IPN subscription cancelled for order.', WC_Subscriptions::$text_domain)); WC_Subscriptions_Manager::cancel_subscriptions_for_order($order); break; case 'subscr_eot': // Subscription ended if (self::$debug) { self::$log->add('paypal', 'IPN subscription expired for order ' . $order_id); } // Subscription Payment completed $order->add_order_note(__('IPN subscription expired for order.', WC_Subscriptions::$text_domain)); WC_Subscriptions_Manager::expire_subscriptions_for_order($order); break; case 'subscr_failed': // Subscription sign up failed if (self::$debug) { self::$log->add('paypal', 'IPN subscription sign up failure for order ' . $order_id); } // Subscription Payment completed $order->add_order_note(__('IPN subscription sign up failure.', WC_Subscriptions::$text_domain)); WC_Subscriptions_Manager::failed_subscription_sign_ups_for_order($order); break; } // Prevent default IPN handling for subscription txn_types exit; }
/** * callback_handler function. * * Is called after a payment has been submitted in the QuickPay payment window. * * @access public * @return void */ public function callback_handler() { $request_body = file_get_contents("php://input"); $json = json_decode($request_body); $payment = new WC_QuickPay_API_Payment($request_body); if ($payment->is_authorized_callback($request_body)) { // Fetch order number; $order_number = WC_QuickPay_Order::get_order_id_from_callback($json); // Instantiate order object $order = new WC_QuickPay_Order($order_number); // Get last transaction in operation history $transaction = end($json->operations); // Is the transaction accepted? if ($json->accepted) { // Add order transaction fee $order->add_transaction_fee($transaction->amount); // Perform action depending on the operation status type try { switch ($transaction->type) { // // Cancel callbacks are currently not supported by the QuickPay API // case 'cancel': if (WC_QuickPay_Helper::subscription_is_active()) { if ($order->contains_subscription()) { WC_Subscriptions_Manager::cancel_subscriptions_for_order($order->id); } } // Write a note to the order history $order->note(__('Payment cancelled.', 'woo-quickpay')); break; case 'capture': // Write a note to the order history $order->note(__('Payment captured.', 'woo-quickpay')); break; case 'refund': $order->note(sprintf(__('Refunded %s %s', 'woo-quickpay'), WC_QuickPay_Helper::price_normalize($transaction->amount), $json->currency)); break; case 'authorize': // Set the transaction ID $order->set_transaction_id($json->id); // Set the transaction order ID $order->set_transaction_order_id($json->order_id); // Remove payment link $order->delete_payment_link(); // Remove payment ID, now we have the transaction ID $order->delete_payment_id(); // Subscription authorization if (isset($json->type) and strtolower($json->type) == 'subscription') { // Create subscription instance $subscription = new WC_QuickPay_API_Subscription($request_body); // Write log $order->note(sprintf(__('Subscription authorized. Transaction ID: %s', 'woo-quickpay'), $json->id)); // If 'capture first payment on subscription' is enabled if (WC_QuickPay_Helper::option_is_enabled($this->s('quickpay_autodraw_subscription'))) { // Check if there is an initial payment on the subscription $subscription_initial_payment = WC_Subscriptions_Order::get_total_initial_payment($order); // Only make an instant payment if there is an initial payment if ($subscription_initial_payment > 0) { // New subscription instance $subscription = new WC_QuickPay_API_Subscription(); // Perform API recurring payment request $recurring = $subscription->recurring($json->id, $order, $subscription_initial_payment); // Process the recurring response data WC_QuickPay_API_Subscription::process_recurring_response($recurring, $order); } } } else { // Write a note to the order history $order->note(sprintf(__('Payment authorized. Transaction ID: %s', 'woo-quickpay'), $json->id)); } // Register the payment on the order $order->payment_complete(); break; } } catch (QuickPay_API_Exception $e) { $e->write_to_logs(); } } else { // Write debug information $this->log->separator(); $this->log->add(sprintf(__('Transaction failed for #%s.', 'woo-quickpay'), $order_number)); $this->log->add(sprintf(__('QuickPay status code: %s.', 'woo-quickpay'), $transaction->qp_status_code)); $this->log->add(sprintf(__('QuickPay status message: %s.', 'woo-quickpay'), $transaction->qp_status_msg)); $this->log->add(sprintf(__('Acquirer status code: %s', 'woo-quickpay'), $transaction->aq_status_code)); $this->log->add(sprintf(__('Acquirer status message: %s', 'woo-quickpay'), $transaction->aq_status_msg)); $this->log->separator(); // Update the order statuses if ($transaction->type == 'subscribe' or $transaction->type == 'recurring') { WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order); } else { $order->update_status('failed'); } } } else { $this->log->add(sprintf(__('Invalid callback body for order #%s.', 'woo-quickpay'), $order_number)); } }