| 1 |
<?php |
| 2 |
|
| 3 |
namespace FluentCart\App\Modules\PaymentMethods\StripeGateway; |
| 4 |
|
| 5 |
use FluentCart\Api\CurrencySettings; |
| 6 |
use FluentCart\Api\Orders; |
| 7 |
use FluentCart\Api\StoreSettings; |
| 8 |
use FluentCart\App\Helpers\CartCheckoutHelper; |
| 9 |
use FluentCart\App\Helpers\CartHelper; |
| 10 |
use FluentCart\App\Helpers\CurrenciesHelper; |
| 11 |
use FluentCart\App\Helpers\Helper; |
| 12 |
use FluentCart\App\Hooks\Cart\WebCheckoutHandler; |
| 13 |
use FluentCart\App\Modules\PaymentMethods\Core\AbstractPaymentGateway; |
| 14 |
use FluentCart\App\Modules\PaymentMethods\Core\BaseGatewaySettings; |
| 15 |
use FluentCart\App\Modules\PaymentMethods\StripeGateway\API\API; |
| 16 |
use FluentCart\App\Modules\PaymentMethods\StripeGateway\Connect\ConnectConfig; |
| 17 |
use FluentCart\App\Modules\PaymentMethods\StripeGateway\Webhook\IPN; |
| 18 |
use FluentCart\App\Modules\PaymentMethods\StripeGateway\Webhook\Webhook; |
| 19 |
use FluentCart\App\Services\CustomPayment\PaymentIntent; |
| 20 |
use FluentCart\App\Services\Payments\PaymentHelper; |
| 21 |
use FluentCart\App\Services\Payments\PaymentInstance; |
| 22 |
use FluentCart\App\Vite; |
| 23 |
use FluentCart\Framework\Support\Arr; |
| 24 |
|
| 25 |
class Stripe extends AbstractPaymentGateway |
| 26 |
{ |
| 27 |
|
| 28 |
private $methodSlug = 'stripe'; |
| 29 |
|
| 30 |
public array $supportedFeatures = ['payment', 'refund', 'webhook', 'custom_payment', 'card_update', 'switch_payment_method' => [ |
| 31 |
'supported_gateways' => ['stripe', 'paypal'], |
| 32 |
], 'dispute_handler', 'subscriptions', 'zero_recurring']; |
| 33 |
|
| 34 |
public BaseGatewaySettings $settings; |
| 35 |
|
| 36 |
public function __construct() |
| 37 |
{ |
| 38 |
parent::__construct( |
| 39 |
new StripeSettingsBase(), |
| 40 |
new StripeSubscriptions() |
| 41 |
); |
| 42 |
|
| 43 |
add_action('fluent_cart_action_stripe_connect', function ($data) { |
| 44 |
ConnectConfig::handleConnect($data); |
| 45 |
}); |
| 46 |
|
| 47 |
} |
| 48 |
|
| 49 |
public function boot() |
| 50 |
{ |
| 51 |
(new IPN)->init(); |
| 52 |
(new Confirmations)->init(); |
| 53 |
add_filter('fluent_cart/payment_methods/stripe_pub_key', [$this, 'getPublicKey'], 10); |
| 54 |
} |
| 55 |
|
| 56 |
public function meta(): array |
| 57 |
{ |
| 58 |
return [ |
| 59 |
'title' => 'Card', |
| 60 |
'route' => 'stripe', |
| 61 |
'slug' => 'stripe', |
| 62 |
'label' => 'Stripe', |
| 63 |
'admin_title' => 'Stripe', |
| 64 |
'description' => __("Stripe's payments platform lets you accept credit cards, debit cards, and popular payment methods around the world all with a single integration.", "fluent-cart"), |
| 65 |
'logo' => Vite::getAssetUrl('images/payment-methods/card.svg'), |
| 66 |
'icon' => Vite::getAssetUrl('images/payment-methods/stripe-icon.svg'), |
| 67 |
'status' => $this->settings->get('is_active') === 'yes', |
| 68 |
'brand_color' => '#635bff', |
| 69 |
'upcoming' => false, |
| 70 |
'supported_features' => $this->supportedFeatures |
| 71 |
]; |
| 72 |
} |
| 73 |
|
| 74 |
public function makePaymentFromPaymentInstance(PaymentInstance $paymentInstance) |
| 75 |
{ |
| 76 |
$order = $paymentInstance->order; |
| 77 |
|
| 78 |
$storeName = (new StoreSettings())->get('store_name'); |
| 79 |
|
| 80 |
$transactionCurrency = $paymentInstance->transaction->currency; |
| 81 |
$chargeAmount = (int)$paymentInstance->transaction->total; |
| 82 |
|
| 83 |
if ($transactionCurrency && CurrenciesHelper::isZeroDecimal($transactionCurrency)) { |
| 84 |
$chargeAmount = (int)round($chargeAmount / 100); |
| 85 |
} |
| 86 |
|
| 87 |
$paymentArgs = array( |
| 88 |
'client_reference_id' => $order->uuid, |
| 89 |
'amount' => $chargeAmount, |
| 90 |
'currency' => strtolower($transactionCurrency), |
| 91 |
'description' => $storeName . ' #' . $order->invoice_no, // @todo: We will replace with order summary with item names later |
| 92 |
'customer_email' => $paymentInstance->order->email, |
| 93 |
'success_url' => $this->getSuccessUrl($paymentInstance->transaction), |
| 94 |
'custom_payment_url' => PaymentHelper::getCustomPaymentLink($paymentInstance->order->uuid) |
| 95 |
); |
| 96 |
|
| 97 |
if ($paymentInstance->subscription) { |
| 98 |
return (new Processor())->handleSubscription($paymentInstance, $paymentArgs); |
| 99 |
} |
| 100 |
|
| 101 |
return (new Processor())->handleSinglePayment($paymentInstance, $paymentArgs); |
| 102 |
} |
| 103 |
|
| 104 |
public function processRefund($transaction, $amount, $args) |
| 105 |
{ |
| 106 |
if (!$amount) { |
| 107 |
return new \WP_Error( |
| 108 |
'fluent_cart_stripe_refund_error', |
| 109 |
__('Refund amount is required.', 'fluent-cart') |
| 110 |
); |
| 111 |
} |
| 112 |
|
| 113 |
return \FluentCart\App\Modules\PaymentMethods\StripeGateway\StripeHelper::processRemoteRefund($transaction, $amount, $args); |
| 114 |
} |
| 115 |
|
| 116 |
public function webHookPaymentMethodName() |
| 117 |
{ |
| 118 |
return $this->getMeta('route'); |
| 119 |
} |
| 120 |
|
| 121 |
public function handleIPN(): void |
| 122 |
{ |
| 123 |
(new IPN($this))->verifyAndProcess(); |
| 124 |
} |
| 125 |
|
| 126 |
public function getEnqueueScriptSrc($hasSubscription = 'no'): array |
| 127 |
{ |
| 128 |
$checkoutMode = $this->settings->get('checkout_mode') ?? 'onsite'; |
| 129 |
|
| 130 |
if ($checkoutMode == 'hosted') { |
| 131 |
return [ |
| 132 |
[ |
| 133 |
'handle' => 'fluent-cart-checkout-handler-stripe-hosted', |
| 134 |
'src' => Vite::getEnqueuePath('public/payment-methods/stripe-hosted-checkout.js'), |
| 135 |
] |
| 136 |
]; |
| 137 |
} |
| 138 |
|
| 139 |
// For embedded/onsite mode, load Stripe SDK and full handler |
| 140 |
return [ |
| 141 |
[ |
| 142 |
'handle' => 'fluent-cart-checkout-sdk-stripe', |
| 143 |
'src' => 'https://js.stripe.com/v3/', |
| 144 |
], |
| 145 |
[ |
| 146 |
'handle' => 'fluent-cart-checkout-handler-stripe', |
| 147 |
'src' => Vite::getEnqueuePath('public/payment-methods/stripe-checkout.js'), |
| 148 |
'deps' => ['fluent-cart-checkout-sdk-stripe'] |
| 149 |
] |
| 150 |
]; |
| 151 |
} |
| 152 |
|
| 153 |
private function getStripeLocale(): string |
| 154 |
{ |
| 155 |
$parts = explode('_', get_locale()); |
| 156 |
$lang = strtolower($parts[0]); |
| 157 |
$region = isset($parts[1]) ? strtoupper($parts[1]) : ''; |
| 158 |
|
| 159 |
if ($region) { |
| 160 |
$full = $lang . '-' . $region; |
| 161 |
if (in_array($full, ['en-GB', 'fr-CA', 'zh-HK', 'zh-TW', 'pt-BR', 'es-419'])) { |
| 162 |
return $full; |
| 163 |
} |
| 164 |
} |
| 165 |
|
| 166 |
return $lang ?: 'auto'; |
| 167 |
} |
| 168 |
|
| 169 |
public function getLocalizeData(): array |
| 170 |
{ |
| 171 |
return [ |
| 172 |
'fct_stripe_data' => [ |
| 173 |
'locale' => $this->getStripeLocale(), |
| 174 |
'translations' => [ |
| 175 |
'Payment module not available to checkout! Please reload again, or contact admin!' => __('Payment module not available to checkout! Please reload again, or contact admin!', 'fluent-cart'), |
| 176 |
'See Errors' => __('See Errors', 'fluent-cart'), |
| 177 |
'Pay Now' => __('Pay Now', 'fluent-cart'), |
| 178 |
'Place Order' => __('Place Order', 'fluent-cart'), |
| 179 |
'Card details are not valid!' => __('Card details are not valid!', 'fluent-cart'), |
| 180 |
'Total amount is not valid, please add some items to cart!' => __('Total amount is not valid, please add some items to cart!', 'fluent-cart'), |
| 181 |
'An error occurred while parsing the response.' => __('An error occurred while parsing the response.', 'fluent-cart'), |
| 182 |
'An error occurred while loading the payment method.' => __('An error occurred while loading the payment method.', 'fluent-cart'), |
| 183 |
'Loading Payment Processor...' => __('Loading Payment Processor...', 'fluent-cart'), |
| 184 |
'redirecting for action' => __('redirecting for action', 'fluent-cart'), |
| 185 |
'You will be redirected to Stripe to complete your payment securely.' => __('You will be redirected to Stripe to complete your payment securely.', 'fluent-cart'), |
| 186 |
'Something went wrong' => __('Something went wrong', 'fluent-cart'), |
| 187 |
'Payment confirmation failed' => __('Payment confirmation failed', 'fluent-cart'), |
| 188 |
] |
| 189 |
] |
| 190 |
]; |
| 191 |
} |
| 192 |
|
| 193 |
public static function beforeSettingsUpdate($data, $oldSettings): array |
| 194 |
{ |
| 195 |
$provider = Arr::get($data, 'provider', 'connect'); |
| 196 |
$mode = Arr::get($data, 'payment_mode', 'test'); |
| 197 |
|
| 198 |
if ('connect' == $provider) { |
| 199 |
$currentKey = Arr::get($data, $mode . '_secret_key', ''); |
| 200 |
$oldKey = Arr::get($oldSettings, $mode . '_secret_key', ''); |
| 201 |
|
| 202 |
if ($currentKey !== $oldKey) { |
| 203 |
$data[$mode . '_secret_key'] = Helper::encryptKey($currentKey); |
| 204 |
} |
| 205 |
} |
| 206 |
|
| 207 |
if (Arr::get($data, 'provider') === 'api_keys') { |
| 208 |
$data['test_publishable_key'] = ''; |
| 209 |
$data['live_publishable_key'] = ''; |
| 210 |
$data['test_secret_key'] = ''; |
| 211 |
$data['live_secret_key'] = ''; |
| 212 |
} |
| 213 |
|
| 214 |
return $data; |
| 215 |
} |
| 216 |
|
| 217 |
public static function validateSettings($data): array |
| 218 |
{ |
| 219 |
$mode = Arr::get($data, 'payment_mode', 'test'); |
| 220 |
$provider = Arr::get($data, 'provider', 'connect'); |
| 221 |
|
| 222 |
if ($provider === 'api_keys') { |
| 223 |
if ($mode === 'live') { |
| 224 |
$sk = defined('FCT_STRIPE_LIVE_SECRET_KEY') ? FCT_STRIPE_LIVE_SECRET_KEY : Arr::get($data, 'live_secret_key'); |
| 225 |
} else { |
| 226 |
$sk = defined('FCT_STRIPE_TEST_SECRET_KEY') ? FCT_STRIPE_TEST_SECRET_KEY : Arr::get($data, 'test_secret_key'); |
| 227 |
} |
| 228 |
} else { |
| 229 |
$sk = $mode === 'live' ? Arr::get($data, 'live_secret_key') : Arr::get($data, 'test_secret_key'); |
| 230 |
if (empty($sk)) { |
| 231 |
$errorMessage = $mode === 'live' ? __('Stripe not connected in live mode!', 'fluent-cart') : __('Stripe not connected in test mode!', 'fluent-cart'); |
| 232 |
return [ |
| 233 |
'status' => 'failed', |
| 234 |
'message' => $errorMessage |
| 235 |
]; |
| 236 |
} else { |
| 237 |
return [ |
| 238 |
'status' => 'success', |
| 239 |
'message' => __('Stripe account already verified!', 'fluent-cart') |
| 240 |
]; |
| 241 |
} |
| 242 |
} |
| 243 |
|
| 244 |
if (empty($sk)) { |
| 245 |
return [ |
| 246 |
'status' => 'failed', |
| 247 |
'message' => __('Please provide a valid secret key!', 'fluent-cart') |
| 248 |
]; |
| 249 |
} |
| 250 |
|
| 251 |
if ($mode === 'live' && !str_contains($sk, 'sk_live')) { |
| 252 |
return [ |
| 253 |
'status' => 'failed', |
| 254 |
'message' => __('Please provide a valid LIVE secret key!', 'fluent-cart') |
| 255 |
]; |
| 256 |
} else if ($mode === 'test' && !str_contains($sk, 'sk_test')) { |
| 257 |
return [ |
| 258 |
'status' => 'failed', |
| 259 |
'message' => __('Please provide a valid TEST secret key!', 'fluent-cart') |
| 260 |
]; |
| 261 |
} |
| 262 |
|
| 263 |
$response = (new API)->remoteRequest('account', [], $sk, 'GET'); |
| 264 |
|
| 265 |
if (isset($response['error'])) { |
| 266 |
return [ |
| 267 |
'status' => 'failed', |
| 268 |
'message' => $response['error']['message'] ? $response['error']['message'] : __('Invalid credentials!', 'fluent-cart') |
| 269 |
]; |
| 270 |
} |
| 271 |
|
| 272 |
if (!isset($response['id'])) { |
| 273 |
return [ |
| 274 |
'status' => 'failed', |
| 275 |
'message' => $response['error']['message'] ? $response['error']['message'] : __('Invalid credentials!', 'fluent-cart') |
| 276 |
]; |
| 277 |
} |
| 278 |
|
| 279 |
return [ |
| 280 |
'status' => 'success', |
| 281 |
'message' => __('Stripe account verified!', 'fluent-cart') |
| 282 |
]; |
| 283 |
} |
| 284 |
|
| 285 |
public function fields(): array |
| 286 |
{ |
| 287 |
$disabled = apply_filters_deprecated('fluent_cart_form_disable_stripe_connect', [false, []], '1.3.16', 'fluent_cart/form_disable_stripe_connect', 'Use fluent_cart/form_disable_stripe_connect instead of fluent_cart_form_disable_stripe_connect. It will be removed in v1.4.3.'); |
| 288 |
$providerValue = apply_filters('fluent_cart/form_disable_stripe_connect', $disabled, []) ? 'api_keys' : 'connect'; |
| 289 |
|
| 290 |
return array( |
| 291 |
'notice' => [ |
| 292 |
'value' => $this->renderStoreModeNotice(), |
| 293 |
'label' => __('Store Mode notice', 'fluent-cart'), |
| 294 |
'type' => 'notice' |
| 295 |
], |
| 296 |
'payment_mode' => [ |
| 297 |
'type' => 'tabs', |
| 298 |
'schema' => [ |
| 299 |
[ |
| 300 |
'type' => 'tab', |
| 301 |
'label' => __('Live credentials', 'fluent-cart'), |
| 302 |
'value' => 'live', |
| 303 |
'schema' => [] |
| 304 |
], |
| 305 |
[ |
| 306 |
'type' => 'tab', |
| 307 |
'label' => __('Test credentials', 'fluent-cart'), |
| 308 |
'value' => 'test', |
| 309 |
'schema' => [], |
| 310 |
] |
| 311 |
] |
| 312 |
], |
| 313 |
'provider' => array( |
| 314 |
'value' => $providerValue, |
| 315 |
'label' => __('Provider', 'fluent-cart'), |
| 316 |
'type' => 'provider' |
| 317 |
), |
| 318 |
'setup_guide' => array( |
| 319 |
'value' => '<h4>' . __('Or Setup keys manually.', 'fluent-cart') . '</h4><hr/>', |
| 320 |
'label' => __('Or Setup keys manually', 'fluent-cart'), |
| 321 |
'type' => 'html_attr', |
| 322 |
'visible' => 'no' |
| 323 |
), |
| 324 |
'checkout_mode' => array( |
| 325 |
'value' => 'onsite', |
| 326 |
'label' => __('Checkout Mode', 'fluent-cart'), |
| 327 |
'type' => 'radio', |
| 328 |
'options' => [ |
| 329 |
'onsite' => [ |
| 330 |
'label' => __('Embedded checkout (Recommended)', 'fluent-cart'), |
| 331 |
'text' => __('Renders inside your checkout page. Supports all standard card payments with a seamless branded experience.', 'fluent-cart'), |
| 332 |
'icon' => Vite::getAssetUrl('images/bill-line.svg') |
| 333 |
], |
| 334 |
'hosted' => [ |
| 335 |
'label' => __('Stripe Hosted Checkout', 'fluent-cart'), |
| 336 |
'text' => __("Redirects to Stripe's hosted page. Required for Bank Transfers and methods not supported in embedded mode.", 'fluent-cart'), |
| 337 |
'icon' => Vite::getAssetUrl('images/external-link-line.svg') |
| 338 |
] |
| 339 |
], |
| 340 |
'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'), |
| 341 |
'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') |
| 342 |
), |
| 343 |
'webhook_desc' => array( |
| 344 |
'value' => Webhook::webhookInstruction(), |
| 345 |
'label' => __('Webhook URL', 'fluent-cart'), |
| 346 |
'type' => 'html_attr' |
| 347 |
), |
| 348 |
'test_active_methods' => [ |
| 349 |
'value' => (new API())->getActivatedPaymentMethodsConfigs('test'), |
| 350 |
'label' => __('Activated Methods', 'fluent-cart'), |
| 351 |
'type' => 'active_methods' |
| 352 |
], |
| 353 |
'live_active_methods' => [ |
| 354 |
'value' => (new API())->getActivatedPaymentMethodsConfigs('live'), |
| 355 |
'label' => __('Activated Methods', 'fluent-cart'), |
| 356 |
'type' => 'active_methods' |
| 357 |
], |
| 358 |
); |
| 359 |
|
| 360 |
} |
| 361 |
|
| 362 |
public function getPublicKey($pre = '') |
| 363 |
{ |
| 364 |
return $this->settings->getPublicKey(); |
| 365 |
} |
| 366 |
|
| 367 |
public function getTransactionUrl($url, $data): string |
| 368 |
{ |
| 369 |
$transaction = Arr::get($data, 'transaction', null); |
| 370 |
if (!$transaction) { |
| 371 |
return $url; |
| 372 |
} |
| 373 |
|
| 374 |
if ($transaction->transaction_type === 'refund') { |
| 375 |
return 'https://dashboard.stripe.com/refunds/' . $transaction->vendor_charge_id; |
| 376 |
} |
| 377 |
|
| 378 |
return 'https://dashboard.stripe.com/payments/' . $transaction->vendor_charge_id; |
| 379 |
} |
| 380 |
|
| 381 |
public function getSubscriptionUrl($url, $data): string |
| 382 |
{ |
| 383 |
return 'https://dashboard.stripe.com/subscriptions/' . Arr::get($data, 'vendor_subscription_id'); |
| 384 |
} |
| 385 |
|
| 386 |
public function getOrderInfo($data) |
| 387 |
{ |
| 388 |
// For hosted mode, we don't need to return intent data as checkout session is created on order placement |
| 389 |
|
| 390 |
/* |
| 391 |
* Filter the Stripe Elements appearance configuration |
| 392 |
* |
| 393 |
* This filter allows developers to customize the appearance of Stripe Elements. |
| 394 |
* For example: |
| 395 |
* |
| 396 |
* function stripe_appearance($appearance) { |
| 397 |
* return array( |
| 398 |
* 'theme' => 'night', |
| 399 |
* 'labels' => 'floating', |
| 400 |
* 'variables' => array( |
| 401 |
* 'colorPrimary' => '#0570de', |
| 402 |
* 'colorBackground' => '#ffffff', |
| 403 |
* 'colorText' => '#30313d', |
| 404 |
* 'colorDanger' => '#df1b41', |
| 405 |
* 'fontFamily' => 'Ideal Sans, system-ui, sans-serif', |
| 406 |
* 'spacingUnit' => '2px', |
| 407 |
* 'borderRadius' => '4px', |
| 408 |
* ) |
| 409 |
* ); |
| 410 |
* } |
| 411 |
* add_filter('fluent_cart/stripe_appearance', 'stripe_appearance', 10, 1); |
| 412 |
* |
| 413 |
* @see https://docs.stripe.com/elements/appearance-api for all available options |
| 414 |
* @param array $appearance The appearance configuration |
| 415 |
* @return array The modified appearance configuration |
| 416 |
*/ |
| 417 |
if (($this->settings->get('checkout_mode') ?? 'onsite') == 'hosted') { |
| 418 |
wp_send_json( |
| 419 |
[ |
| 420 |
'status' => 'success', |
| 421 |
'message' => __('Order info retrieved!', 'fluent-cart'), |
| 422 |
'data' => [], |
| 423 |
'payment_args' => [ |
| 424 |
'checkout_mode' => 'hosted' |
| 425 |
], |
| 426 |
], |
| 427 |
200 |
| 428 |
); |
| 429 |
} |
| 430 |
|
| 431 |
$cart = CartHelper::getCart(); |
| 432 |
$checkOutHelper = CartCheckoutHelper::make(); |
| 433 |
$shippingChargeData = (new WebCheckoutHandler())->getShippingChargeData($cart); |
| 434 |
$shippingCharge = Arr::get($shippingChargeData, 'charge'); |
| 435 |
$totalPrice = $checkOutHelper->getItemsAmountTotal(false) + $shippingCharge; |
| 436 |
|
| 437 |
$tax = $checkOutHelper->getCart()->checkout_data['tax_data'] ?? []; |
| 438 |
$taxBehavior = (int) Arr::get($tax, 'tax_behavior', 0); |
| 439 |
$storeTaxBehavior = (int) Arr::get($tax, 'store_tax_behavior', $taxBehavior); |
| 440 |
|
| 441 |
if ($taxBehavior === 1) { |
| 442 |
// Pure exclusive — add all tax including fee tax (tax_total contains both). |
| 443 |
$totalPrice = $totalPrice + (int) Arr::get($tax, 'tax_total', 0) |
| 444 |
+ (int) Arr::get($tax, 'shipping_tax', 0); |
| 445 |
} elseif ($taxBehavior === 3) { |
| 446 |
// Mixed — add only exclusive product tax + fee/shipping if store is exclusive. |
| 447 |
$totalPrice = $totalPrice + (int) Arr::get($tax, 'exclusive_tax_total', 0); |
| 448 |
if ($storeTaxBehavior === 1) { |
| 449 |
$totalPrice = $totalPrice + (int) Arr::get($tax, 'fee_tax', 0) |
| 450 |
+ (int) Arr::get($tax, 'shipping_tax', 0); |
| 451 |
} |
| 452 |
} |
| 453 |
|
| 454 |
$items = $this->getCheckoutItems(); |
| 455 |
|
| 456 |
$hasSubscription = $this->validateSubscriptions($items); |
| 457 |
|
| 458 |
$stripeSettings = new StripeSettingsBase(); |
| 459 |
$publicKey = $stripeSettings->getPublicKey(); |
| 460 |
|
| 461 |
if (empty($publicKey)) { |
| 462 |
fluent_cart_add_log( |
| 463 |
'Stripe Credential Validation', |
| 464 |
sprintf('Stripe %s keys are missing or invalid.', $stripeSettings->getMode()), |
| 465 |
'error', |
| 466 |
['log_type' => 'payment'] |
| 467 |
); |
| 468 |
wp_send_json([ |
| 469 |
'status' => 'failed', |
| 470 |
'message' => __('No valid public key found! Please contact the site administrator.', 'fluent-cart') |
| 471 |
], 422); |
| 472 |
} |
| 473 |
|
| 474 |
$paymentArgs['public_key'] = $publicKey; |
| 475 |
|
| 476 |
// Allow filtering the appearance configuration for Stripe Elements |
| 477 |
$appearance = apply_filters_deprecated('fluent_cart_stripe_appearance', [ |
| 478 |
['theme' => 'stripe'] |
| 479 |
], '1.3.16', 'fluent_cart/stripe_appearance', 'Use fluent_cart/stripe_appearance instead of fluent_cart_stripe_appearance. It will be removed in v1.4.3.'); |
| 480 |
$appearance = apply_filters('fluent_cart/stripe_appearance', $appearance); |
| 481 |
|
| 482 |
$storeCurrency = CurrencySettings::get('currency'); |
| 483 |
$intentAmount = (int)$totalPrice; |
| 484 |
|
| 485 |
if ($storeCurrency && CurrenciesHelper::isZeroDecimal($storeCurrency)) { |
| 486 |
$intentAmount = (int)($intentAmount / 100); |
| 487 |
} |
| 488 |
|
| 489 |
$intentData = [ |
| 490 |
'mode' => 'payment', |
| 491 |
'amount' => $intentAmount, |
| 492 |
'currency' => strtolower($storeCurrency), |
| 493 |
'automatic_payment_methods' => ['enabled' => true] |
| 494 |
]; |
| 495 |
|
| 496 |
if ($hasSubscription) { |
| 497 |
$intentData['mode'] = 'subscription'; |
| 498 |
$intentData['setup_future_usage'] = 'off_session'; |
| 499 |
} elseif (Arr::get($data, 'save_payment_method') === 'yes') { |
| 500 |
$intentData['setup_future_usage'] = 'on_session'; |
| 501 |
} |
| 502 |
|
| 503 |
wp_send_json( |
| 504 |
[ |
| 505 |
'status' => 'success', |
| 506 |
'message' => __('Order info retrieved!', 'fluent-cart'), |
| 507 |
'data' => [], |
| 508 |
'payment_args' => $paymentArgs, |
| 509 |
'intent' => $intentData, |
| 510 |
'appearance' => $appearance, |
| 511 |
], |
| 512 |
200 |
| 513 |
); |
| 514 |
} |
| 515 |
|
| 516 |
public function getConnectInfo(): array |
| 517 |
{ |
| 518 |
return ConnectConfig::getConnectConfig(); |
| 519 |
} |
| 520 |
|
| 521 |
public function disconnect($mode): bool |
| 522 |
{ |
| 523 |
return ConnectConfig::disconnect($mode); |
| 524 |
} |
| 525 |
|
| 526 |
public function getCounterDisputeUrl($transaction) |
| 527 |
{ |
| 528 |
if (!$transaction->meta['dispute_id']) { |
| 529 |
return ''; |
| 530 |
} |
| 531 |
|
| 532 |
return 'https://dashboard.stripe.com/disputes/' . $transaction->meta['dispute_id']; |
| 533 |
} |
| 534 |
|
| 535 |
public function acceptRemoteDispute($transaction, $args = []) |
| 536 |
{ |
| 537 |
$disputeId = Arr::get($transaction->meta, 'dispute_id'); |
| 538 |
if (!$disputeId) { |
| 539 |
$charge = (new API())->getStripeObject('payment_intents/' . $transaction->vendor_charge_id, ['expand' => ['latest_charge']]); |
| 540 |
|
| 541 |
if (is_wp_error($charge) || empty($charge['dispute'])) { |
| 542 |
new \WP_Error('No dispute ID found!', __('Please check stripe if the dispute is already accepted or not!', 'fluent-cart')); |
| 543 |
} |
| 544 |
|
| 545 |
$disputeId = Arr::get($charge, 'dispute', ''); |
| 546 |
} |
| 547 |
|
| 548 |
$closeDispute = (new API())->createStripeObject('disputes/' . $disputeId . '/close'); |
| 549 |
|
| 550 |
if (is_wp_error($closeDispute)) { |
| 551 |
return $closeDispute; |
| 552 |
} |
| 553 |
|
| 554 |
return $closeDispute; |
| 555 |
} |
| 556 |
|
| 557 |
public function isCurrencySupported(): bool |
| 558 |
{ |
| 559 |
// stripe support all listed currencies except for IRR (Iranian Rial) |
| 560 |
return strtoupper(CurrencySettings::get('currency')) !== 'IRR'; |
| 561 |
} |
| 562 |
|
| 563 |
} |
| 564 |
|