PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / trunk
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder vtrunk
6.2.15 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 All 197 releases
← All changes | app/Modules/Payments/PaymentMethods/Stripe/StripeSettings.php +80 -5 6.2.6 → trunk View file →
@@ -1,8 +1,9 @@
1 1 <?php
2 2
3 3 namespace FluentForm\App\Modules\Payments\PaymentMethods\Stripe;
4 4
5 +use FluentForm\App\Models\Subscription;
5 6 use FluentForm\Framework\Helpers\ArrayHelper;
6 7 use FluentForm\App\Modules\Payments\PaymentHelper;
7 8
8 9 if (!defined('ABSPATH')) {
@@ -50,12 +51,41 @@
50 51 $settings = self::getSettings();
51 52 $settings = wp_parse_args($data, $settings);
52 53
53 54 $settings = self::encryptKeys($settings);
55 + $settings = self::preserveUnreadableKeys($settings);
54 56 update_option('fluentform_payment_settings_stripe', $settings);
55 57 return self::getSettings();
56 58 }
57 59
60 + /**
61 + * A decrypt-failed stored blob is the only recoverable copy of that key
62 + * (restoring the old salts revives it), and every writer works from the
63 + * decrypted settings where a failed key reads as ''. Keep the stored blob
64 + * unless the mode is explicitly torn down (account/publishable cleared too),
65 + * so the failure flag and admin notice persist until the key really works.
66 + *
67 + * @param array $settings encrypted settings about to be persisted
68 + * @return array
69 + */
70 + public static function preserveUnreadableKeys($settings)
71 + {
72 + if (get_option('fluentform_stripe_key_decrypt_failed') !== 'yes') {
73 + return $settings;
74 + }
75 +
76 + $stored = get_option('fluentform_payment_settings_stripe', []);
77 +
78 + foreach (['test', 'live'] as $mode) {
79 + $modeConfigured = !empty($settings[$mode . '_publishable_key']) || !empty($settings[$mode . '_account_id']);
80 + if (empty($settings[$mode . '_secret_key']) && $modeConfigured && !empty($stored[$mode . '_secret_key'])) {
81 + $settings[$mode . '_secret_key'] = $stored[$mode . '_secret_key'];
82 + }
83 + }
84 +
85 + return $settings;
86 + }
87 +
58 88 public static function encryptKeys($settings)
59 89 {
60 90 $settings['live_secret_key'] = PaymentHelper::encryptKey($settings['live_secret_key']);
61 91 $settings['test_secret_key'] = PaymentHelper::encryptKey($settings['test_secret_key']);
@@ -66,15 +96,49 @@
66 96
67 97 public static function maybeDecryptKeys($settings)
68 98 {
69 99 if (ArrayHelper::get($settings, 'is_encrypted') == 'yes') {
70 - if (!empty($settings['live_secret_key'])) {
71 - $settings['live_secret_key'] = PaymentHelper::decryptKey($settings['live_secret_key']);
100 + $storedLive = ArrayHelper::get($settings, 'live_secret_key');
101 + $storedTest = ArrayHelper::get($settings, 'test_secret_key');
102 + $failed = false;
103 + $legacyFound = false;
104 +
105 + if (!empty($storedLive)) {
106 + $decrypted = PaymentHelper::decryptKey($storedLive);
107 + if ($decrypted === false) {
108 + $failed = true;
109 + $settings['live_secret_key'] = '';
110 + } else {
111 + $settings['live_secret_key'] = $decrypted;
112 + $legacyFound = $legacyFound || strpos($storedLive, 'v2:') !== 0;
113 + }
72 114 }
73 115
74 - if (!empty($settings['test_secret_key'])) {
75 - $settings['test_secret_key'] = PaymentHelper::decryptKey($settings['test_secret_key']);
116 + if (!empty($storedTest)) {
117 + $decrypted = PaymentHelper::decryptKey($storedTest);
118 + if ($decrypted === false) {
119 + $failed = true;
120 + $settings['test_secret_key'] = '';
121 + } else {
122 + $settings['test_secret_key'] = $decrypted;
123 + $legacyFound = $legacyFound || strpos($storedTest, 'v2:') !== 0;
124 + }
76 125 }
126 +
127 + if ($failed) {
128 + update_option('fluentform_stripe_key_decrypt_failed', 'yes');
129 + } else {
130 + if (get_option('fluentform_stripe_key_decrypt_failed')) {
131 + delete_option('fluentform_stripe_key_decrypt_failed');
132 + }
133 + if ($legacyFound) {
134 + $stored = get_option('fluentform_payment_settings_stripe', []);
135 + $stored['live_secret_key'] = PaymentHelper::encryptKey($settings['live_secret_key']);
136 + $stored['test_secret_key'] = PaymentHelper::encryptKey($settings['test_secret_key']);
137 + $stored['is_encrypted'] = 'yes';
138 + update_option('fluentform_payment_settings_stripe', $stored);
139 + }
140 + }
77 141 } else {
78 142 $encrypted = self::encryptKeys($settings);
79 143 update_option('fluentform_payment_settings_stripe', $encrypted);
80 144 }
@@ -174,10 +238,13 @@
174 238 $eventType = $event->type;
175 239
176 240 $metaDataEvents = [
177 241 'checkout.session.completed',
242 + 'checkout.session.async_payment_succeeded',
243 + 'checkout.session.async_payment_failed',
178 244 'charge.refunded',
179 - 'charge.succeeded'
245 + 'charge.succeeded',
246 + 'charge.failed',
180 247 ];
181 248
182 249 if (in_array($eventType, $metaDataEvents)) {
183 250 $data = $event->data->object;
@@ -182,8 +249,16 @@
182 249 if (in_array($eventType, $metaDataEvents)) {
183 250 $data = $event->data->object;
184 251 $metaData = (array)$data->metadata;
185 252 return ArrayHelper::get($metaData, 'form_id');
253 + }
254 +
255 + // A delayed subscription's first invoice settles the entry, so it needs the form's own key too. Once the entry is
256 + // linked, renewals keep the global key: a form moved to its own account still bills older subscriptions on it.
257 + $invoice = $event->data->object;
258 + if ('invoice.payment_succeeded' === $eventType && isset($invoice->subscription, $invoice->subscription_details->metadata->form_id)
259 + && !Subscription::byVendorSubscriptionId($invoice->subscription)->first()) {
260 + return $invoice->subscription_details->metadata->form_id;
186 261 }
187 262
188 263 return false;
189 264 }