spotny-sdk 0.6.2 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of spotny-sdk might be problematic. Click here for more details.
- package/android/src/main/java/com/spotnysdk/SpotnySdkModule.kt +24 -11
- package/ios/SpotnyBeaconScanner.swift +25 -16
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/index.d.ts +6 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/index.tsx +6 -3
|
@@ -47,9 +47,9 @@ class SpotnySdkModule(private val reactContext: ReactApplicationContext) :
|
|
|
47
47
|
private var maxDetectionDistance = 8.0 // metres
|
|
48
48
|
/** Multiplier applied to raw BLE distance. Default 0.5 matches Kontakt.io -12 dBm beacons. */
|
|
49
49
|
private var distanceCorrectionFactor = 0.5
|
|
50
|
-
/**
|
|
51
|
-
private var
|
|
52
|
-
/**
|
|
50
|
+
/** API key identifying the SDK consumer (e.g. "nike"). Set during initialize(). */
|
|
51
|
+
private var apiKey: String? = null
|
|
52
|
+
/** JWT returned by /api/app/sdk/verify. Injected as Authorization header on all subsequent calls. */
|
|
53
53
|
private var sdkToken: String? = null
|
|
54
54
|
|
|
55
55
|
// ── Timing constants ──────────────────────────────────────────────────────
|
|
@@ -137,7 +137,7 @@ class SpotnySdkModule(private val reactContext: ReactApplicationContext) :
|
|
|
137
137
|
override fun initialize(config: ReadableMap?, promise: Promise) {
|
|
138
138
|
config?.getDouble("maxDetectionDistance").takeIf { config?.hasKey("maxDetectionDistance") == true }
|
|
139
139
|
?.let { maxDetectionDistance = it; Log.d(TAG, "maxDetectionDistance = $it m") }
|
|
140
|
-
config?.getString("
|
|
140
|
+
config?.getString("apiKey")?.let { apiKey = it; Log.d(TAG, "apiKey = $it") }
|
|
141
141
|
config?.getDouble("distanceCorrectionFactor")
|
|
142
142
|
.takeIf { config?.hasKey("distanceCorrectionFactor") == true && it > 0 }
|
|
143
143
|
?.let { distanceCorrectionFactor = it; Log.d(TAG, "distanceCorrectionFactor = $it") }
|
|
@@ -146,22 +146,35 @@ class SpotnySdkModule(private val reactContext: ReactApplicationContext) :
|
|
|
146
146
|
promise.reject("MISSING_TOKEN", "initialize() requires a token")
|
|
147
147
|
return
|
|
148
148
|
}
|
|
149
|
-
|
|
149
|
+
val key = apiKey?.takeIf { it.isNotBlank() }
|
|
150
|
+
if (key == null) {
|
|
151
|
+
promise.reject("MISSING_API_KEY", "initialize() requires an apiKey")
|
|
152
|
+
return
|
|
153
|
+
}
|
|
150
154
|
|
|
151
|
-
//
|
|
152
|
-
|
|
155
|
+
// Send token + apiKey to backend — response returns a JWT used for all subsequent calls
|
|
156
|
+
// sdkToken is null here so no Authorization header is injected on this call
|
|
157
|
+
val verifyPayload = mapOf("token" to token, "api_key" to key)
|
|
158
|
+
post("/api/app/sdk/verify", verifyPayload) { status, body ->
|
|
153
159
|
when {
|
|
154
160
|
status in 200..299 -> {
|
|
155
|
-
|
|
161
|
+
val jwt = try {
|
|
162
|
+
val json = parseJsonObject(body)
|
|
163
|
+
((json?.get("data") as? Map<*, *>)?.get("jwt") as? String)?.takeIf { it.isNotBlank() }
|
|
164
|
+
} catch (_: Exception) { null }
|
|
165
|
+
if (jwt == null) {
|
|
166
|
+
promise.reject("VERIFY_FAILED", "SDK verification response missing JWT")
|
|
167
|
+
return@post
|
|
168
|
+
}
|
|
169
|
+
sdkToken = jwt
|
|
170
|
+
Log.d(TAG, "SDK initialized — JWT stored (apiKey: $apiKey)")
|
|
156
171
|
promise.resolve("SDK initialized")
|
|
157
172
|
}
|
|
158
173
|
status == 401 || status == 403 -> {
|
|
159
|
-
sdkToken = null
|
|
160
174
|
Log.w(TAG, "Token unauthorized ($status)")
|
|
161
175
|
promise.reject("UNAUTHORIZED", "Invalid or expired SDK token")
|
|
162
176
|
}
|
|
163
177
|
else -> {
|
|
164
|
-
sdkToken = null
|
|
165
178
|
Log.w(TAG, "Verification failed ($status)")
|
|
166
179
|
promise.reject("VERIFY_FAILED", "SDK verification returned status $status")
|
|
167
180
|
}
|
|
@@ -421,7 +434,7 @@ class SpotnySdkModule(private val reactContext: ReactApplicationContext) :
|
|
|
421
434
|
setRequestProperty("Content-Type", "application/json")
|
|
422
435
|
setRequestProperty("X-Device-ID", getDeviceId())
|
|
423
436
|
sdkToken?.let { setRequestProperty("Authorization", "Bearer $it") }
|
|
424
|
-
|
|
437
|
+
apiKey?.let { setRequestProperty("X-Api-Key", it) }
|
|
425
438
|
connectTimeout = 10_000
|
|
426
439
|
readTimeout = 10_000
|
|
427
440
|
doOutput = true
|
|
@@ -59,9 +59,9 @@ public class SpotnyBeaconScanner: NSObject {
|
|
|
59
59
|
/// Multiplier applied to raw RSSI-derived distance to compensate for low TX power.
|
|
60
60
|
/// Overridable via configure(). Default 0.5 matches Kontakt.io -12 dBm beacons.
|
|
61
61
|
private var distanceCorrectionFactor: Double = 0.5
|
|
62
|
-
///
|
|
63
|
-
private var
|
|
64
|
-
///
|
|
62
|
+
/// API key identifying the SDK consumer (e.g. "nike"). Set during initialize().
|
|
63
|
+
private var apiKey: String?
|
|
64
|
+
/// JWT returned by /api/app/sdk/verify. Injected as Authorization header on all subsequent calls.
|
|
65
65
|
private var sdkToken: String?
|
|
66
66
|
|
|
67
67
|
// ── Session TTL ────────────────────────────────────────────────────────────
|
|
@@ -203,9 +203,9 @@ public class SpotnyBeaconScanner: NSObject {
|
|
|
203
203
|
maxDetectionDistance = dist
|
|
204
204
|
print("⚙️ SpotnySDK: maxDetectionDistance = \(dist)m")
|
|
205
205
|
}
|
|
206
|
-
if let
|
|
207
|
-
|
|
208
|
-
print("⚙️ SpotnySDK:
|
|
206
|
+
if let key = config["apiKey"] as? String {
|
|
207
|
+
apiKey = key
|
|
208
|
+
print("⚙️ SpotnySDK: apiKey = \(key)")
|
|
209
209
|
}
|
|
210
210
|
if let factor = config["distanceCorrectionFactor"] as? Double, factor > 0 {
|
|
211
211
|
distanceCorrectionFactor = factor
|
|
@@ -215,27 +215,36 @@ public class SpotnyBeaconScanner: NSObject {
|
|
|
215
215
|
reject("MISSING_TOKEN", "initialize() requires a token", nil)
|
|
216
216
|
return
|
|
217
217
|
}
|
|
218
|
-
|
|
218
|
+
guard let key = apiKey, !key.isEmpty else {
|
|
219
|
+
reject("MISSING_API_KEY", "initialize() requires an apiKey", nil)
|
|
220
|
+
return
|
|
221
|
+
}
|
|
219
222
|
|
|
220
|
-
//
|
|
221
|
-
|
|
223
|
+
// Send token + apiKey to backend — response returns a JWT used for all subsequent calls
|
|
224
|
+
// sdkToken is nil here so no Authorization header is injected on this call
|
|
225
|
+
let verifyPayload: [String: Any] = ["token": token, "api_key": key]
|
|
226
|
+
post(endpoint: "/api/app/sdk/verify", payload: verifyPayload) { [weak self] result in
|
|
222
227
|
guard let self = self else { return }
|
|
223
228
|
switch result {
|
|
224
|
-
case .success(let (status,
|
|
229
|
+
case .success(let (status, data)):
|
|
225
230
|
if 200...299 ~= status {
|
|
226
|
-
|
|
231
|
+
guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
|
|
232
|
+
let dataObj = json["data"] as? [String: Any],
|
|
233
|
+
let jwt = dataObj["jwt"] as? String, !jwt.isEmpty else {
|
|
234
|
+
reject("VERIFY_FAILED", "SDK verification response missing JWT", nil)
|
|
235
|
+
return
|
|
236
|
+
}
|
|
237
|
+
self.sdkToken = jwt
|
|
238
|
+
print("✅ SpotnySDK: Initialized — JWT stored (apiKey: \(self.apiKey ?? "none"))")
|
|
227
239
|
resolve("SDK initialized")
|
|
228
240
|
} else if status == 401 || status == 403 {
|
|
229
|
-
self.sdkToken = nil
|
|
230
241
|
print("❌ SpotnySDK: Token unauthorized (\(status))")
|
|
231
242
|
reject("UNAUTHORIZED", "Invalid or expired SDK token", nil)
|
|
232
243
|
} else {
|
|
233
|
-
self.sdkToken = nil
|
|
234
244
|
print("❌ SpotnySDK: Verification failed (\(status))")
|
|
235
245
|
reject("VERIFY_FAILED", "SDK verification returned status \(status)", nil)
|
|
236
246
|
}
|
|
237
247
|
case .failure(let error):
|
|
238
|
-
self.sdkToken = nil
|
|
239
248
|
print("❌ SpotnySDK: Verification network error — \(error.localizedDescription)")
|
|
240
249
|
reject("VERIFY_ERROR", error.localizedDescription, error)
|
|
241
250
|
}
|
|
@@ -474,9 +483,9 @@ public class SpotnyBeaconScanner: NSObject {
|
|
|
474
483
|
req.httpMethod = "POST"
|
|
475
484
|
req.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
476
485
|
if let token = sdkToken { req.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") }
|
|
477
|
-
// device_id and
|
|
486
|
+
// device_id and api_key injected on every request — backend can verify without parsing the body
|
|
478
487
|
req.setValue(getDeviceId(), forHTTPHeaderField: "X-Device-ID")
|
|
479
|
-
if let
|
|
488
|
+
if let key = apiKey { req.setValue(key, forHTTPHeaderField: "X-Api-Key") }
|
|
480
489
|
req.timeoutInterval = 10.0
|
|
481
490
|
|
|
482
491
|
var bg: UIBackgroundTaskIdentifier = .invalid
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["NativeEventEmitter","NativeModules","NativeSpotnySdk","SpotnyEvents","ON_BEACONS_RANGED","ON_BEACON_REGION_EVENT","eventEmitter","SpotnySdk","startScanner","stopScanner","isScanning","initialize","config","identify","userId","requestNotificationPermissions","getDebugLogs","clearDebugLogs","setDebounceInterval","interval","clearDebounceCache","getDebounceStatus","addBeaconsRangedListener","callback","addListener","addBeaconRegionListener"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,kBAAkB,EAAEC,aAAa,QAAQ,cAAc;AAChE,OAAOC,eAAe,MAAM,sBAAmB;;AAE/C;AACA,OAAO,MAAMC,YAAY,GAAG;EAC1BC,iBAAiB,EAAE,iBAAiB;EACpCC,sBAAsB,EAAE;AAC1B,CAAU;;AAEV;;
|
|
1
|
+
{"version":3,"names":["NativeEventEmitter","NativeModules","NativeSpotnySdk","SpotnyEvents","ON_BEACONS_RANGED","ON_BEACON_REGION_EVENT","eventEmitter","SpotnySdk","startScanner","stopScanner","isScanning","initialize","config","identify","userId","requestNotificationPermissions","getDebugLogs","clearDebugLogs","setDebounceInterval","interval","clearDebounceCache","getDebounceStatus","addBeaconsRangedListener","callback","addListener","addBeaconRegionListener"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,kBAAkB,EAAEC,aAAa,QAAQ,cAAc;AAChE,OAAOC,eAAe,MAAM,sBAAmB;;AAE/C;AACA,OAAO,MAAMC,YAAY,GAAG;EAC1BC,iBAAiB,EAAE,iBAAiB;EACpCC,sBAAsB,EAAE;AAC1B,CAAU;;AAEV;;AA6CA;AACA,MAAMC,YAAY,GAAG,IAAIN,kBAAkB,CAACC,aAAa,CAACM,SAAS,CAAC;;AAEpE;;AAEA;AACA,OAAO,SAASC,YAAYA,CAAA,EAAoB;EAC9C,OAAON,eAAe,CAACM,YAAY,CAAC,CAAC;AACvC;;AAEA;AACA,OAAO,SAASC,WAAWA,CAAA,EAAoB;EAC7C,OAAOP,eAAe,CAACO,WAAW,CAAC,CAAC;AACtC;;AAEA;AACA,OAAO,SAASC,UAAUA,CAAA,EAAqB;EAC7C,OAAOR,eAAe,CAACQ,UAAU,CAAC,CAAC;AACrC;;AAEA;;AAEA;AACA;AACA;AACA,OAAO,SAASC,UAAUA,CAACC,MAAuB,EAAmB;EACnE,OAAOV,eAAe,CAACS,UAAU,CAACC,MAAgB,CAAC;AACrD;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,QAAQA,CAACC,MAAc,EAAmB;EACxD,OAAOZ,eAAe,CAACW,QAAQ,CAACC,MAAM,CAAC;AACzC;;AAEA;;AAEA;AACA,OAAO,SAASC,8BAA8BA,CAAA,EAAoB;EAChE,OAAOb,eAAe,CAACa,8BAA8B,CAAC,CAAC;AACzD;;AAEA;;AAEA;AACA,OAAO,SAASC,YAAYA,CAAA,EAAoB;EAC9C,OAAOd,eAAe,CAACc,YAAY,CAAC,CAAC;AACvC;;AAEA;AACA,OAAO,SAASC,cAAcA,CAAA,EAAoB;EAChD,OAAOf,eAAe,CAACe,cAAc,CAAC,CAAC;AACzC;;AAEA;;AAEA;AACA,OAAO,SAASC,mBAAmBA,CAACC,QAAgB,EAAmB;EACrE,OAAOjB,eAAe,CAACgB,mBAAmB,CAACC,QAAQ,CAAC;AACtD;;AAEA;AACA,OAAO,SAASC,kBAAkBA,CAAA,EAAoB;EACpD,OAAOlB,eAAe,CAACkB,kBAAkB,CAAC,CAAC;AAC7C;;AAEA;AACA,OAAO,SAASC,iBAAiBA,CAAA,EAAoB;EACnD,OAAOnB,eAAe,CAACmB,iBAAiB,CAAC,CAAC;AAC5C;;AAEA;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASC,wBAAwBA,CACtCC,QAA4C,EAC5C;EACA,OAAOjB,YAAY,CAACkB,WAAW,CAC7BrB,YAAY,CAACC,iBAAiB,EAC9BmB,QACF,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASE,uBAAuBA,CACrCF,QAA4C,EAC5C;EACA,OAAOjB,YAAY,CAACkB,WAAW,CAC7BrB,YAAY,CAACE,sBAAsB,EACnCkB,QACF,CAAC;AACH","ignoreList":[]}
|
|
@@ -25,11 +25,14 @@ export type BeaconRegionEvent = {
|
|
|
25
25
|
export type SpotnySdkConfig = {
|
|
26
26
|
/**
|
|
27
27
|
* SDK token issued by Spotny for your app.
|
|
28
|
-
*
|
|
28
|
+
* Sent to the backend during initialize() to obtain a session JWT. Required.
|
|
29
29
|
*/
|
|
30
30
|
token: string;
|
|
31
|
-
/**
|
|
32
|
-
|
|
31
|
+
/**
|
|
32
|
+
* API key identifying your brand or app (e.g. 'nike').
|
|
33
|
+
* Sent to the backend during initialize() alongside the token. Required.
|
|
34
|
+
*/
|
|
35
|
+
apiKey: string;
|
|
33
36
|
/** Maximum BLE detection distance in metres (default: 8.0) */
|
|
34
37
|
maxDetectionDistance?: number;
|
|
35
38
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAIA,eAAO,MAAM,YAAY;;;CAGf,CAAC;AAIX,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,YAAY,CAAC;IACvC,KAAK,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAIA,eAAO,MAAM,YAAY;;;CAGf,CAAC;AAIX,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,YAAY,CAAC;IACvC,KAAK,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;OAGG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC,CAAC;AAOF,6BAA6B;AAC7B,wBAAgB,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAE9C;AAED,mDAAmD;AACnD,wBAAgB,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,CAE7C;AAED,uDAAuD;AACvD,wBAAgB,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC,CAE7C;AAID;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAEnE;AAID;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAExD;AAID,gEAAgE;AAChE,wBAAgB,8BAA8B,IAAI,OAAO,CAAC,MAAM,CAAC,CAEhE;AAID,yCAAyC;AACzC,wBAAgB,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAE9C;AAED,2CAA2C;AAC3C,wBAAgB,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,CAEhD;AAID,gEAAgE;AAChE,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAErE;AAED,2DAA2D;AAC3D,wBAAgB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAEpD;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC,CAEnD;AAID;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,4CAM7C;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,4CAM7C"}
|
package/package.json
CHANGED
package/src/index.tsx
CHANGED
|
@@ -35,11 +35,14 @@ export type BeaconRegionEvent = {
|
|
|
35
35
|
export type SpotnySdkConfig = {
|
|
36
36
|
/**
|
|
37
37
|
* SDK token issued by Spotny for your app.
|
|
38
|
-
*
|
|
38
|
+
* Sent to the backend during initialize() to obtain a session JWT. Required.
|
|
39
39
|
*/
|
|
40
40
|
token: string;
|
|
41
|
-
/**
|
|
42
|
-
|
|
41
|
+
/**
|
|
42
|
+
* API key identifying your brand or app (e.g. 'nike').
|
|
43
|
+
* Sent to the backend during initialize() alongside the token. Required.
|
|
44
|
+
*/
|
|
45
|
+
apiKey: string;
|
|
43
46
|
/** Maximum BLE detection distance in metres (default: 8.0) */
|
|
44
47
|
maxDetectionDistance?: number;
|
|
45
48
|
/**
|