PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 6.2.13
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v6.2.13
6.2.14 6.2.13 6.2.12 6.2.10 6.2.11 6.2.9 6.2.8 6.2.7 6.2.6 6.2.5 6.2.4 6.2.3 6.2.2 3.6.22 3.6.31 3.6.40 3.6.41 3.6.42 3.6.50 3.6.51 3.6.60 3.6.61 3.6.62 3.6.64 3.6.65 All 196 releases
fluentform / app / Modules / Payments / PaymentMethods / Stripe / StripeInlineProcessor.php

StripeInlineProcessor.php in Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder 6.2.13, at app/Modules/Payments/PaymentMethods/Stripe/StripeInlineProcessor.php

662 lines 27.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentForm\App\Modules\Payments\PaymentMethods\Stripe;
4
5 use FluentForm\App\Helpers\Helper;
6 use FluentForm\App\Modules\Payments\PaymentHelper;
7 use FluentForm\Framework\Helpers\ArrayHelper;
8 use FluentForm\App\Modules\Payments\PaymentMethods\Stripe\API\SCA;
9 use FluentForm\App\Modules\Payments\PaymentMethods\Stripe\API\Plan;
10 use FluentForm\App\Modules\Payments\PaymentMethods\Stripe\API\Invoice;
11 use FluentForm\App\Modules\Payments\PaymentMethods\Stripe\API\Customer;
12
13 if (!defined('ABSPATH')) {
14 exit; // Exit if accessed directly.
15 }
16
17 class StripeInlineProcessor extends StripeProcessor
18 {
19
20 public function init()
21 {
22 /*
23 * After form submission this hooks fire to start Making payment
24 */
25 add_action('fluentform/process_payment_stripe_inline', [$this, 'handlePaymentAction'], 10, 6);
26
27 /*
28 * Mainly for single payment items
29 */
30 add_action('wp_ajax_fluentform_sca_inline_confirm_payment', [$this, 'confirmScaPayment']);
31 add_action('wp_ajax_nopriv_fluentform_sca_inline_confirm_payment', [$this, 'confirmScaPayment']);
32
33 /*
34 * For Subscription payment + maybe single payment items
35 */
36 add_action('wp_ajax_fluentform_sca_inline_confirm_payment_setup_intents', array($this, 'confirmScaSetupIntentsPayment'));
37 add_action('wp_ajax_nopriv_fluentform_sca_inline_confirm_payment_setup_intents', array($this, 'confirmScaSetupIntentsPayment'));
38 }
39
40 public function handlePaymentAction($submissionId, $submissionData, $form, $methodSettings, $hasSubscriptions, $totalPayable)
41 {
42 $this->setSubmissionId($submissionId);
43 $this->form = $form;
44 $submission = $this->getSubmission();
45 $paymentTotal = $this->getAmountTotal();
46
47 if (!$paymentTotal && !$hasSubscriptions) {
48 return false;
49 }
50
51 // Create the initial transaction here
52 $transaction = $this->createInitialPendingTransaction($submission, $hasSubscriptions);
53
54 $paymentMethodId = ArrayHelper::get($submissionData['response'], '__stripe_payment_method_id');
55 $customerArgs = $this->customerArguments($paymentMethodId, $submission);
56
57 $customer = Customer::createCustomer($customerArgs, $this->form->id);
58
59 if (is_wp_error($customer)) {
60 // We have errors
61 $this->handlePaymentChargeError($customer->get_error_message(), $submission, $transaction);
62 }
63
64 if ($transaction->transaction_type == 'subscription') {
65 $this->handleSetupIntent($submission, $paymentMethodId, $customer, $transaction, $totalPayable);
66 } else {
67 // Let's create the one time payment first
68 // We will handle One-Time Payment Here only
69 $paymentSettings = PaymentHelper::getFormSettings($form->id, 'admin');
70 $intentArgs = [
71 'payment_method' => $paymentMethodId,
72 'amount' => $transaction->payment_total,
73 'currency' => $transaction->currency,
74 'confirmation_method' => 'manual',
75 'confirm' => 'true',
76 'description' => $this->getProductNames(),
77 'statement_descriptor_suffix' => StripeSettings::getPaymentDescriptor($form),
78 'metadata' => $this->getIntentMetaData($submission, $form, $transaction, $paymentSettings),
79 'customer' => $customer->id,
80 ];
81
82 $intentArgs = apply_filters('fluentform/stripe_checkout_args_inline', $intentArgs, $submission, $transaction, $form);
83
84 // If FluentForm Pro is not installed, apply the fee 1.9% of the total amount
85 if (!Helper::hasPro()) {
86 $applicationFeeAmount = $this->calculateApplicationFeeAmount(
87 $totalPayable,
88 $transaction->currency
89 );
90 $intentArgs['application_fee_amount'] = $applicationFeeAmount;
91 }
92 $this->handlePaymentIntent($transaction, $submission, $intentArgs);
93 }
94 }
95
96 // This is only for Subscription Payment
97 protected function handleSetupIntent($submission, $paymentMethodId, $customer, $transaction, $totalPayable)
98 {
99 if (is_wp_error($customer)) {
100 $this->handlePaymentChargeError($customer->get_error_message(), $submission, $transaction, false, 'customer');
101 }
102
103 $subscriptions = $this->getSubscriptions();
104
105 $subscription = $subscriptions[0];
106
107 $subscriptionTransactionArgs = Plan::getPriceIdsFromSubscriptionTransaction($subscription, $transaction);
108
109 if (is_wp_error($subscriptionTransactionArgs)) {
110 $this->handlePaymentChargeError($customer->get_error_message(), $submission, $transaction, false, 'customer');
111 }
112
113 $subscriptionArgs = [
114 'customer' => $customer->id,
115 'metadata' => $this->getIntentMetaData($submission, $this->getForm(), $transaction),
116 'payment_behavior' => 'allow_incomplete',
117 ];
118
119 $subscriptionArgs['items'] = $subscriptionTransactionArgs['items'];
120
121 if ($signupFee = $subscriptionTransactionArgs['signup_fee']) {
122 Invoice::createItem([
123 'amount' => $signupFee,
124 'currency' => $submission->currency,
125 'customer' => $customer->id,
126 /* translators: %s is the plan name */
127 'description' => sprintf(__('Signup fee for %s', 'fluentform'), $subscription->plan_name),
128 ], $submission->form_id);
129 }
130
131 // Maybe we have to set a cancel_at parameter to subscription args
132 if ($cancelledAt = Plan::getCancelledAtTimestamp($subscription)) {
133 $subscriptionArgs['cancel_at'] = $cancelledAt;
134 }
135
136 if ($subscription->trial_days) {
137 $dateTime = current_datetime();
138 $localtime = $dateTime->getTimestamp() + $dateTime->getOffset();
139 $subscriptionArgs['trial_end'] = $localtime + $subscription->trial_days * 86400;
140 }
141
142 $subscriptionArgs = apply_filters('fluentform/stripe_subscription_args_inline', $subscriptionArgs, $submission, $transaction, $this->getForm());
143
144 // If FluentForm Pro is not installed, apply the fee 1.9%
145 if (!Helper::hasPro()) {
146 $subscriptionArgs['application_fee_percent'] = 1.9;
147 }
148
149 $subscriptionPayment = Plan::subscribe($subscriptionArgs, $submission->form_id);
150
151 if (is_wp_error($subscriptionPayment)) {
152 $this->handlePaymentChargeError($subscriptionPayment->get_error_message(), $submission, $transaction, false, 'subscription');
153 }
154
155 $invoice = Invoice::retrieve(
156 $subscriptionPayment->latest_invoice,
157 $this->form->id,
158 [
159 'expand' => ['payment_intent.charges'],
160 ]
161 );
162 if (is_wp_error($invoice)) {
163 $this->handlePaymentChargeError($invoice->get_error_message(), $submission, $transaction, false, 'invoice');
164 }
165
166 if (
167 $invoice->payment_intent &&
168 $invoice->payment_intent->status == 'requires_action' &&
169 $invoice->payment_intent->next_action->type == 'use_stripe_sdk'
170 ) {
171 $transactionId = false;
172 if ($transaction) {
173 $transactionId = $transaction->id;
174 }
175 $this->processScaBeforeVerification($submission->form_id, $submission->id, $transactionId, $invoice->payment_intent->id);
176
177 $nonceAction = 'fluentform_sca_confirm_' . $submission->id;
178 $nonce = wp_create_nonce($nonceAction);
179
180 wp_send_json_success([
181 'nextAction' => 'payment',
182 'actionName' => 'stripeSetupIntent',
183 'stripe_subscription_id' => $subscriptionPayment->id,
184 'payment_method_id' => $paymentMethodId,
185 'intent' => $invoice->payment_intent,
186 'submission_id' => $submission->id,
187 'customer_name' => ($transaction) ? $transaction->payer_name : '',
188 'customer_email' => ($transaction) ? $transaction->payer_email : '',
189 'client_secret' => $invoice->payment_intent->client_secret,
190 '_ff_stripe_nonce' => $nonce,
191 'message' => __('Verifying your card details. Please wait...', 'fluentform'),
192 'result' => [
193 'insert_id' => $submission->id,
194 ],
195 ], 200);
196 }
197
198 // now this payment is successful. We don't need anything else
199 $this->handlePaidSubscriptionInvoice($invoice, $submission);
200 }
201
202 protected function customerArguments($paymentMethodId, $submission)
203 {
204 $customerArgs = [
205 'payment_method' => $paymentMethodId,
206 'invoice_settings' => [
207 'default_payment_method' => $paymentMethodId,
208 ],
209 'metadata' => [
210 'submission_id' => $submission->id,
211 'form_id' => $submission->form_id,
212 'form_name' => wp_strip_all_tags($this->form->title),
213 ],
214 ];
215
216 $receiptEmail = PaymentHelper::getCustomerEmail($submission, $this->form);
217
218 if ($receiptEmail) {
219 $customerArgs['email'] = $receiptEmail;
220 }
221
222 $receiptName = PaymentHelper::getCustomerName($submission, $this->form);
223
224 if ($receiptName) {
225 $customerArgs['name'] = $receiptName;
226 $customerArgs['description'] = $receiptName;
227 }
228
229 $address = PaymentHelper::getCustomerAddress($submission);
230 if ($address) {
231 $customerArgs['address'] = [
232 'city' => ArrayHelper::get($address, 'city'),
233 'country' => ArrayHelper::get($address, 'country'),
234 'line1' => ArrayHelper::get($address, 'address_line_1'),
235 'line2' => ArrayHelper::get($address, 'address_line_2'),
236 'postal_code' => ArrayHelper::get($address, 'zip'),
237 'state' => ArrayHelper::get($address, 'state'),
238 ];
239 }
240
241 return $customerArgs;
242 }
243
244 protected function handlePaidSubscriptionInvoice($invoice, $submission)
245 {
246 if ($invoice->status !== 'paid') {
247 wp_send_json([
248 'errors' => __('Stripe Error: Payment Failed! Please try again.', 'fluentform'),
249 ], 423);
250 }
251
252 // Submission status as paid
253 $this->changeSubmissionPaymentStatus('paid');
254
255 $subscriptions = $this->getSubscriptions();
256
257 $this->processSubscriptionSuccess($subscriptions, $invoice, $submission);
258
259 $transaction = $this->getLastTransaction($submission->id);
260
261 $paymentStatus = $this->getIntentSuccessName($invoice->payment_intent);
262 $this->processOnetimeSuccess($invoice, $transaction, $paymentStatus);
263
264 $this->recalculatePaidTotal();
265
266 $this->sendSuccess($submission);
267 }
268
269 protected function handlePaymentIntent($transaction, $submission, $intentArgs)
270 {
271 $formSettings = PaymentHelper::getFormSettings($submission->form_id);
272
273 if (PaymentHelper::isZeroDecimal($transaction->currency)) {
274 $intentArgs['amount'] = intval($transaction->payment_total / 100);
275 }
276
277 $receiptEmail = PaymentHelper::getCustomerEmail($submission, $this->form);
278
279 if ($receiptEmail && ArrayHelper::get($formSettings, 'disable_stripe_payment_receipt') != 'yes') {
280 $intentArgs['receipt_email'] = $receiptEmail;
281 }
282
283 $intent = SCA::createPaymentIntent($intentArgs, $this->form->id);
284
285 if (is_wp_error($intent)) {
286 $this->handlePaymentChargeError($intent->get_error_message(), $submission, $transaction, false, 'payment_intent');
287 }
288
289 if (
290 $intent->status == 'requires_action' &&
291 $intent->next_action &&
292 $intent->next_action->type == 'use_stripe_sdk'
293 ) {
294 $this->processScaBeforeVerification($submission->form_id, $submission->id, $transaction->id, $intent->id);
295
296 // Generate nonce for secure SCA confirmation
297 $nonceAction = 'fluentform_sca_confirm_' . $submission->id;
298 $nonce = wp_create_nonce($nonceAction);
299
300 # Tell the client to handle the action
301 wp_send_json_success([
302 'nextAction' => 'payment',
303 'actionName' => 'initStripeSCAModal',
304 'submission_id' => $submission->id,
305 'client_secret' => $intent->client_secret,
306 '_ff_stripe_nonce' => $nonce,
307 'message' => apply_filters('fluentform/stripe_strong_customer_verify_waiting_message', __('Verifying strong customer authentication. Please wait...', 'fluentform')),
308 'result' => [
309 'insert_id' => $submission->id,
310 ],
311 ], 200);
312
313 } elseif ('succeeded' == $intent->status) {
314 // Payment is succeeded here
315 $charge = $intent->charges->data[0];
316
317 $this->handlePaymentSuccess($charge, $transaction, $submission);
318 } else {
319 $message = __('Payment Failed! Your card may have been declined.', 'fluentform');
320
321 if (!empty($intent->error->message)) {
322 $message = $intent->error->message;
323 }
324
325 $this->handlePaymentChargeError($message, $submission, $transaction, false, 'payment_intent');
326 }
327 }
328
329 protected function handlePaymentSuccess($charge, $transaction, $submission)
330 {
331 $transactionData = [
332 'charge_id' => $charge->payment_intent,
333 'payment_method' => 'stripe',
334 'payment_mode' => $this->getPaymentMode(),
335 'payment_note' => maybe_serialize($charge),
336 ];
337
338 $methodDetails = $charge->payment_method_details;
339 if ($methodDetails && !empty($methodDetails->card)) {
340 $transactionData['card_brand'] = $methodDetails->card->brand;
341 $transactionData['card_last_4'] = $methodDetails->card->last4;
342 }
343
344 $this->updateTransaction($transaction->id, $transactionData);
345
346 $this->changeTransactionStatus($transaction->id, 'paid');
347
348 $logData = [
349 'parent_source_id' => $submission->form_id,
350 'source_type' => 'submission_item',
351 'source_id' => $submission->id,
352 'component' => 'Payment',
353 'status' => 'info',
354 'title' => __('Payment Status changed', 'fluentform'),
355 'description' => __('Payment status changed to paid', 'fluentform'),
356 ];
357
358 do_action('fluentform/log_data', $logData);
359
360 $this->updateSubmission($submission->id, [
361 'payment_method' => 'stripe',
362 ]);
363
364 // Trigger fluentform/after_payment_status_change (via BaseProcessor),
365 // consistent with hosted Stripe checkout and offline payment flows.
366 $this->changeSubmissionPaymentStatus('paid');
367
368 $logData = [
369 'parent_source_id' => $submission->form_id,
370 'source_type' => 'submission_item',
371 'source_id' => $submission->id,
372 'component' => 'Payment',
373 'status' => 'success',
374 'title' => __('Payment Complete', 'fluentform'),
375 'description' => __('One time Payment Successfully made via Stripe. Charge ID: ', 'fluentform') . $charge->id,
376 ];
377
378 do_action('fluentform/log_data', $logData);
379
380 $this->recalculatePaidTotal();
381
382 $this->sendSuccess($submission);
383 }
384
385 /**
386 * Validate SCA payment confirmation request
387 *
388 * @param int $submissionId Submission ID
389 * @param string $paymentIntentId Payment Intent ID
390 * @param object|null $submission Submission object
391 * @param object|null $transaction Transaction object
392 * @return array|WP_Error Array with validation result or WP_Error on strict mode failure
393 */
394 protected function validateScaRequest($submissionId, $paymentIntentId, $submission = null, $transaction = null)
395 {
396 $warnings = [];
397
398 // Validate nonce — always required by default.
399 // Filter allows opt-out only for backward compat; emits deprecation notice.
400 $nonce = isset($_REQUEST['_ff_stripe_nonce']) ? sanitize_text_field(wp_unslash($_REQUEST['_ff_stripe_nonce'])) : '';
401
402 if ($nonce) {
403 $nonceAction = 'fluentform_sca_confirm_' . $submissionId;
404 if (!wp_verify_nonce($nonce, $nonceAction)) {
405 return new \WP_Error('invalid_nonce', __('Security verification failed. Invalid nonce.', 'fluentform'));
406 }
407 } else {
408 $strictMode = apply_filters('fluentform/stripe_sca_strict_security', true);
409 if ($strictMode) {
410 return new \WP_Error('missing_nonce', __('Security verification failed. Nonce required.', 'fluentform'));
411 }
412 _deprecated_argument(
413 'fluentform/stripe_sca_strict_security',
414 '6.2.0',
415 esc_html(__('Disabling strict SCA nonce verification is deprecated and will be removed in a future version.', 'fluentform'))
416 );
417 $warnings[] = 'No nonce provided for SCA payment confirmation';
418 }
419
420 // Validate submission exists
421 if (!$submission || !$submission->id) {
422 return new \WP_Error('invalid_submission', __('Invalid submission.', 'fluentform'));
423 }
424
425 if ($submission->payment_status === 'paid') {
426 return new \WP_Error(
427 'already_paid',
428 __('This payment has already been completed and cannot be modified.', 'fluentform')
429 );
430 }
431
432 // Transaction must exist and be in 'intended' status (set by processScaBeforeVerification
433 // when the SCA flow starts). A 'pending' transaction means SCA was never initiated,
434 // 'paid'/'failed' means it's already been processed.
435 if (!$transaction) {
436 return new \WP_Error('no_transaction', __('No transaction found for this submission.', 'fluentform'));
437 }
438
439 if ($transaction->status !== 'intended') {
440 return new \WP_Error(
441 'invalid_transaction_status',
442 __('This transaction is not awaiting payment confirmation.', 'fluentform')
443 );
444 }
445
446 // Verify the payment intent ID matches what was stored during SCA initiation.
447 // processScaBeforeVerification() stores the intent as charge_id.
448 if ($transaction->charge_id && $transaction->charge_id !== $paymentIntentId) {
449 return new \WP_Error(
450 'payment_intent_mismatch',
451 __('Payment verification failed. Payment intent does not match.', 'fluentform')
452 );
453 }
454
455 // Log warnings for monitoring
456 if (!empty($warnings) && defined('WP_DEBUG') && WP_DEBUG) {
457 $logData = [
458 'parent_source_id' => $submission->form_id,
459 'source_type' => 'submission_item',
460 'source_id' => $submission->id,
461 'component' => 'Payment',
462 'status' => 'warning',
463 'title' => __('Stripe SCA Security Warning', 'fluentform'),
464 'description' => implode('; ', $warnings),
465 ];
466 do_action('fluentform/log_data', $logData);
467 }
468
469 return [
470 'valid' => true,
471 'warnings' => $warnings,
472 ];
473 }
474
475 public function confirmScaPayment()
476 {
477 $submissionId = isset($_REQUEST['submission_id']) ? (int) $_REQUEST['submission_id'] : 0;
478 $paymentMethod = isset($_REQUEST['payment_method']) ? sanitize_text_field(wp_unslash($_REQUEST['payment_method'])) : '';
479 $paymentIntentId = isset($_REQUEST['payment_intent_id']) ? sanitize_text_field(wp_unslash($_REQUEST['payment_intent_id'])) : '';
480
481 $this->setSubmissionId($submissionId);
482 $submission = $this->getSubmission();
483 $this->form = $this->getForm();
484
485 $transaction = $this->getLastTransaction($submissionId);
486
487 $validation = $this->validateScaRequest($submissionId, $paymentIntentId, $submission, $transaction);
488
489 if (is_wp_error($validation)) {
490 wp_send_json([
491 'errors' => $validation->get_error_message(),
492 ], 423);
493 }
494
495 // Use submission's form_id rather than trusting $_REQUEST
496 $formId = $submission->form_id;
497
498 $confirmation = SCA::confirmPayment($paymentIntentId, [
499 'payment_method' => $paymentMethod,
500 ], $formId);
501
502 if (is_wp_error($confirmation)) {
503 $message = 'Payment has been failed. ' . $confirmation->get_error_message();
504 $this->handlePaymentChargeError($message, $submission, $transaction, $confirmation, 'payment_error');
505 }
506
507 if ($confirmation->status == 'succeeded') {
508 $charge = $confirmation->charges->data[0];
509
510 $confirmedCurrency = strtolower((string) $confirmation->currency);
511 $transactionCurrency = strtolower((string) $transaction->currency);
512 if (!$confirmedCurrency || $confirmedCurrency !== $transactionCurrency) {
513 $logData = [
514 'parent_source_id' => $submission->form_id,
515 'source_type' => 'submission_item',
516 'source_id' => $submission->id,
517 'component' => 'Payment',
518 'status' => 'error',
519 'title' => __('Stripe Currency Mismatch', 'fluentform'),
520 'description' => sprintf(
521 // translators: %1$s is the expected currency, %2$s is the confirmed currency
522 __('Expected %1$s but Stripe confirmed %2$s. Payment rejected.', 'fluentform'),
523 strtoupper($transactionCurrency),
524 strtoupper($confirmedCurrency)
525 ),
526 ];
527 do_action('fluentform/log_data', $logData);
528
529 wp_send_json([
530 'errors' => __('Payment currency verification failed.', 'fluentform'),
531 ], 423);
532 }
533
534 // Verify the confirmed amount matches the transaction amount.
535 // Normalize for zero-decimal currencies: FluentForm stores amounts x100 internally,
536 // but Stripe returns amounts in the currency's smallest unit (e.g. yen for JPY).
537 $confirmedAmount = (int) $confirmation->amount;
538 if (PaymentHelper::isZeroDecimal($transaction->currency)) {
539 $confirmedAmount = $confirmedAmount * 100;
540 }
541 if ($transaction->payment_total && $confirmedAmount != intval($transaction->payment_total)) {
542 $logData = [
543 'parent_source_id' => $submission->form_id,
544 'source_type' => 'submission_item',
545 'source_id' => $submission->id,
546 'component' => 'Payment',
547 'status' => 'error',
548 'title' => __('Stripe Amount Mismatch', 'fluentform'),
549 'description' => sprintf(
550 // translators: %1$d is the expected amount, %2$d is the confirmed amount
551 __('Expected %1$d but Stripe confirmed %2$d. Payment rejected.', 'fluentform'),
552 intval($transaction->payment_total),
553 intval($confirmation->amount)
554 ),
555 ];
556 do_action('fluentform/log_data', $logData);
557
558 wp_send_json([
559 'errors' => __('Payment amount verification failed.', 'fluentform'),
560 ], 423);
561 }
562
563 $this->handlePaymentSuccess($charge, $transaction, $submission);
564 } else {
565 $this->handlePaymentChargeError('We could not verify your payment. Please try again', $submission, $transaction, $confirmation, 'payment_error');
566 }
567 }
568
569 public function confirmScaSetupIntentsPayment()
570 {
571 $submissionId = isset($_REQUEST['submission_id']) ? intval($_REQUEST['submission_id']) : 0;
572 $intentId = isset($_REQUEST['payment_intent_id']) ? sanitize_text_field(wp_unslash($_REQUEST['payment_intent_id'])) : '';
573
574 $this->setSubmissionId($submissionId);
575 $this->form = $this->getForm();
576
577 $submission = $this->getSubmission();
578 $transaction = $this->getLastTransaction($submissionId);
579
580 // Validate the request
581 $validation = $this->validateScaRequest($submissionId, $intentId, $submission, $transaction);
582
583 if (is_wp_error($validation)) {
584 wp_send_json([
585 'errors' => $validation->get_error_message(),
586 ], 423);
587 }
588
589 // Use submission's form_id rather than trusting $_REQUEST
590 $formId = $submission->form_id;
591
592 // Let's retrieve the intent
593 $intent = SCA::retrievePaymentIntent($intentId, [
594 'expand' => [
595 'invoice.payment_intent',
596 ],
597 ], $formId);
598
599 if (is_wp_error($intent)) {
600 $this->handlePaymentChargeError($intent->get_error_message(), $submission, false, false, 'payment_intent');
601 }
602
603 $invoice = $intent->invoice;
604
605 $this->handlePaidSubscriptionInvoice($invoice, $submission);
606 }
607
608 protected function sendSuccess($submission)
609 {
610 try {
611 $returnData = $this->getReturnData();
612 wp_send_json_success($returnData, 200);
613
614 } catch (\Exception $e) {
615 wp_send_json([
616 'errors' => $e->getMessage(),
617 ], 423);
618 }
619 }
620
621 protected function processScaBeforeVerification($formId, $submissionId, $transactionId, $chargeId)
622 {
623 if ($transactionId) {
624 $this->updateTransaction($transactionId, [
625 'charge_id' => $chargeId,
626 'payment_mode' => $this->getPaymentMode(),
627 ]);
628
629 $this->changeTransactionStatus($transactionId, 'intended');
630 }
631
632 $logData = [
633 'parent_source_id' => $formId,
634 'source_type' => 'submission_item',
635 'source_id' => $submissionId,
636 'component' => 'Payment',
637 'status' => 'info',
638 'title' => __('Stripe SCA Required', 'fluentform'),
639 'description' => __('SCA is required for this payment. Requested SCA info from customer', 'fluentform'),
640 ];
641
642 do_action('fluentform/log_data', $logData);
643 }
644
645 /**
646 * Products name comma separated
647 *
648 * @return string
649 */
650 public function getProductNames()
651 {
652 $orderItems = $this->getOrderItems();
653 $itemsHtml = '';
654 foreach ($orderItems as $item) {
655 '' != $itemsHtml && $itemsHtml .= ', ';
656 $itemsHtml .= $item->item_name;
657 }
658
659 return $itemsHtml;
660 }
661 }
662