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 / StripeHelper.php

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

262 lines 9.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\Status;
7 use FluentCart\App\Models\Customer;
8 use FluentCart\App\Models\OrderTransaction;
9 use FluentCart\App\Models\Order;
10 use FluentCart\App\Modules\PaymentMethods\StripeGateway\API\API;
11 use FluentCart\App\Services\Payments\PaymentHelper;
12 use FluentCart\Api\StoreSettings;
13 use FluentCart\App\App;
14 use FluentCart\Framework\Support\Arr;
15
16 class StripeHelper
17 {
18 public static function createOrGetStripeCustomer(Customer $customer)
19 {
20 // check if we already have a stripe_customer_id for this person
21 $existingStripeCustomerId = $customer->getMeta('stripe_customer_id', false);
22 if ($existingStripeCustomerId) {
23 $existingStripeCustomer = (new API())->getStripeObject('customers/' . $existingStripeCustomerId);
24
25 if (!is_wp_error($existingStripeCustomer) && is_array($existingStripeCustomer) && !empty($existingStripeCustomer['id']) && isset($existingStripeCustomer['email']) && $existingStripeCustomer['email'] === $customer->email) {
26 return $existingStripeCustomer;
27 }
28 }
29
30 $customerInfo = array_filter([
31 'name' => $customer->full_name,
32 'email' => $customer->email,
33 'phone' => $customer->phone ?? '',
34 'address' => array_filter([
35 'city' => $customer->city ?? '',
36 'country' => $customer->country ?? '',
37 'postal_code' => $customer->postcode ?? '',
38 'state' => $customer->state ?? '',
39 ])
40 ]);
41
42 $newStripeCustomer = (new API())->createStripeObject('customers', $customerInfo);
43
44 if (is_wp_error($newStripeCustomer)) {
45 return $newStripeCustomer;
46 }
47
48 $id = Arr::get($newStripeCustomer, 'id', false);
49
50 if ($id) {
51 $customer->updateMeta('stripe_customer_id', $id);
52 }
53
54 return $newStripeCustomer;
55 }
56
57 public static function transformSubscriptionStatus($stripeSubscription, $subscriptionModel = null)
58 {
59 $status = strtolower($stripeSubscription['status']);
60
61 if ($status === 'active') {
62 $status = Status::SUBSCRIPTION_ACTIVE;
63 } else if ($status === 'incomplete' || $status === 'incomplete_expired') {
64 $status = Status::SUBSCRIPTION_INTENDED;
65 } else if ($status === 'trialing') {
66 $status = Status::SUBSCRIPTION_TRIALING;
67 } else if ($status === 'canceled') {
68 $status = Status::SUBSCRIPTION_CANCELED;
69 if (Arr::get($stripeSubscription, 'cancellation_details.reason', '') === 'payment_failed') {
70 $status = Status::SUBSCRIPTION_EXPIRED;
71 }
72 } else if ($status === 'unpaid') {
73 $status = Status::SUBSCRIPTION_EXPIRED;
74 } else if ($status === 'paused') {
75 $status = Status::SUBSCRIPTION_PAUSED;
76 } else if ($status === 'past_due') {
77 $status = Status::SUBSCRIPTION_EXPIRING;
78 if ($subscriptionModel && $subscriptionModel->status === 'expired') {
79 $status = Status::SUBSCRIPTION_EXPIRED;
80 }
81 }
82
83 return $status;
84 }
85
86 public static function getSubscriptionUpdateData($stripeSubscription, $subscriptionModel = null)
87 {
88 $stripeStatus = strtolower(Arr::get($stripeSubscription, 'status', ''));
89
90 $status = self::transformSubscriptionStatus($stripeSubscription, $subscriptionModel);
91
92 $amount = Arr::get($stripeSubscription, 'plan.amount', 0);
93 $currency = Arr::get($stripeSubscription, 'plan.currency', null);
94
95 if ($currency && CurrenciesHelper::isZeroDecimal($currency)) {
96 $amount = $amount * 100;
97 }
98
99 $subscriptionUpdateData = array_filter([
100 'current_payment_method' => 'stripe',
101 'status' => $status,
102 'recurring_total' => $amount,
103 ]);
104
105 if ($stripeStatus == Status::SUBSCRIPTION_CANCELED) {
106 $cancelledAt = (int)Arr::get($stripeSubscription, 'canceled_at');
107 if ($cancelledAt) {
108 $subscriptionUpdateData['canceled_at'] = gmdate('Y-m-d H:i:s', $cancelledAt);
109 }
110 }
111
112 $currentPeriodEnds = (int)Arr::get($stripeSubscription, 'current_period_end');
113 // we have to check if the last invoice is paid or not!
114 // If not paid, then we have to use the $stripeSubscription['current_period_start']
115 $latestInvoice = Arr::get($stripeSubscription, 'latest_invoice', null);
116 if ($latestInvoice && !empty($latestInvoice['id'])) {
117 // we have the latest invoice
118 if (Arr::get($latestInvoice, 'status') !== 'paid') {
119 $currentPeriodEnds = (int)Arr::get($stripeSubscription, 'current_period_start');
120 }
121 }
122
123 if ($currentPeriodEnds) {
124 $subscriptionUpdateData['next_billing_date'] = gmdate('Y-m-d H:i:s', $currentPeriodEnds);
125 }
126
127 return $subscriptionUpdateData;
128 }
129
130
131 public static function processRemoteRefund($transaction, $amount, $args)
132 {
133 $intentId = $transaction->vendor_charge_id;
134 if (!$intentId) {
135 return new \WP_Error('invalid_refund', __('Invalid transaction ID for refund.', 'fluent-cart'));
136 }
137
138 $refundAmount = (int)$amount;
139 $refundCurrency = $transaction->currency;
140
141 if ($refundCurrency && CurrenciesHelper::isZeroDecimal($refundCurrency)) {
142 $refundAmount = (int)($refundAmount / 100);
143 }
144
145 $refundData = [
146 'payment_intent' => $intentId,
147 'amount' => $refundAmount,
148 ];
149
150 $reason = Arr::get($args, 'reason', '');
151
152 if ($reason && in_array($reason, ['duplicate', 'fraudulent', 'requested_by_customer'])) {
153 $refundData['reason'] = $reason;
154 }
155
156 $refunded = (new API())->createStripeObject('refunds', $refundData, $transaction->payment_mode);
157
158 if (is_wp_error($refunded)) {
159 return $refunded;
160 }
161
162 $status = Arr::get($refunded, 'status');
163 $acceptedStatus = ['succeeded', 'pending'];
164 if (!in_array($status, $acceptedStatus)) {
165 return new \WP_Error('refund_failed', __('Refund could not be processed in stripe. Please check on your stripe account', 'fluent-cart'));
166 }
167
168 return Arr::get($refunded, 'id');
169 }
170
171 public static function createOrUpdateIpnRefund($refundData, $parentTransaction)
172 {
173 $allRefunds = OrderTransaction::query()
174 ->where('order_id', $refundData['order_id'])
175 ->where('transaction_type', Status::TRANSACTION_TYPE_REFUND)
176 ->orderBy('id', 'DESC')
177 ->get();
178
179 if ($allRefunds->isEmpty()) {
180 // this is the first refund for this order
181 return OrderTransaction::query()->create($refundData);
182 }
183
184 $currentRefundTransactionId = Arr::get($refundData, 'meta.parent_id', '');
185
186 $existingLocalRefund = null;
187 foreach ($allRefunds as $refund) {
188 if ($refund->vendor_charge_id == $refundData['vendor_charge_id']) {
189 if ($refund->total != $refundData['total']) {
190 $refund->fill($refundData);
191 $refund->save();
192 }
193 // this refund already exists
194 return $refund;
195 }
196
197 if (!$refund->vendor_charge_id) { // this is a local redfund without vendor charge id
198 $refundTransactionId = Arr::get($refund->meta, 'parent_id', '');
199 $isTransactionMatched = $refundTransactionId == $currentRefundTransactionId;
200
201 // this is a local refund without vendor charge id, we will update it
202 if ($refund->total == $refundData['total'] && $isTransactionMatched) {
203 // this refund already exists
204 $existingLocalRefund = $refund;
205 }
206 }
207 }
208
209 if ($existingLocalRefund) {
210 $existingLocalRefund->fill($refundData);
211 $existingLocalRefund->save();
212 return $existingLocalRefund;
213 }
214
215 $createdRefund = OrderTransaction::query()->create($refundData);
216
217 PaymentHelper::updateTransactionRefundedTotal($parentTransaction, $createdRefund->total);
218 return $createdRefund;
219 }
220
221 /*
222 * To validate by session, id
223 *
224 */
225 public static function validateBySession($id)
226 {
227 $apiKey = (new StripeSettingsBase())->getApiKey();
228
229 $session = (new API())->makeRequest('checkout/sessions/' . $id, [], $apiKey, 'GET');
230
231 if (!$session || is_wp_error($session)) {
232 return null;
233 }
234
235
236 $order = Order::query()
237 ->where('uuid', Arr::get($session, 'client_reference_id'))
238 ->first();
239
240 if (!$order) {
241 return null;
242 }
243
244 return $order;
245
246 }
247
248 public static function getCancelUrl(): string
249 {
250 $checkoutPage = (new StoreSettings())->getCheckoutPage();
251 // get cart hash from url
252 $cartHash = App::request()->get('fct_cart_hash', '');
253 if ($cartHash) {
254 return add_query_arg([
255 'fct_cart_hash' => $cartHash
256 ], $checkoutPage);
257 }
258 return $checkoutPage;
259 }
260
261 }
262