PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.6
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.6
1.6.6 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 All 49 releases
← All changes | app/Services/Payments/PaymentHelper.php +66 -12 1.3.21 → 1.6.6 View file →
@@ -4,8 +4,9 @@
4 4
5 5 use FluentCart\Api\StoreSettings;
6 6 use FluentCart\App\Helpers\Helper;
7 7 use FluentCart\App\Helpers\Status;
8 +use FluentCart\App\Models\OrderTransaction;
8 9 use FluentCart\App\Modules\PaymentMethods\Core\GatewayManager;
9 10 use FluentCart\App\Services\URL;
10 11 use FluentCart\Framework\Support\Arr;
11 12
@@ -19,18 +20,34 @@
19 20 }
20 21
21 22 public function listenerUrl($args = [])
22 23 {
23 - $listener = '?fct_payment_listener=1&method=' . $this->slug;
24 - $data = apply_filters_deprecated('fluent_cart_ipn_url_' . $this->slug, [
25 - ['listener_url' => site_url($listener)]
26 - ], '1.3.16', 'fluent_cart/ipn_url_' . $this->slug, 'Use fluent_cart/ipn_url_' . $this->slug . ' instead of fluent_cart_ipn_url_' . $this->slug . '.');
24 + // Must match the WebRoutes dispatch contract: it reads $_REQUEST['fluent-cart']
25 + // as the routed page and fires do_action('fluent_cart_action_' . $page), and
26 + // GlobalPaymentHandler listens on 'fluent_cart_action_fct_payment_listener_ipn'.
27 + $listener = '/?fluent-cart=fct_payment_listener_ipn&method=' . $this->slug;
28 + $data = ['listener_url' => site_url($listener)];
27 29
28 30 return apply_filters('fluent_cart/ipn_url_' . $this->slug, $data);
29 31 }
30 32
31 - public function successUrl($uuid, $args = null)
33 + /**
34 + * Build the filterable post-payment success URL.
35 + *
36 + * @param OrderTransaction|string $transaction Transaction model or its uuid
37 + * @param array|null $args Extra query args merged into the URL
38 + * @return string
39 + */
40 + public function successUrl($transaction, $args = null)
32 41 {
42 + if ($transaction instanceof OrderTransaction) {
43 + $transactionModel = $transaction;
44 + $uuid = $transaction->uuid;
45 + } else {
46 + $uuid = (string)$transaction;
47 + $transactionModel = OrderTransaction::query()->where('uuid', $uuid)->first();
48 + }
49 +
33 50 $queryArgs = array_merge(
34 51 array(
35 52 'method' => $this->slug,
36 53 'trx_hash' => $uuid,
@@ -47,13 +64,13 @@
47 64
48 65 $context = [
49 66 'transaction_hash' => $uuid,
50 67 'args' => $args,
51 - 'payment_method' => $this->slug ?? ''
68 + 'payment_method' => $this->slug ?? '',
69 + 'transaction' => $transactionModel,
70 + 'order' => $transactionModel ? $transactionModel->order : null
52 71 ];
53 - $url = apply_filters_deprecated('fluentcart/payment/success_url', [add_query_arg($queryArgs, $receiptUrl), $context], '1.3.16', 'fluent_cart/payment/success_url', 'Use fluent_cart/payment/success_url instead of fluentcart/payment/success_url.');
54 -
55 - return apply_filters('fluent_cart/payment/success_url', $url, $context);
72 + return apply_filters('fluent_cart/payment/success_url', add_query_arg($queryArgs, $receiptUrl), $context);
56 73 }
57 74
58 75 public static function getCustomPaymentLink($orderHash): string
59 76 {
@@ -70,10 +87,12 @@
70 87 public static function validateAndGetPayMethod($cartCheckoutHelper, $orderData, $extraCharge = 0)
71 88 {
72 89 $paymentMethod = Arr::get($orderData, 'others._fct_pay_method');
73 90 $isZeroPayment = $cartCheckoutHelper->getItemsAmountTotal(false, false) + $extraCharge <= 0;
91 + $zeroMethodForced = false;
74 92 if ($isZeroPayment && $cartCheckoutHelper->getCart()->getEstimatedRecurringTotal() <= 0) {
75 93 $paymentMethod = apply_filters('fluent_cart/default_payment_method_for_zero_payment', 'offline_payment', []);
94 + $zeroMethodForced = true;
76 95 }
77 96
78 97 if (!GatewayManager::has($paymentMethod)) {
79 98 wp_send_json([
@@ -100,8 +119,33 @@
100 119 ], 423
101 120 );
102 121 }
103 122
123 + // The checkout gateway-visibility filters (manual-subscription admission,
124 + // store-managed capability gate, renewal pre-due-date block, reactivation
125 + // restriction) only hide gateways in the rendered UI. The POSTed method must
126 + // pass the same gate, or a crafted request can start/convert a subscription
127 + // through a gateway the store never offered. Only the forced-offline zero
128 + // checkout (no recurring — the UI renders no method picker and the method is
129 + // overridden above) is exempt; a zero-payable SUBSCRIPTION cart (free trial)
130 + // keeps the customer-picked gateway and must pass the gate like any other.
131 + $cart = $cartCheckoutHelper->getCart();
132 + if ($cart && !$zeroMethodForced) {
133 + $allowedGateways = apply_filters('fluent_cart/checkout_active_payment_methods', [$gateway], [
134 + 'cart' => $cart
135 + ]);
136 +
137 + if (!in_array($gateway, (array) $allowedGateways, true)) {
138 + wp_send_json(
139 + [
140 + 'status' => 'failed',
141 + 'message' => __('This payment method is not available for this order. Please choose another payment method!', 'fluent-cart'),
142 + 'data' => []
143 + ], 423
144 + );
145 + }
146 + }
147 +
104 148 return $paymentMethod;
105 149 }
106 150
107 151 public static function updateTransactionRefundedTotal($parentTransaction, $refundedAmount)
@@ -221,14 +265,20 @@
221 265 return $billingInfo;
222 266 }
223 267
224 268
269 + /**
270 + * Days in one billing cycle. Returns 0 when the interval cannot be resolved —
271 + * neither a core interval nor one a fluent_cart/subscription_interval_in_days
272 + * callback resolved to a positive day count. Callers must treat < 1 as
273 + * unresolvable, never as a one-day cycle.
274 + */
225 275 public static function getIntervalDays($interval = ''): int
226 276 {
227 277 if ($interval === 'yearly') {
228 278 $days = 365;
229 279 } elseif ($interval === 'monthly') {
230 - $days = 30;
280 + $days = (int) gmdate('t'); // exact days in current month (handles leap year) to avoid false remaining days calculation
231 281 } elseif ($interval === 'weekly') {
232 282 $days = 7;
233 283 } elseif ($interval === 'quarterly') {
234 284 $days = 90;
@@ -233,14 +283,18 @@
233 283 } elseif ($interval === 'quarterly') {
234 284 $days = 90;
235 285 } elseif ($interval === 'half_yearly') {
236 286 $days = 182;
287 + } elseif ($interval === 'daily') {
288 + $days = 1;
237 289 } else {
238 - $days = 1;
290 + $days = 0;
239 291 }
240 292
241 - return apply_filters('fluent_cart/subscription_interval_in_days', $days, [
293 + $days = (int) apply_filters('fluent_cart/subscription_interval_in_days', $days, [
242 294 'interval' => $interval
243 295 ]);
296 +
297 + return $days > 0 ? $days : 0;
244 298 }
245 299
246 300 }