react-native-fpay 0.4.30 → 0.4.33

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.
Files changed (71) hide show
  1. package/lib/module/FountainPayProvider.js +5 -0
  2. package/lib/module/FountainPayProvider.js.map +1 -1
  3. package/lib/module/core/api/index.js +59 -0
  4. package/lib/module/core/api/index.js.map +1 -1
  5. package/lib/module/core/types/index.js +32 -0
  6. package/lib/module/core/types/index.js.map +1 -1
  7. package/lib/module/engine/FPEngine.js +9 -0
  8. package/lib/module/engine/FPEngine.js.map +1 -1
  9. package/lib/module/ui/components/ConfirmScreen.js +43 -51
  10. package/lib/module/ui/components/ConfirmScreen.js.map +1 -1
  11. package/lib/module/ui/components/RecurringToggle.js +94 -0
  12. package/lib/module/ui/components/RecurringToggle.js.map +1 -0
  13. package/lib/module/ui/modals/FPShell.js +19 -0
  14. package/lib/module/ui/modals/FPShell.js.map +1 -1
  15. package/lib/module/ui/screens/BillsScreen.js +187 -0
  16. package/lib/module/ui/screens/BillsScreen.js.map +1 -0
  17. package/lib/module/ui/screens/ResultScreen.js +113 -28
  18. package/lib/module/ui/screens/ResultScreen.js.map +1 -1
  19. package/lib/module/ui/screens/SendScreen.js +54 -6
  20. package/lib/module/ui/screens/SendScreen.js.map +1 -1
  21. package/lib/module/ui/screens/sub/billPayment/AirtimeScreen.js +257 -0
  22. package/lib/module/ui/screens/sub/billPayment/AirtimeScreen.js.map +1 -0
  23. package/lib/module/ui/screens/sub/billPayment/CableScreen.js +264 -0
  24. package/lib/module/ui/screens/sub/billPayment/CableScreen.js.map +1 -0
  25. package/lib/module/ui/screens/sub/billPayment/DataScreen.js +273 -0
  26. package/lib/module/ui/screens/sub/billPayment/DataScreen.js.map +1 -0
  27. package/lib/module/ui/screens/sub/billPayment/ElectricityScreen.js +337 -0
  28. package/lib/module/ui/screens/sub/billPayment/ElectricityScreen.js.map +1 -0
  29. package/lib/typescript/src/FountainPayProvider.d.ts.map +1 -1
  30. package/lib/typescript/src/core/api/index.d.ts +52 -63
  31. package/lib/typescript/src/core/api/index.d.ts.map +1 -1
  32. package/lib/typescript/src/core/types/index.d.ts +146 -0
  33. package/lib/typescript/src/core/types/index.d.ts.map +1 -1
  34. package/lib/typescript/src/engine/FPEngine.d.ts +4 -2
  35. package/lib/typescript/src/engine/FPEngine.d.ts.map +1 -1
  36. package/lib/typescript/src/index.d.ts +1 -1
  37. package/lib/typescript/src/index.d.ts.map +1 -1
  38. package/lib/typescript/src/ui/components/ConfirmScreen.d.ts +25 -4
  39. package/lib/typescript/src/ui/components/ConfirmScreen.d.ts.map +1 -1
  40. package/lib/typescript/src/ui/components/RecurringToggle.d.ts +7 -0
  41. package/lib/typescript/src/ui/components/RecurringToggle.d.ts.map +1 -0
  42. package/lib/typescript/src/ui/modals/FPShell.d.ts.map +1 -1
  43. package/lib/typescript/src/ui/screens/BillsScreen.d.ts +10 -0
  44. package/lib/typescript/src/ui/screens/BillsScreen.d.ts.map +1 -0
  45. package/lib/typescript/src/ui/screens/ResultScreen.d.ts +20 -3
  46. package/lib/typescript/src/ui/screens/ResultScreen.d.ts.map +1 -1
  47. package/lib/typescript/src/ui/screens/SendScreen.d.ts.map +1 -1
  48. package/lib/typescript/src/ui/screens/sub/billPayment/AirtimeScreen.d.ts +15 -0
  49. package/lib/typescript/src/ui/screens/sub/billPayment/AirtimeScreen.d.ts.map +1 -0
  50. package/lib/typescript/src/ui/screens/sub/billPayment/CableScreen.d.ts +14 -0
  51. package/lib/typescript/src/ui/screens/sub/billPayment/CableScreen.d.ts.map +1 -0
  52. package/lib/typescript/src/ui/screens/sub/billPayment/DataScreen.d.ts +14 -0
  53. package/lib/typescript/src/ui/screens/sub/billPayment/DataScreen.d.ts.map +1 -0
  54. package/lib/typescript/src/ui/screens/sub/billPayment/ElectricityScreen.d.ts +16 -0
  55. package/lib/typescript/src/ui/screens/sub/billPayment/ElectricityScreen.d.ts.map +1 -0
  56. package/package.json +2 -2
  57. package/src/FountainPayProvider.tsx +7 -0
  58. package/src/core/api/index.ts +149 -27
  59. package/src/core/types/index.ts +181 -0
  60. package/src/engine/FPEngine.ts +12 -1
  61. package/src/index.ts +9 -1
  62. package/src/ui/components/ConfirmScreen.tsx +47 -54
  63. package/src/ui/components/RecurringToggle.tsx +106 -0
  64. package/src/ui/modals/FPShell.tsx +26 -3
  65. package/src/ui/screens/BillsScreen.tsx +198 -0
  66. package/src/ui/screens/ResultScreen.tsx +129 -28
  67. package/src/ui/screens/SendScreen.tsx +43 -6
  68. package/src/ui/screens/sub/billPayment/AirtimeScreen.tsx +252 -0
  69. package/src/ui/screens/sub/billPayment/CableScreen.tsx +274 -0
  70. package/src/ui/screens/sub/billPayment/DataScreen.tsx +263 -0
  71. package/src/ui/screens/sub/billPayment/ElectricityScreen.tsx +344 -0
@@ -31,6 +31,7 @@ import type {
31
31
  FPTransferRecipient,
32
32
  FPAccount,
33
33
  FPBalance,
34
+ FPBillCategory,
34
35
  } from './core/types';
35
36
  import { accountAPI, authenticateAPI, transferAPI } from './core/api';
36
37
 
@@ -255,6 +256,12 @@ export function FountainPayProvider({
255
256
  FPEngine.showReceive(amount, currency);
256
257
  },
257
258
 
259
+ /** Open the Bill Payment bottom sheet for a category. */
260
+ payBill(amount: number, category: FPBillCategory): void {
261
+ console.log('[FountainPay] payBill() called:', amount, category);
262
+ FPEngine.showBills(amount, category);
263
+ },
264
+
258
265
  /** Generate a virtual account number (no UI shown). */
259
266
  async generateAccountNumber(user: FPUserInfo): Promise<FPAccount> {
260
267
  console.log(
@@ -13,6 +13,18 @@ import {
13
13
  type FPTransactionResponse,
14
14
  type FPBalance,
15
15
  type HttpCallResponseFormat,
16
+ type FPNetworkOperator,
17
+ type FPNetworkCode,
18
+ type FPDataPlan,
19
+ type FPBillProvider,
20
+ type FPBillTariff,
21
+ type FPMeterLookupResult,
22
+ type FPMeterType,
23
+ type FPAirtimePurchaseRequest,
24
+ type FPDataPurchaseRequest,
25
+ type FPElectricityPurchaseRequest,
26
+ type FPCablePurchaseRequest,
27
+ type FPBillTransaction,
16
28
  BUSINESSID,
17
29
  } from '../types';
18
30
 
@@ -20,32 +32,32 @@ export const healthAPI = {
20
32
  ping: () =>
21
33
  http()
22
34
  .get('/health')
23
- .then((r) => r.data),
35
+ .then((r: any) => r.data),
24
36
  };
25
37
 
26
38
  export const authenticateAPI = {
27
39
  login: (appId: string) =>
28
40
  http()
29
41
  .post('/auth', { user_id: appId })
30
- .then((r) => r.data),
42
+ .then((r: any) => r.data),
31
43
 
32
44
  profile: () =>
33
45
  http()
34
46
  .get(`/get-user-details`)
35
- .then((r) => r.data.payload),
47
+ .then((r: any) => r.data.payload),
36
48
 
37
49
  logout: () =>
38
50
  http()
39
51
  .post('/auth/logout')
40
- .then((r) => r.data),
52
+ .then((r: any) => r.data),
41
53
 
42
54
  validateOtp: (otp: string, email: string) =>
43
55
  http()
44
56
  .post<{ Response: any }>('/verify-otp', { otp, email })
45
- .then((r) => r.data),
57
+ .then((r: any) => r.data),
46
58
 
47
59
  validateToken: () =>
48
- http().get<{response: HttpCallResponseFormat}>('/auth/agent/validate-token').then(r => r.data),
60
+ http().get<{response: HttpCallResponseFormat}>('/auth/agent/validate-token').then((r: any) => r.data),
49
61
 
50
62
  sendSmsOtp: async(payload: any)=>
51
63
  http()
@@ -54,22 +66,22 @@ export const authenticateAPI = {
54
66
  message: string;
55
67
  payload:any
56
68
  }>("shared/send-sms-otp", payload)
57
- .then((r)=>r.data),
69
+ .then((r: any)=>r.data),
58
70
 
59
71
  verifyBvn: async() =>
60
72
  http()
61
73
  .get<{response: HttpCallResponseFormat}>("verify-bvn")
62
- .then((r)=>r.data),
74
+ .then((r: any)=>r.data),
63
75
 
64
76
  verifySms: async(payload: any)=>
65
77
  http()
66
78
  .post<{response: HttpCallResponseFormat}>("verify-bvn", payload)
67
- .then((r)=>r.data),
79
+ .then((r: any)=>r.data),
68
80
 
69
81
  updateProfile: async(payload: any)=>
70
82
  http()
71
83
  .put<{response: HttpCallResponseFormat}>("update-user-agent", payload)
72
- .then((r)=>r.data),
84
+ .then((r: any)=>r.data),
73
85
  };
74
86
 
75
87
  export const accountAPI = {
@@ -85,17 +97,17 @@ export const accountAPI = {
85
97
  nin: user.nin ?? '',
86
98
  dateOfBirth: user.dateOfBirth ?? '',
87
99
  })
88
- .then((r) => r.data.payload),
100
+ .then((r: any) => r.data.payload),
89
101
 
90
102
  // Get account details using PSSP id
91
103
  getAccount: () =>
92
104
  http()
93
105
  .get<{ payload: FPAccount }>(`/get-accounts-details`)
94
- .then((r) => r.data.payload),
106
+ .then((r: any) => r.data.payload),
95
107
 
96
108
  getAgentBalance: () =>
97
109
  http().get<{ payload: FPBalance }>(`/get-balance`)
98
- .then(r => r.data.payload),
110
+ .then((r: any) => r.data.payload),
99
111
 
100
112
  };
101
113
 
@@ -103,7 +115,7 @@ export const transferAPI = {
103
115
  getBanks: () =>
104
116
  http()
105
117
  .get<FPBankItem[]>('/get-banks')
106
- .then((r) => r.data),
118
+ .then((r: any) => r.data),
107
119
 
108
120
  verifyAccount: (accountNumber: string, bankCode: string) =>
109
121
  http()
@@ -116,7 +128,7 @@ export const transferAPI = {
116
128
  account_no: accountNumber,
117
129
  institution_code: bankCode,
118
130
  })
119
- .then((r) => r.data),
131
+ .then((r: any) => r.data),
120
132
 
121
133
  verifyWalletAccount: (accountNumber: string) =>
122
134
  http()
@@ -126,16 +138,16 @@ export const transferAPI = {
126
138
  bankName: string;
127
139
  bankCode: string;
128
140
  }>('/wallet-name-enquiry', { account_number: accountNumber })
129
- .then((r) => r.data),
141
+ .then((r: any) => r.data),
130
142
 
131
143
  validateTransfer: (pin: string, userId: string, receiverId: string) =>
132
144
  http()
133
- .post<{ status: boolean; message: string }>('/validate-transaction', {
145
+ .post<{ status: boolean; message: string; payload?: { temp_id: string } }>('/validate-transaction', {
134
146
  pin,
135
147
  sender_type: 'AGENT',
136
148
  receiver_id: receiverId,
137
149
  })
138
- .then((r) => r.data),
150
+ .then((r: any) => r.data),
139
151
 
140
152
  sendToWallet: (payload: any, temptId: string) =>
141
153
  http()
@@ -143,7 +155,7 @@ export const transferAPI = {
143
155
  ...payload,
144
156
  temp_id: temptId,
145
157
  })
146
- .then((r) => r.data),
158
+ .then((r: any) => r.data),
147
159
 
148
160
  // Send to external bank account
149
161
  sendToBank: (payload: FPSendPaymentRequest, temptId: string) =>
@@ -152,19 +164,19 @@ export const transferAPI = {
152
164
  ...payload,
153
165
  temp_id: temptId,
154
166
  })
155
- .then((r) => r.data),
167
+ .then((r: any) => r.data),
156
168
 
157
169
  status: (reference: string) =>
158
170
  http()
159
171
  .get<{ status: FPTxStatus; reference: string }>(
160
172
  '/get-transaction-status/' + reference
161
173
  )
162
- .then((r) => r.data),
174
+ .then((r: any) => r.data),
163
175
 
164
176
  verify: (reference: string) =>
165
177
  http()
166
178
  .get<{ FPTransactionResponse: any }>('/transaction/verify/' + reference)
167
- .then((r) => r.data),
179
+ .then((r: any) => r.data),
168
180
  };
169
181
 
170
182
  export const nqrAPI = {
@@ -176,19 +188,19 @@ export const nqrAPI = {
176
188
  }) =>
177
189
  http()
178
190
  .post<FPNQRData>('/generate-nqr', payload)
179
- .then((r) => r.data),
191
+ .then((r: any) => r.data),
180
192
 
181
193
  pay: (payload: FPSendPaymentRequest, temptId: string) =>
182
194
  http()
183
195
  .post<FPTransactionResponse>('/pay-nqr', { ...payload, temp_id: temptId })
184
- .then((r) => r.data),
196
+ .then((r: any) => r.data),
185
197
  };
186
198
 
187
199
  export const nfcAPI = {
188
200
  pay: (payload: FPSendPaymentRequest, temptId: string) =>
189
201
  http()
190
202
  .post<FPTransactionResponse>('/pay-nfc', { ...payload, temp_id: temptId })
191
- .then((r) => r.data),
203
+ .then((r: any) => r.data),
192
204
  };
193
205
 
194
206
  export const proximityAPI = {
@@ -198,7 +210,7 @@ export const proximityAPI = {
198
210
  ) =>
199
211
  http()
200
212
  .post<{ sessionId: string }>(`/broadcast-proximity/${psspId}`, payload)
201
- .then((r) => r.data),
213
+ .then((r: any) => r.data),
202
214
 
203
215
  heartbeat: (sessionId: string, lat: number, lng: number) =>
204
216
  http().patch(`/broadcast-proximity/${sessionId}`, {
@@ -216,5 +228,115 @@ export const proximityAPI = {
216
228
  longitude: lng,
217
229
  radius_meters,
218
230
  })
219
- .then((r) => r.data),
231
+ .then((r: any) => r.data),
220
232
  };
233
+
234
+
235
+ export const subscriptionAPI = {
236
+ create: (
237
+ payload: any
238
+ ) =>
239
+ http()
240
+ .post<{ sessionId: string }>(`/subscription/`, payload)
241
+ .then((r: any) => r.data),
242
+ }
243
+
244
+ export const billsAPI = {
245
+ getNetworks: () =>
246
+ http()
247
+ .get<{ status: boolean; payload: FPNetworkOperator[] }>('/bills/networks')
248
+ .then((r: any) => r.data),
249
+
250
+ getDataPlans: (network: FPNetworkCode) =>
251
+ http()
252
+ .get<{ status: boolean; payload: FPDataPlan[] }>('/bills/data-plans', {
253
+ params: { network },
254
+ })
255
+ .then((r: any) => r.data),
256
+
257
+ getDiscos: () =>
258
+ http()
259
+ .get<{ status: boolean; payload: FPBillProvider[] }>('/bills/electricity/discos')
260
+ .then((r: any) => r.data),
261
+
262
+ /** Validates a meter number against a disco before purchase — mirrors
263
+ * the existing CheckMeterNo() lookup in Tapit's BuyPower-backed
264
+ * Electricity screen, generalized behind this SDK's own endpoint. */
265
+ validateMeter: (meterNumber: string, disco: string, meterType: FPMeterType) =>
266
+ http()
267
+ .post<{ status: boolean; message: string; payload: FPMeterLookupResult }>(
268
+ '/bills/electricity/validate-meter',
269
+ { meter_number: meterNumber, disco, meter_type: meterType }
270
+ )
271
+ .then((r: any) => r.data),
272
+
273
+ getCableProviders: () =>
274
+ http()
275
+ .get<{ status: boolean; payload: FPBillProvider[] }>('/bills/cable/providers')
276
+ .then((r: any) => r.data),
277
+
278
+ getCableTariffs: (provider: string) =>
279
+ http()
280
+ .get<{ status: boolean; payload: FPBillTariff[] }>('/bills/cable/tariffs', {
281
+ params: { provider },
282
+ })
283
+ .then((r: any) => r.data),
284
+
285
+ /** Validates a smartcard/IUC number before purchase. */
286
+ validateSmartcard: (smartcardNumber: string, provider: string) =>
287
+ http()
288
+ .post<{ status: boolean; message: string; payload: FPMeterLookupResult }>(
289
+ '/bills/cable/validate-smartcard',
290
+ { smartcard_number: smartcardNumber, provider }
291
+ )
292
+ .then((r: any) => r.data),
293
+
294
+ /** Separate from transferAPI.validateTransfer — bills have no transfer
295
+ * recipient, so this hits its own backend endpoint rather than reusing
296
+ * /validate-transaction's receiver_id-shaped contract. One endpoint
297
+ * covers all four categories — the backend doesn't need to know which
298
+ * category is being authorized at PIN-check time. */
299
+ validateBillPin: (pin: string, userId: string) =>
300
+ http()
301
+ .post<{ status: boolean; message: string; payload: { temp_id: string } }>(
302
+ '/bills/validate-pin',
303
+ { pin, user_id: userId }
304
+ )
305
+ .then((r: any) => r.data),
306
+
307
+ purchaseAirtime: (payload: FPAirtimePurchaseRequest, tempId: string) =>
308
+ http()
309
+ .post<{ status: boolean; message: string; payload: FPBillTransaction }>(
310
+ '/bills/airtime',
311
+ { ...payload, temp_id: tempId }
312
+ )
313
+ .then((r: any) => r.data),
314
+
315
+ purchaseData: (payload: FPDataPurchaseRequest, tempId: string) =>
316
+ http()
317
+ .post<{ status: boolean; message: string; payload: FPBillTransaction }>(
318
+ '/bills/data',
319
+ { ...payload, temp_id: tempId }
320
+ )
321
+ .then((r: any) => r.data),
322
+
323
+ purchaseElectricity: (payload: FPElectricityPurchaseRequest, tempId: string) =>
324
+ http()
325
+ .post<{ status: boolean; message: string; payload: FPBillTransaction }>(
326
+ '/bills/electricity',
327
+ { ...payload, temp_id: tempId }
328
+ )
329
+ .then((r: any) => r.data),
330
+
331
+ purchaseCable: (payload: FPCablePurchaseRequest, tempId: string) =>
332
+ http()
333
+ .post<{ status: boolean; message: string; payload: FPBillTransaction }>(
334
+ '/bills/cable',
335
+ { ...payload, temp_id: tempId }
336
+ )
337
+ .then((r: any) => r.data),
338
+
339
+ /** Bill transactions live in the same agencyTransaction table as
340
+ * transfers, so the existing status endpoint is reused as-is. */
341
+ status: (reference: string) => transferAPI.status(reference),
342
+ };
@@ -213,6 +213,11 @@ export interface FPInstance {
213
213
  initializeSDK: (user: FPUserInfo | null, callbacks?: FPCallbacks) => Promise<void>;
214
214
  send: (amount: number, currency: FPCurrency) => void;
215
215
  receive: (amount?: number, currency?: FPCurrency) => void;
216
+ /** Open the Bill Payment bottom sheet for a given category and amount —
217
+ * mirrors send(amount, currency). The amount may be adjusted inside the
218
+ * sheet for categories where the user picks a plan/tariff (DATA, CABLE)
219
+ * rather than typing a free amount (AIRTIME, ELECTRICITY). */
220
+ payBill: (amount: number, category: FPBillCategory) => void;
216
221
  generateAccountNumber: (user: FPUserInfo) => Promise<FPAccount>;
217
222
  refreshBalance: () => Promise<void>;
218
223
  listen: () => void;
@@ -254,3 +259,179 @@ export interface ReceiveTransferScreenProps {
254
259
  onClose?: () => void;
255
260
  onPaymentReceived?: (data: any) => void;
256
261
  }
262
+
263
+ export type SubscriptionFreq = 'DAILY' | 'WEEKLY' | 'MONTHLY' | 'YEARLY';
264
+ export type SubscriptionStatus = 'ACTIVE' | 'PAUSED' | 'CANCELLED';
265
+ export type PaymentStatus = 'PENDING' | 'SUCCESS' | 'FAILED';
266
+
267
+ export interface Subscription {
268
+ id: string;
269
+ serviceName: string;
270
+ serviceCategory: string;
271
+ amount: number;
272
+ currency: string;
273
+ frequency: SubscriptionFreq;
274
+ nextPaymentDate: string;
275
+ accountNumber: string;
276
+ status: SubscriptionStatus;
277
+ logoUrl?: string;
278
+ description?: string;
279
+ createdAt: string;
280
+ payments?: SubscriptionPayment[];
281
+ }
282
+
283
+ export interface SubscriptionPayment {
284
+ id: string;
285
+ amount: number;
286
+ status: PaymentStatus;
287
+ processedAt?: string;
288
+ failureReason?: string;
289
+ createdAt: string;
290
+ }
291
+
292
+ export interface CreateSubscriptionPayload {
293
+ serviceName: string;
294
+ serviceCategory: string;
295
+ amount: number;
296
+ currency: string;
297
+ frequency: SubscriptionFreq;
298
+ nextPaymentDate: string;
299
+ accountNumber: string;
300
+ logoUrl?: string;
301
+ }
302
+
303
+ // ── Bill Payment ───────────────────────────────────────────────
304
+ // Added to support FPInstance.payBill(). Kept fully separate from
305
+ // FPTransferRecipient / FPSendPaymentRequest — bills have no recipient,
306
+ // so nothing here is unioned into the transfer types above.
307
+ //
308
+ // Provider routing (BuyPower today, Paga later, possibly per-category) is
309
+ // resolved entirely on the backend behind /bills/*. Nothing here encodes
310
+ // which provider services a category — the mobile layer is provider-agnostic
311
+ // by design, same as the rest of the SDK.
312
+
313
+ export type FPBillCategory = 'AIRTIME' | 'DATA' | 'ELECTRICITY' | 'CABLE';
314
+ export type FPNetworkCode = 'MTN' | 'AIRTEL' | 'GLO' | '9MOBILE';
315
+ export type FPMeterType = 'PREPAID' | 'POSTPAID';
316
+
317
+ export interface FPNetworkOperator {
318
+ code: FPNetworkCode;
319
+ displayName: string;
320
+ logoUrl: string;
321
+ brandColor: string;
322
+ isActive: boolean;
323
+ }
324
+
325
+ export interface FPDataPlan {
326
+ id: string;
327
+ network: FPNetworkCode;
328
+ label: string; // e.g. "1.5GB"
329
+ validity: string; // e.g. "30 days"
330
+ priceInKobo: number;
331
+ isAvailable: boolean;
332
+ }
333
+
334
+ /** A disco (electricity) or cable provider, loaded dynamically — same
335
+ * pattern as FPNetworkOperator, just for a different bill category. */
336
+ export interface FPBillProvider {
337
+ code: string; // e.g. "BENIN", "DSTV"
338
+ displayName: string;
339
+ logoUrl?: string;
340
+ isActive: boolean;
341
+ }
342
+
343
+ /** A purchasable tariff/plan for electricity (band) or cable (bouquet).
344
+ * Distinct from FPDataPlan because the fields that matter differ —
345
+ * electricity tariffs don't have "validity", cable bouquets don't have
346
+ * a meter-type concept. */
347
+ export interface FPBillTariff {
348
+ code: string;
349
+ label: string; // e.g. "DSTV Compact", "Band A"
350
+ priceInKobo: number; // 0 for electricity, where the user enters amount
351
+ isAvailable: boolean;
352
+ }
353
+
354
+ /** Returned by the meter/smartcard validation lookup, before purchase —
355
+ * mirrors what Electricity/index.tsx's checkMeterNumber() currently
356
+ * resolves locally against BuyPower, now generalized for any category
357
+ * that requires a pre-purchase ownership check. */
358
+ export interface FPMeterLookupResult {
359
+ customerName: string;
360
+ minVendAmountInKobo?: number; // electricity only
361
+ }
362
+
363
+ /** Generic shape ConfirmScreen/ResultScreen render — built by the caller
364
+ * (SendScreen builds one from `recipient`, BillsScreen builds one from
365
+ * network/phone/plan/meter) so neither shared component needs to know what
366
+ * a "recipient" or a "bill" is. */
367
+ export interface FPSummaryRow {
368
+ label: string;
369
+ value: string;
370
+ }
371
+
372
+ // Discriminated union — each category carries only the fields it actually
373
+ // needs, instead of one shape with a pile of optional fields where "which
374
+ // ones matter" depends on a category nobody can see from the type alone.
375
+
376
+ export interface FPAirtimePurchaseRequest {
377
+ category: 'AIRTIME';
378
+ phoneNumber: string;
379
+ network: FPNetworkCode;
380
+ amountInKobo: number;
381
+ idempotencyKey: string;
382
+ }
383
+
384
+ export interface FPDataPurchaseRequest {
385
+ category: 'DATA';
386
+ phoneNumber: string;
387
+ network: FPNetworkCode;
388
+ planId: string;
389
+ /** The selected plan's price — included here (not just inferred from
390
+ * planId) so BillsScreen can read amountInKobo generically across every
391
+ * category for the Confirm screen's amount display, the same way
392
+ * AIRTIME/ELECTRICITY/CABLE do. */
393
+ amountInKobo: number;
394
+ idempotencyKey: string;
395
+ }
396
+
397
+ export interface FPElectricityPurchaseRequest {
398
+ category: 'ELECTRICITY';
399
+ meterNumber: string;
400
+ disco: string;
401
+ meterType: FPMeterType;
402
+ amountInKobo: number;
403
+ forSelf: boolean;
404
+ phoneNumber: string;
405
+ email: string;
406
+ idempotencyKey: string;
407
+ }
408
+
409
+ export interface FPCablePurchaseRequest {
410
+ category: 'CABLE';
411
+ smartcardNumber: string;
412
+ provider: string; // e.g. "DSTV"
413
+ tariffCode: string;
414
+ amountInKobo: number;
415
+ phoneNumber: string;
416
+ idempotencyKey: string;
417
+ }
418
+
419
+ export type FPBillPurchaseRequest =
420
+ | FPAirtimePurchaseRequest
421
+ | FPDataPurchaseRequest
422
+ | FPElectricityPurchaseRequest
423
+ | FPCablePurchaseRequest;
424
+
425
+ export interface FPBillTransaction {
426
+ id: string;
427
+ reference: string;
428
+ category: FPBillCategory;
429
+ status: FPTxStatus;
430
+ amount: number;
431
+ currency: FPCurrency;
432
+ identifier: string;
433
+ network?: FPNetworkCode; // AIRTIME/DATA only
434
+ provider?: string; // ELECTRICITY/CABLE only
435
+ planLabel?: string;
436
+ createdAt: string;
437
+ }
@@ -22,6 +22,7 @@ import type {
22
22
  FPTransferRecipient,
23
23
  FPAccount,
24
24
  FPBalance,
25
+ FPBillCategory,
25
26
  } from '../core/types';
26
27
  import BLEReceiverService, {
27
28
  type BLEPaymentRequest,
@@ -55,7 +56,9 @@ type FPEventName =
55
56
  | 'incoming_payment_request'
56
57
  | 'show_send'
57
58
  | 'show_receive'
58
- | 'close_send';
59
+ | 'close_send'
60
+ | 'show_bills'
61
+ | 'close_bills';
59
62
  const _listeners = new Map<FPEventName, Set<Function>>();
60
63
 
61
64
  export function _emitEvent(event: FPEventName, data?: unknown): void {
@@ -633,6 +636,14 @@ export const FPEngine = {
633
636
  _emitEvent('show_receive', { amount, currency });
634
637
  },
635
638
 
639
+ closeBills(): void {
640
+ _emitEvent('close_bills');
641
+ },
642
+
643
+ showBills(amount: number, category: FPBillCategory): void {
644
+ _emitEvent('show_bills', { amount, category });
645
+ },
646
+
636
647
  getUser: () => _user,
637
648
  getAccount: () => _account,
638
649
  isReady: () => _isReady,
package/src/index.ts CHANGED
@@ -34,4 +34,12 @@ export type {
34
34
  FPError,
35
35
  FPTxStatus,
36
36
  FPChannel,
37
- } from './core/types';
37
+ FPBillCategory,
38
+ FPNetworkCode,
39
+ FPMeterType,
40
+ FPNetworkOperator,
41
+ FPDataPlan,
42
+ FPBillProvider,
43
+ FPBillTariff,
44
+ FPBillTransaction,
45
+ } from './core/types';