react-native-fpay 0.1.1 → 0.2.2
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/lib/module/FountainPayProvider.js +123 -7
- package/lib/module/FountainPayProvider.js.map +1 -1
- package/lib/module/useFountainPay.js +3 -79
- package/lib/module/useFountainPay.js.map +1 -1
- package/lib/typescript/src/FountainPayProvider.d.ts +15 -2
- package/lib/typescript/src/FountainPayProvider.d.ts.map +1 -1
- package/lib/typescript/src/useFountainPay.d.ts +1 -2
- package/lib/typescript/src/useFountainPay.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/FountainPayProvider.tsx +145 -10
- package/src/useFountainPay.ts +3 -95
|
@@ -1,17 +1,133 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
//
|
|
3
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
4
4
|
// FountainPayProvider
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
|
|
5
|
+
//
|
|
6
|
+
// Mount once at the root of your app and pass your API key here.
|
|
7
|
+
// Every component can then call useFountainPay() (no arguments) and get
|
|
8
|
+
// the SAME instance — no re-initialization, no prop drilling.
|
|
9
|
+
//
|
|
10
|
+
// Usage:
|
|
11
|
+
// <FountainPayProvider apiKey="fp_live_...">
|
|
12
|
+
// <YourApp />
|
|
13
|
+
// </FountainPayProvider>
|
|
14
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
15
|
+
import React, { createContext, useContext, useMemo, useRef } from 'react';
|
|
9
16
|
import { FPShell } from "./ui/modals/FPShell.js";
|
|
10
|
-
import {
|
|
17
|
+
import { FPEngine } from "./engine/FPEngine.js";
|
|
18
|
+
import { initClient } from "./core/api/client.js";
|
|
19
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
20
|
+
// ── Context ───────────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
const FountainPayContext = /*#__PURE__*/createContext(null);
|
|
23
|
+
|
|
24
|
+
// ── Hook — the only thing components import ───────────────────────────────────
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Returns the shared FountainPay instance created by the nearest
|
|
28
|
+
* <FountainPayProvider>. No API key needed here — it was given to the provider.
|
|
29
|
+
*
|
|
30
|
+
* Call from any component:
|
|
31
|
+
* const pay = useFountainPay();
|
|
32
|
+
* await pay.initializeSDK(user, callbacks); // call once after login
|
|
33
|
+
* pay.send(500, 'NGN'); // call anywhere, anytime
|
|
34
|
+
*/
|
|
35
|
+
export function useFountainPay() {
|
|
36
|
+
const instance = useContext(FountainPayContext);
|
|
37
|
+
if (!instance) {
|
|
38
|
+
throw new Error('[FountainPay] useFountainPay() must be called inside <FountainPayProvider>.\n' + 'Make sure you have wrapped your app root with <FountainPayProvider apiKey="...">.');
|
|
39
|
+
}
|
|
40
|
+
return instance;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ── Provider ──────────────────────────────────────────────────────────────────
|
|
44
|
+
|
|
11
45
|
export function FountainPayProvider({
|
|
46
|
+
apiKey,
|
|
47
|
+
options,
|
|
12
48
|
children
|
|
13
49
|
}) {
|
|
14
|
-
|
|
50
|
+
// Boot the HTTP client as soon as the provider mounts —
|
|
51
|
+
// this means generateAccountNumber works even before initializeSDK
|
|
52
|
+
const clientBooted = useRef(false);
|
|
53
|
+
if (!clientBooted.current) {
|
|
54
|
+
initClient(apiKey, {
|
|
55
|
+
baseUrl: options?.baseUrl,
|
|
56
|
+
environment: options?.environment
|
|
57
|
+
});
|
|
58
|
+
clientBooted.current = true;
|
|
59
|
+
console.log('[FountainPay] Provider mounted. HTTP client ready.');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Build the instance once per provider lifetime.
|
|
63
|
+
// All components that call useFountainPay() share this exact object.
|
|
64
|
+
const instance = useMemo(() => {
|
|
65
|
+
console.log('[FountainPay] Creating shared FPInstance for apiKey:', apiKey.slice(0, 8) + '...');
|
|
66
|
+
return {
|
|
67
|
+
/**
|
|
68
|
+
* Call once after the user logs in.
|
|
69
|
+
* Starts BLE advertising, proximity broadcast, and the payment listener.
|
|
70
|
+
*/
|
|
71
|
+
async initializeSDK(user, callbacks) {
|
|
72
|
+
console.log('[FountainPay] initializeSDK() called for user:', user.accountName);
|
|
73
|
+
try {
|
|
74
|
+
await FPEngine.initialize(apiKey, user, options ?? {}, callbacks ?? {});
|
|
75
|
+
console.log('[FountainPay] initializeSDK() complete. SDK is ready.');
|
|
76
|
+
} catch (err) {
|
|
77
|
+
console.error('[FountainPay] initializeSDK() FAILED:', err);
|
|
78
|
+
throw err;
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
/** Open the Send Money bottom sheet. */
|
|
82
|
+
send(amount, currency) {
|
|
83
|
+
console.log('[FountainPay] send() called:', amount, currency);
|
|
84
|
+
if (!FPEngine.isReady()) {
|
|
85
|
+
console.warn('[FountainPay] send() called before initializeSDK() — call initializeSDK first.');
|
|
86
|
+
}
|
|
87
|
+
FPEngine.showSend(amount, currency);
|
|
88
|
+
},
|
|
89
|
+
/** Open the Receive Money bottom sheet. */
|
|
90
|
+
receive(amount, currency = 'NGN') {
|
|
91
|
+
console.log('[FountainPay] receive() called:', amount, currency);
|
|
92
|
+
if (!FPEngine.isReady()) {
|
|
93
|
+
console.warn('[FountainPay] receive() called before initializeSDK().');
|
|
94
|
+
}
|
|
95
|
+
FPEngine.showReceive(amount, currency);
|
|
96
|
+
},
|
|
97
|
+
/** Generate a virtual account number (no UI shown). */
|
|
98
|
+
async generateAccountNumber(request) {
|
|
99
|
+
console.log('[FountainPay] generateAccountNumber() called:', request.accountName);
|
|
100
|
+
return FPEngine.generateAccount(request);
|
|
101
|
+
},
|
|
102
|
+
/**
|
|
103
|
+
* Activate the incoming Bluetooth payment request listener.
|
|
104
|
+
* When a nearby device sends a payment request, the Accept/Decline
|
|
105
|
+
* modal will appear automatically.
|
|
106
|
+
*/
|
|
107
|
+
listen() {
|
|
108
|
+
console.log('[FountainPay] listen() called — activating BT payment listener.');
|
|
109
|
+
if (!FPEngine.isReady()) {
|
|
110
|
+
console.warn('[FountainPay] listen() called before initializeSDK().');
|
|
111
|
+
}
|
|
112
|
+
FPEngine.startListening();
|
|
113
|
+
},
|
|
114
|
+
/** Stop all background services. Call on logout. */
|
|
115
|
+
async destroy() {
|
|
116
|
+
console.log('[FountainPay] destroy() called — stopping all services.');
|
|
117
|
+
await FPEngine.destroy();
|
|
118
|
+
console.log('[FountainPay] All services stopped.');
|
|
119
|
+
},
|
|
120
|
+
get isReady() {
|
|
121
|
+
return FPEngine.isReady();
|
|
122
|
+
},
|
|
123
|
+
get currentUser() {
|
|
124
|
+
return FPEngine.getUser();
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
// Re-create instance only if the apiKey changes (which should never happen at runtime)
|
|
128
|
+
}, [apiKey]);
|
|
129
|
+
return /*#__PURE__*/_jsxs(FountainPayContext.Provider, {
|
|
130
|
+
value: instance,
|
|
15
131
|
children: [children, /*#__PURE__*/_jsx(FPShell, {})]
|
|
16
132
|
});
|
|
17
133
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","FPShell","
|
|
1
|
+
{"version":3,"names":["React","createContext","useContext","useMemo","useRef","FPShell","FPEngine","initClient","jsx","_jsx","jsxs","_jsxs","FountainPayContext","useFountainPay","instance","Error","FountainPayProvider","apiKey","options","children","clientBooted","current","baseUrl","environment","console","log","slice","initializeSDK","user","callbacks","accountName","initialize","err","error","send","amount","currency","isReady","warn","showSend","receive","showReceive","generateAccountNumber","request","generateAccount","listen","startListening","destroy","currentUser","getUser","Provider","value"],"sourceRoot":"../../src","sources":["FountainPayProvider.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAOA,KAAK,IACMC,aAAa,EAAEC,UAAU,EAAEC,OAAO,EAAEC,MAAM,QACrD,OAAO;AACd,SAASC,OAAO,QAAQ,wBAAqB;AAC7C,SAASC,QAAQ,QAAQ,sBAAmB;AAC5C,SAASC,UAAU,QAAQ,sBAAmB;AAAC,SAAAC,GAAA,IAAAC,IAAA,EAAAC,IAAA,IAAAC,KAAA;AAW/C;;AAEA,MAAMC,kBAAkB,gBAAGX,aAAa,CAAoB,IAAI,CAAC;;AAEjE;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASY,cAAcA,CAAA,EAAe;EAC3C,MAAMC,QAAQ,GAAGZ,UAAU,CAACU,kBAAkB,CAAC;EAC/C,IAAI,CAACE,QAAQ,EAAE;IACb,MAAM,IAAIC,KAAK,CACb,+EAA+E,GAC/E,mFACF,CAAC;EACH;EACA,OAAOD,QAAQ;AACjB;;AAEA;;AAQA,OAAO,SAASE,mBAAmBA,CAAC;EAAEC,MAAM;EAAEC,OAAO;EAAEC;AAAwB,CAAC,EAAE;EAChF;EACA;EACA,MAAMC,YAAY,GAAGhB,MAAM,CAAC,KAAK,CAAC;EAClC,IAAI,CAACgB,YAAY,CAACC,OAAO,EAAE;IACzBd,UAAU,CAACU,MAAM,EAAE;MAAEK,OAAO,EAAEJ,OAAO,EAAEI,OAAO;MAAEC,WAAW,EAAEL,OAAO,EAAEK;IAAY,CAAC,CAAC;IACpFH,YAAY,CAACC,OAAO,GAAG,IAAI;IAC3BG,OAAO,CAACC,GAAG,CAAC,oDAAoD,CAAC;EACnE;;EAEA;EACA;EACA,MAAMX,QAAQ,GAAGX,OAAO,CAAa,MAAM;IACzCqB,OAAO,CAACC,GAAG,CAAC,sDAAsD,EAAER,MAAM,CAACS,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC;IAE/F,OAAO;MACL;AACN;AACA;AACA;MACM,MAAMC,aAAaA,CAACC,IAAgB,EAAEC,SAAuB,EAAiB;QAC5EL,OAAO,CAACC,GAAG,CAAC,gDAAgD,EAAEG,IAAI,CAACE,WAAW,CAAC;QAC/E,IAAI;UACF,MAAMxB,QAAQ,CAACyB,UAAU,CAACd,MAAM,EAAEW,IAAI,EAAEV,OAAO,IAAI,CAAC,CAAC,EAAEW,SAAS,IAAI,CAAC,CAAC,CAAC;UACvEL,OAAO,CAACC,GAAG,CAAC,uDAAuD,CAAC;QACtE,CAAC,CAAC,OAAOO,GAAG,EAAE;UACZR,OAAO,CAACS,KAAK,CAAC,uCAAuC,EAAED,GAAG,CAAC;UAC3D,MAAMA,GAAG;QACX;MACF,CAAC;MAED;MACAE,IAAIA,CAACC,MAAc,EAAEC,QAAoB,EAAQ;QAC/CZ,OAAO,CAACC,GAAG,CAAC,8BAA8B,EAAEU,MAAM,EAAEC,QAAQ,CAAC;QAC7D,IAAI,CAAC9B,QAAQ,CAAC+B,OAAO,CAAC,CAAC,EAAE;UACvBb,OAAO,CAACc,IAAI,CAAC,gFAAgF,CAAC;QAChG;QACAhC,QAAQ,CAACiC,QAAQ,CAACJ,MAAM,EAAEC,QAAQ,CAAC;MACrC,CAAC;MAED;MACAI,OAAOA,CAACL,MAAe,EAAEC,QAAoB,GAAG,KAAK,EAAQ;QAC3DZ,OAAO,CAACC,GAAG,CAAC,iCAAiC,EAAEU,MAAM,EAAEC,QAAQ,CAAC;QAChE,IAAI,CAAC9B,QAAQ,CAAC+B,OAAO,CAAC,CAAC,EAAE;UACvBb,OAAO,CAACc,IAAI,CAAC,wDAAwD,CAAC;QACxE;QACAhC,QAAQ,CAACmC,WAAW,CAACN,MAAM,EAAEC,QAAQ,CAAC;MACxC,CAAC;MAED;MACA,MAAMM,qBAAqBA,CAACC,OAAiC,EAA6B;QACxFnB,OAAO,CAACC,GAAG,CAAC,+CAA+C,EAAEkB,OAAO,CAACb,WAAW,CAAC;QACjF,OAAOxB,QAAQ,CAACsC,eAAe,CAACD,OAAO,CAAC;MAC1C,CAAC;MAED;AACN;AACA;AACA;AACA;MACME,MAAMA,CAAA,EAAS;QACbrB,OAAO,CAACC,GAAG,CAAC,iEAAiE,CAAC;QAC9E,IAAI,CAACnB,QAAQ,CAAC+B,OAAO,CAAC,CAAC,EAAE;UACvBb,OAAO,CAACc,IAAI,CAAC,uDAAuD,CAAC;QACvE;QACAhC,QAAQ,CAACwC,cAAc,CAAC,CAAC;MAC3B,CAAC;MAED;MACA,MAAMC,OAAOA,CAAA,EAAkB;QAC7BvB,OAAO,CAACC,GAAG,CAAC,yDAAyD,CAAC;QACtE,MAAMnB,QAAQ,CAACyC,OAAO,CAAC,CAAC;QACxBvB,OAAO,CAACC,GAAG,CAAC,qCAAqC,CAAC;MACpD,CAAC;MAED,IAAIY,OAAOA,CAAA,EAAY;QACrB,OAAO/B,QAAQ,CAAC+B,OAAO,CAAC,CAAC;MAC3B,CAAC;MAED,IAAIW,WAAWA,CAAA,EAAsB;QACnC,OAAO1C,QAAQ,CAAC2C,OAAO,CAAC,CAAC;MAC3B;IACF,CAAC;IACH;EACA,CAAC,EAAE,CAAChC,MAAM,CAAC,CAAC;EAEZ,oBACEN,KAAA,CAACC,kBAAkB,CAACsC,QAAQ;IAACC,KAAK,EAAErC,QAAS;IAAAK,QAAA,GAC1CA,QAAQ,eAETV,IAAA,CAACJ,OAAO,IAAE,CAAC;EAAA,CACgB,CAAC;AAElC","ignoreList":[]}
|
|
@@ -1,82 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
|
|
6
|
-
// Usage:
|
|
7
|
-
// const pay = useFountainPay('your-api-key');
|
|
8
|
-
// await pay.initializeSDK(userInfo, callbacks);
|
|
9
|
-
// pay.send(500, 'NGN');
|
|
10
|
-
// pay.receive(200, 'NGN');
|
|
11
|
-
// const acct = await pay.generateAccountNumber({ accountName: 'John' });
|
|
12
|
-
// pay.listen();
|
|
13
|
-
// pay.destroy();
|
|
14
|
-
// ─────────────────────────────────────────────────────────────
|
|
15
|
-
import { useMemo } from 'react';
|
|
16
|
-
import { FPEngine } from "./engine/FPEngine.js";
|
|
17
|
-
import { initClient } from "./core/api/client.js";
|
|
18
|
-
export function useFountainPay(apiKey, options) {
|
|
19
|
-
// Initialize HTTP client immediately so API calls can be made
|
|
20
|
-
// even before initializeSDK (e.g. generateAccountNumber for onboarding)
|
|
21
|
-
useMemo(() => {
|
|
22
|
-
initClient(apiKey, {
|
|
23
|
-
baseUrl: options?.baseUrl,
|
|
24
|
-
environment: options?.environment
|
|
25
|
-
});
|
|
26
|
-
}, [apiKey]);
|
|
27
|
-
const instance = useMemo(() => ({
|
|
28
|
-
/**
|
|
29
|
-
* Call this once after the user logs in.
|
|
30
|
-
* Starts proximity broadcasting, BLE peripheral, and BT listener.
|
|
31
|
-
*/
|
|
32
|
-
async initializeSDK(user, callbacks) {
|
|
33
|
-
await FPEngine.initialize(apiKey, user, options ?? {}, callbacks ?? {});
|
|
34
|
-
},
|
|
35
|
-
/**
|
|
36
|
-
* Open the Send Money sheet.
|
|
37
|
-
* @param amount Major denomination (e.g. 500 for NGN 500)
|
|
38
|
-
* @param currency e.g. 'NGN', 'USD'
|
|
39
|
-
*/
|
|
40
|
-
send(amount, currency) {
|
|
41
|
-
FPEngine.showSend(amount, currency);
|
|
42
|
-
},
|
|
43
|
-
/**
|
|
44
|
-
* Open the Receive Money sheet.
|
|
45
|
-
* @param amount Expected amount (optional)
|
|
46
|
-
* @param currency e.g. 'NGN'
|
|
47
|
-
*/
|
|
48
|
-
receive(amount, currency = 'NGN') {
|
|
49
|
-
FPEngine.showReceive(amount, currency);
|
|
50
|
-
},
|
|
51
|
-
/**
|
|
52
|
-
* Generate a virtual account number.
|
|
53
|
-
* No UI is shown — returns the account directly.
|
|
54
|
-
*/
|
|
55
|
-
async generateAccountNumber(request) {
|
|
56
|
-
return FPEngine.generateAccount(request);
|
|
57
|
-
},
|
|
58
|
-
/**
|
|
59
|
-
* Start listening for incoming Bluetooth payment requests.
|
|
60
|
-
* When one arrives, the SDK auto-shows the Accept/Decline modal.
|
|
61
|
-
* The result fires onPaymentReceived / onPaymentDeclined callbacks
|
|
62
|
-
* that were passed into initializeSDK.
|
|
63
|
-
*/
|
|
64
|
-
listen() {
|
|
65
|
-
FPEngine.startListening();
|
|
66
|
-
},
|
|
67
|
-
/**
|
|
68
|
-
* Stop all background services. Call on logout.
|
|
69
|
-
*/
|
|
70
|
-
destroy() {
|
|
71
|
-
FPEngine.destroy();
|
|
72
|
-
},
|
|
73
|
-
get isReady() {
|
|
74
|
-
return FPEngine.isReady();
|
|
75
|
-
},
|
|
76
|
-
get currentUser() {
|
|
77
|
-
return FPEngine.getUser();
|
|
78
|
-
}
|
|
79
|
-
}), [apiKey]);
|
|
80
|
-
return instance;
|
|
81
|
-
}
|
|
3
|
+
// useFountainPay re-exported from FountainPayProvider.
|
|
4
|
+
// The hook lives in FountainPayProvider.tsx now.
|
|
5
|
+
export { useFountainPay } from "./FountainPayProvider.js";
|
|
82
6
|
//# sourceMappingURL=useFountainPay.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["
|
|
1
|
+
{"version":3,"names":["useFountainPay"],"sourceRoot":"../../src","sources":["useFountainPay.ts"],"mappings":";;AAAA;AACA;AACA,SAASA,cAAc,QAAQ,0BAAuB","ignoreList":[]}
|
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
|
-
|
|
2
|
+
import type { FPInstance, FPSDKOptions } from './core/types';
|
|
3
|
+
/**
|
|
4
|
+
* Returns the shared FountainPay instance created by the nearest
|
|
5
|
+
* <FountainPayProvider>. No API key needed here — it was given to the provider.
|
|
6
|
+
*
|
|
7
|
+
* Call from any component:
|
|
8
|
+
* const pay = useFountainPay();
|
|
9
|
+
* await pay.initializeSDK(user, callbacks); // call once after login
|
|
10
|
+
* pay.send(500, 'NGN'); // call anywhere, anytime
|
|
11
|
+
*/
|
|
12
|
+
export declare function useFountainPay(): FPInstance;
|
|
13
|
+
interface ProviderProps {
|
|
14
|
+
apiKey: string;
|
|
15
|
+
options?: FPSDKOptions;
|
|
3
16
|
children: ReactNode;
|
|
4
17
|
}
|
|
5
|
-
export declare function FountainPayProvider({ children }:
|
|
18
|
+
export declare function FountainPayProvider({ apiKey, options, children }: ProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
6
19
|
export {};
|
|
7
20
|
//# sourceMappingURL=FountainPayProvider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FountainPayProvider.d.ts","sourceRoot":"","sources":["../../../src/FountainPayProvider.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FountainPayProvider.d.ts","sourceRoot":"","sources":["../../../src/FountainPayProvider.tsx"],"names":[],"mappings":"AAYA,OAAc,EACZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAIf,OAAO,KAAK,EACV,UAAU,EAEV,YAAY,EAKb,MAAM,cAAc,CAAC;AAQtB;;;;;;;;GAQG;AACH,wBAAgB,cAAc,IAAI,UAAU,CAS3C;AAID,UAAU,aAAa;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,aAAa,2CA6F/E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFountainPay.d.ts","sourceRoot":"","sources":["../../../src/useFountainPay.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useFountainPay.d.ts","sourceRoot":"","sources":["../../../src/useFountainPay.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,21 +1,156 @@
|
|
|
1
|
-
//
|
|
1
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
2
2
|
// FountainPayProvider
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
3
|
+
//
|
|
4
|
+
// Mount once at the root of your app and pass your API key here.
|
|
5
|
+
// Every component can then call useFountainPay() (no arguments) and get
|
|
6
|
+
// the SAME instance — no re-initialization, no prop drilling.
|
|
7
|
+
//
|
|
8
|
+
// Usage:
|
|
9
|
+
// <FountainPayProvider apiKey="fp_live_...">
|
|
10
|
+
// <YourApp />
|
|
11
|
+
// </FountainPayProvider>
|
|
12
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
13
|
+
import React, {
|
|
14
|
+
type ReactNode, createContext, useContext, useMemo, useRef,
|
|
15
|
+
} from 'react';
|
|
7
16
|
import { FPShell } from './ui/modals/FPShell';
|
|
17
|
+
import { FPEngine } from './engine/FPEngine';
|
|
18
|
+
import { initClient } from './core/api/client';
|
|
19
|
+
import type {
|
|
20
|
+
FPInstance,
|
|
21
|
+
FPUserInfo,
|
|
22
|
+
FPSDKOptions,
|
|
23
|
+
FPCallbacks,
|
|
24
|
+
FPCurrency,
|
|
25
|
+
FPGenerateAccountRequest,
|
|
26
|
+
FPVirtualAccount,
|
|
27
|
+
} from './core/types';
|
|
8
28
|
|
|
9
|
-
|
|
29
|
+
// ── Context ───────────────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
const FountainPayContext = createContext<FPInstance | null>(null);
|
|
32
|
+
|
|
33
|
+
// ── Hook — the only thing components import ───────────────────────────────────
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Returns the shared FountainPay instance created by the nearest
|
|
37
|
+
* <FountainPayProvider>. No API key needed here — it was given to the provider.
|
|
38
|
+
*
|
|
39
|
+
* Call from any component:
|
|
40
|
+
* const pay = useFountainPay();
|
|
41
|
+
* await pay.initializeSDK(user, callbacks); // call once after login
|
|
42
|
+
* pay.send(500, 'NGN'); // call anywhere, anytime
|
|
43
|
+
*/
|
|
44
|
+
export function useFountainPay(): FPInstance {
|
|
45
|
+
const instance = useContext(FountainPayContext);
|
|
46
|
+
if (!instance) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
'[FountainPay] useFountainPay() must be called inside <FountainPayProvider>.\n' +
|
|
49
|
+
'Make sure you have wrapped your app root with <FountainPayProvider apiKey="...">.'
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
return instance;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ── Provider ──────────────────────────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
interface ProviderProps {
|
|
58
|
+
apiKey: string;
|
|
59
|
+
options?: FPSDKOptions;
|
|
10
60
|
children: ReactNode;
|
|
11
61
|
}
|
|
12
62
|
|
|
13
|
-
export function FountainPayProvider({ children }:
|
|
63
|
+
export function FountainPayProvider({ apiKey, options, children }: ProviderProps) {
|
|
64
|
+
// Boot the HTTP client as soon as the provider mounts —
|
|
65
|
+
// this means generateAccountNumber works even before initializeSDK
|
|
66
|
+
const clientBooted = useRef(false);
|
|
67
|
+
if (!clientBooted.current) {
|
|
68
|
+
initClient(apiKey, { baseUrl: options?.baseUrl, environment: options?.environment });
|
|
69
|
+
clientBooted.current = true;
|
|
70
|
+
console.log('[FountainPay] Provider mounted. HTTP client ready.');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Build the instance once per provider lifetime.
|
|
74
|
+
// All components that call useFountainPay() share this exact object.
|
|
75
|
+
const instance = useMemo<FPInstance>(() => {
|
|
76
|
+
console.log('[FountainPay] Creating shared FPInstance for apiKey:', apiKey.slice(0, 8) + '...');
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
/**
|
|
80
|
+
* Call once after the user logs in.
|
|
81
|
+
* Starts BLE advertising, proximity broadcast, and the payment listener.
|
|
82
|
+
*/
|
|
83
|
+
async initializeSDK(user: FPUserInfo, callbacks?: FPCallbacks): Promise<void> {
|
|
84
|
+
console.log('[FountainPay] initializeSDK() called for user:', user.accountName);
|
|
85
|
+
try {
|
|
86
|
+
await FPEngine.initialize(apiKey, user, options ?? {}, callbacks ?? {});
|
|
87
|
+
console.log('[FountainPay] initializeSDK() complete. SDK is ready.');
|
|
88
|
+
} catch (err) {
|
|
89
|
+
console.error('[FountainPay] initializeSDK() FAILED:', err);
|
|
90
|
+
throw err;
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
/** Open the Send Money bottom sheet. */
|
|
95
|
+
send(amount: number, currency: FPCurrency): void {
|
|
96
|
+
console.log('[FountainPay] send() called:', amount, currency);
|
|
97
|
+
if (!FPEngine.isReady()) {
|
|
98
|
+
console.warn('[FountainPay] send() called before initializeSDK() — call initializeSDK first.');
|
|
99
|
+
}
|
|
100
|
+
FPEngine.showSend(amount, currency);
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
/** Open the Receive Money bottom sheet. */
|
|
104
|
+
receive(amount?: number, currency: FPCurrency = 'NGN'): void {
|
|
105
|
+
console.log('[FountainPay] receive() called:', amount, currency);
|
|
106
|
+
if (!FPEngine.isReady()) {
|
|
107
|
+
console.warn('[FountainPay] receive() called before initializeSDK().');
|
|
108
|
+
}
|
|
109
|
+
FPEngine.showReceive(amount, currency);
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
/** Generate a virtual account number (no UI shown). */
|
|
113
|
+
async generateAccountNumber(request: FPGenerateAccountRequest): Promise<FPVirtualAccount> {
|
|
114
|
+
console.log('[FountainPay] generateAccountNumber() called:', request.accountName);
|
|
115
|
+
return FPEngine.generateAccount(request);
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Activate the incoming Bluetooth payment request listener.
|
|
120
|
+
* When a nearby device sends a payment request, the Accept/Decline
|
|
121
|
+
* modal will appear automatically.
|
|
122
|
+
*/
|
|
123
|
+
listen(): void {
|
|
124
|
+
console.log('[FountainPay] listen() called — activating BT payment listener.');
|
|
125
|
+
if (!FPEngine.isReady()) {
|
|
126
|
+
console.warn('[FountainPay] listen() called before initializeSDK().');
|
|
127
|
+
}
|
|
128
|
+
FPEngine.startListening();
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
/** Stop all background services. Call on logout. */
|
|
132
|
+
async destroy(): Promise<void> {
|
|
133
|
+
console.log('[FountainPay] destroy() called — stopping all services.');
|
|
134
|
+
await FPEngine.destroy();
|
|
135
|
+
console.log('[FountainPay] All services stopped.');
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
get isReady(): boolean {
|
|
139
|
+
return FPEngine.isReady();
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
get currentUser(): FPUserInfo | null {
|
|
143
|
+
return FPEngine.getUser();
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
// Re-create instance only if the apiKey changes (which should never happen at runtime)
|
|
147
|
+
}, [apiKey]);
|
|
148
|
+
|
|
14
149
|
return (
|
|
15
|
-
|
|
150
|
+
<FountainPayContext.Provider value={instance}>
|
|
16
151
|
{children}
|
|
17
|
-
{/* FPShell is invisible until pay.send() or pay.receive()
|
|
152
|
+
{/* FPShell is invisible until pay.send() or pay.receive() fires */}
|
|
18
153
|
<FPShell />
|
|
19
|
-
|
|
154
|
+
</FountainPayContext.Provider>
|
|
20
155
|
);
|
|
21
156
|
}
|
package/src/useFountainPay.ts
CHANGED
|
@@ -1,95 +1,3 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
// Usage:
|
|
5
|
-
// const pay = useFountainPay('your-api-key');
|
|
6
|
-
// await pay.initializeSDK(userInfo, callbacks);
|
|
7
|
-
// pay.send(500, 'NGN');
|
|
8
|
-
// pay.receive(200, 'NGN');
|
|
9
|
-
// const acct = await pay.generateAccountNumber({ accountName: 'John' });
|
|
10
|
-
// pay.listen();
|
|
11
|
-
// pay.destroy();
|
|
12
|
-
// ─────────────────────────────────────────────────────────────
|
|
13
|
-
import { useMemo } from 'react';
|
|
14
|
-
import { FPEngine } from './engine/FPEngine';
|
|
15
|
-
import { initClient } from './core/api/client';
|
|
16
|
-
import type {
|
|
17
|
-
FPInstance,
|
|
18
|
-
FPUserInfo,
|
|
19
|
-
FPSDKOptions,
|
|
20
|
-
FPCallbacks,
|
|
21
|
-
FPCurrency,
|
|
22
|
-
FPGenerateAccountRequest,
|
|
23
|
-
FPVirtualAccount,
|
|
24
|
-
} from './core/types';
|
|
25
|
-
|
|
26
|
-
export function useFountainPay(apiKey: string, options?: FPSDKOptions): FPInstance {
|
|
27
|
-
// Initialize HTTP client immediately so API calls can be made
|
|
28
|
-
// even before initializeSDK (e.g. generateAccountNumber for onboarding)
|
|
29
|
-
useMemo(() => {
|
|
30
|
-
initClient(apiKey, { baseUrl: options?.baseUrl, environment: options?.environment });
|
|
31
|
-
}, [apiKey]);
|
|
32
|
-
|
|
33
|
-
const instance = useMemo<FPInstance>(() => ({
|
|
34
|
-
/**
|
|
35
|
-
* Call this once after the user logs in.
|
|
36
|
-
* Starts proximity broadcasting, BLE peripheral, and BT listener.
|
|
37
|
-
*/
|
|
38
|
-
async initializeSDK(user: FPUserInfo, callbacks?: FPCallbacks): Promise<void> {
|
|
39
|
-
await FPEngine.initialize(apiKey, user, options ?? {}, callbacks ?? {});
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Open the Send Money sheet.
|
|
44
|
-
* @param amount Major denomination (e.g. 500 for NGN 500)
|
|
45
|
-
* @param currency e.g. 'NGN', 'USD'
|
|
46
|
-
*/
|
|
47
|
-
send(amount: number, currency: FPCurrency): void {
|
|
48
|
-
FPEngine.showSend(amount, currency);
|
|
49
|
-
},
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Open the Receive Money sheet.
|
|
53
|
-
* @param amount Expected amount (optional)
|
|
54
|
-
* @param currency e.g. 'NGN'
|
|
55
|
-
*/
|
|
56
|
-
receive(amount?: number, currency: FPCurrency = 'NGN'): void {
|
|
57
|
-
FPEngine.showReceive(amount, currency);
|
|
58
|
-
},
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Generate a virtual account number.
|
|
62
|
-
* No UI is shown — returns the account directly.
|
|
63
|
-
*/
|
|
64
|
-
async generateAccountNumber(request: FPGenerateAccountRequest): Promise<FPVirtualAccount> {
|
|
65
|
-
return FPEngine.generateAccount(request);
|
|
66
|
-
},
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Start listening for incoming Bluetooth payment requests.
|
|
70
|
-
* When one arrives, the SDK auto-shows the Accept/Decline modal.
|
|
71
|
-
* The result fires onPaymentReceived / onPaymentDeclined callbacks
|
|
72
|
-
* that were passed into initializeSDK.
|
|
73
|
-
*/
|
|
74
|
-
listen(): void {
|
|
75
|
-
FPEngine.startListening();
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Stop all background services. Call on logout.
|
|
80
|
-
*/
|
|
81
|
-
destroy(): void {
|
|
82
|
-
FPEngine.destroy();
|
|
83
|
-
},
|
|
84
|
-
|
|
85
|
-
get isReady(): boolean {
|
|
86
|
-
return FPEngine.isReady();
|
|
87
|
-
},
|
|
88
|
-
|
|
89
|
-
get currentUser(): FPUserInfo | null {
|
|
90
|
-
return FPEngine.getUser();
|
|
91
|
-
},
|
|
92
|
-
}), [apiKey]);
|
|
93
|
-
|
|
94
|
-
return instance;
|
|
95
|
-
}
|
|
1
|
+
// useFountainPay re-exported from FountainPayProvider.
|
|
2
|
+
// The hook lives in FountainPayProvider.tsx now.
|
|
3
|
+
export { useFountainPay } from './FountainPayProvider';
|