PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.5
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.5
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/Helpers/StatusHelper.php +51 -4 1.6.1 → 1.6.5 View file →
@@ -8,8 +8,9 @@
8 8 use FluentCart\App\Models\Cart;
9 9 use FluentCart\App\Models\Order;
10 10 use FluentCart\App\Models\OrderTransaction;
11 11 use FluentCart\App\Models\Subscription;
12 +use FluentCart\App\Modules\PaymentMethods\Core\GatewayManager;
12 13 use FluentCart\App\Modules\Subscriptions\Services\SubscriptionService;
13 14 use FluentCart\App\Services\DateTime\DateTime;
14 15 use FluentCart\App\Services\Payments\PaymentHelper;
15 16 use FluentCart\Framework\Support\Arr;
@@ -86,8 +87,37 @@
86 87 'order' => $this->order,
87 88 ]);
88 89 }
89 90
91 + /**
92 + * Backfills payment_method_title when it was never stamped at creation.
93 + *
94 + * Only changeOrderStatus() (the COD-only path) writes payment_method_title.
95 + * Every other gateway settles via syncOrderStatuses(), which never touched
96 + * it, so the column stays empty for those orders.
97 + */
98 + protected function resolvePaymentMethodTitle()
99 + {
100 + $title = $this->order->payment_method_title;
101 + if ($title) {
102 + return $title;
103 + }
104 +
105 + $slug = $this->order->payment_method;
106 + if (!$slug || !class_exists(GatewayManager::class)) {
107 + return $title;
108 + }
109 +
110 + $gateway = GatewayManager::getInstance($slug);
111 + if (!$gateway || !method_exists($gateway, 'getMeta')) {
112 + return $title;
113 + }
114 +
115 + $resolvedTitle = (string) $gateway->getMeta('title');
116 +
117 + return $resolvedTitle !== '' ? $resolvedTitle : $title;
118 + }
119 +
90 120 public function updateTotalPaid($amount)
91 121 {
92 122 $this->order->total_paid = intval($amount) + intval($this->order->total_paid);
93 123 if ($this->order->total_paid >= $this->order->total_amount) {
@@ -150,11 +180,20 @@
150 180 ->sum('total');
151 181
152 182 $isFullyPaid = $this->order->total_amount <= ($transactionPaidTotal - $refundedTotal);
153 183
184 + // total_paid stays gross for a MoR order (cover invariant — see
185 + // Order::netAmount()); net it out here so a full refund of the actually
186 + // captured amount resolves to "refunded" instead of being stuck at
187 + // "partially_refunded" on every later idempotent resync (e.g. a Paddle webhook
188 + // replay for the already-succeeded transaction).
189 + $netPaidTotal = $this->order->netAmount($transactionPaidTotal);
190 +
154 191 $orderPaymentStatus = $this->order->payment_status;
155 192 if ($isFullyPaid) {
156 193 $orderPaymentStatus = Status::PAYMENT_PAID;
194 + } else if ($refundedTotal && $refundedTotal >= $netPaidTotal) {
195 + $orderPaymentStatus = Status::PAYMENT_REFUNDED;
157 196 } else if ($refundedTotal) {
158 197 $orderPaymentStatus = Status::PAYMENT_PARTIALLY_REFUNDED;
159 198 }
160 199
@@ -167,12 +206,19 @@
167 206
168 207 $oldOrderStatus = $this->order->status;
169 208 $oldPaymentStatus = $this->order->payment_status;
170 209
210 + $paymentMethodTitle = $this->resolvePaymentMethodTitle();
211 +
212 + if ($orderPaymentStatus === Status::PAYMENT_REFUNDED && !$this->order->refunded_at) {
213 + $this->order->refunded_at = DateTime::gmtNow();
214 + }
215 +
171 216 $this->order->status = $orderStatus;
172 217 $this->order->payment_status = $orderPaymentStatus;
173 218 $this->order->total_paid = $transactionPaidTotal;
174 219 $this->order->total_refund = $refundedTotal;
220 + $this->order->payment_method_title = $paymentMethodTitle;
175 221
176 222 // When transitioning to PAID, use an atomic UPDATE to prevent concurrent requests
177 223 // (e.g., payment gateway webhook + browser confirmation) from both processing
178 224 // the same payment — which would dispatch OrderPaid twice, generating duplicate
@@ -185,12 +231,13 @@
185 231 $q->whereNull('payment_status')
186 232 ->orWhere('payment_status', '!=', Status::PAYMENT_PAID);
187 233 })
188 234 ->update([
189 - 'status' => $orderStatus,
190 - 'payment_status' => $orderPaymentStatus,
191 - 'total_paid' => $transactionPaidTotal,
192 - 'total_refund' => $refundedTotal,
235 + 'status' => $orderStatus,
236 + 'payment_status' => $orderPaymentStatus,
237 + 'total_paid' => $transactionPaidTotal,
238 + 'total_refund' => $refundedTotal,
239 + 'payment_method_title' => $paymentMethodTitle,
193 240 ]);
194 241
195 242 if (!$claimed) {
196 243 // Another process already transitioned this order to paid