/** * scheduled_subscription_payment * * Hooked to woocommerce_scheduled_subscription_payment_{gateway_id} * Completes recurring payments for a subscription */ public function scheduled_subscription_payment($amount_to_charge, $order) { $this->log(__FUNCTION__, "Info: Beginning processing of scheduled payment for order {$order->id} for the amount of {$amount_to_charge}"); $this->log(__FUNCTION__, "Info: Merchant ID = {$this->merchant_id}"); // token is required $payment_method_token = get_post_meta($order->id, '_wc_paypal_braintree_payment_method_token', true); if (empty($payment_method_token)) { $this->log(__FUNCTION__, "Error: Payment method token is missing on order meta"); WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order); return; } // as is the customer id $braintree_customer_id = get_post_meta($order->id, '_wc_paypal_braintree_customer_id', true); if (empty($braintree_customer_id)) { $this->log(__FUNCTION__, "Error: Braintree customer ID is missing on order meta"); WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order); return; } // Create the gateway instance require_once dirname(__FILE__) . '/../braintree_sdk/lib/Braintree.php'; $gateway = new Braintree_Gateway(array('accessToken' => $this->merchant_access_token)); // Process the sale with the stored token and customer $sale_args = apply_filters('wc_gateway_paypal_braintree_sale_args', array('amount' => $amount_to_charge, 'paymentMethodToken' => $payment_method_token, 'recurring' => true, 'customerId' => $braintree_customer_id, 'channel' => 'WooThemes_BT', 'orderId' => $order->id, 'options' => array('submitForSettlement' => true, 'storeInVaultOnSuccess' => true))); try { $result = $gateway->transaction()->sale($sale_args); } catch (Exception $e) { $this->log(__FUNCTION__, 'Error: Unable to process scheduled payment. Reason: ' . $e->getMessage()); return false; } if (!$result->success) { $this->log(__FUNCTION__, "Error: Unable to process scheduled payment: {$result->message}"); return false; } $transaction_id = $result->transaction->id; $this->log(__FUNCTION__, "Info: Successfully processed schedule payment, transaction id = {$transaction_id}"); WC_Subscriptions_Manager::process_subscription_payments_on_order($order); $this->log(__FUNCTION__, "Info: Completed processing of scheduled payment for order {$order->id}"); $order->add_order_note(sprintf(__('PayPal Braintree charge complete (Charge ID: %s)', 'woocommerce-gateway-paypal-braintree'), $transaction_id)); $order->payment_complete($transaction_id); }
/** * process_scheduled_subscription_payment function. * * @param float $amount_to_charge The amount to charge. * @param WC_Order $order The WC_Order object of the order which the subscription was purchased in. * @param int $product_id The ID of the subscription product for which this payment relates. */ public function process_scheduled_subscription_payment($amount_to_charge, $order, $product_id) { $result = $this->process_subscription_payment($order, $amount_to_charge); if (is_wp_error($result)) { WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order, $product_id); } else { WC_Subscriptions_Manager::process_subscription_payments_on_order($order); } }
/** * scheduled_subscription_payment function. * * @param $amount_to_charge float The amount to charge. * @param $order WC_Order The WC_Order object of the order which the subscription was purchased in. * @param $product_id int The ID of the subscription product for which this payment relates. * @access public * @return void */ function scheduled_subscription_payment($amount_to_charge, $order, $product_id) { $result = $this->process_subscription_payment($order, $amount_to_charge); if (is_wp_error($result)) { $order->add_order_note(sprintf(__('eWAY subscription renewal failed - %s', 'wc-eway'), $this->response_message_lookup($result->get_error_message()))); WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order, $product_id); } else { WC_Subscriptions_Manager::process_subscription_payments_on_order($order); } }
/** * Update subscription status. * * @param int $order_id * @param string $invoice_status * * @return bool */ protected function update_subscription_status($order_id, $invoice_status) { $order = new WC_Order($order_id); $invoice_status = strtolower($invoice_status); $order_updated = false; if ('paid' == $invoice_status) { $order->add_order_note(__('Iugu: Subscription paid successfully.', 'iugu-woocommerce')); // Payment complete $order->payment_complete(); $order_updated = true; } elseif (in_array($invoice_status, array('canceled', 'refunded', 'expired'))) { $order->add_order_note(__('Iugu: Subscription payment failed.', 'iugu-woocommerce')); WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order); $order_updated = true; } // Allow custom actions when update the order status. do_action('iugu_woocommerce_update_order_status', $order, $invoice_status, $order_updated); }
/** * Process the subscription payment (manually... well via wp_cron) * * @param $amount the amount for this payment * @param $order the order ID * @param $product_id the product ID */ function scheduled_subscription_payment($amount_to_charge, $order, $product_id) { $this->params = array(); $this->params["amount"] = (int) ($amount_to_charge * 100); $this->params["test"] = $test_mode; $this->params["reference"] = $order->id . "-" . date("dmY"); // Reference for order ID 123 will become 123-01022012 $token = get_post_meta($order->id, "fatzebra_card_token", true); $this->params["card_token"] = $token; $ip = get_post_meta($post_id, "Customer IP Address", true); if (empty($ip)) { $ip = "127.0.0.1"; } $this->params["customer_ip"] = $ip; $this->params["deferred"] = false; $result = $this->do_payment($this->params); if (is_wp_error($result)) { $error = ""; $txn_id = "None"; switch ($result->get_error_code()) { case 1: // Non-200 response, so failed... (e.g. 401, 403, 500 etc). $error = $result->get_error_message(); break; case 2: // Gateway error (data etc) $errors = $result->get_error_data(); $error = implode(", ", $errors); error_log("WooCommerce Fat Zebra - Gateway Error: " . print_r($errors, true)); break; case 3: // Declined - error data is array with keys: message, id $error = $this->response_data->response->message; $txn_id = $this->response_data->response->transaction_id; break; case 4: // Exception caught, something bad happened. Data is exception // Exception caught, something bad happened. Data is exception default: $error = "Unknown - Error - See error log"; error_log("WC Fat Zebra (Subscriptions) - Unknown Error (exception): " . print_r($result->get_error_data(), true)); break; } // Add the error details and return $order->add_order_note(__("Subscription Payment Failed: " . $error . ". Transaction ID: " . $txn_id)); WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order, $product_id); } else { // Success! Returned is an array with the transaction ID etc // Update the subscription and return // Add a note to the order $order->add_order_note(__("Subscription Payment Successful. Transaction ID: " . $result["transaction_id"])); WC_Subscriptions_Manager::process_subscription_payments_on_order($order); } }
/** * 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; }
/** * If the payment for a renewal order has previously failed and is then paid, we need to make sure the * subscription payment function is called. * * @param int $user_id The id of the user who purchased the subscription * @param string $subscription_key A subscription key of the form created by @see WC_Subscriptions_Manager::get_subscription_key() * @since 1.2 */ public static function process_subscription_payment_on_child_order($order_id, $payment_status = 'completed') { if (self::is_renewal($order_id, array('order_role' => 'child'))) { $child_order = new WC_Order($order_id); $parent_order = self::get_parent_order($child_order); $subscriptions_in_order = $child_order->get_items(); // Should only be one subscription in the renewal order, but just in case foreach ($subscriptions_in_order as $item) { $item_id = WC_Subscriptions_Order::get_items_product_id($item); if (WC_Subscriptions_Order::is_item_subscription($parent_order, $item_id)) { if ('failed' == $payment_status) { // Don't duplicate renewal order remove_action('processed_subscription_payment_failure', __CLASS__ . '::generate_failed_payment_renewal_order', 10, 2); WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($parent_order->id, $item_id); // But make sure orders are still generated for other payments in the same request add_action('processed_subscription_payment_failure', __CLASS__ . '::generate_failed_payment_renewal_order', 10, 2); } else { // Don't duplicate renewal order remove_action('processed_subscription_payment', __CLASS__ . '::generate_paid_renewal_order', 10, 2); WC_Subscriptions_Manager::process_subscription_payments_on_order($parent_order->id, $item_id); // But make sure orders are still generated for other payments in the same request add_action('processed_subscription_payment', __CLASS__ . '::generate_paid_renewal_order', 10, 2); // Reactivate the subscription - activate_subscription doesn't operate on child orders $subscription_key = WC_Subscriptions_Manager::get_subscription_key($parent_order->id, $item_id); WC_Subscriptions_Manager::reactivate_subscription($parent_order->customer_user, $subscription_key); } } } } }
/** * 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; }
/** * Process subscription renewal * * @since 4.1.0 * @param float $amount_to_charge subscription amount to charge, could include multiple renewals if they've previously failed and the admin has enabled it * @param WC_Order $order original order containing the subscription * @param int $product_id the subscription product id */ public function process_renewal_payment_1_5($amount_to_charge, $order, $product_id) { try { // set order defaults $order = $this->get_gateway()->get_order($order->id); // zero-dollar subscription renewal. weird, but apparently it happens if (0 == $amount_to_charge) { // add order note $order->add_order_note(sprintf(_x('%s0 Subscription Renewal Approved', 'Supports direct credit card subscriptions', $this->get_gateway()->get_text_domain()), get_woocommerce_currency_symbol())); // update subscription WC_Subscriptions_Manager::process_subscription_payments_on_order($order, $product_id); return; } // set the amount to charge, ensuring that we have a decimal point, even if it's 1.00 $order->payment_total = SV_WC_Helper::number_format($amount_to_charge); // required if (!$order->payment->token || !$order->get_user_id()) { throw new SV_WC_Payment_Gateway_Exception('Subscription Renewal: Payment Token or User ID is missing/invalid.'); } // get the token, we've already verified it's good $token = $this->get_gateway()->get_payment_token($order->get_user_id(), $order->payment->token); // perform the transaction if ($this->get_gateway()->is_credit_card_gateway()) { if ($this->get_gateway()->perform_credit_card_charge()) { $response = $this->get_gateway()->get_api()->credit_card_charge($order); } else { $response = $this->get_gateway()->get_api()->credit_card_authorization($order); } } elseif ($this->get_gateway()->is_echeck_gateway()) { $response = $this->get_gateway()->get_api()->check_debit($order); } // check for success if ($response->transaction_approved()) { // order note based on gateway type if ($this->get_gateway()->is_credit_card_gateway()) { $message = sprintf(_x('%s %s Subscription Renewal Payment Approved: %s ending in %s (expires %s)', 'Supports direct credit card subscriptions', $this->get_gateway()->get_text_domain()), $this->get_gateway()->get_method_title(), $this->get_gateway()->perform_credit_card_authorization() ? 'Authorization' : 'Charge', $token->get_card_type() ? $token->get_type_full() : 'card', $token->get_last_four(), $token->get_exp_month() . '/' . $token->get_exp_year()); } elseif ($this->get_gateway()->is_echeck_gateway()) { // there may or may not be an account type (checking/savings) available, which is fine $message = sprintf(_x('%s Check Subscription Renewal Payment Approved: %s account ending in %s', 'Supports direct cheque subscriptions', $this->get_gateway()->get_text_domain()), $this->get_gateway()->get_method_title(), $token->get_account_type(), $token->get_last_four()); } // add order note $order->add_order_note($message); // set transaction ID manually, WCS 1.5.x calls WC_Order::payment_complete() internally if ($response->get_transaction_id()) { update_post_meta($order->id, '_transaction_id', $response->get_transaction_id()); } // update subscription WC_Subscriptions_Manager::process_subscription_payments_on_order($order, $product_id); } else { // failure throw new SV_WC_Payment_Gateway_Exception(sprintf('%s: %s', $response->get_status_code(), $response->get_status_message())); } } catch (SV_WC_Plugin_Exception $e) { // don't mark the order as failed, Subscriptions will handle marking the renewal order as failed $order->add_order_note(sprintf(_x('%s Renewal Payment Failed (%s)', $this->get_gateway()->get_text_domain()), $this->get_gateway()->get_method_title(), $e->getMessage())); // update subscription WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order, $product_id); } }
function paymill_webhooks() { global $wpdb; // is there a webhook from Paymill? if (class_exists('WC_Subscriptions_Manager')) { // grab data from webhook $body = @file_get_contents('php://input'); $event_json = json_decode($body, true); // retrieve sub ID if (isset($event_json['event']['event_resource']['id']) && strlen($event_json['event']['event_resource']['id']) > 0) { $paymill_sub_id = $event_json['event']['event_resource']['id']; } elseif (isset($event_json['event']['event_resource']['subscription']['id']) && strlen($event_json['event']['event_resource']['subscription']['id']) > 0) { $paymill_sub_id = $event_json['event']['event_resource']['subscription']['id']; } error_log("\n\n########################################################################################################################\n\n", 3, PAYMILL_DIR . 'lib/debug/PHP_errors.log'); error_log(date(DATE_RFC822) . ' - Webhook ' . $event_json['event']['event_type'] . ' (Resource-ID: ' . $paymill_sub_id . ') triggered - start processing' . "\n\n", 3, PAYMILL_DIR . 'lib/debug/PHP_errors.log'); /* output example: array(1) { ["event"]=> array(4) { ["event_type"]=> string(20) "subscription.deleted" ["event_resource"]=> array(13) { ["id"]=> string(24) "sub_b71adbf5....." ["offer"]=> array(10) { ["id"]=> string(26) "offer_8083a5b....." ["name"]=> string(39) "woo_91_73da6....." ["amount"]=> int(100) ["currency"]=> string(3) "EUR" ["interval"]=> string(5) "1 DAY" ["trial_period_days"]=> int(0) ["created_at"]=> int(1389547028) ["updated_at"]=> int(1389547028) ["subscription_count"]=> array(2) { ["active"]=> string(1) "1" ["inactive"]=> string(1) "1" } ["app_id"]=> NULL } ["livemode"]=> bool(false) ["cancel_at_period_end"]=> bool(false) ["trial_start"]=> NULL ["trial_end"]=> NULL ["next_capture_at"]=> int(1389836717) ["created_at"]=> int(1389663382) ["updated_at"]=> int(1389750317) ["canceled_at"]=> NULL ["app_id"]=> NULL ["payment"]=> array(12) { ["id"]=> string(28) "pay_4e3759f....." ["type"]=> string(10) "creditcard" ["client"]=> string(27) "client_dbe164....." ["card_type"]=> string(4) "visa" ["country"]=> NULL ["expire_month"]=> string(2) "12" ["expire_year"]=> string(4) "2020" ["card_holder"]=> string(13) "dfgdfgdfgdfgd" ["last4"]=> string(4) "1111" ["created_at"]=> int(1389663369) ["updated_at"]=> int(1389663380) ["app_id"]=> NULL } ["client"]=> array(8) { ["id"]=> string(27) "client_dbe164....." ["email"]=> string(22) "*****@*****.**" ["description"]=> string(15) "Matthias Reuter" ["created_at"]=> int(1389547027) ["updated_at"]=> int(1389547027) ["app_id"]=> NULL ["payment"]=> array(2) { [0]=> array(12) { ["id"]=> string(28) "pay_1a5ff8....." ["type"]=> string(10) "creditcard" ["client"]=> string(27) "client_dbe16....." ["card_type"]=> string(4) "visa" ["country"]=> NULL ["expire_month"]=> string(2) "12" ["expire_year"]=> string(4) "2020" ["card_holder"]=> string(10) "dfgdfgdfgd" ["last4"]=> string(4) "1111" ["created_at"]=> int(1389547027) ["updated_at"]=> int(1389547028) ["app_id"]=> NULL } [1]=> array(12) { ["id"]=> string(28) "pay_4e375....." ["type"]=> string(10) "creditcard" ["client"]=> string(27) "client_dbe164....." ["card_type"]=> string(4) "visa" ["country"]=> NULL ["expire_month"]=> string(2) "12" ["expire_year"]=> string(4) "2020" ["card_holder"]=> string(13) "dfgdfgdfgdfgd" ["last4"]=> string(4) "1111" ["created_at"]=> int(1389663369) ["updated_at"]=> int(1389663380) ["app_id"]=> NULL } } ["subscription"]=> array(2) { [0]=> string(24) "sub_fcc4....." [1]=> string(24) "sub_b71a....." } } } ["created_at"]=> int(1389816435) ["app_id"]=> NULL } } */ //error_log(var_export($event_json,true)."\n\n", 3, PAYMILL_DIR.'lib/debug/PHP_errors.log'); // get subscription info, if available if (isset($paymill_sub_id) && strlen($paymill_sub_id) > 0) { $sql = $wpdb->prepare('SELECT * FROM ' . $wpdb->prefix . 'paymill_subscriptions WHERE paymill_sub_id=%s', array($paymill_sub_id)); $sub_cache = $wpdb->get_results($sql, ARRAY_A); $sub_cache = $sub_cache[0]; /* output example: SELECT * FROM wp_paymill_subscriptions WHERE paymill_sub_id="sub_b71adbf5e097bbe5ba80" */ error_log("\n\n" . $sql . "\n\n", 3, PAYMILL_DIR . 'lib/debug/PHP_errors.log'); /* output example: 1 30 */ //error_log($sub_cache['woo_user_id']."\n\n".$sub_cache['woo_offer_id']."\n\n", 3, PAYMILL_DIR.'lib/debug/PHP_errors.log'); $subscription = WC_Subscriptions_Manager::get_subscription($sub_cache['woo_offer_id']); // update subscriptions when webhook is triggered if (isset($sub_cache['woo_offer_id']) && strlen($sub_cache['woo_offer_id']) > 0) { // subscription successfully created if ($event_json['event']['event_type'] == 'subscription.created') { } // tell WooCommerce when payment for subscription is successfully processed if ($event_json['event']['event_type'] == 'subscription.succeeded') { /* example data WC_Subscriptions_Manager::get_subscription: array(15) { ["order_id"]=> string(3) "201" ["product_id"]=> string(2) "91" ["variation_id"]=> string(0) "" ["status"]=> string(6) "active" ["period"]=> string(3) "day" ["interval"]=> string(1) "1" ["length"]=> string(2) "12" ["start_date"]=> string(19) "2014-01-12 17:17:10" ["expiry_date"]=> string(19) "2014-01-24 17:17:10" ["end_date"]=> string(1) "0" ["trial_expiry_date"]=> string(1) "0" ["failed_payments"]=> string(1) "0" ["completed_payments"]=> array(1) { [0]=> string(19) "2014-01-12 17:17:10" } ["suspension_count"]=> string(1) "0" ["last_payment_date"]=> string(19) "2014-01-12 17:17:10" } */ error_log(var_export($subscription, true) . "\n\n", 3, PAYMILL_DIR . 'lib/debug/PHP_errors.log'); // prevent multiple subscription renewals because of multiple webhook attempts. $whole_period = 0; switch ($subscription['period']) { case 'day': default: $whole_period = intval($subscription['interval']) * 86400; break; case 'week': $whole_period = intval($subscription['interval']) * 604800; break; case 'month': $whole_period = intval($subscription['interval']) * 2160000; // using 25 days to prevent problems with shorter months break; case 'year': $whole_period = intval($subscription['interval']) * 30240000; // using 350 days to prevent any timezone problems whatsoever break; } if (count($subscription['completed_payments']) >= 1) { if (strtotime(date(DATE_RFC822)) > strtotime($subscription['last_payment_date']) + $whole_period - 18000) { // minus 5 hours to prevent any problems with pending triggers $order = new WC_Order($subscription['order_id']); //WC_Subscriptions_Manager::process_subscription_payments_on_order($order, $subscription['product_id']); WC_Subscriptions_Manager::process_subscription_payments_on_order($order); } } else { $order = new WC_Order($subscription['order_id']); $order->payment_complete(); WC_Subscriptions_Manager::activate_subscriptions_for_order($subscription['order_id']); } WC_Subscriptions_Manager::set_next_payment_date($sub_cache['woo_offer_id'], $order->customer_user); } // cancel subscription, as it was deleted through Paymill dashboard if ($event_json['event']['event_type'] == 'subscription.deleted') { $sql = $wpdb->prepare('DELETE FROM ' . $wpdb->prefix . 'paymill_subscriptions WHERE woo_user_id=%s AND woo_offer_id=%s', array($sub_cache['woo_user_id'], $sub_cache['woo_offer_id'])); $wpdb->query($sql); error_log("\n\n" . $sql . "\n\n", 3, PAYMILL_DIR . 'lib/debug/PHP_errors.log'); //WC_Subscriptions_Manager::cancel_subscriptions_for_order( $order ); WC_Subscriptions_Manager::cancel_subscription($sub_cache['woo_user_id'], $sub_cache['woo_offer_id']); } // tell WC that payment failure occured if ($event_json['event']['event_type'] == 'subscription.failed') { WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($subscription['order_id'], $subscription['product_id']); } } } error_log(date(DATE_RFC822) . ' - Webhook ' . $event_json['event']['event_type'] . ' finished - end processing' . "\n\n", 3, PAYMILL_DIR . 'lib/debug/PHP_errors.log'); error_log("\n\n########################################################################################################################\n\n", 3, PAYMILL_DIR . 'lib/debug/PHP_errors.log'); } }
/** * 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; }
/** * Process subscription renewal * * @since 2.0 * @param float $amount_to_charge subscription amount to charge, could include multiple renewals if they've previously failed and the admin has enabled it * @param WC_Order $order original order containing the subscription * @throws Exception invalid or missing token */ public function process_subscription_renewal_payment($amount_to_charge, WC_Order $order) { try { // set order defaults $order = $this->get_order($order->id); // set the amount to charge $order->amazon_order_total = $amount_to_charge; // add a timestamp to the order ID so Amazon doesn't consider it a duplicate of the original payment request $order->amazon_caller_reference .= '-' . time(); // token is required if (!isset($order->wc_amazon_fps_token_id)) { throw new Exception(__('invalid or missing token', WC_Amazon_FPS::TEXT_DOMAIN)); } parent::process_transaction($order); } catch (Exception $e) { WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order); $order->add_order_note(__('Amazon Subscription Renewal Transaction Failed: ' . $e->getMessage())); } }
/** * Process subscription renewal * * @since 1.4 * @param float $amount_to_charge subscription amount to charge, could include * multiple renewals if they've previously failed and the admin * has enabled it * @param WC_Order $order original order containing the subscription * @param int $product_id the ID of the subscription product */ public function process_renewal_payment($amount_to_charge, $order, $product_id = null) { require_once 'class-wc-realex-api.php'; $realex_subscription_count = 0; if (is_numeric($order->realex_subscription_count) && $order->realex_subscription_count) { $realex_subscription_count = $order->realex_subscription_count; } // increment the subscription count so we don't get order number clashes $realex_subscription_count++; update_post_meta($order->id, '_realex_subscription_count', $realex_subscription_count); // set custom class member used by the realex gateway $order->payment_total = SV_WC_Helper::number_format($amount_to_charge); // zero-dollar subscription renewal. weird, but apparently it happens -- only applicable to Subs 1.5.x if (!SV_WC_Plugin_Compatibility::is_wc_subscriptions_version_gte_2_0()) { if (0 == $order->payment_total) { // add order note $order->add_order_note(sprintf(__('%s0 Subscription Renewal Approved', 'woocommerce-gateway-realex'), get_woocommerce_currency_symbol())); // update subscription WC_Subscriptions_Manager::process_subscription_payments_on_order($order, $product_id); return; } } // This order is missing a tokenized card, lets see whether there's one available for the customer if (!get_post_meta($order->id, '_realex_cardref', true)) { $credit_cards = get_user_meta($order->get_user_id(), 'woocommerce_realex_cc', true); if (is_array($credit_cards)) { $card_ref = (object) current($credit_cards); $card_ref = $card_ref->ref; update_post_meta($order->id, '_realex_cardref', $card_ref); if (SV_WC_Plugin_Compatibility::is_wc_subscriptions_version_gte_2_0()) { foreach (wcs_get_subscriptions_for_renewal_order($order) as $subscription) { update_post_meta($subscription->id, '_realex_cardref', $card_ref); } } } } // create the realex api client $realex_client = new Realex_API($this->get_endpoint_url(), $this->get_realvault_endpoint_url(), $this->get_shared_secret()); // create the customer/cc tokens, and authorize the initial payment amount, if any $response = $this->authorize($realex_client, $order); if ($response && '00' == $response->result) { // add order note $order->add_order_note(sprintf(__('Credit Card Subscription Renewal Payment Approved (Payment Reference: %s) ', 'woocommerce-gateway-realex'), $response->pasref)); // update subscription if (SV_WC_Plugin_Compatibility::is_wc_subscriptions_version_gte_2_0()) { $order->payment_complete((string) $response->pasref); } else { WC_Subscriptions_Manager::process_subscription_payments_on_order($order, $product_id); } } else { // generate the result message $message = __('Credit Card Subscription Renewal Payment Failed', 'woocommerce-gateway-realex'); /* translators: Placeholders: %1$s - result, %2$s - result message */ if ($response) { $message .= sprintf(__(' (Result: %1$s - "%2$s").', 'woocommerce-gateway-realex'), $response->result, $response->message); } $order->add_order_note($message); // update subscription if (!SV_WC_Plugin_Compatibility::is_wc_subscriptions_version_gte_2_0()) { WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order, $product_id); } } }
/** * Scheduled subscription payment. * * @since 2.0 **/ function scheduled_subscription_payment($amount_to_charge, $order) { // Check if order was created using this method if ($this->id == get_post_meta($order->id, '_payment_method', true)) { // Prevent hook from firing twice if (!get_post_meta($order->id, '_schedule_klarna_subscription_payment', true)) { $result = $this->process_subscription_payment($amount_to_charge, $order); if (false == $result) { WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order); } else { WC_Subscriptions_Manager::process_subscription_payments_on_order($order); $order->payment_complete(); // Need to mark new order complete, so Subscription is marked as Active again } add_post_meta($order->id, '_schedule_klarna_subscription_payment', 'no', true); } else { delete_post_meta($order->id, '_schedule_klarna_subscription_payment', 'no'); } } }
/** * Process subscription renewal * * @since 2.0 * @param float $amount_to_charge subscription amount to charge, could include multiple renewals if they've previously failed and the admin has enabled it * @param \WC_Order $order original order containing the subscription * @param int $product_id the ID of the subscription product * @throws WC_Gateway_Braintree_Exception * @throws Exception */ public function process_subscription_renewal_payment($amount_to_charge, $order, $product_id) { try { // set order defaults $order = $this->get_order($order->id); // set the amount to charge $order->braintree_order['amount'] = $amount_to_charge; // set credit card token $order->braintree_order['paymentMethodToken'] = get_post_meta($order->id, '_wc_braintree_cc_token', true); // required if (!$order->braintree_order['customerId'] || !$order->braintree_order['paymentMethodToken']) { throw new Exception(__('Subscription Renewal: Customer ID or Credit Card Token is missing.', WC_Braintree::TEXT_DOMAIN)); } $response = Braintree_Transaction::sale($order->braintree_order); // check for success if ($response->success) { // add order note $order->add_order_note(sprintf(__('Braintree Subscription Renewal Payment Approved (Transaction ID: %s) ', WC_Braintree::TEXT_DOMAIN), $response->transaction->id)); // update subscription WC_Subscriptions_Manager::process_subscription_payments_on_order($order, $product_id); } else { // failure throw new WC_Gateway_Braintree_Exception('transaction', $response); } } catch (WC_Gateway_Braintree_Exception $e) { // mark order as failed, which adds an order note for the admin and displays a generic "payment error" to the customer $this->mark_order_as_failed($order, $e->getMessage()); // add detailed debugging information $this->add_debug_message($e->getErrors()); // update subscription WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order, $product_id); } catch (Braintree_Exception_Authorization $e) { $this->mark_order_as_failed($order, __('Authorization failed, ensure that your API key is correct and has permissions to create transactions.', WC_Braintree::TEXT_DOMAIN)); // update subscription WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order, $product_id); } catch (Exception $e) { $this->mark_order_as_failed($order, sprintf(__('%s - Error Type %s', WC_Braintree::TEXT_DOMAIN), $e->getMessage(), get_class($e))); // update subscription WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order, $product_id); } }
/** * @param float $amount * @param WC_Order $order * @param int $product_id * @return bool|WP_Error */ public function scheduled_subscription_payment($amount, $order, $product_id) { global $x3m_account_funds; $order_items = $order->get_items(); $product = $order->get_product_from_item(array_shift($order_items)); $subscription_name = sprintf(__('Subscription for "%s"', 'wc_account_funds'), $product->get_title()) . ' ' . sprintf(__('(Order %s)', 'wc_account_funds'), $order->get_order_number()); $user_id = get_post_meta($order->id, '_customer_user', true); $error = false; if (!$user_id) { WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order, $product_id); return new WP_Error('accountfunds', __('Customer not found', 'wc_account_funds')); } $funds = $x3m_account_funds->get_account_funds($user_id, false); if ($amount > $funds) { WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order, $product_id); return new WP_Error('accountfunds', __('Insufficient funds', 'wc_account_funds')); } $funds -= $amount; update_user_meta($user_id, 'account_funds', $funds); $order->add_order_note(__('Account Funds subscription payment completed', 'wc_account_funds')); WC_Subscriptions_Manager::process_subscription_payments_on_order($order); return true; }
/** * Process a payment for an ongoing subscription. */ function process_scheduled_subscription_payment($amount_to_charge, $order, $product_id) { $user = new WP_User($order->user_id); $this->check_payment_method_conversion($user->user_login, $user->ID); $customer_vault_ids = get_user_meta($user->ID, 'customer_vault_ids', true); $payment_method_number = get_post_meta($order->id, 'payment_method_number', true); $inspire_request = array('username' => $this->username, 'password' => $this->password, 'amount' => $amount_to_charge, 'type' => $this->salemethod, 'billing_method' => 'recurring'); $id = $customer_vault_ids[$payment_method_number]; if (substr($id, 0, 1) !== '_') { $inspire_request['customer_vault_id'] = $id; } else { $inspire_request['customer_vault_id'] = $user->user_login; $inspire_request['billing_id'] = substr($id, 1); $inspire_request['ver'] = 2; } $response = $this->post_and_get_response($inspire_request); if ($response['response'] == 1) { // Success $order->add_order_note(__('Inspire Commerce scheduled subscription payment completed. Transaction ID: ', 'woocommerce') . $response['transactionid']); WC_Subscriptions_Manager::process_subscription_payments_on_order($order); } else { if ($response['response'] == 2) { // Decline $order->add_order_note(__('Inspire Commerce scheduled subscription payment failed. Payment declined.', 'woocommerce')); WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order); } else { if ($response['response'] == 3) { // Other transaction error $order->add_order_note(__('Inspire Commerce scheduled subscription payment failed. Error: ', 'woocommerce') . $response['responsetext']); WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order); } else { // No response or unexpected response $order->add_order_note(__('Inspire Commerce scheduled subscription payment failed. Couldn\'t connect to gateway server.', 'woocommerce')); } } } }
/** * Process a scheduled payment * * @access public * @param float $amount_to_charge * @param WC_Order $order * @param int $product_id * @return void */ public function scheduled_subscription_payment($amount_to_charge, $order, $product_id) { $this->order = $order; $charge = $this->process_subscription_payment($amount_to_charge); if ($charge) { WC_Subscriptions_Manager::process_subscription_payments_on_order($order); } else { WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order, $product_id); } }
/** * scheduled_subscription_payment function. * * @param $amount_to_charge float The amount to charge. * @param $order WC_Order The WC_Order object of the order which the subscription was purchased in. * @param $product_id int The ID of the subscription product for which this payment relates. * * @access public * @return void * * @since 0.6.0 */ public function scheduled_subscription_payment($amount_to_charge, $order, $product_id) { // Process the payment $result = $this->process_subscription_payment($order, $amount_to_charge); // If the process results in error, then marked order as failed. If not, continue subscription if (is_wp_error($result)) { WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order, $product_id); } else { WC_Subscriptions_Manager::process_subscription_payments_on_order($order); } }
/** * 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; }
function scheduled_subscription_payment($amount_to_charge, $order, $product_id) { require_once epay_LIB . 'class.epaysoap.php'; try { $subscriptionid = get_post_meta($order->id, 'Subscription ID', true); $webservice = new epaysoap($this->remotepassword, true); $authorize = $webservice->authorize($this->merchant, $subscriptionid, date("dmY") . $order->id, $amount_to_charge * 100, $this->get_iso_code(get_woocommerce_currency()), (bool) $this->yesnotoint($this->instantcapture), $this->group, $this->authmail); if (!is_wp_error($authorize)) { if ($authorize) { WC_Subscriptions_Manager::process_subscription_payments_on_order($order); } } else { foreach ($authorize->get_error_messages() as $error) { throw new Exception($error->get_error_message()); } } } catch (Exception $error) { WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order, $product_id); } }
/** * 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)); } }