@jazadev/react-native 0.1.0

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/README.md ADDED
@@ -0,0 +1,241 @@
1
+ # `@jazadev/react-native`
2
+
3
+ Official React Native / Expo SDK for Jaza.
4
+
5
+ Use this in your **mobile app** with your publishable key (`jz_*_pk_*`) and a short-lived top-up token from **your backend**. Never put your secret key in the app.
6
+
7
+ Server-side calls (create customer, issue top-up JWT, read balance) use [`@jazadev/node`](https://www.npmjs.com/package/@jazadev/node) or any HTTP client with your secret key.
8
+
9
+ ### Try the sample
10
+
11
+ A private Expo app lives in [`example/`](./example). It demos sign-in (local JSON + Jaza customer), balance, and top-up with Expo Router API routes. See [example/README.md](./example/README.md). It is **not** published with this package.
12
+
13
+ ---
14
+
15
+ ## 1. Install
16
+
17
+ ```bash
18
+ npm install @jazadev/react-native
19
+ ```
20
+
21
+ Install peer dependencies (Expo):
22
+
23
+ ```bash
24
+ npx expo install react react-native react-native-reanimated react-native-gesture-handler react-native-safe-area-context react-native-screens @gorhom/bottom-sheet @expo/vector-icons
25
+ ```
26
+
27
+ ---
28
+
29
+ ## 2. Expo config
30
+
31
+ Add this so the phone number field stays visible when the keyboard opens on Android (the SDK also sets `android_keyboardInputMode="adjustResize"` on the bottom sheet, but the Activity must resize too).
32
+
33
+ **`app.json`**
34
+
35
+ ```json
36
+ {
37
+ "expo": {
38
+ "android": {
39
+ "softwareKeyboardLayoutMode": "resize"
40
+ }
41
+ }
42
+ }
43
+ ```
44
+
45
+ **`app.config.js`**
46
+
47
+ ```js
48
+ export default {
49
+ expo: {
50
+ android: {
51
+ softwareKeyboardLayoutMode: 'resize',
52
+ },
53
+ },
54
+ };
55
+ ```
56
+
57
+ ---
58
+
59
+ ## 3. Provider setup (app root)
60
+
61
+ Wrap your app with these providers, outermost first:
62
+
63
+ 1. `GestureHandlerRootView` — gestures for bottom sheets
64
+ 2. `JazaProvider` — Jaza state, theme, and checkout sheet
65
+
66
+ (`BottomSheetModalProvider` is not required; the SDK uses React Native `Modal` + `@gorhom/bottom-sheet`.)
67
+
68
+ `getBalance` is defined here once; `JazaBalanceWidget` and the offer step both use it.
69
+
70
+ ### Expo Router (`app/_layout.tsx`)
71
+
72
+ ```tsx
73
+ import { Stack } from 'expo-router';
74
+ import { GestureHandlerRootView } from 'react-native-gesture-handler';
75
+ import { JazaProvider } from '@jazadev/react-native';
76
+
77
+ const API_BASE = process.env.EXPO_PUBLIC_API_URL!;
78
+
79
+ export default function RootLayout() {
80
+ return (
81
+ <GestureHandlerRootView style={{ flex: 1 }}>
82
+ <JazaProvider
83
+ publishableKey={process.env.EXPO_PUBLIC_JAZA_PUBLISHABLE_KEY!}
84
+ getBalance={async () => {
85
+ const res = await fetch(`${API_BASE}/jaza/balance`, {
86
+ credentials: 'include',
87
+ });
88
+ const data = await res.json();
89
+ return data.balanceCredits as number;
90
+ }}
91
+ onTopUpComplete={({ credits }) => {
92
+ console.log('Top-up completed', credits);
93
+ }}
94
+ theme="system"
95
+ >
96
+ <Stack />
97
+ </JazaProvider>
98
+ </GestureHandlerRootView>
99
+ );
100
+ }
101
+ ```
102
+
103
+ ### Alternative: classic `App.tsx`
104
+
105
+ Same wrappers; replace `<Stack />` with your navigation tree or screen components.
106
+
107
+ ```tsx
108
+ import { GestureHandlerRootView } from 'react-native-gesture-handler';
109
+ import { JazaProvider } from '@jazadev/react-native';
110
+ import { HomeScreen } from './screens/HomeScreen';
111
+
112
+ export default function App() {
113
+ return (
114
+ <GestureHandlerRootView style={{ flex: 1 }}>
115
+ <JazaProvider
116
+ publishableKey={process.env.EXPO_PUBLIC_JAZA_PUBLISHABLE_KEY!}
117
+ getBalance={async () => {
118
+ const res = await fetch(`${process.env.EXPO_PUBLIC_API_URL}/jaza/balance`);
119
+ return (await res.json()).balanceCredits;
120
+ }}
121
+ theme="system"
122
+ >
123
+ <HomeScreen />
124
+ </JazaProvider>
125
+ </GestureHandlerRootView>
126
+ );
127
+ }
128
+ ```
129
+
130
+ ---
131
+
132
+ ## 4. Show balance — `JazaBalanceWidget`
133
+
134
+ Render on any screen inside `JazaProvider`. It calls `getBalance` on mount and after a successful top-up.
135
+
136
+ **`app/(tabs)/index.tsx`** (or your home screen)
137
+
138
+ ```tsx
139
+ import { View } from 'react-native';
140
+ import { JazaBalanceWidget } from '@jazadev/react-native';
141
+
142
+ export default function HomeScreen() {
143
+ return (
144
+ <View style={{ padding: 20 }}>
145
+ <JazaBalanceWidget />
146
+ </View>
147
+ );
148
+ }
149
+ ```
150
+
151
+ **Your server** (uses `@jazadev/node` with the secret key):
152
+
153
+ ```ts
154
+ import { Jaza } from '@jazadev/node';
155
+
156
+ const jaza = new Jaza({ secretKey: process.env.JAZA_SECRET_KEY!, publicKey: process.env.JAZA_PUBLIC_KEY! });
157
+
158
+ // GET /jaza/balance — resolve customerId from your auth session
159
+ app.get('/jaza/balance', async (req, res) => {
160
+ const wallet = await jaza.getBalance({ customerId: req.user.jazaCustomerId });
161
+ res.json({ balanceCredits: wallet.balanceCredits });
162
+ });
163
+ ```
164
+
165
+ ---
166
+
167
+ ## 5. Top up — `JazaTopUpButton`
168
+
169
+ Add the button on the same screen (or elsewhere under `JazaProvider`). It does **not** open the sheet until `onRequestToken` returns a JWT.
170
+
171
+ ```tsx
172
+ import { View } from 'react-native';
173
+ import { JazaBalanceWidget, JazaTopUpButton } from '@jazadev/react-native';
174
+
175
+ const API_BASE = process.env.EXPO_PUBLIC_API_URL!;
176
+
177
+ export default function HomeScreen() {
178
+ return (
179
+ <View style={{ padding: 20, gap: 16 }}>
180
+ <JazaBalanceWidget />
181
+ <JazaTopUpButton
182
+ label="Top up credits"
183
+ onRequestToken={async () => {
184
+ const res = await fetch(`${API_BASE}/jaza/top-up-token`, {
185
+ method: 'POST',
186
+ credentials: 'include',
187
+ });
188
+ if (!res.ok) throw new Error('Could not start top-up');
189
+ const data = await res.json();
190
+ return data.token as string;
191
+ }}
192
+ />
193
+ </View>
194
+ );
195
+ }
196
+ ```
197
+
198
+ **Your server**:
199
+
200
+ ```ts
201
+ // POST /jaza/top-up-token
202
+ app.post('/jaza/top-up-token', async (req, res) => {
203
+ const session = await jaza.topUp({ customerId: req.user.jazaCustomerId });
204
+ res.json({ token: session.token });
205
+ });
206
+ ```
207
+
208
+ Optional props:
209
+
210
+ - `label` — button text (default: `"Top up credits"`)
211
+ - `style` — `ViewStyle` for the button container
212
+
213
+ `JazaProvider` also accepts `onTopUpComplete` for when a payment succeeds.
214
+
215
+ ---
216
+
217
+ ## 6. What happens after the user taps Top up
218
+
219
+ 1. App calls your backend → you return a top-up JWT (`jaza.topUp`)
220
+ 2. Bottom sheet opens → bundles and current balance
221
+ 3. User picks a bundle → enters phone, country, currency → sees quote
222
+ 4. User confirms → payment starts → SDK polls until success or failure
223
+ 5. Balance refreshes via `getBalance`; sheet shows success or retry
224
+
225
+ ---
226
+
227
+ ## Exports
228
+
229
+ | Export | Description |
230
+ |--------|-------------|
231
+ | `JazaProvider` | Context, theme, sheet, API client |
232
+ | `JazaBalanceWidget` | Credits balance card |
233
+ | `JazaTopUpButton` | Opens sheet after `onRequestToken` |
234
+ | `useJaza` | Advanced access to sheet state |
235
+ | `PublicClient` | Low-level public API client |
236
+
237
+ ---
238
+
239
+ ## License
240
+
241
+ MIT