PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.6
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.6
1.6.6 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 All 49 releases
← All changes | app/Modules/PaymentMethods/StripeGateway/API/API.php +39 -7 1.4.0 → 1.6.6 View file →
@@ -26,12 +26,12 @@
26 26
27 27 return $this->remoteRequest($path, $data, $apiKey, 'GET');
28 28 }
29 29
30 - public function createStripeObject($path, $data = [], $mode = 'current')
30 + public function createStripeObject($path, $data = [], $mode = 'current', $headers = [])
31 31 {
32 32 $apiKey = (new StripeSettingsBase())->getApiKey($mode);
33 - return $this->remoteRequest($path, $data, $apiKey, 'POST');
33 + return $this->remoteRequest($path, $data, $apiKey, 'POST', $headers);
34 34 }
35 35
36 36 public function deleteStripeObject($path, $data = [], $mode = 'current')
37 37 {
@@ -38,11 +38,24 @@
38 38 $apiKey = (new StripeSettingsBase())->getApiKey($mode);
39 39 return $this->remoteRequest($path, $data, $apiKey, 'DELETE');
40 40 }
41 41
42 - public function remoteRequest($path, $data, $apiKey, $method)
42 + public function remoteRequest($path, $data, $apiKey, $method, $extraHeaders = [])
43 43 {
44 44 $stripeApiKey = $apiKey;
45 +
46 + // Never fire a request with an empty Authorization header — Stripe replies
47 + // with the cryptic "You did not provide an API key" error. This happens when
48 + // the secret key for the requested mode is not configured (e.g. a store in
49 + // test mode charging a live-mode order, or unconfigured keys). Fail early
50 + // with an actionable message instead.
51 + if (empty($stripeApiKey)) {
52 + return new \WP_Error(
53 + 'stripe_missing_api_key',
54 + __('Stripe API key is not configured for this payment mode. Please add your Stripe keys in Payment Settings.', 'fluent-cart')
55 + );
56 + }
57 +
45 58 $apiVersion = '2025-02-24.acacia';
46 59 $sessionHeaders = array(
47 60 'Authorization' => 'Bearer ' . $stripeApiKey,
48 61 'Content-Type' => 'application/x-www-form-urlencoded',
@@ -48,8 +61,13 @@
48 61 'Content-Type' => 'application/x-www-form-urlencoded',
49 62 'Stripe-Version' => $apiVersion
50 63 );
51 64
65 + // Per-request headers (e.g. Idempotency-Key for off-session renewal charges)
66 + if ($extraHeaders && is_array($extraHeaders)) {
67 + $sessionHeaders = array_merge($sessionHeaders, $extraHeaders);
68 + }
69 +
52 70 $url = $this->apiUrl . $path;
53 71
54 72 if ($method === 'GET' && is_array($data) && !empty($data)) {
55 73 $url .= '?' . http_build_query($data);
@@ -109,18 +127,32 @@
109 127
110 128 return $data;
111 129 }
112 130
113 - public function getEvent($eventId)
131 + /**
132 + * Event ids are namespaced per mode, so a live id is unfetchable with a test key
133 + * and vice versa. The store's global mode toggle is not a reliable proxy — a
134 + * renewal is billed by Stripe on its own schedule, whatever the store is set to.
135 + *
136 + * @param string $eventId
137 + * @param bool|null $livemode Mode the event belongs to; null falls back to the store setting.
138 + * @return object|null|\WP_Error Null when the response body is not decodable JSON.
139 + */
140 + public function getEvent($eventId, $livemode = null)
114 141 {
115 - $api = $this->getApi();
142 + $mode = 'current';
143 + if (!is_null($livemode)) {
144 + $mode = $livemode ? 'live' : 'test';
145 + }
146 +
147 + $api = $this->getApi($mode);
116 148 return $api::request([], 'events/' . $eventId, 'GET');
117 149 }
118 150
119 - public function getApi()
151 + public function getApi($mode = 'current')
120 152 {
121 153 $api = new ApiRequest();
122 - $api::set_secret_key((new StripeSettingsBase())->getApiKey());
154 + $api::set_secret_key((new StripeSettingsBase())->getApiKey($mode));
123 155 return $api;
124 156 }
125 157
126 158 public function getApiKey($mode = 'current')