← All changes
|
app/Modules/PaymentMethods/StripeGateway/Stripe.php
+55
-23
1.6.2
→
1.7.1
View file →
| @@ -55,9 +55,9 @@ | ||
| 55 | 55 | |
| 56 | 56 | public function meta(): array |
| 57 | 57 | { |
| 58 | 58 | return [ |
| 59 | - 'title' => 'Card', | |
| 59 | + 'title' => __('Card', 'fluent-cart'), | |
| 60 | 60 | 'route' => 'stripe', |
| 61 | 61 | 'slug' => 'stripe', |
| 62 | 62 | 'label' => 'Stripe', |
| 63 | 63 | 'admin_title' => 'Stripe', |
| @@ -91,8 +91,9 @@ | ||
| 91 | 91 | 'description' => $storeName . ' #' . $order->invoice_no, // @todo: We will replace with order summary with item names later |
| 92 | 92 | 'customer_email' => $paymentInstance->order->email, |
| 93 | 93 | 'success_url' => $paymentInstance->transaction->getSuccessUrl(), |
| 94 | 94 | 'gateway_return_url' => Processor::getOnsiteGatewayReturnUrl($paymentInstance->transaction), |
| 95 | + 'trx_hash' => $paymentInstance->transaction->uuid, | |
| 95 | 96 | 'custom_payment_url' => PaymentHelper::getCustomPaymentLink($paymentInstance->order->uuid) |
| 96 | 97 | ); |
| 97 | 98 | |
| 98 | 99 | if ($paymentInstance->subscription) { |
| @@ -480,8 +481,11 @@ | ||
| 480 | 481 | 'redirecting for action' => __('redirecting for action', 'fluent-cart'), |
| 481 | 482 | 'You will be redirected to Stripe to complete your payment securely.' => __('You will be redirected to Stripe to complete your payment securely.', 'fluent-cart'), |
| 482 | 483 | 'Something went wrong' => __('Something went wrong', 'fluent-cart'), |
| 483 | 484 | 'Payment confirmation failed' => __('Payment confirmation failed', 'fluent-cart'), |
| 485 | + 'Payment failed. Please try again.' => __('Payment failed. Please try again.', 'fluent-cart'), | |
| 486 | + 'We could not record that failed attempt. Please reload the page before trying again.' => __('We could not record that failed attempt. Please reload the page before trying again.', 'fluent-cart'), | |
| 487 | + 'We could not verify your payment status. Please do not pay again. Contact the store to check your order status.' => __('We could not verify your payment status. Please do not pay again. Contact the store to check your order status.', 'fluent-cart'), | |
| 484 | 488 | ] |
| 485 | 489 | ] |
| 486 | 490 | ]; |
| 487 | 491 | } |
| @@ -635,8 +639,23 @@ | ||
| 635 | 639 | ], |
| 636 | 640 | 'tooltip' => __('Choose between Embedded and Hosted checkout modes. Embedded mode is recommended for most use cases. For Bank transfers, use Hosted mode. (checkout.session.completed webhook event will be triggered only for hosted mode)', 'fluent-cart'), |
| 637 | 641 | 'description' => __("Embedded checkout is recommended for most use cases. For Bank transfers , or if any payment methods are not showing up on embedded checkout, try Hosted checkout. ('checkout.session.completed' webhook event will be triggered only for hosted checkout)", 'fluent-cart') |
| 638 | 642 | ), |
| 643 | + 'submit_type' => array( | |
| 644 | + 'value' => 'auto', | |
| 645 | + 'label' => __('Submit Button Label', 'fluent-cart'), | |
| 646 | + 'type' => 'select', | |
| 647 | + 'filterable' => false, | |
| 648 | + 'options' => [ | |
| 649 | + ['value' => 'auto', 'label' => __('Automatic (Pay / Subscribe)', 'fluent-cart')], | |
| 650 | + ['value' => 'pay', 'label' => __('Pay', 'fluent-cart')], | |
| 651 | + ['value' => 'book', 'label' => __('Book', 'fluent-cart')], | |
| 652 | + ['value' => 'donate', 'label' => __('Donate', 'fluent-cart')], | |
| 653 | + ['value' => 'subscribe', 'label' => __('Subscribe', 'fluent-cart')], | |
| 654 | + ], | |
| 655 | + 'tooltip' => __('Stripe customises the submit button and surrounding copy from this. Applies to Stripe Hosted Checkout only.', 'fluent-cart'), | |
| 656 | + 'description' => __('Applies to Stripe Hosted Checkout only, and is ignored when a card is being saved without a charge. Automatic gives one-time orders the "Buy" button and subscriptions the "Subscribe" button.', 'fluent-cart') | |
| 657 | + ), | |
| 639 | 658 | 'webhook_desc' => array( |
| 640 | 659 | 'value' => Webhook::webhookInstruction(), |
| 641 | 660 | 'label' => __('Webhook URL', 'fluent-cart'), |
| 642 | 661 | 'type' => 'html_attr' |
| @@ -789,13 +808,11 @@ | ||
| 789 | 808 | ], 422); |
| 790 | 809 | } |
| 791 | 810 | |
| 792 | 811 | $paymentArgs['public_key'] = $publicKey; |
| 812 | + $appearance = $this->getElementsAppearance(); | |
| 813 | + $fonts = $this->getElementsFonts(); | |
| 793 | 814 | |
| 794 | - // Allow filtering the appearance configuration for Stripe Elements | |
| 795 | - $appearance = ['theme' => 'stripe']; | |
| 796 | - $appearance = apply_filters('fluent_cart/stripe_appearance', $appearance); | |
| 797 | - | |
| 798 | 815 | $storeCurrency = CurrencySettings::get('currency'); |
| 799 | 816 | $intentAmount = (int)$totalPrice; |
| 800 | 817 | |
| 801 | 818 | if ($storeCurrency && CurrenciesHelper::isZeroDecimal($storeCurrency)) { |
| @@ -826,11 +843,13 @@ | ||
| 826 | 843 | // mode — a zero-amount payment mode is invalid — and consent becomes a |
| 827 | 844 | // required checkbox: without a saved card the trial can never bill. |
| 828 | 845 | $payableNow = \FluentCart\App\Services\OrderService::getItemsAmountTotal($cart->cart_data ?? [], false, false); |
| 829 | 846 | if ($payableNow <= 0) { |
| 847 | + // Match the SetupIntent usage even when the optional save-card flag is set. | |
| 830 | 848 | $intentData = [ |
| 831 | - 'mode' => 'setup', | |
| 832 | - 'currency' => strtolower($storeCurrency), | |
| 849 | + 'mode' => 'setup', | |
| 850 | + 'currency' => strtolower($storeCurrency), | |
| 851 | + 'setup_future_usage' => 'off_session', | |
| 833 | 852 | ]; |
| 834 | 853 | $consentRequired = true; |
| 835 | 854 | } |
| 836 | 855 | } |
| @@ -841,22 +860,20 @@ | ||
| 841 | 860 | } elseif (empty($intentData['setup_future_usage']) && Arr::get($data, 'save_payment_method') === 'yes') { |
| 842 | 861 | $intentData['setup_future_usage'] = 'on_session'; |
| 843 | 862 | } |
| 844 | 863 | |
| 845 | - // The browser cannot pass setup_future_usage per-request (this endpoint | |
| 846 | - // receives no body), so extensions that vault cards (e.g. saved payment | |
| 847 | - // methods) resolve it here, server-side. Must be matched on the actual | |
| 848 | - // PaymentIntent at place-order (fluent_cart/payments/stripe_onetime_intent_args) | |
| 849 | - // or Stripe rejects the confirmation for a setup_future_usage mismatch. | |
| 850 | - $setupFutureUsage = apply_filters( | |
| 851 | - 'fluent_cart/stripe/client_setup_future_usage', | |
| 852 | - Arr::get($intentData, 'setup_future_usage'), | |
| 853 | - ['data' => $data, 'has_subscription' => $hasSubscription] | |
| 854 | - ); | |
| 855 | - if ($setupFutureUsage) { | |
| 856 | - $intentData['setup_future_usage'] = $setupFutureUsage; | |
| 857 | - } else { | |
| 858 | - unset($intentData['setup_future_usage']); | |
| 864 | + // a one-time payment may be changed here. | |
| 865 | + if ($intentData['mode'] === 'payment') { | |
| 866 | + $setupFutureUsage = apply_filters( | |
| 867 | + 'fluent_cart/stripe/client_setup_future_usage', | |
| 868 | + Arr::get($intentData, 'setup_future_usage'), | |
| 869 | + ['data' => $data, 'has_subscription' => $hasSubscription] | |
| 870 | + ); | |
| 871 | + if ($setupFutureUsage) { | |
| 872 | + $intentData['setup_future_usage'] = $setupFutureUsage; | |
| 873 | + } else { | |
| 874 | + unset($intentData['setup_future_usage']); | |
| 875 | + } | |
| 859 | 876 | } |
| 860 | 877 | |
| 861 | 878 | wp_send_json( |
| 862 | 879 | [ |
| @@ -865,8 +882,9 @@ | ||
| 865 | 882 | 'data' => [], |
| 866 | 883 | 'payment_args' => $paymentArgs, |
| 867 | 884 | 'intent' => $intentData, |
| 868 | 885 | 'appearance' => $appearance, |
| 886 | + 'fonts' => $fonts, | |
| 869 | 887 | 'system_consent' => $systemConsent, |
| 870 | 888 | 'consent_required' => $consentRequired, |
| 871 | 889 | ], |
| 872 | 890 | 200 |
| @@ -872,8 +890,22 @@ | ||
| 872 | 890 | 200 |
| 873 | 891 | ); |
| 874 | 892 | } |
| 875 | 893 | |
| 894 | + public function getElementsAppearance(): array | |
| 895 | + { | |
| 896 | + $appearance = ['theme' => 'stripe']; | |
| 897 | + | |
| 898 | + return (array) apply_filters('fluent_cart/stripe_appearance', $appearance); | |
| 899 | + } | |
| 900 | + | |
| 901 | + public function getElementsFonts(): array | |
| 902 | + { | |
| 903 | + $fonts = (array) apply_filters('fluent_cart/stripe_elements_fonts', []); | |
| 904 | + | |
| 905 | + return array_values(array_filter($fonts, 'is_array')); | |
| 906 | + } | |
| 907 | + | |
| 876 | 908 | public function getConnectInfo(): array |
| 877 | 909 | { |
| 878 | 910 | return ConnectConfig::getConnectConfig(); |
| 879 | 911 | } |
| @@ -895,9 +927,9 @@ | ||
| 895 | 927 | public function acceptRemoteDispute($transaction, $args = []) |
| 896 | 928 | { |
| 897 | 929 | $disputeId = Arr::get($transaction->meta, 'dispute_id'); |
| 898 | 930 | if (!$disputeId) { |
| 899 | - $charge = (new API())->getStripeObject('payment_intents/' . $transaction->vendor_charge_id, ['expand' => ['latest_charge']]); | |
| 931 | + $charge = (new API())->getStripeObject('payment_intents/' . $transaction->vendor_charge_id, ['expand' => ['latest_charge']], $transaction->payment_mode); | |
| 900 | 932 | |
| 901 | 933 | if (is_wp_error($charge) || empty($charge['dispute'])) { |
| 902 | 934 | new \WP_Error('No dispute ID found!', __('Please check stripe if the dispute is already accepted or not!', 'fluent-cart')); |
| 903 | 935 | } |
| @@ -904,9 +936,9 @@ | ||
| 904 | 936 | |
| 905 | 937 | $disputeId = Arr::get($charge, 'dispute', ''); |
| 906 | 938 | } |
| 907 | 939 | |
| 908 | - $closeDispute = (new API())->createStripeObject('disputes/' . $disputeId . '/close'); | |
| 940 | + $closeDispute = (new API())->createStripeObject('disputes/' . $disputeId . '/close', [], $transaction->payment_mode); | |
| 909 | 941 | |
| 910 | 942 | if (is_wp_error($closeDispute)) { |
| 911 | 943 | return $closeDispute; |
| 912 | 944 | } |