PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.1
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.1
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 / StripeSubscriptions.php

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

203 lines 8.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\Modules\PaymentMethods\StripeGateway;
4
5 use FluentCart\App\Helpers\Status;
6 use FluentCart\App\Helpers\StatusHelper;
7 use FluentCart\App\Helpers\CurrenciesHelper;
8 use FluentCart\App\Models\OrderTransaction;
9 use FluentCart\App\Models\Subscription;
10 use FluentCart\App\Modules\PaymentMethods\Core\AbstractSubscriptionModule;
11 use FluentCart\App\Modules\PaymentMethods\StripeGateway\API\API;
12 use FluentCart\App\Modules\Subscriptions\Services\SubscriptionService;
13 use FluentCart\App\Services\DateTime\DateTime;
14 use FluentCart\Framework\Support\Arr;
15
16 class StripeSubscriptions extends AbstractSubscriptionModule
17 {
18
19 public function reSyncSubscriptionFromRemote(Subscription $subscriptionModel)
20 {
21 if ($subscriptionModel->current_payment_method !== 'stripe') {
22 return new \WP_Error('invalid_payment_method', __('This subscription is not using Stripe as payment method.', 'fluent-cart'));
23 }
24
25 $order = $subscriptionModel->order;
26
27 $vendorSubscriptionId = $subscriptionModel->vendor_subscription_id;
28 if (!$vendorSubscriptionId) {
29 return new \WP_Error('invalid_subscription', __('Invalid vendor subscription ID.', 'fluent-cart'));
30 }
31
32 $stripeSubscription = (new API())->getStripeObject('subscriptions/' . $vendorSubscriptionId, [
33 'expand' => ['latest_invoice']
34 ], $order->mode);
35
36 if (is_wp_error($stripeSubscription)) {
37 return $stripeSubscription;
38 }
39
40 $invoices = (new API())->getStripeObject('invoices', [
41 'subscription' => $vendorSubscriptionId,
42 'status' => 'paid'
43 ], $order->mode);
44
45 if (is_wp_error($invoices)) {
46 return $invoices;
47 }
48
49 $invoices = Arr::get($invoices, 'data', []);
50
51 $subscriptionUpdateData = StripeHelper::getSubscriptionUpdateData($stripeSubscription, $subscriptionModel);
52
53 // reverse the array to get the latest transaction last
54 $invoices = array_reverse($invoices);
55 $newPayment = false;
56
57 foreach ($invoices as $key => $invoice) {
58 //$invoice is array
59 if (Arr::get($invoice, 'amount_paid') == 0) {
60 continue;
61 }
62
63 $transaction = OrderTransaction::query()
64 ->whereIn('vendor_charge_id', [
65 Arr::get($invoice, 'payment_intent'),
66 Arr::get($invoice, 'charge')
67 ])
68 ->where('transaction_type', 'charge')
69 ->first();
70
71 if (!$transaction) {
72 // check local transactions missing vendor_charge_id
73 $transaction = OrderTransaction::query()
74 ->select(['id', 'order_id'])
75 ->where('subscription_id', $subscriptionModel->id)
76 ->where('vendor_charge_id', '')
77 ->where('transaction_type', 'charge')
78 ->where('total', (int)Arr::get($invoice, 'amount_paid'))
79 ->first();
80
81 if ($transaction) {
82 $transaction->update([
83 'vendor_charge_id' => Arr::get($invoice, 'payment_intent')
84 ]);
85 continue;
86 }
87
88 $amountPaid = Arr::get($invoice, 'amount_paid');
89 $chargeCurrency = Arr::get($invoice, 'currency');
90
91 if ($chargeCurrency && CurrenciesHelper::isZeroDecimal($chargeCurrency)) {
92 $amountPaid = $amountPaid * 100;
93 }
94
95 $transactionData = [
96 'payment_method' => 'stripe',
97 'total' => (int)$amountPaid,
98 'vendor_charge_id' => Arr::get($invoice, 'payment_intent'),
99 'created_at' => ($paidAt = Arr::get($invoice, 'status_transitions.paid_at')) ? DateTime::anyTimeToGmt($paidAt)->format('Y-m-d H:i:s') : DateTime::now()->format('Y-m-d H:i:s'),
100 ];
101
102 $paymentIntent = (new API())->getStripeObject('payment_intents/' . Arr::get($invoice, 'payment_intent'), ['expand' => ['latest_charge']], $order->mode);
103
104 if (!is_wp_error($paymentIntent) && Arr::get($paymentIntent, 'latest_charge')) {
105 $transactionData['created_at'] = DateTime::anyTimeToGmt(Arr::get($paymentIntent, 'latest_charge.created'))->format('Y-m-d H:i:s');
106 $paymentMethodType = Arr::get($paymentIntent, 'latest_charge.payment_method_details.type', '');
107 $transactionData['payment_method_type'] = $paymentMethodType;
108 if ($paymentMethodType === 'sepa_debit') {
109 $transactionData['card_last_4'] = Arr::get($paymentIntent, 'latest_charge.payment_method_details.sepa_debit.last4', '');
110 $transactionData['card_brand'] = 'sepa_debit';
111 } else {
112 $transactionData['card_last_4'] = Arr::get($paymentIntent, 'latest_charge.payment_method_details.card.last4', '');
113 $transactionData['card_brand'] = Arr::get($paymentIntent, 'latest_charge.payment_method_details.card.brand', '');
114 }
115 } else {
116 $activePaymentMethod = $subscriptionModel->getMeta('active_payment_method', []);
117 if (!$activePaymentMethod || !is_array($activePaymentMethod)) {
118 $activePaymentMethod = [];
119 }
120 if ($activePaymentMethod) {
121 $transactionData['card_last_4'] = Arr::get($activePaymentMethod, 'details.last_4');
122 $transactionData['card_brand'] = Arr::get($activePaymentMethod, 'details.brand');
123 }
124 }
125
126 $newPayment = true;
127 SubscriptionService::recordRenewalPayment($transactionData, $subscriptionModel, $subscriptionUpdateData);
128 } else {
129 $transaction->update([
130 'vendor_charge_id' => Arr::get($invoice, 'payment_intent'),
131 'status' => Status::TRANSACTION_SUCCEEDED,
132 'total' => (int)Arr::get($invoice, 'amount_paid')
133 ]);
134
135 (new StatusHelper($transaction->order))->syncOrderStatuses($transaction);
136 }
137 }
138
139 if (!$newPayment) {
140 $subscriptionModel = SubscriptionService::syncSubscriptionStates($subscriptionModel, $subscriptionUpdateData);
141 } else {
142 $subscriptionModel = Subscription::find($subscriptionModel->id);
143 }
144
145 if ($subscriptionModel->status == Status::SUBSCRIPTION_COMPLETED && $stripeSubscription['status'] === 'active') {
146 $response = (new API)->deleteStripeObject('subscriptions/' . $vendorSubscriptionId, [], $order->mode);
147
148 if (is_wp_error($response)) {
149 fluent_cart_error_log('Stripe Subscription Deletion Error. Subscription ID: ' . $subscriptionModel->id, $response->get_error_message());
150 }
151 }
152
153 return $subscriptionModel;
154 }
155
156 public function cancel($vendorSubscriptionId, $args = [])
157 {
158 if (!$vendorSubscriptionId) {
159 return new \WP_Error('invalid_subscription', __('Invalid vendor subscription ID.', 'fluent-cart'));
160 }
161
162 // first check if the subscription is already canceled in Stripe
163 $response = (new API())->getStripeObject('subscriptions/' . $vendorSubscriptionId, [], Arr::get($args, 'mode', 'live'));
164
165 if (is_wp_error($response)) {
166 return $response;
167 }
168
169 $status = StripeHelper::transformSubscriptionStatus($response);
170
171 if ($status == Status::SUBSCRIPTION_CANCELED) {
172 $canceledAt = Arr::get($response, 'canceled_at');
173 return [
174 'status' => Status::SUBSCRIPTION_CANCELED,
175 'canceled_at' => $canceledAt ? gmdate('Y-m-d H:i:s', $canceledAt) : NULL
176 ];
177 }
178
179 $response = (new API())->deleteStripeObject('subscriptions/' . $vendorSubscriptionId, [], Arr::get($args, 'mode', 'live'));
180
181 if (is_wp_error($response)) {
182 return $response;
183 }
184
185 $canceledAt = Arr::get($response, 'canceled_at');
186
187 return [
188 'status' => StripeHelper::transformSubscriptionStatus($response),
189 'canceled_at' => $canceledAt ? gmdate('Y-m-d H:i:s', $canceledAt) : NULL
190 ];
191 }
192
193 public function cardUpdate($data, $subscriptionId)
194 {
195 (new UpdateCustomerPaymentMethod())->update($data, $subscriptionId);
196 }
197
198 public function switchPaymentMethod($data, $subscriptionId)
199 {
200 (new SwitchCustomerMethod())->switchPayMethod($data, $subscriptionId);
201 }
202 }
203