makeRequest('billing/subscriptions/' . $vendorSubscriptionId . '/suspend', 'v1', 'POST', [ 'reason' => $reason ]); } catch (\Exception $e) { throw new \Exception(esc_html($e->getMessage())); } $subscription = Subscription::query()->where('id', $subscription->id)->first(); if ($subscription) { $subscription->status = Status::SUBSCRIPTION_PAUSED; $subscription->save(); } wp_send_json(array( 'message' => __('Subscription has been paused successfully', 'fluent-cart') ), 200); } /** * @throws \Exception */ public function resumeSubscription($data, $order, $subscription) { if (!current_user_can('manage_options')) { throw new \Exception(esc_html__('Sorry, You do not have permission to resume subscription!', 'fluent-cart')); } $vendorSubscriptionId = Arr::get($data, 'vendor_subscription_id'); $reason = Arr::get($data, 'reason'); if (!$vendorSubscriptionId || !$subscription) { throw new \Exception(esc_html__('Sorry, Subscription not found!', 'fluent-cart')); } $response = (new API())->makeRequest('billing/subscriptions/' . $vendorSubscriptionId . '/activate', 'v1', 'POST', [ 'reason' => $reason ?? 'Customer requested' ]); if (is_wp_error($response)) { throw new \Exception(esc_html($response->get_error_message())); } $subscription = Subscription::query()->where('id', $subscription->id)->first(); if ($subscription) { $subscription->status = Status::SUBSCRIPTION_ACTIVE; $subscription->save(); } wp_send_json(array( 'message' => __('Subscription has been resumed successfully', 'fluent-cart') ), 200); } /** * @throws \Exception */ public function getOrCreateNewPlan($subscriptionId, $reason) { if (!$subscriptionId) { wp_send_json([ 'message' => __('Sorry, Subscription ID is not available!', 'fluent-cart'), ], 423); } $subscriptionModel = Subscription::query()->where('id', $subscriptionId)->first(); $order = Order::query()->where('id', $subscriptionModel->parent_order_id)->with('order_items')->first(); if (!$subscriptionModel || !$order) { wp_send_json([ 'message' => __('Sorry, Subscription or Order not found!', 'fluent-cart'), ], 423); } // get the variation from variation id $variation = ProductVariation::query()->findOrFail($subscriptionModel->variation_id); $processedSubscriptionItem = $this->getSubscriptionItemForUpdate($subscriptionModel, $variation); $data = wp_parse_args($processedSubscriptionItem, [ 'order_id' => $subscriptionModel->parent_order_id, 'product_id' => $subscriptionModel->product_id, 'variation_id' => $subscriptionModel->variation_id, 'currency' => $subscriptionModel->order->currency, 'interval_count' => 1, 'signup_fee' => 0, // default setup fee in cents ($0.00) ]); $plan = PayPalHelper::getPayPalPlan($data); if (is_wp_error($plan)) { wp_send_json([ 'status' => 'error', 'message' => $plan->get_error_message(), ], 422); } wp_send_json([ 'status' => 'success', 'message' => 'Plan created successfully', 'plan' => $plan, ], 200); } public function confirmSubscriptionSwitch($data, $subscriptionId) { $newVendorSubscriptionId = Arr::get($data, 'newVendorSubscriptionId'); $vendorOrderId = Arr::get($data, 'vendorOrderId'); $subscription = Subscription::query()->where('id', $subscriptionId)->first(); $order = Order::query()->where('id', $subscription->parent_order_id)->first(); if (!$newVendorSubscriptionId || !$vendorOrderId) { wp_send_json([ 'status' => 'error', 'message' => __('Sorry, New Subscription ID or Vendor Order ID is not available!', 'fluent-cart'), ], 422); } // get the subscription from paypal $paypalSubscription = (new API())->verifySubscription($newVendorSubscriptionId); if (is_wp_error($paypalSubscription)) { // log that subscription was created but not found in paypal or connecting issue fluent_cart_error_log( __('Subscription was created but not found in paypal or connecting issue', 'fluent-cart'), $paypalSubscription->get_error_message(), [ 'module_name' => 'Subscription', 'module_id' => $subscription->id, 'log_type' => 'api' ] ); wp_send_json([ 'status' => 'error', 'message' => $paypalSubscription->get_error_message(), ], 422); } $oldPaymentMethod = $subscription->current_payment_method; $oldVendorSubscriptionId = $subscription->vendor_subscription_id; $oldVendorCustomerId = $subscription->vendor_customer_id; $oldVendorPlanId = $subscription->vendor_plan_id; $vendorSubscriptionId = Arr::get($paypalSubscription, 'id'); $vendorPlanId = Arr::get($paypalSubscription, 'plan_id'); $vendorCustomerId = Arr::get($paypalSubscription, 'subscriber.payer_id'); $nextBillingDate = Arr::get($paypalSubscription, 'billing_info.next_billing_time') ?? null; if (!empty($nextBillingDate)) { $nextBillingDate = gmdate('Y-m-d H:i:s', strtotime($nextBillingDate)); } $config = $subscription->config ?: []; // update subscription in table $data = array_filter([ 'vendor_subscription_id' => $vendorSubscriptionId, 'vendor_plan_id' => $vendorPlanId, 'current_payment_method' => 'paypal', 'vendor_customer_id' => $vendorCustomerId, 'next_billing_date' => $nextBillingDate, 'status' => $this->getCorrectSubscriptionStatus(Arr::get($paypalSubscription, 'status')), 'config' => array_merge($config, ['is_trial_days_simulated' => 'yes']) ]); Subscription::query()->where('id', $subscriptionId)->update($data); $billingInfo = PaymentHelper::parsePaymentMethodDetails('paypal', [ 'email' => Arr::get($paypalSubscription, 'subscriber.email_address'), 'payer_id' => Arr::get($paypalSubscription, 'subscriber.payer_id'), 'name' => Arr::get($paypalSubscription, 'subscriber.name.given_name') . ' ' . Arr::get($paypalSubscription, 'subscriber.name.surname'), 'address' => Arr::get($paypalSubscription, 'subscriber.shipping_address.address') ]); // update subscription meta for active payment method $subscription->updateMeta('active_payment_method', $billingInfo); $gateway = App::gateway($oldPaymentMethod); if ($gateway && $gateway->subscriptions) { $gateway->subscriptions->cancel( $oldVendorSubscriptionId, [ 'reason' => __('Subscription switched to PayPal', 'fluent-cart'), 'mode' => $order->mode ] ); } // add or update subscription meta of old subscriptions $paymentSource = SubscriptionMeta::query() ->where('subscription_id', $subscription->id) ->where('meta_key', 'active_payment_method') ->first(); if ($paymentSource) { $paymentSource = $paymentSource->meta_value; } $oldSubData = [ 'payment_method' => $oldPaymentMethod, 'vendor_subscription_id' => $oldVendorSubscriptionId, 'vendor_customer_id' => $oldVendorCustomerId, 'vendor_plan_id' => $oldVendorPlanId, 'reason' => 'switch_payment_method', 'payment_source' => $paymentSource ?? '', 'canceled_at' => DateTime::gmtNow(), ]; self::addOldSubscriptionMeta($subscription, $oldSubData); wp_send_json([ 'status' => 'success', 'message' => __('Subscription updated successfully', 'fluent-cart'), 'data' => $vendorSubscriptionId ], 200); } /** * Confirm subscription reactivation * * @param array $data | newVendorSubscriptionId (string) required * @param int $subscriptionId * @return void */ public static function addOldSubscriptionMeta($subscription, $oldSubData) { $defaults = [ 'payment_method' => '', 'vendor_subscription_id' => '', 'vendor_customer_id' => '', 'vendor_plan_id' => '', 'bill_count' => 0, 'payment_source' => '', 'canceled_at' => null, 'reason' => '', 'expire_at' => null, ]; $oldSubscription = array_merge($defaults, $oldSubData); // get if exists $oldSubscriptions = SubscriptionMeta::query() ->where('subscription_id', '=', $subscription->id) ->where('meta_key', '=', 'old_subscriptions') ->first(); if ($oldSubscriptions && $oldSubscriptions->meta_value) { if (is_string($oldSubscriptions->meta_value)) { $oldSubscriptions = $oldSubscriptions->meta_value; } $oldSubscriptions = (array)$oldSubscriptions ?: []; $oldSubscriptions[] = $oldSubscription; } else { $oldSubscriptions = [$oldSubscription]; } SubscriptionMeta::updateOrCreate([ 'subscription_id' => $subscription->id, //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key 'meta_key' => 'old_subscriptions' ], [ //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value 'meta_value' => $oldSubscriptions ]); } public function getSubscriptionItemForUpdate($subscriptionModel, $variation) { $trialDays = 0; // trial days is the difference between the next billing date and the current date in days $nextBillingDate = $subscriptionModel->next_billing_date; $nextBillingTimestamp = strtotime($nextBillingDate); if ($nextBillingTimestamp && $nextBillingTimestamp > time()) { $trialDays = ceil(($nextBillingTimestamp - time()) / 86400); } $billCount = OrderTransaction::query()->where('subscription_id', $subscriptionModel->id) ->where('transaction_type', Status::TRANSACTION_TYPE_CHARGE) ->where('status', Status::TRANSACTION_SUCCEEDED) ->count(); $trialDays = SubscriptionHelper::checkTrailDaysLoopHole($subscriptionModel, $trialDays); // max trial days is 365 if ($trialDays > 365) { $trialDays = 365; } $billTimes = Arr::get($subscriptionModel, 'bill_times', 0); if ($billTimes && $billCount) { $billTimes = $billTimes - $billCount; } else { $billTimes = 0; } $expireAt = null; // expire at is the end date of the subscription, if bill times is 0 then it is null if ($billTimes) { $expireAt = SubscriptionHelper::getSubscriptionCancelAtTimeStamp($trialDays, $billTimes, $subscriptionModel->billing_interval); } $processedSubscriptionItem = [ 'billing_interval' => Arr::get($subscriptionModel, 'billing_interval'), 'recurring_amount' => Arr::get($subscriptionModel, 'recurring_total'), 'line_total' => intval(Arr::get($subscriptionModel, 'recurring_total')), 'id' => Arr::get($subscriptionModel, 'variation_id'), 'trial_days' => $trialDays, 'product_id' => Arr::get($subscriptionModel, 'product_id'), 'parent_order_id' => Arr::get($subscriptionModel, 'parent_order_id'), 'item_name' => Arr::get($subscriptionModel, 'item_name'), 'expire_at' => $expireAt, 'bill_times' => $billTimes, ]; if ($trialDays > 0) { $processedSubscriptionItem['trial_end'] = $nextBillingTimestamp; } return $processedSubscriptionItem; } public function getCorrectSubscriptionStatus($status): string { $status = strtolower($status); if ('active' == $status) { $status = Status::SUBSCRIPTION_ACTIVE; } else if ('trialing' == $status) { $status = Status::SUBSCRIPTION_TRIALING; } else if ('cancelled' == $status || 'canceled' == $status) { $status = Status::SUBSCRIPTION_CANCELED; } else if ('expired' == $status) { $status = Status::SUBSCRIPTION_EXPIRED; } else if ('paused' == $status) { $status = Status::SUBSCRIPTION_PAUSED; } else if ('expiring' == $status) { $status = Status::SUBSCRIPTION_EXPIRING; } else if ('suspended' == $status) { $status = Status::SUBSCRIPTION_PAUSED; } return $status; } public function sendError($message, $code = 422): void { wp_send_json([ 'status' => 'failed', 'message' => $message ], $code); } }