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 / Services / Payments / PaymentHelper.php

PaymentHelper.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.6.2, at app/Services/Payments/PaymentHelper.php

288 lines 11.7 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\Services\Payments;
4
5 use FluentCart\Api\StoreSettings;
6 use FluentCart\App\Helpers\Helper;
7 use FluentCart\App\Helpers\Status;
8 use FluentCart\App\Models\OrderTransaction;
9 use FluentCart\App\Modules\PaymentMethods\Core\GatewayManager;
10 use FluentCart\App\Services\URL;
11 use FluentCart\Framework\Support\Arr;
12
13 class PaymentHelper
14 {
15 public string $slug = '';
16
17 public function __construct($slug)
18 {
19 $this->slug = $slug;
20 }
21
22 public function listenerUrl($args = [])
23 {
24 $listener = '/?fct_payment_listener=1&method=' . $this->slug;
25 $data = ['listener_url' => site_url($listener)];
26
27 return apply_filters('fluent_cart/ipn_url_' . $this->slug, $data);
28 }
29
30 /**
31 * Build the filterable post-payment success URL.
32 *
33 * @param OrderTransaction|string $transaction Transaction model or its uuid
34 * @param array|null $args Extra query args merged into the URL
35 * @return string
36 */
37 public function successUrl($transaction, $args = null)
38 {
39 if ($transaction instanceof OrderTransaction) {
40 $transactionModel = $transaction;
41 $uuid = $transaction->uuid;
42 } else {
43 $uuid = (string)$transaction;
44 $transactionModel = OrderTransaction::query()->where('uuid', $uuid)->first();
45 }
46
47 $queryArgs = array_merge(
48 array(
49 'method' => $this->slug,
50 'trx_hash' => $uuid,
51 'fct_redirect' => 'yes'
52 ),
53 is_array($args) ? $args : []
54 );
55
56 $receiptUrl = (new StoreSettings())->getReceiptPage();
57
58 if (empty($receiptUrl)) {
59 $receiptUrl = site_url();
60 }
61
62 $context = [
63 'transaction_hash' => $uuid,
64 'args' => $args,
65 'payment_method' => $this->slug ?? '',
66 'transaction' => $transactionModel,
67 'order' => $transactionModel ? $transactionModel->order : null
68 ];
69 return apply_filters('fluent_cart/payment/success_url', add_query_arg($queryArgs, $receiptUrl), $context);
70 }
71
72 public static function getCustomPaymentLink($orderHash): string
73 {
74 return wp_sanitize_redirect(
75 URL::appendQueryParams(home_url('/?fluent-cart=custom_checkout'), [
76 'order_hash' => $orderHash,
77 ]));
78 }
79
80 /*
81 *This will be hooked from basePaymentMethod
82 * validate payment method is active for checkout items, before order creation
83 */
84 public static function validateAndGetPayMethod($cartCheckoutHelper, $orderData, $extraCharge = 0)
85 {
86 $paymentMethod = Arr::get($orderData, 'others._fct_pay_method');
87 $isZeroPayment = $cartCheckoutHelper->getItemsAmountTotal(false, false) + $extraCharge <= 0;
88 $zeroMethodForced = false;
89 if ($isZeroPayment && $cartCheckoutHelper->getCart()->getEstimatedRecurringTotal() <= 0) {
90 $paymentMethod = apply_filters('fluent_cart/default_payment_method_for_zero_payment', 'offline_payment', []);
91 $zeroMethodForced = true;
92 }
93
94 if (!GatewayManager::has($paymentMethod)) {
95 wp_send_json([
96 'status' => 'failed',
97 'message' => __('No valid payment method found!', 'fluent-cart'),
98 'data' => []
99 ], 423
100 );
101 };
102
103 $gateway = GatewayManager::getInstance($paymentMethod);
104 $status = $gateway->validatePaymentMethod([
105 'isValid' => false,
106 'reason' => __('No payment method found!', 'fluent-cart'),
107 'isZeroPayment' => $isZeroPayment
108 ]);
109
110 if (!Arr::get($status, 'isValid')) {
111 wp_send_json(
112 [
113 'status' => 'failed',
114 'message' => Arr::get($status, 'reason'),
115 'data' => []
116 ], 423
117 );
118 }
119
120 // The checkout gateway-visibility filters (manual-subscription admission,
121 // store-managed capability gate, renewal pre-due-date block, reactivation
122 // restriction) only hide gateways in the rendered UI. The POSTed method must
123 // pass the same gate, or a crafted request can start/convert a subscription
124 // through a gateway the store never offered. Only the forced-offline zero
125 // checkout (no recurring — the UI renders no method picker and the method is
126 // overridden above) is exempt; a zero-payable SUBSCRIPTION cart (free trial)
127 // keeps the customer-picked gateway and must pass the gate like any other.
128 $cart = $cartCheckoutHelper->getCart();
129 if ($cart && !$zeroMethodForced) {
130 $allowedGateways = apply_filters('fluent_cart/checkout_active_payment_methods', [$gateway], [
131 'cart' => $cart
132 ]);
133
134 if (!in_array($gateway, (array) $allowedGateways, true)) {
135 wp_send_json(
136 [
137 'status' => 'failed',
138 'message' => __('This payment method is not available for this order. Please choose another payment method!', 'fluent-cart'),
139 'data' => []
140 ], 423
141 );
142 }
143 }
144
145 return $paymentMethod;
146 }
147
148 public static function updateTransactionRefundedTotal($parentTransaction, $refundedAmount)
149 {
150 // update the transaction. only update refunded_total, in meta, if exist add
151 $meta = $parentTransaction->meta;
152 $alreadyRefunded = Arr::get($meta, 'refunded_total', 0);
153 $meta['refunded_total'] = $alreadyRefunded + $refundedAmount;
154 $parentTransaction->meta = $meta;
155 $parentTransaction->save();
156 }
157
158 // public static function updateOrderRefundedTotal($order, $refundedAmount, &$type): void
159 // {
160 // // update order data
161 // $netOrderPaidAmount = $order->total_paid - $order->total_refunded;
162 // $isFullRefund = $refundedAmount == $netOrderPaidAmount;
163 //
164 // if ($isFullRefund) {
165 // $order->payment_status = Status::PAYMENT_REFUNDED;
166 // $type = 'full';
167 // } else {
168 // $order->payment_status = Status::PAYMENT_PARTIALLY_REFUNDED;
169 // $type = 'partial';
170 // }
171 // $order->total_refund += $refundedAmount;
172 // $order->save();
173 // }
174
175 /**
176 * @param string $paymentMethod
177 * @param array $paymentMethodDetails
178 * @param array $additionalData
179 * @return array
180 * parse payment method details and return a common format
181 * filter hook: 'fluent_cart/payments/parse_payment_method_details'
182 */
183 public static function parsePaymentMethodDetails($paymentGateway, $paymentMethodDetails, $additionalData = []): array
184 {
185 $billingInfo = [
186 'method' => $paymentGateway,
187 'type' => null,
188 'details' => [],
189 'billing_details' => [
190 'name' => null,
191 'email' => null,
192 'phone' => null,
193 'address' => [
194 'country' => null,
195 'postal_code' => null,
196 'line1' => null,
197 'line2' => null,
198 'city' => null,
199 'state' => null,
200 ]
201 ]
202 ];
203
204 if ($paymentGateway === 'stripe') {
205 $type = Arr::get($paymentMethodDetails, 'type', 'card');
206 $billingInfo['type'] = $type;
207
208 if ($type === 'card') {
209 $billingInfo['details'] = [
210 'type' => 'card',
211 'brand' => sanitize_text_field(Arr::get($paymentMethodDetails, 'card.brand')),
212 'last_4' => sanitize_text_field(Arr::get($paymentMethodDetails, 'card.last4')),
213 'exp_month' => sanitize_text_field(Arr::get($paymentMethodDetails, 'card.exp_month')),
214 'exp_year' => sanitize_text_field(Arr::get($paymentMethodDetails, 'card.exp_year')),
215 'fingerprint' => sanitize_text_field(Arr::get($paymentMethodDetails, 'card.fingerprint')),
216 'payment_method_id' => sanitize_text_field(Arr::get($paymentMethodDetails, 'id'))
217 ];
218 } else {
219 // For other Stripe payment methods (ACH, SEPA, etc.)
220 $billingInfo['details'] = [
221 'payment_method_id' => sanitize_text_field(Arr::get($paymentMethodDetails, 'id')),
222 'type' => Arr::get($paymentMethodDetails, $type, [])
223 ];
224 }
225
226 // Common billing details for Stripe
227 $billingInfo['billing_details'] = [
228 'name' => sanitize_text_field(Arr::get($paymentMethodDetails, 'billing_details.name')),
229 'email' => sanitize_email(Arr::get($paymentMethodDetails, 'billing_details.email', '')),
230 'phone' => sanitize_text_field(Arr::get($paymentMethodDetails, 'billing_details.phone')),
231 'address' => [
232 'country' => sanitize_text_field(Arr::get($paymentMethodDetails, 'billing_details.address.country')),
233 'postal_code' => sanitize_text_field(Arr::get($paymentMethodDetails, 'billing_details.address.postal_code')),
234 'line1' => sanitize_text_field(Arr::get($paymentMethodDetails, 'billing_details.address.line1')),
235 'line2' => sanitize_text_field(Arr::get($paymentMethodDetails, 'billing_details.address.line2')),
236 'city' => sanitize_text_field(Arr::get($paymentMethodDetails, 'billing_details.address.city')),
237 'state' => sanitize_text_field(Arr::get($paymentMethodDetails, 'billing_details.address.state')),
238 ]
239 ];
240
241 } elseif ($paymentGateway === 'paypal') {
242 $billingInfo['type'] = 'standard';
243 $billingInfo['details'] = [
244 'email' => sanitize_email(Arr::get($paymentMethodDetails, 'email', '')),
245 'payer_id' => sanitize_text_field(Arr::get($paymentMethodDetails, 'payer_id')),
246 ];
247
248 // PayPal billing details
249 $billingInfo['billing_details'] = [
250 'name' => sanitize_text_field(Arr::get($paymentMethodDetails, 'name')),
251 'email' => sanitize_email(Arr::get($paymentMethodDetails, 'email', '')),
252 'phone' => sanitize_text_field(Arr::get($paymentMethodDetails, 'phone')),
253 'address' => [
254 'country' => sanitize_text_field(Arr::get($paymentMethodDetails, 'address.country_code')),
255 'postal_code' => sanitize_text_field(Arr::get($paymentMethodDetails, 'address.postal_code')),
256 'line1' => sanitize_text_field(Arr::get($paymentMethodDetails, 'address.address_line_1')),
257 'line2' => sanitize_text_field(Arr::get($paymentMethodDetails, 'address.address_line_2')),
258 ]
259 ];
260 }
261
262 return $billingInfo;
263 }
264
265
266 public static function getIntervalDays($interval = ''): int
267 {
268 if ($interval === 'yearly') {
269 $days = 365;
270 } elseif ($interval === 'monthly') {
271 $days = 30;
272 } elseif ($interval === 'weekly') {
273 $days = 7;
274 } elseif ($interval === 'quarterly') {
275 $days = 90;
276 } elseif ($interval === 'half_yearly') {
277 $days = 182;
278 } else {
279 $days = 1;
280 }
281
282 return apply_filters('fluent_cart/subscription_interval_in_days', $days, [
283 'interval' => $interval
284 ]);
285 }
286
287 }
288