PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.2
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.2
1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 trunk All 48 releases
fluent-cart / app / Modules / PaymentMethods / StripeGateway / Plan.php

Plan.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.6.2, at app/Modules/PaymentMethods/StripeGateway/Plan.php

215 lines 7.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Modules\PaymentMethods\StripeGateway;
4
5 use FluentCart\App\Helpers\CurrenciesHelper;
6 use FluentCart\App\Helpers\Helper;
7 use FluentCart\App\Models\Product;
8 use FluentCart\App\Models\ProductVariation;
9 use FluentCart\App\Modules\PaymentMethods\StripeGateway\API\API;
10 use FluentCart\App\Services\ProductItemService;
11 use FluentCart\Framework\Support\Arr;
12
13 class Plan
14 {
15
16 /**
17 * Get or create a Stripe pricing plan for a product variation.
18 *
19 * @param array $data {
20 * @type string $product_id Product ID.
21 * @type string $variation_id Variation ID.
22 * @type string $trial_days Trial Days.
23 * @type string $billing_interval Billing interval (e.g., 'month', 'year').
24 * @type string $currency Currency code (e.g., 'usd').
25 * @type int $interval_count Number of intervals.
26 * @type int $recurring_total Recurring total amount (in cents).
27 * }
28 *
29 * @return \WP_Error|array
30 */
31 public static function getStripePricing($data = [])
32 {
33 $item = ProductItemService::getItem($data);
34 $product = $item->product;
35 $variation = $item->variation;
36
37 if (!$variation || !$product) {
38 return new \WP_Error('invalid_product', esc_html__('Invalid product or variation.', 'fluent-cart'));
39 }
40
41 $originalInterval = Arr::get($data, 'billing_interval');
42
43 // Determine interval_count based on billing_interval
44 $intervalCount = 1;
45 if ($originalInterval === 'quarterly') {
46 $intervalCount = 3;
47 } elseif ($originalInterval === 'half_yearly') {
48 $intervalCount = 6;
49 }
50
51 $interval = self::convertFctIntervalToStripeInterval($originalInterval);
52
53 $billingPeriod = [
54 'interval_unit' => $interval,
55 'interval_frequency' => $intervalCount,
56 ];
57
58 $billingPeriod = apply_filters('fluent_cart/subscription_billing_period', $billingPeriod, [
59 'subscription_interval' => $originalInterval,
60 'payment_method' => 'stripe',
61 ]);
62
63 $sitePrefix = Helper::getSitePrefix();
64 $pricingId = 'fct_' . $sitePrefix . '_price_' . $data['variation_id'] . '_' . $data['recurring_total'] . '_' . $data['billing_interval'] . '_' . $intervalCount . '_' . $data['trial_days'] . '_' . $data['currency'];
65 $productId = 'fct_' . $sitePrefix . '_product_' . $data['product_id'];
66
67 // Let's see if we have the price already created
68 $pricePlan = (new API())->getStripeObject('plans/' . $pricingId);
69
70 if (!is_wp_error($pricePlan)) {
71 return $pricePlan;
72 }
73
74 // We don't have this price yet. Now we have to create the price from subscription
75 $stripeProduct = self::retriveOrCreateProduct([
76 'id' => $productId,
77 'name' => $product->post_title,
78 'metadata' => [
79 'product_id' => $product->ID,
80 'provider' => 'fluent-cart',
81 ]
82 ]);
83
84 if (is_wp_error($stripeProduct)) {
85 return $stripeProduct;
86 }
87
88 // let's create the price now
89 $currency = Arr::get($data, 'currency');
90 $recurringAmount = (int)Arr::get($data, 'recurring_total', 0);
91
92 if (CurrenciesHelper::isZeroDecimal($currency)) {
93 $recurringAmount = (int)($recurringAmount / 100);
94 }
95
96 $priceData = [
97 'id' => $pricingId,
98 'product' => $stripeProduct['id'],
99 'currency' => $currency,
100 'amount' => $recurringAmount,
101 'trial_period_days' => Arr::get($data, 'trial_days', 0),
102 'interval' => Arr::get($billingPeriod, 'interval_unit'),
103 'interval_count' => Arr::get($billingPeriod, 'interval_frequency'),
104 'metadata' => [
105 'fct_product_id' => $data['product_id'],
106 'fct_variation_id' => $data['variation_id'],
107 'provider' => 'fluent-cart'
108 ]
109 ];
110
111
112 return (new API())->createStripeObject('plans', $priceData);
113 }
114
115 /**
116 * Get or create a Stripe pricing plan for a product variation.
117 *
118 * @param array $data {
119 * @type string $product_id Product ID.\
120 * @type string $currency Currency code (e.g., 'usd').
121 * @type int $amount Amount in cents.
122 * }
123 *
124 * @return \WP_Error|array
125 */
126 public static function getOneTimeAddonPrice($data = [])
127 {
128 $item = ProductItemService::getItem($data);
129 $product = $item->product;
130
131 if (!$product) {
132 return new \WP_Error('invalid_product', esc_html__('Invalid product.', 'fluent-cart'));
133 }
134
135 $sitePrefix = Helper::getSitePrefix();
136 $productId = 'fct_' . $sitePrefix . '_product_' . $data['product_id'];
137
138
139 $priceId = 'fct_' . $data['amount'] . '_' . $data['currency'];
140
141 $currency = Arr::get($data, 'currency');
142 $unitAmount = (int)Arr::get($data, 'amount', 0);
143
144 if (CurrenciesHelper::isZeroDecimal($currency)) {
145 $unitAmount = (int)($unitAmount / 100);
146 }
147
148 $priceData = [
149 'unit_amount' => $unitAmount,
150 'currency' => $currency,
151 'product' => $productId,
152 'metadata' => [
153 'fct_product_id' => $data['product_id'],
154 'price_id' => $priceId,
155 'provider' => 'fluent-cart'
156 ]
157 ];
158
159 // Let's see if we have the price already created
160 $pricePlans = (new API())->getStripeObject('prices/search', [
161 'query' => 'active:"true" AND metadata["price_id"]:"' . $priceId . '" AND product:"' . $productId . '"'
162 ]);
163
164 if (!is_wp_error($pricePlans) && !empty($pricePlans['data'])) {
165 return $pricePlans['data'][0];
166 }
167
168 $newlyCreated = (new API())->createStripeObject('prices', $priceData);
169
170 if (!is_wp_error($newlyCreated)) {
171 return $newlyCreated;
172 }
173
174 // This is a fallback in case the price creation fails
175
176 // Let's created a price without anything!
177 $priceData['product_data'] = [
178 'name' => 'Addon Item'
179 ];
180
181 unset($pricePlans['product']);
182
183 return (new API())->createStripeObject('prices', $priceData);
184 }
185
186 public static function retriveOrCreateProduct($productData = [])
187 {
188 $existingProduct = (new API())->getStripeObject('products/' . $productData['id']);
189 if (!is_wp_error($existingProduct)) {
190 return $existingProduct;
191 }
192
193 return (new API())->createStripeObject('products', $productData);
194 }
195
196 public static function convertFctIntervalToStripeInterval($billingInterval)
197 {
198 $monthlyIntervals = ['quarterly', 'half_yearly', 'monthly'];
199 // Quarterly uses month with interval_count=3
200 // Half-yearly uses month with interval_count=6
201
202 if ($billingInterval === 'daily') {
203 $billingInterval = 'day';
204 } elseif (in_array($billingInterval, $monthlyIntervals)) {
205 $billingInterval = 'month';
206 } elseif ($billingInterval === 'yearly') {
207 $billingInterval = 'year';
208 } elseif ($billingInterval === 'weekly') {
209 $billingInterval = 'week';
210 }
211
212 return $billingInterval;
213 }
214 }
215