halo-sdk-react-native 1.0.1 → 1.0.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.
- package/CHANGELOG.md +27 -0
- package/README.md +130 -126
- package/android/build.gradle +2 -2
- package/android/src/main/AndroidManifest.xml +3 -1
- package/package.json +3 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `halo-sdk-react-native` will be documented here.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## [1.0.2] - 2026-03-03
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- Changed Halo SDK dependency declaration from `compileOnly` to `api` in `build.gradle` so it is exposed transitively to host apps. This prevents a `NoClassDefFoundError: HaloSDK` crash on launch, host apps no longer need to add the Halo SDK as a direct dependency.
|
|
11
|
+
|
|
12
|
+
### Documentation
|
|
13
|
+
- Updated README to remove the now-unnecessary manual Halo SDK runtime dependency step
|
|
14
|
+
- Fixed incorrect `resultType` check in code examples (`'success'`-> `'Initialized'`)
|
|
15
|
+
- Added Result Types reference table for `HaloInitializationResult` and `HaloTransactionResult`
|
|
16
|
+
- Added FAQ entries for common setup issues (Metro bundler `SyntaxError`, `JWTExpired` on init)
|
|
17
|
+
- Corrected the JWT section to reference official Halo documentation
|
|
18
|
+
- Fixed Full Example to use `requestHaloPermissions()` from `src/permissions.ts`
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## [1.0.1] - Initial release
|
|
23
|
+
|
|
24
|
+
- Initial React Native plugin wrapping the Halo Dot Android SDK
|
|
25
|
+
- Supports `initialize`, `startTransaction`, `cardRefundTransaction`, and `cancelTransaction`
|
|
26
|
+
- Event-driven callbacks via `NativeEventEmitter`: initialization, transaction result, UI messages, JWT requests, attestation errors, security errors, and camera events
|
|
27
|
+
- Scheme animations (Visa / Mastercard / Amex) on approved transactions via `AnimationActivity`
|
package/README.md
CHANGED
|
@@ -19,8 +19,6 @@ The diagram below shows the SDK boundary and how it interacts with your app, the
|
|
|
19
19
|
- [Native Module Setup](#native-module-setup)
|
|
20
20
|
- [AndroidManifest Permissions](#androidmanifest-permissions)
|
|
21
21
|
- [JWT — What It Is and How to Set It Up](#jwt--what-it-is-and-how-to-set-it-up)
|
|
22
|
-
- [JWT Lifetime](#jwt-lifetime)
|
|
23
|
-
- [JWT Claims](#jwt-claims)
|
|
24
22
|
- [Usage](#usage)
|
|
25
23
|
- [Step 1 — Request Permissions](#step-1--request-permissions)
|
|
26
24
|
- [Step 2 — Set Up Callbacks](#step-2--set-up-callbacks)
|
|
@@ -28,6 +26,7 @@ The diagram below shows the SDK boundary and how it interacts with your app, the
|
|
|
28
26
|
- [Step 4 — Run a Transaction](#step-4--run-a-transaction)
|
|
29
27
|
- [Full Example](#full-example)
|
|
30
28
|
- [API Reference](#api-reference)
|
|
29
|
+
- [Result Types](#result-types)
|
|
31
30
|
- [Documentation](#documentation)
|
|
32
31
|
- [Testing](#testing)
|
|
33
32
|
- [FAQ](#faq)
|
|
@@ -50,10 +49,7 @@ You will also need:
|
|
|
50
49
|
- A signed Non-Disclosure Agreement (NDA), available on the portal
|
|
51
50
|
- A JWT — generated via the developer portal or your own backend (see [JWT section](#jwt--what-it-is-and-how-to-set-it-up))
|
|
52
51
|
|
|
53
|
-
|
|
54
|
-
- [`react-native-permissions`](https://www.npmjs.com/package/react-native-permissions) `^4.0.0` — runtime permission handling
|
|
55
|
-
|
|
56
|
-
> **Note:** Android is the only supported platform.
|
|
52
|
+
> **Note:** Android is the only supported platform for now.
|
|
57
53
|
|
|
58
54
|
---
|
|
59
55
|
|
|
@@ -80,7 +76,7 @@ You must register on the QA (UAT) environment before going to production.
|
|
|
80
76
|
If you don't already have a React Native project, create one:
|
|
81
77
|
|
|
82
78
|
```bash
|
|
83
|
-
npx react-native init MyHaloApp
|
|
79
|
+
npx react-native init MyHaloApp
|
|
84
80
|
cd MyHaloApp
|
|
85
81
|
```
|
|
86
82
|
|
|
@@ -114,13 +110,7 @@ npm install halo-sdk-react-native
|
|
|
114
110
|
yarn add halo-sdk-react-native
|
|
115
111
|
```
|
|
116
112
|
|
|
117
|
-
**2.**
|
|
118
|
-
|
|
119
|
-
```bash
|
|
120
|
-
npm install react-native-permissions
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
**3.** The plugin downloads the Halo SDK binaries from an S3-backed Maven repo. Add your credentials (from the [developer portal](#developer-portal-registration)) to `android/local.properties` (create the file if it doesn't exist):
|
|
113
|
+
**2.** The plugin downloads the Halo SDK binaries from an S3-backed Maven repo. Add your credentials (from the [developer portal](#developer-portal-registration)) to `android/local.properties` (create the file if it doesn't exist):
|
|
124
114
|
|
|
125
115
|
```properties
|
|
126
116
|
aws.accesskey=YOUR_ACCESS_KEY
|
|
@@ -129,7 +119,7 @@ aws.secretkey=YOUR_SECRET_KEY
|
|
|
129
119
|
|
|
130
120
|
> **Important:** Never commit `local.properties` to source control. Add it to your `.gitignore`
|
|
131
121
|
|
|
132
|
-
**
|
|
122
|
+
**3.** Add the following snippet to `android/app/build.gradle` so Gradle can read `local.properties` (it may already be there in newer RN templates):
|
|
133
123
|
|
|
134
124
|
```gradle
|
|
135
125
|
def localProperties = new Properties()
|
|
@@ -141,6 +131,18 @@ if (localPropertiesFile.exists()) {
|
|
|
141
131
|
}
|
|
142
132
|
```
|
|
143
133
|
|
|
134
|
+
**4.** Add the following `packagingOptions` block inside the `android { }` closure in `android/app/build.gradle`. This prevents a duplicate-file error caused by OSGI metadata bundled in the SDK's transitive dependencies:
|
|
135
|
+
|
|
136
|
+
```gradle
|
|
137
|
+
android {
|
|
138
|
+
// ... your existing config ...
|
|
139
|
+
|
|
140
|
+
packagingOptions {
|
|
141
|
+
resources.excludes.add("META-INF/versions/9/OSGI-INF/MANIFEST.MF")
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
144
146
|
### Native Module Setup
|
|
145
147
|
|
|
146
148
|
**5.** Open `android/app/src/main/kotlin/.../MainActivity.kt` and extend `HaloReactActivity` instead of `ReactActivity`:
|
|
@@ -161,43 +163,9 @@ class MainActivity : HaloReactActivity() {
|
|
|
161
163
|
|
|
162
164
|
This replaces `ReactActivity` so that NFC foreground dispatch and the Halo SDK lifecycle are managed automatically.
|
|
163
165
|
|
|
164
|
-
**6.** Open `android/app/src/main/kotlin/.../MainApplication.kt` and register `HaloSdkPackage`:
|
|
165
|
-
|
|
166
|
-
```kotlin
|
|
167
|
-
import android.app.Application
|
|
168
|
-
import com.facebook.react.PackageList
|
|
169
|
-
import com.facebook.react.ReactApplication
|
|
170
|
-
import com.facebook.react.ReactNativeHost
|
|
171
|
-
import com.facebook.react.ReactPackage
|
|
172
|
-
import com.facebook.react.defaults.DefaultReactNativeHost
|
|
173
|
-
import com.facebook.soloader.SoLoader
|
|
174
|
-
import za.co.synthesis.halo.sdkreactnativeplugin.HaloSdkPackage // <-- add this import
|
|
175
|
-
|
|
176
|
-
class MainApplication : Application(), ReactApplication {
|
|
177
|
-
|
|
178
|
-
override val reactNativeHost: ReactNativeHost =
|
|
179
|
-
object : DefaultReactNativeHost(this) {
|
|
180
|
-
override fun getPackages(): List<ReactPackage> =
|
|
181
|
-
PackageList(this).packages.apply {
|
|
182
|
-
add(HaloSdkPackage()) // <-- add this line
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
override fun getJSMainModuleName(): String = "index"
|
|
186
|
-
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
|
|
187
|
-
override val isNewArchEnabled: Boolean = false
|
|
188
|
-
override val isHermesEnabled: Boolean = true
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
override fun onCreate() {
|
|
192
|
-
super.onCreate()
|
|
193
|
-
SoLoader.init(this, false)
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
```
|
|
197
|
-
|
|
198
166
|
### AndroidManifest Permissions
|
|
199
167
|
|
|
200
|
-
**
|
|
168
|
+
**6.** Add the required permissions to `android/app/src/main/AndroidManifest.xml`:
|
|
201
169
|
|
|
202
170
|
```xml
|
|
203
171
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
@@ -228,7 +196,14 @@ class MainApplication : Application(), ReactApplication {
|
|
|
228
196
|
|
|
229
197
|
<uses-feature android:name="android.hardware.nfc" android:required="true" />
|
|
230
198
|
|
|
231
|
-
|
|
199
|
+
<!--
|
|
200
|
+
tools:replace is required because the Halo SDK (and its bundled Visa library)
|
|
201
|
+
declare android:label and android:allowBackup in their own manifests.
|
|
202
|
+
Without these overrides the manifest merger will refuse to build.
|
|
203
|
+
-->
|
|
204
|
+
<application
|
|
205
|
+
...
|
|
206
|
+
tools:replace="android:label,android:allowBackup">
|
|
232
207
|
<activity
|
|
233
208
|
android:name=".MainActivity"
|
|
234
209
|
...>
|
|
@@ -251,25 +226,21 @@ class MainApplication : Application(), ReactApplication {
|
|
|
251
226
|
|
|
252
227
|
## JWT — What It Is and How to Set It Up
|
|
253
228
|
|
|
254
|
-
Every call to the Halo SDK must include a valid JWT.
|
|
229
|
+
Every call to the Halo SDK must include a valid JWT. The SDK requests one via the `onRequestJWT` callback whenever it needs to authenticate.
|
|
255
230
|
|
|
256
|
-
|
|
231
|
+
A JWT must be obtained from your backend or generated using your RSA private key and the credentials from the [developer portal](https://go.developerportal.qa.haloplus.io/). See the [Halo developer documentation](https://halo-dot-developer-docs.gitbook.io/halo-dot/sdk) for the required claims. For testing, paste a valid token directly into `Config.tempJwt` as shown below.
|
|
257
232
|
|
|
258
|
-
|
|
233
|
+
Split your config and JWT supply into two files:
|
|
259
234
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
**`src/config.ts`** — stores your app's settings (never commit real values to source control):
|
|
235
|
+
**`src/config.ts`** — stores your app settings (never commit real values to source control):
|
|
263
236
|
|
|
264
237
|
```typescript
|
|
265
|
-
// Copy this file to config.ts and fill in your values.
|
|
266
|
-
// config.ts should be added to .gitignore.
|
|
267
238
|
export const Config = {
|
|
268
|
-
// Paste a JWT
|
|
269
|
-
//
|
|
239
|
+
// Paste a valid JWT here for testing.
|
|
240
|
+
// Regenerate it if the SDK reports JWTExpired on startup.
|
|
270
241
|
tempJwt: 'eyJ...',
|
|
271
242
|
|
|
272
|
-
//
|
|
243
|
+
// Obtained from the developer portal
|
|
273
244
|
applicationPackageName: 'com.yourcompany.myapp',
|
|
274
245
|
applicationVersion: '1.0.0',
|
|
275
246
|
onStartTransactionTimeOut: 300000, // ms before "tap card" times out
|
|
@@ -277,46 +248,20 @@ export const Config = {
|
|
|
277
248
|
} as const;
|
|
278
249
|
```
|
|
279
250
|
|
|
280
|
-
**`src/jwt/JwtToken.ts
|
|
251
|
+
**`src/jwt/JwtToken.ts`**: supplies the JWT when the SDK asks for one:
|
|
281
252
|
|
|
282
253
|
```typescript
|
|
283
254
|
import { Config } from '../config';
|
|
284
255
|
|
|
285
|
-
/**
|
|
286
|
-
* Returns the JWT for the Halo SDK's onRequestJWT callback.
|
|
287
|
-
*
|
|
288
|
-
* For testing: set Config.tempJwt to a token from the developer portal.
|
|
289
|
-
* For production: replace this with a fetch from your backend server.
|
|
290
|
-
*/
|
|
291
256
|
export function getJwt(): string {
|
|
292
257
|
if (Config.tempJwt) {
|
|
293
258
|
return Config.tempJwt;
|
|
294
259
|
}
|
|
295
|
-
throw new Error(
|
|
296
|
-
'No JWT configured. Set Config.tempJwt in src/config.ts, ' +
|
|
297
|
-
'or implement a backend fetch here.',
|
|
298
|
-
);
|
|
260
|
+
throw new Error('No JWT configured. Set Config.tempJwt in src/config.ts.');
|
|
299
261
|
}
|
|
300
262
|
```
|
|
301
263
|
|
|
302
|
-
> **
|
|
303
|
-
|
|
304
|
-
### JWT Lifetime
|
|
305
|
-
|
|
306
|
-
Keep the lifetime short — 15 minutes is the recommended maximum. A stolen JWT can only be abused for as long as it remains valid.
|
|
307
|
-
|
|
308
|
-
### JWT Claims
|
|
309
|
-
|
|
310
|
-
| Field | Type | Description |
|
|
311
|
-
|---|---|---|
|
|
312
|
-
| `alg` | String | `RS512` — RSA + SHA-512. Asymmetric signing is required so the server can verify without being able to forge tokens. |
|
|
313
|
-
| `iss` | String | Your issuer name, registered on the developer portal (e.g. `authserver.haloplus.io`). |
|
|
314
|
-
| `sub` | String | Your merchant or application ID. |
|
|
315
|
-
| `aud` | String | The Halo kernel server URL (e.g. `kernelserver.qa.haloplus.io`). |
|
|
316
|
-
| `usr` | String | The signed-in operator/user identifier. |
|
|
317
|
-
| `iat` | NumericDate | UTC timestamp of when the token was created. |
|
|
318
|
-
| `exp` | NumericDate | UTC expiry timestamp. |
|
|
319
|
-
| `aud_fingerprints` | String | CSV of SHA-256 TLS fingerprints for the kernel server. Supports multiple values for certificate rotation. |
|
|
264
|
+
> **Important:** Add `config.ts` to `.gitignore` so you never accidentally commit a real token to source control.
|
|
320
265
|
|
|
321
266
|
---
|
|
322
267
|
|
|
@@ -376,12 +321,13 @@ export function buildCallbacks(options: {
|
|
|
376
321
|
onTransactionResult: (result: HaloTransactionResult) => void;
|
|
377
322
|
}): IHaloCallbacks {
|
|
378
323
|
return {
|
|
379
|
-
// Called when the SDK finishes starting up
|
|
324
|
+
// Called when the SDK finishes starting up.
|
|
325
|
+
// Check resultType === 'Initialized' for successful init.
|
|
380
326
|
onInitializationResult(result: HaloInitializationResult) {
|
|
381
|
-
if (result.resultType === '
|
|
327
|
+
if (result.resultType === 'Initialized') {
|
|
382
328
|
options.onStatusChange('SDK ready — present a card to pay');
|
|
383
329
|
} else {
|
|
384
|
-
options.onError(`Initialisation failed: ${result.errorCode}`);
|
|
330
|
+
options.onError(`Initialisation failed: ${result.resultType} (${result.errorCode})`);
|
|
385
331
|
}
|
|
386
332
|
},
|
|
387
333
|
|
|
@@ -471,7 +417,7 @@ const refund = await HaloSdk.cardRefundTransaction(10.50, 'ORDER-001', 'ZAR');
|
|
|
471
417
|
await HaloSdk.cancelTransaction();
|
|
472
418
|
```
|
|
473
419
|
|
|
474
|
-
`startTransaction` and `cardRefundTransaction` resolve immediately once the tap is registered
|
|
420
|
+
`startTransaction` and `cardRefundTransaction` resolve immediately once the tap is registered, the final payment outcome arrives through `onHaloTransactionResult`.
|
|
475
421
|
|
|
476
422
|
### Full Example
|
|
477
423
|
|
|
@@ -479,11 +425,9 @@ Below is a minimal but complete payment screen taken directly from the example a
|
|
|
479
425
|
|
|
480
426
|
```tsx
|
|
481
427
|
// App.tsx
|
|
482
|
-
import
|
|
428
|
+
import { useEffect, useState } from 'react';
|
|
483
429
|
import {
|
|
484
430
|
ActivityIndicator,
|
|
485
|
-
PermissionsAndroid,
|
|
486
|
-
Platform,
|
|
487
431
|
Text,
|
|
488
432
|
TextInput,
|
|
489
433
|
TouchableOpacity,
|
|
@@ -496,6 +440,7 @@ import {
|
|
|
496
440
|
} from 'halo-sdk-react-native';
|
|
497
441
|
import { getJwt } from './src/jwt/JwtToken';
|
|
498
442
|
import { Config } from './src/config';
|
|
443
|
+
import { requestHaloPermissions } from './src/permissions';
|
|
499
444
|
|
|
500
445
|
export default function App() {
|
|
501
446
|
const [amount, setAmount] = useState('');
|
|
@@ -508,33 +453,15 @@ export default function App() {
|
|
|
508
453
|
}, []);
|
|
509
454
|
|
|
510
455
|
async function initSdk() {
|
|
511
|
-
|
|
512
|
-
if (Platform.OS === 'android') {
|
|
513
|
-
const sdkVersion =
|
|
514
|
-
typeof Platform.Version === 'number'
|
|
515
|
-
? Platform.Version
|
|
516
|
-
: parseInt(Platform.Version, 10);
|
|
517
|
-
|
|
518
|
-
const perms: string[] = [
|
|
519
|
-
PermissionsAndroid.PERMISSIONS.CAMERA,
|
|
520
|
-
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
|
|
521
|
-
];
|
|
522
|
-
if (sdkVersion >= 31) {
|
|
523
|
-
perms.push(
|
|
524
|
-
PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN,
|
|
525
|
-
PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT,
|
|
526
|
-
);
|
|
527
|
-
}
|
|
528
|
-
await PermissionsAndroid.requestMultiple(perms);
|
|
529
|
-
}
|
|
456
|
+
await requestHaloPermissions();
|
|
530
457
|
|
|
531
458
|
const callbacks: IHaloCallbacks = {
|
|
532
459
|
onInitializationResult(result) {
|
|
533
|
-
setIsReady(result.resultType === '
|
|
460
|
+
setIsReady(result.resultType === 'Initialized');
|
|
534
461
|
setStatus(
|
|
535
|
-
result.resultType === '
|
|
462
|
+
result.resultType === 'Initialized'
|
|
536
463
|
? 'Ready — enter amount and tap Charge'
|
|
537
|
-
: `Init failed: ${result.errorCode}`,
|
|
464
|
+
: `Init failed: ${result.resultType} (${result.errorCode})`,
|
|
538
465
|
);
|
|
539
466
|
},
|
|
540
467
|
onHaloTransactionResult(result: HaloTransactionResult) {
|
|
@@ -656,13 +583,42 @@ Cancels the current in-progress transaction (e.g. if the user presses Cancel whi
|
|
|
656
583
|
| Callback | When it fires |
|
|
657
584
|
|---|---|
|
|
658
585
|
| `onInitializationResult(result)` | SDK startup complete (success or failure) |
|
|
659
|
-
| `onHaloTransactionResult(result)` | Final payment outcome
|
|
586
|
+
| `onHaloTransactionResult(result)` | Final payment outcome; approved, declined, or error |
|
|
660
587
|
| `onHaloUIMessage(message)` | Prompt to show the user during a tap: `PRESENT_CARD`, `PROCESSING`, `APPROVED`, etc. |
|
|
661
|
-
| `onRequestJWT(jwtCallback)` | SDK needs a fresh JWT
|
|
588
|
+
| `onRequestJWT(jwtCallback)` | SDK needs a fresh JWT; call `jwtCallback(yourJwtString)` |
|
|
662
589
|
| `onAttestationError(details)` | Device integrity check failed (rooted device, emulator, etc.) |
|
|
663
590
|
| `onSecurityError(errorCode)` | JWT invalid, merchant revoked, or other security failure |
|
|
664
591
|
| `onCameraControlLost()` | SDK has finished using the camera |
|
|
665
592
|
|
|
593
|
+
### Result Types
|
|
594
|
+
|
|
595
|
+
`resultType` and `errorCode` are plain strings serialised from native Android enums. Use these known values in your conditional logic.
|
|
596
|
+
|
|
597
|
+
**`HaloInitializationResult.resultType`**
|
|
598
|
+
|
|
599
|
+
| Value | Meaning |
|
|
600
|
+
|---|---|
|
|
601
|
+
| `'Initialized'` | SDK ready; safe to call `startTransaction` |
|
|
602
|
+
| `'RemoteAttestationFailure'` | Cloud attestation failed; inspect `errorCode` for the reason |
|
|
603
|
+
| `'LocalAttestationFailure'` | Device integrity check failed (rooted device, emulator, etc.) |
|
|
604
|
+
|
|
605
|
+
**`HaloInitializationResult.errorCode`** (when `resultType !== 'Initialized'`)
|
|
606
|
+
|
|
607
|
+
| Value | Meaning |
|
|
608
|
+
|---|---|
|
|
609
|
+
| `'OK'` | No error — accompanies `resultType: 'Initialized'` |
|
|
610
|
+
| `'JWTExpired'` | The JWT has expired — generate a fresh one |
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
**`HaloTransactionResult.resultType`**
|
|
614
|
+
|
|
615
|
+
| Value | Meaning |
|
|
616
|
+
|---|---|
|
|
617
|
+
| `'Approved'` | Transaction approved by the payment network |
|
|
618
|
+
| `'Declined'` | Card declined |
|
|
619
|
+
| `'Cancelled'` | Transaction cancelled (e.g. via `cancelTransaction()`) |
|
|
620
|
+
| `'Error'` | An error occurred — inspect `errorCode` and `errorDetails` |
|
|
621
|
+
|
|
666
622
|
---
|
|
667
623
|
|
|
668
624
|
## Documentation
|
|
@@ -686,7 +642,7 @@ You can simulate card taps using a virtual NFC card app such as [Visa Mobile CDE
|
|
|
686
642
|
You can define them in `android/local.properties` and read them in Gradle:
|
|
687
643
|
|
|
688
644
|
```properties
|
|
689
|
-
sdk.dir
|
|
645
|
+
sdk.dir=C\:\\Users\\yourname\\Library\\Android/Sdk
|
|
690
646
|
aws.accesskey=YOUR_ACCESS_KEY
|
|
691
647
|
aws.secretkey=YOUR_SECRET_KEY
|
|
692
648
|
compileSdkVersion=34
|
|
@@ -711,7 +667,6 @@ compileSdkVersion localProperties.getProperty('compileSdkVersion').toInteger()
|
|
|
711
667
|
**Q: I get a build error about `HaloReactActivity` or `HaloSdkPackage` not found.**
|
|
712
668
|
|
|
713
669
|
- Confirm the npm package is installed: `npm install halo-sdk-react-native`
|
|
714
|
-
- Confirm `HaloSdkPackage()` is added in `MainApplication.kt`
|
|
715
670
|
- Confirm `MainActivity` extends `HaloReactActivity` (not `ReactActivity`)
|
|
716
671
|
- Run a Gradle sync in Android Studio
|
|
717
672
|
|
|
@@ -726,6 +681,55 @@ compileSdkVersion localProperties.getProperty('compileSdkVersion').toInteger()
|
|
|
726
681
|
|
|
727
682
|
---
|
|
728
683
|
|
|
729
|
-
**Q: How do I get a
|
|
684
|
+
**Q: How do I get a JWT for testing?**
|
|
685
|
+
|
|
686
|
+
A JWT must be generated using your RSA private key and the credentials from the [developer portal](https://go.developerportal.qa.haloplus.io/). See the [Halo developer documentation](https://halo-dot-developer-docs.gitbook.io/halo-dot/sdk) for the required claims and signing algorithm. Once you have a valid token, paste it into `Config.tempJwt` in your `config.ts`.
|
|
687
|
+
|
|
688
|
+
---
|
|
689
|
+
|
|
690
|
+
**Q: Manifest merger fails with an attribute conflict (e.g. `android:label`, `android:allowBackup`).**
|
|
691
|
+
|
|
692
|
+
The Halo SDK bundles several sub-libraries (Visa Sensory Branding, etc.), each with their own `AndroidManifest.xml`. Any `processDebugMainManifest` failure caused by an attribute clash is fixed by adding the conflicting attribute name to `tools:replace` on your `<application>` element:
|
|
693
|
+
|
|
694
|
+
```xml
|
|
695
|
+
<application
|
|
696
|
+
...
|
|
697
|
+
tools:replace="android:label,android:allowBackup">
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
If you add a new attribute to `tools:replace` and the **same error persists on the very next build**, Gradle may have cached the previously failed manifest merge. Run a clean build and try again:
|
|
701
|
+
|
|
702
|
+
```bash
|
|
703
|
+
cd android && ./gradlew clean
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
Then re run your normal build (`npx react-native run-android` or Android Studio).
|
|
707
|
+
|
|
708
|
+
---
|
|
709
|
+
|
|
710
|
+
**Q: TypeScript build errors about `customConditions` or `moduleResolution` after editing `tsconfig.json`.**
|
|
711
|
+
|
|
712
|
+
Do not override `moduleResolution` in your project's `tsconfig.json`. The base `@react-native/typescript-config` sets `"moduleResolution": "bundler"`, which is the only value compatible with its `customConditions` setting. Overriding it to `"node"` causes a TypeScript error.
|
|
713
|
+
|
|
714
|
+
Instead, extend the base config and only add project specific overrides:
|
|
715
|
+
|
|
716
|
+
```json
|
|
717
|
+
{
|
|
718
|
+
"extends": "@react-native/typescript-config/tsconfig.json",
|
|
719
|
+
"compilerOptions": {
|
|
720
|
+
"skipLibCheck": true
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
`skipLibCheck: true` suppresses spurious type errors that originate inside `node_modules` (e.g. phantom `@types/react` v19 conflicts) without changing how your own code is compiled.
|
|
726
|
+
|
|
727
|
+
---
|
|
728
|
+
|
|
729
|
+
**Q: `onInitializationResult` fires with `resultType: 'RemoteAttestationFailure'` and `errorCode: 'JWTExpired'`.**
|
|
730
|
+
|
|
731
|
+
Your temp JWT has expired. Developer-portal tokens are short-lived (typically 15 minutes). This is expected behaviour — the SDK fires a failure callback with the cached/expired token, then automatically requests a new JWT via `onRequestJWT` and retries. If `onInitializationResult` fires a **second time** shortly after with `resultType: 'Initialized'`, everything is working correctly.
|
|
730
732
|
|
|
731
|
-
|
|
733
|
+
If the second successful callback never arrives:
|
|
734
|
+
1. Generate a fresh token and update `Config.tempJwt`
|
|
735
|
+
2. Make sure your `onRequestJWT` callback calls `jwtCallback(yourJwt)` synchronously — async calls are not supported without extra handling
|
package/android/build.gradle
CHANGED
|
@@ -81,8 +81,8 @@ dependencies {
|
|
|
81
81
|
implementation 'androidx.appcompat:appcompat:1.6.1'
|
|
82
82
|
|
|
83
83
|
// Halo SDK
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
releaseApi group: "za.co.synthesis.halo", name: "sdk", version: "$sdkVersion", changing: true
|
|
85
|
+
debugApi group: "za.co.synthesis.halo", name: "sdk", version: "$sdkVersion-debug", changing: true
|
|
86
86
|
|
|
87
87
|
implementation(name: 'VisaSensoryBranding', ext: 'aar')
|
|
88
88
|
implementation(name: 'sonic-sdk-release-1.5.0', ext: 'aar')
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2
|
+
xmlns:tools="http://schemas.android.com/tools"
|
|
2
3
|
package="za.co.synthesis.halo.sdkreactnativeplugin">
|
|
3
|
-
<application
|
|
4
|
+
<application
|
|
5
|
+
tools:replace="android:label,android:allowBackup">
|
|
4
6
|
<activity
|
|
5
7
|
android:name=".AnimationActivity"
|
|
6
8
|
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "halo-sdk-react-native",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "React Native plugin for Halo SDK",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"android/libs",
|
|
11
11
|
"android/build.gradle",
|
|
12
12
|
"android/settings.gradle",
|
|
13
|
-
"README.md"
|
|
13
|
+
"README.md",
|
|
14
|
+
"CHANGELOG.md"
|
|
14
15
|
],
|
|
15
16
|
"scripts": {
|
|
16
17
|
"build": "tsc"
|