flintn-checkout 0.0.1

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,193 @@
1
+ # flintn-checkout
2
+
3
+ FlintN Payment SDK — Iframe-based payment widget with postMessage communication.
4
+
5
+ ## Installation
6
+ ```bash
7
+ npm install flintn-checkout
8
+ ```
9
+
10
+ ## Quick Start
11
+
12
+ ### React
13
+ ```tsx
14
+ import { useFlintNPayment } from 'flintn-checkout/react';
15
+
16
+ function Checkout() {
17
+ const { containerRef, isReady, paymentResult, error } = useFlintNPayment({
18
+ config: {
19
+ clientToken: 'your_client_token',
20
+ },
21
+ onPayment: (result) => {
22
+ if (result.status === 'PAYMENT_SUCCESS') {
23
+ console.log('Payment succeeded:', result.data);
24
+ } else {
25
+ console.log('Payment failed:', result.error);
26
+ }
27
+ },
28
+ onExpressPayment: (result) => {
29
+ console.log('Express payment:', result);
30
+ },
31
+ });
32
+
33
+ return (
34
+ <div style={{ display: 'flex', justifyContent: 'center' }}>
35
+ <div ref={containerRef} style={{ width: '100%', maxWidth: '440px', height: '600px' }} />
36
+ </div>
37
+ );
38
+ }
39
+ ```
40
+
41
+ ### Vanilla JavaScript
42
+ ```javascript
43
+ import { FlintNPayment } from 'flintn-checkout';
44
+
45
+ const payment = new FlintNPayment({
46
+ config: {
47
+ clientToken: 'your_client_token',
48
+ maxWidth: '440px',
49
+ isCardHolderRequired: true,
50
+ successRedirectUrl: 'https://example.com/success',
51
+ },
52
+ onPayment: (result) => {
53
+ if (result.status === 'PAYMENT_SUCCESS') {
54
+ console.log('Payment succeeded:', result.data);
55
+ } else {
56
+ console.log('Payment failed:', result.error);
57
+ }
58
+ },
59
+ onExpressPayment: (result) => {
60
+ console.log('Express payment:', result);
61
+ },
62
+ onReady: () => {
63
+ console.log('Widget ready');
64
+ },
65
+ debug: true,
66
+ });
67
+
68
+ payment.mount('#payment-container');
69
+
70
+ // Cleanup when done
71
+ payment.unmount();
72
+ ```
73
+
74
+ ### HTML
75
+ ```html
76
+ <!DOCTYPE html>
77
+ <html>
78
+ <head>
79
+ <title>FlintN Checkout</title>
80
+ </head>
81
+ <body>
82
+ <div id="payment-container" style="max-width: 440px; height: 600px; margin: 0 auto;"></div>
83
+
84
+ <script type="module">
85
+ import { FlintNPayment } from 'flintn-checkout';
86
+
87
+ const payment = new FlintNPayment({
88
+ config: {
89
+ clientToken: 'your_client_token',
90
+ },
91
+ onPayment: (result) => {
92
+ console.log('Payment result:', result);
93
+ },
94
+ });
95
+
96
+ payment.mount('#payment-container');
97
+ </script>
98
+ </body>
99
+ </html>
100
+ ```
101
+
102
+ ## Configuration
103
+
104
+ | Option | Type | Required | Default | Description |
105
+ |--------|------|----------|---------|-------------|
106
+ | `clientToken` | `string` | ✅ | — | Client token from your backend |
107
+ | `maxWidth` | `string` | ❌ | `'440px'` | Maximum width of widget |
108
+ | `isCardHolderRequired` | `boolean` | ❌ | `true` | Show cardholder name field |
109
+ | `successRedirectUrl` | `string` | ❌ | — | Redirect URL after successful payment |
110
+
111
+ ## Callbacks
112
+
113
+ | Callback | Description |
114
+ |----------|-------------|
115
+ | `onPayment` | Fired when card payment completes (success or error) |
116
+ | `onExpressPayment` | Fired when express payment completes (Apple Pay, Google Pay, PayPal) |
117
+ | `onReady` | Fired when widget is loaded and ready |
118
+ | `onError` | Fired on SDK initialization error |
119
+
120
+ ## React Hook Return Values
121
+
122
+ | Value | Type | Description |
123
+ |-------|------|-------------|
124
+ | `containerRef` | `RefObject<HTMLDivElement>` | Ref to attach to container element |
125
+ | `isReady` | `boolean` | Widget is loaded and ready |
126
+ | `paymentResult` | `PaymentResult \| null` | Result after payment attempt |
127
+ | `error` | `PaymentError \| null` | SDK error if any |
128
+
129
+ ## Types
130
+
131
+ ### PaymentResult
132
+ ```typescript
133
+ interface PaymentResult {
134
+ status: 'PAYMENT_SUCCESS' | 'PAYMENT_ERROR' | 'PAYMENT_CANCELLED';
135
+ data?: string; // Payment ID on success
136
+ error?: {
137
+ code: string;
138
+ message: string;
139
+ };
140
+ }
141
+ ```
142
+
143
+ ### PaymentError
144
+ ```typescript
145
+ interface PaymentError {
146
+ code: string;
147
+ message: string;
148
+ }
149
+ ```
150
+
151
+ ## Supported Payment Methods
152
+
153
+ - 💳 Credit/Debit Cards (Visa, Mastercard, Amex, Discover)
154
+ - 🍎 Apple Pay
155
+ - 🔵 Google Pay
156
+ - 🟡 PayPal
157
+
158
+ ## Debug Mode
159
+
160
+ Enable debug logs in console:
161
+ ```typescript
162
+ // React
163
+ useFlintNPayment({
164
+ config: { clientToken: '...' },
165
+ debug: true,
166
+ });
167
+
168
+ // Vanilla JS
169
+ new FlintNPayment({
170
+ config: { clientToken: '...' },
171
+ debug: true,
172
+ });
173
+ ```
174
+
175
+ ## Test Cards
176
+
177
+ | Card Number | Result |
178
+ |-------------|--------|
179
+ | `4111 1111 1111 1111` | Success |
180
+ | `4000 0000 0000 0002` | Declined |
181
+
182
+ Use any future expiry date and any 3-digit CVV.
183
+
184
+ ## Browser Support
185
+
186
+ - Chrome (latest)
187
+ - Firefox (latest)
188
+ - Safari (latest)
189
+ - Edge (latest)
190
+
191
+ ## License
192
+
193
+ MIT
@@ -0,0 +1,66 @@
1
+ declare const EventType: {
2
+ readonly WIDGET_READY: "WIDGET_READY";
3
+ readonly WIDGET_CONFIG: "WIDGET_CONFIG";
4
+ readonly PAYMENT: "PAYMENT";
5
+ readonly EXPRESS_PAYMENT: "EXPRESS_PAYMENT";
6
+ readonly PAYMENT_SUCCESS: "PAYMENT_SUCCESS";
7
+ readonly PAYMENT_ERROR: "PAYMENT_ERROR";
8
+ readonly PAYMENT_CANCELLED: "PAYMENT_CANCELLED";
9
+ readonly REDIRECT: "REDIRECT";
10
+ };
11
+ type TEventType = (typeof EventType)[keyof typeof EventType];
12
+ interface FlintNConfig {
13
+ clientToken: string;
14
+ maxWidth?: string;
15
+ isCardHolderRequired?: boolean;
16
+ successRedirectUrl?: string;
17
+ }
18
+ interface PaymentResult {
19
+ status: 'PAYMENT_SUCCESS' | 'PAYMENT_ERROR' | 'PAYMENT_CANCELLED';
20
+ data?: string;
21
+ error?: {
22
+ code: string;
23
+ message: string;
24
+ };
25
+ }
26
+ interface PaymentError {
27
+ code: string;
28
+ message: string;
29
+ }
30
+ interface FlintNPaymentOptions {
31
+ origin?: string;
32
+ config: FlintNConfig;
33
+ onPayment?: (result: PaymentResult) => void;
34
+ onExpressPayment?: (result: PaymentResult) => void;
35
+ onReady?: () => void;
36
+ onError?: (error: PaymentError) => void;
37
+ debug?: boolean;
38
+ }
39
+
40
+ declare class FlintNPayment {
41
+ private options;
42
+ private origin;
43
+ private iframe;
44
+ private container;
45
+ private isReady;
46
+ private messageHandler;
47
+ constructor(options: FlintNPaymentOptions);
48
+ mount(selector: string | HTMLElement): void;
49
+ unmount(): void;
50
+ getIsReady(): boolean;
51
+ private handleMessage;
52
+ private isValidRedirectUrl;
53
+ private handlePaymentResult;
54
+ private sendConfig;
55
+ private log;
56
+ }
57
+
58
+ declare const parseOrigin: (origin: string) => string;
59
+ declare const buildIframeSrc: (origin: string) => string;
60
+ declare const createIframeElement: (src: string) => HTMLIFrameElement;
61
+ declare const validateConfig: (config: {
62
+ clientToken?: string;
63
+ }) => void;
64
+ declare const sanitizeToken: (token: string) => string;
65
+
66
+ export { EventType, type FlintNConfig, FlintNPayment, type FlintNPaymentOptions, type PaymentError, type PaymentResult, type TEventType, buildIframeSrc, createIframeElement, parseOrigin, sanitizeToken, validateConfig };
@@ -0,0 +1,66 @@
1
+ declare const EventType: {
2
+ readonly WIDGET_READY: "WIDGET_READY";
3
+ readonly WIDGET_CONFIG: "WIDGET_CONFIG";
4
+ readonly PAYMENT: "PAYMENT";
5
+ readonly EXPRESS_PAYMENT: "EXPRESS_PAYMENT";
6
+ readonly PAYMENT_SUCCESS: "PAYMENT_SUCCESS";
7
+ readonly PAYMENT_ERROR: "PAYMENT_ERROR";
8
+ readonly PAYMENT_CANCELLED: "PAYMENT_CANCELLED";
9
+ readonly REDIRECT: "REDIRECT";
10
+ };
11
+ type TEventType = (typeof EventType)[keyof typeof EventType];
12
+ interface FlintNConfig {
13
+ clientToken: string;
14
+ maxWidth?: string;
15
+ isCardHolderRequired?: boolean;
16
+ successRedirectUrl?: string;
17
+ }
18
+ interface PaymentResult {
19
+ status: 'PAYMENT_SUCCESS' | 'PAYMENT_ERROR' | 'PAYMENT_CANCELLED';
20
+ data?: string;
21
+ error?: {
22
+ code: string;
23
+ message: string;
24
+ };
25
+ }
26
+ interface PaymentError {
27
+ code: string;
28
+ message: string;
29
+ }
30
+ interface FlintNPaymentOptions {
31
+ origin?: string;
32
+ config: FlintNConfig;
33
+ onPayment?: (result: PaymentResult) => void;
34
+ onExpressPayment?: (result: PaymentResult) => void;
35
+ onReady?: () => void;
36
+ onError?: (error: PaymentError) => void;
37
+ debug?: boolean;
38
+ }
39
+
40
+ declare class FlintNPayment {
41
+ private options;
42
+ private origin;
43
+ private iframe;
44
+ private container;
45
+ private isReady;
46
+ private messageHandler;
47
+ constructor(options: FlintNPaymentOptions);
48
+ mount(selector: string | HTMLElement): void;
49
+ unmount(): void;
50
+ getIsReady(): boolean;
51
+ private handleMessage;
52
+ private isValidRedirectUrl;
53
+ private handlePaymentResult;
54
+ private sendConfig;
55
+ private log;
56
+ }
57
+
58
+ declare const parseOrigin: (origin: string) => string;
59
+ declare const buildIframeSrc: (origin: string) => string;
60
+ declare const createIframeElement: (src: string) => HTMLIFrameElement;
61
+ declare const validateConfig: (config: {
62
+ clientToken?: string;
63
+ }) => void;
64
+ declare const sanitizeToken: (token: string) => string;
65
+
66
+ export { EventType, type FlintNConfig, FlintNPayment, type FlintNPaymentOptions, type PaymentError, type PaymentResult, type TEventType, buildIframeSrc, createIframeElement, parseOrigin, sanitizeToken, validateConfig };
package/dist/index.js ADDED
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ EventType: () => EventType,
24
+ FlintNPayment: () => FlintNPayment,
25
+ buildIframeSrc: () => buildIframeSrc,
26
+ createIframeElement: () => createIframeElement,
27
+ parseOrigin: () => parseOrigin,
28
+ sanitizeToken: () => sanitizeToken,
29
+ validateConfig: () => validateConfig
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/types.ts
34
+ var EventType = {
35
+ WIDGET_READY: "WIDGET_READY",
36
+ WIDGET_CONFIG: "WIDGET_CONFIG",
37
+ PAYMENT: "PAYMENT",
38
+ EXPRESS_PAYMENT: "EXPRESS_PAYMENT",
39
+ PAYMENT_SUCCESS: "PAYMENT_SUCCESS",
40
+ PAYMENT_ERROR: "PAYMENT_ERROR",
41
+ PAYMENT_CANCELLED: "PAYMENT_CANCELLED",
42
+ REDIRECT: "REDIRECT"
43
+ };
44
+
45
+ // src/utils.ts
46
+ var parseOrigin = (origin) => {
47
+ try {
48
+ return new URL(origin).origin;
49
+ } catch {
50
+ try {
51
+ return new URL(`https://${origin}`).origin;
52
+ } catch {
53
+ throw new Error(`[FlintN SDK] Invalid origin: ${origin}`);
54
+ }
55
+ }
56
+ };
57
+ var buildIframeSrc = (origin) => {
58
+ return parseOrigin(origin) + "/";
59
+ };
60
+ var createIframeElement = (src) => {
61
+ const iframe = document.createElement("iframe");
62
+ iframe.src = src;
63
+ iframe.title = "FlintN Checkout";
64
+ iframe.style.cssText = "width: 100%; height: 100%; border: none;";
65
+ iframe.setAttribute("sandbox", "allow-scripts allow-forms allow-popups allow-same-origin");
66
+ iframe.setAttribute("allow", "payment; clipboard-write");
67
+ return iframe;
68
+ };
69
+ var validateConfig = (config) => {
70
+ if (!config.clientToken) {
71
+ throw new Error("[FlintN SDK] clientToken is required");
72
+ }
73
+ };
74
+ var sanitizeToken = (token) => {
75
+ if (token.length <= 20) return token;
76
+ return token.substring(0, 20) + "...";
77
+ };
78
+
79
+ // src/flintn-payment.ts
80
+ var DEFAULT_ORIGIN = "https://pay.flintn.com/iframe";
81
+ var FlintNPayment = class {
82
+ constructor(options) {
83
+ this.iframe = null;
84
+ this.container = null;
85
+ this.isReady = false;
86
+ this.messageHandler = null;
87
+ validateConfig(options.config);
88
+ this.options = options;
89
+ this.origin = parseOrigin(options.origin || DEFAULT_ORIGIN);
90
+ this.log("Initialized with origin:", this.origin);
91
+ }
92
+ mount(selector) {
93
+ this.container = typeof selector === "string" ? document.querySelector(selector) : selector;
94
+ if (!this.container) {
95
+ const selectorDescription = typeof selector === "string" ? selector : `<${selector.tagName.toLowerCase()}${selector.id ? ` id="${selector.id}"` : ""}${selector.className ? ` class="${selector.className}"` : ""}>`;
96
+ throw new Error(`[FlintN SDK] Container not found: ${selectorDescription}`);
97
+ }
98
+ this.iframe = createIframeElement(buildIframeSrc(this.origin));
99
+ this.container.appendChild(this.iframe);
100
+ this.messageHandler = this.handleMessage.bind(this);
101
+ window.addEventListener("message", this.messageHandler);
102
+ this.log("Mounted to container");
103
+ }
104
+ unmount() {
105
+ if (this.messageHandler) {
106
+ window.removeEventListener("message", this.messageHandler);
107
+ this.messageHandler = null;
108
+ }
109
+ if (this.iframe && this.container && this.container.contains(this.iframe)) {
110
+ this.container.removeChild(this.iframe);
111
+ }
112
+ this.iframe = null;
113
+ this.isReady = false;
114
+ this.log("Unmounted");
115
+ }
116
+ getIsReady() {
117
+ return this.isReady;
118
+ }
119
+ handleMessage(event) {
120
+ if (event.origin !== this.origin) return;
121
+ const { type, payload } = event.data || {};
122
+ if (!type) return;
123
+ this.log("Received message:", type, payload ?? "");
124
+ switch (type) {
125
+ case EventType.WIDGET_READY:
126
+ this.isReady = true;
127
+ this.sendConfig();
128
+ this.options.onReady?.();
129
+ break;
130
+ case EventType.PAYMENT:
131
+ this.handlePaymentResult(payload, this.options.onPayment);
132
+ break;
133
+ case EventType.EXPRESS_PAYMENT:
134
+ this.handlePaymentResult(payload, this.options.onExpressPayment);
135
+ break;
136
+ case EventType.REDIRECT:
137
+ if (payload?.url && this.isValidRedirectUrl(payload.url)) {
138
+ window.location.href = payload.url;
139
+ }
140
+ break;
141
+ }
142
+ }
143
+ isValidRedirectUrl(url) {
144
+ try {
145
+ const parsed = new URL(url);
146
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
147
+ this.log("Invalid redirect URL protocol:", parsed.protocol);
148
+ return false;
149
+ }
150
+ if (parsed.origin === window.location.origin) {
151
+ return true;
152
+ }
153
+ const configRedirectUrl = this.options.config.successRedirectUrl;
154
+ if (configRedirectUrl) {
155
+ const configParsed = new URL(configRedirectUrl);
156
+ if (parsed.origin === configParsed.origin) {
157
+ return true;
158
+ }
159
+ }
160
+ this.log("Redirect URL not in allowed origins:", url);
161
+ return false;
162
+ } catch {
163
+ this.log("Invalid redirect URL:", url);
164
+ return false;
165
+ }
166
+ }
167
+ handlePaymentResult(payload, callback) {
168
+ if (!callback) return;
169
+ callback(payload);
170
+ }
171
+ sendConfig() {
172
+ if (!this.iframe?.contentWindow) return;
173
+ this.iframe.contentWindow.postMessage(
174
+ {
175
+ type: EventType.WIDGET_CONFIG,
176
+ payload: this.options.config
177
+ },
178
+ this.origin
179
+ );
180
+ this.log("Sent config:", sanitizeToken(this.options.config.clientToken));
181
+ }
182
+ log(...args) {
183
+ if (this.options.debug) {
184
+ console.log("[FlintN SDK]", ...args);
185
+ }
186
+ }
187
+ };
188
+ // Annotate the CommonJS export names for ESM import in node:
189
+ 0 && (module.exports = {
190
+ EventType,
191
+ FlintNPayment,
192
+ buildIframeSrc,
193
+ createIframeElement,
194
+ parseOrigin,
195
+ sanitizeToken,
196
+ validateConfig
197
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,164 @@
1
+ // src/types.ts
2
+ var EventType = {
3
+ WIDGET_READY: "WIDGET_READY",
4
+ WIDGET_CONFIG: "WIDGET_CONFIG",
5
+ PAYMENT: "PAYMENT",
6
+ EXPRESS_PAYMENT: "EXPRESS_PAYMENT",
7
+ PAYMENT_SUCCESS: "PAYMENT_SUCCESS",
8
+ PAYMENT_ERROR: "PAYMENT_ERROR",
9
+ PAYMENT_CANCELLED: "PAYMENT_CANCELLED",
10
+ REDIRECT: "REDIRECT"
11
+ };
12
+
13
+ // src/utils.ts
14
+ var parseOrigin = (origin) => {
15
+ try {
16
+ return new URL(origin).origin;
17
+ } catch {
18
+ try {
19
+ return new URL(`https://${origin}`).origin;
20
+ } catch {
21
+ throw new Error(`[FlintN SDK] Invalid origin: ${origin}`);
22
+ }
23
+ }
24
+ };
25
+ var buildIframeSrc = (origin) => {
26
+ return parseOrigin(origin) + "/";
27
+ };
28
+ var createIframeElement = (src) => {
29
+ const iframe = document.createElement("iframe");
30
+ iframe.src = src;
31
+ iframe.title = "FlintN Checkout";
32
+ iframe.style.cssText = "width: 100%; height: 100%; border: none;";
33
+ iframe.setAttribute("sandbox", "allow-scripts allow-forms allow-popups allow-same-origin");
34
+ iframe.setAttribute("allow", "payment; clipboard-write");
35
+ return iframe;
36
+ };
37
+ var validateConfig = (config) => {
38
+ if (!config.clientToken) {
39
+ throw new Error("[FlintN SDK] clientToken is required");
40
+ }
41
+ };
42
+ var sanitizeToken = (token) => {
43
+ if (token.length <= 20) return token;
44
+ return token.substring(0, 20) + "...";
45
+ };
46
+
47
+ // src/flintn-payment.ts
48
+ var DEFAULT_ORIGIN = "https://pay.flintn.com/iframe";
49
+ var FlintNPayment = class {
50
+ constructor(options) {
51
+ this.iframe = null;
52
+ this.container = null;
53
+ this.isReady = false;
54
+ this.messageHandler = null;
55
+ validateConfig(options.config);
56
+ this.options = options;
57
+ this.origin = parseOrigin(options.origin || DEFAULT_ORIGIN);
58
+ this.log("Initialized with origin:", this.origin);
59
+ }
60
+ mount(selector) {
61
+ this.container = typeof selector === "string" ? document.querySelector(selector) : selector;
62
+ if (!this.container) {
63
+ const selectorDescription = typeof selector === "string" ? selector : `<${selector.tagName.toLowerCase()}${selector.id ? ` id="${selector.id}"` : ""}${selector.className ? ` class="${selector.className}"` : ""}>`;
64
+ throw new Error(`[FlintN SDK] Container not found: ${selectorDescription}`);
65
+ }
66
+ this.iframe = createIframeElement(buildIframeSrc(this.origin));
67
+ this.container.appendChild(this.iframe);
68
+ this.messageHandler = this.handleMessage.bind(this);
69
+ window.addEventListener("message", this.messageHandler);
70
+ this.log("Mounted to container");
71
+ }
72
+ unmount() {
73
+ if (this.messageHandler) {
74
+ window.removeEventListener("message", this.messageHandler);
75
+ this.messageHandler = null;
76
+ }
77
+ if (this.iframe && this.container && this.container.contains(this.iframe)) {
78
+ this.container.removeChild(this.iframe);
79
+ }
80
+ this.iframe = null;
81
+ this.isReady = false;
82
+ this.log("Unmounted");
83
+ }
84
+ getIsReady() {
85
+ return this.isReady;
86
+ }
87
+ handleMessage(event) {
88
+ if (event.origin !== this.origin) return;
89
+ const { type, payload } = event.data || {};
90
+ if (!type) return;
91
+ this.log("Received message:", type, payload ?? "");
92
+ switch (type) {
93
+ case EventType.WIDGET_READY:
94
+ this.isReady = true;
95
+ this.sendConfig();
96
+ this.options.onReady?.();
97
+ break;
98
+ case EventType.PAYMENT:
99
+ this.handlePaymentResult(payload, this.options.onPayment);
100
+ break;
101
+ case EventType.EXPRESS_PAYMENT:
102
+ this.handlePaymentResult(payload, this.options.onExpressPayment);
103
+ break;
104
+ case EventType.REDIRECT:
105
+ if (payload?.url && this.isValidRedirectUrl(payload.url)) {
106
+ window.location.href = payload.url;
107
+ }
108
+ break;
109
+ }
110
+ }
111
+ isValidRedirectUrl(url) {
112
+ try {
113
+ const parsed = new URL(url);
114
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
115
+ this.log("Invalid redirect URL protocol:", parsed.protocol);
116
+ return false;
117
+ }
118
+ if (parsed.origin === window.location.origin) {
119
+ return true;
120
+ }
121
+ const configRedirectUrl = this.options.config.successRedirectUrl;
122
+ if (configRedirectUrl) {
123
+ const configParsed = new URL(configRedirectUrl);
124
+ if (parsed.origin === configParsed.origin) {
125
+ return true;
126
+ }
127
+ }
128
+ this.log("Redirect URL not in allowed origins:", url);
129
+ return false;
130
+ } catch {
131
+ this.log("Invalid redirect URL:", url);
132
+ return false;
133
+ }
134
+ }
135
+ handlePaymentResult(payload, callback) {
136
+ if (!callback) return;
137
+ callback(payload);
138
+ }
139
+ sendConfig() {
140
+ if (!this.iframe?.contentWindow) return;
141
+ this.iframe.contentWindow.postMessage(
142
+ {
143
+ type: EventType.WIDGET_CONFIG,
144
+ payload: this.options.config
145
+ },
146
+ this.origin
147
+ );
148
+ this.log("Sent config:", sanitizeToken(this.options.config.clientToken));
149
+ }
150
+ log(...args) {
151
+ if (this.options.debug) {
152
+ console.log("[FlintN SDK]", ...args);
153
+ }
154
+ }
155
+ };
156
+ export {
157
+ EventType,
158
+ FlintNPayment,
159
+ buildIframeSrc,
160
+ createIframeElement,
161
+ parseOrigin,
162
+ sanitizeToken,
163
+ validateConfig
164
+ };
@@ -0,0 +1,60 @@
1
+ declare const EventType: {
2
+ readonly WIDGET_READY: "WIDGET_READY";
3
+ readonly WIDGET_CONFIG: "WIDGET_CONFIG";
4
+ readonly PAYMENT: "PAYMENT";
5
+ readonly EXPRESS_PAYMENT: "EXPRESS_PAYMENT";
6
+ readonly PAYMENT_SUCCESS: "PAYMENT_SUCCESS";
7
+ readonly PAYMENT_ERROR: "PAYMENT_ERROR";
8
+ readonly PAYMENT_CANCELLED: "PAYMENT_CANCELLED";
9
+ readonly REDIRECT: "REDIRECT";
10
+ };
11
+ type TEventType = (typeof EventType)[keyof typeof EventType];
12
+ interface FlintNConfig {
13
+ clientToken: string;
14
+ maxWidth?: string;
15
+ isCardHolderRequired?: boolean;
16
+ successRedirectUrl?: string;
17
+ }
18
+ interface PaymentResult {
19
+ status: 'PAYMENT_SUCCESS' | 'PAYMENT_ERROR' | 'PAYMENT_CANCELLED';
20
+ data?: string;
21
+ error?: {
22
+ code: string;
23
+ message: string;
24
+ };
25
+ }
26
+ interface PaymentError {
27
+ code: string;
28
+ message: string;
29
+ }
30
+ interface FlintNPaymentOptions {
31
+ origin?: string;
32
+ config: FlintNConfig;
33
+ onPayment?: (result: PaymentResult) => void;
34
+ onExpressPayment?: (result: PaymentResult) => void;
35
+ onReady?: () => void;
36
+ onError?: (error: PaymentError) => void;
37
+ debug?: boolean;
38
+ }
39
+
40
+ /**
41
+ * Options for the useFlintNPayment hook.
42
+ *
43
+ * Extends FlintNPaymentOptions but omits `onReady` and `onError` callbacks
44
+ * as they are managed internally by the hook. Use the returned `isReady`
45
+ * and `error` values instead.
46
+ *
47
+ * All other FlintNPaymentOptions fields (config, onPayment, onExpressPayment, debug)
48
+ * can be passed directly.
49
+ */
50
+ interface UseFlintNPaymentOptions extends Omit<FlintNPaymentOptions, 'onReady' | 'onError'> {
51
+ }
52
+ interface UseFlintNPaymentReturn {
53
+ containerRef: React.RefObject<HTMLDivElement | null>;
54
+ isReady: boolean;
55
+ paymentResult: PaymentResult | null;
56
+ error: PaymentError | null;
57
+ }
58
+ declare function useFlintNPayment(options: UseFlintNPaymentOptions): UseFlintNPaymentReturn;
59
+
60
+ export { EventType, type FlintNConfig, type FlintNPaymentOptions, type PaymentError, type PaymentResult, type TEventType, type UseFlintNPaymentOptions, type UseFlintNPaymentReturn, useFlintNPayment };
@@ -0,0 +1,60 @@
1
+ declare const EventType: {
2
+ readonly WIDGET_READY: "WIDGET_READY";
3
+ readonly WIDGET_CONFIG: "WIDGET_CONFIG";
4
+ readonly PAYMENT: "PAYMENT";
5
+ readonly EXPRESS_PAYMENT: "EXPRESS_PAYMENT";
6
+ readonly PAYMENT_SUCCESS: "PAYMENT_SUCCESS";
7
+ readonly PAYMENT_ERROR: "PAYMENT_ERROR";
8
+ readonly PAYMENT_CANCELLED: "PAYMENT_CANCELLED";
9
+ readonly REDIRECT: "REDIRECT";
10
+ };
11
+ type TEventType = (typeof EventType)[keyof typeof EventType];
12
+ interface FlintNConfig {
13
+ clientToken: string;
14
+ maxWidth?: string;
15
+ isCardHolderRequired?: boolean;
16
+ successRedirectUrl?: string;
17
+ }
18
+ interface PaymentResult {
19
+ status: 'PAYMENT_SUCCESS' | 'PAYMENT_ERROR' | 'PAYMENT_CANCELLED';
20
+ data?: string;
21
+ error?: {
22
+ code: string;
23
+ message: string;
24
+ };
25
+ }
26
+ interface PaymentError {
27
+ code: string;
28
+ message: string;
29
+ }
30
+ interface FlintNPaymentOptions {
31
+ origin?: string;
32
+ config: FlintNConfig;
33
+ onPayment?: (result: PaymentResult) => void;
34
+ onExpressPayment?: (result: PaymentResult) => void;
35
+ onReady?: () => void;
36
+ onError?: (error: PaymentError) => void;
37
+ debug?: boolean;
38
+ }
39
+
40
+ /**
41
+ * Options for the useFlintNPayment hook.
42
+ *
43
+ * Extends FlintNPaymentOptions but omits `onReady` and `onError` callbacks
44
+ * as they are managed internally by the hook. Use the returned `isReady`
45
+ * and `error` values instead.
46
+ *
47
+ * All other FlintNPaymentOptions fields (config, onPayment, onExpressPayment, debug)
48
+ * can be passed directly.
49
+ */
50
+ interface UseFlintNPaymentOptions extends Omit<FlintNPaymentOptions, 'onReady' | 'onError'> {
51
+ }
52
+ interface UseFlintNPaymentReturn {
53
+ containerRef: React.RefObject<HTMLDivElement | null>;
54
+ isReady: boolean;
55
+ paymentResult: PaymentResult | null;
56
+ error: PaymentError | null;
57
+ }
58
+ declare function useFlintNPayment(options: UseFlintNPaymentOptions): UseFlintNPaymentReturn;
59
+
60
+ export { EventType, type FlintNConfig, type FlintNPaymentOptions, type PaymentError, type PaymentResult, type TEventType, type UseFlintNPaymentOptions, type UseFlintNPaymentReturn, useFlintNPayment };
package/dist/react.js ADDED
@@ -0,0 +1,251 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/react/index.ts
21
+ var react_exports = {};
22
+ __export(react_exports, {
23
+ EventType: () => EventType,
24
+ useFlintNPayment: () => useFlintNPayment
25
+ });
26
+ module.exports = __toCommonJS(react_exports);
27
+
28
+ // src/react/use-flintn-payment.ts
29
+ var import_react = require("react");
30
+
31
+ // src/types.ts
32
+ var EventType = {
33
+ WIDGET_READY: "WIDGET_READY",
34
+ WIDGET_CONFIG: "WIDGET_CONFIG",
35
+ PAYMENT: "PAYMENT",
36
+ EXPRESS_PAYMENT: "EXPRESS_PAYMENT",
37
+ PAYMENT_SUCCESS: "PAYMENT_SUCCESS",
38
+ PAYMENT_ERROR: "PAYMENT_ERROR",
39
+ PAYMENT_CANCELLED: "PAYMENT_CANCELLED",
40
+ REDIRECT: "REDIRECT"
41
+ };
42
+
43
+ // src/utils.ts
44
+ var parseOrigin = (origin) => {
45
+ try {
46
+ return new URL(origin).origin;
47
+ } catch {
48
+ try {
49
+ return new URL(`https://${origin}`).origin;
50
+ } catch {
51
+ throw new Error(`[FlintN SDK] Invalid origin: ${origin}`);
52
+ }
53
+ }
54
+ };
55
+ var buildIframeSrc = (origin) => {
56
+ return parseOrigin(origin) + "/";
57
+ };
58
+ var createIframeElement = (src) => {
59
+ const iframe = document.createElement("iframe");
60
+ iframe.src = src;
61
+ iframe.title = "FlintN Checkout";
62
+ iframe.style.cssText = "width: 100%; height: 100%; border: none;";
63
+ iframe.setAttribute("sandbox", "allow-scripts allow-forms allow-popups allow-same-origin");
64
+ iframe.setAttribute("allow", "payment; clipboard-write");
65
+ return iframe;
66
+ };
67
+ var validateConfig = (config) => {
68
+ if (!config.clientToken) {
69
+ throw new Error("[FlintN SDK] clientToken is required");
70
+ }
71
+ };
72
+ var sanitizeToken = (token) => {
73
+ if (token.length <= 20) return token;
74
+ return token.substring(0, 20) + "...";
75
+ };
76
+
77
+ // src/flintn-payment.ts
78
+ var DEFAULT_ORIGIN = "https://pay.flintn.com/iframe";
79
+ var FlintNPayment = class {
80
+ constructor(options) {
81
+ this.iframe = null;
82
+ this.container = null;
83
+ this.isReady = false;
84
+ this.messageHandler = null;
85
+ validateConfig(options.config);
86
+ this.options = options;
87
+ this.origin = parseOrigin(options.origin || DEFAULT_ORIGIN);
88
+ this.log("Initialized with origin:", this.origin);
89
+ }
90
+ mount(selector) {
91
+ this.container = typeof selector === "string" ? document.querySelector(selector) : selector;
92
+ if (!this.container) {
93
+ const selectorDescription = typeof selector === "string" ? selector : `<${selector.tagName.toLowerCase()}${selector.id ? ` id="${selector.id}"` : ""}${selector.className ? ` class="${selector.className}"` : ""}>`;
94
+ throw new Error(`[FlintN SDK] Container not found: ${selectorDescription}`);
95
+ }
96
+ this.iframe = createIframeElement(buildIframeSrc(this.origin));
97
+ this.container.appendChild(this.iframe);
98
+ this.messageHandler = this.handleMessage.bind(this);
99
+ window.addEventListener("message", this.messageHandler);
100
+ this.log("Mounted to container");
101
+ }
102
+ unmount() {
103
+ if (this.messageHandler) {
104
+ window.removeEventListener("message", this.messageHandler);
105
+ this.messageHandler = null;
106
+ }
107
+ if (this.iframe && this.container && this.container.contains(this.iframe)) {
108
+ this.container.removeChild(this.iframe);
109
+ }
110
+ this.iframe = null;
111
+ this.isReady = false;
112
+ this.log("Unmounted");
113
+ }
114
+ getIsReady() {
115
+ return this.isReady;
116
+ }
117
+ handleMessage(event) {
118
+ if (event.origin !== this.origin) return;
119
+ const { type, payload } = event.data || {};
120
+ if (!type) return;
121
+ this.log("Received message:", type, payload ?? "");
122
+ switch (type) {
123
+ case EventType.WIDGET_READY:
124
+ this.isReady = true;
125
+ this.sendConfig();
126
+ this.options.onReady?.();
127
+ break;
128
+ case EventType.PAYMENT:
129
+ this.handlePaymentResult(payload, this.options.onPayment);
130
+ break;
131
+ case EventType.EXPRESS_PAYMENT:
132
+ this.handlePaymentResult(payload, this.options.onExpressPayment);
133
+ break;
134
+ case EventType.REDIRECT:
135
+ if (payload?.url && this.isValidRedirectUrl(payload.url)) {
136
+ window.location.href = payload.url;
137
+ }
138
+ break;
139
+ }
140
+ }
141
+ isValidRedirectUrl(url) {
142
+ try {
143
+ const parsed = new URL(url);
144
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
145
+ this.log("Invalid redirect URL protocol:", parsed.protocol);
146
+ return false;
147
+ }
148
+ if (parsed.origin === window.location.origin) {
149
+ return true;
150
+ }
151
+ const configRedirectUrl = this.options.config.successRedirectUrl;
152
+ if (configRedirectUrl) {
153
+ const configParsed = new URL(configRedirectUrl);
154
+ if (parsed.origin === configParsed.origin) {
155
+ return true;
156
+ }
157
+ }
158
+ this.log("Redirect URL not in allowed origins:", url);
159
+ return false;
160
+ } catch {
161
+ this.log("Invalid redirect URL:", url);
162
+ return false;
163
+ }
164
+ }
165
+ handlePaymentResult(payload, callback) {
166
+ if (!callback) return;
167
+ callback(payload);
168
+ }
169
+ sendConfig() {
170
+ if (!this.iframe?.contentWindow) return;
171
+ this.iframe.contentWindow.postMessage(
172
+ {
173
+ type: EventType.WIDGET_CONFIG,
174
+ payload: this.options.config
175
+ },
176
+ this.origin
177
+ );
178
+ this.log("Sent config:", sanitizeToken(this.options.config.clientToken));
179
+ }
180
+ log(...args) {
181
+ if (this.options.debug) {
182
+ console.log("[FlintN SDK]", ...args);
183
+ }
184
+ }
185
+ };
186
+
187
+ // src/react/use-flintn-payment.ts
188
+ function useFlintNPayment(options) {
189
+ const containerRef = (0, import_react.useRef)(null);
190
+ const paymentRef = (0, import_react.useRef)(null);
191
+ const onPaymentRef = (0, import_react.useRef)(options.onPayment);
192
+ const onExpressPaymentRef = (0, import_react.useRef)(options.onExpressPayment);
193
+ const [isReady, setIsReady] = (0, import_react.useState)(false);
194
+ const [paymentResult, setPaymentResult] = (0, import_react.useState)(null);
195
+ const [error, setError] = (0, import_react.useState)(null);
196
+ (0, import_react.useEffect)(() => {
197
+ onPaymentRef.current = options.onPayment;
198
+ }, [options.onPayment]);
199
+ (0, import_react.useEffect)(() => {
200
+ onExpressPaymentRef.current = options.onExpressPayment;
201
+ }, [options.onExpressPayment]);
202
+ (0, import_react.useEffect)(() => {
203
+ if (!containerRef.current) return;
204
+ setIsReady(false);
205
+ setPaymentResult(null);
206
+ setError(null);
207
+ try {
208
+ paymentRef.current = new FlintNPayment({
209
+ ...options,
210
+ onReady: () => setIsReady(true),
211
+ onPayment: (result) => {
212
+ setPaymentResult(result);
213
+ onPaymentRef.current?.(result);
214
+ },
215
+ onExpressPayment: (result) => {
216
+ setPaymentResult(result);
217
+ onExpressPaymentRef.current?.(result);
218
+ },
219
+ onError: (err) => setError(err)
220
+ });
221
+ paymentRef.current.mount(containerRef.current);
222
+ } catch (err) {
223
+ setError({
224
+ code: "INIT_ERROR",
225
+ message: err instanceof Error ? err.message : "Failed to initialize SDK"
226
+ });
227
+ }
228
+ return () => {
229
+ paymentRef.current?.unmount();
230
+ paymentRef.current = null;
231
+ };
232
+ }, [
233
+ options.origin,
234
+ options.debug,
235
+ options.config.clientToken,
236
+ options.config.maxWidth,
237
+ options.config.isCardHolderRequired,
238
+ options.config.successRedirectUrl
239
+ ]);
240
+ return {
241
+ containerRef,
242
+ isReady,
243
+ paymentResult,
244
+ error
245
+ };
246
+ }
247
+ // Annotate the CommonJS export names for ESM import in node:
248
+ 0 && (module.exports = {
249
+ EventType,
250
+ useFlintNPayment
251
+ });
package/dist/react.mjs ADDED
@@ -0,0 +1,223 @@
1
+ // src/react/use-flintn-payment.ts
2
+ import { useEffect, useRef, useState } from "react";
3
+
4
+ // src/types.ts
5
+ var EventType = {
6
+ WIDGET_READY: "WIDGET_READY",
7
+ WIDGET_CONFIG: "WIDGET_CONFIG",
8
+ PAYMENT: "PAYMENT",
9
+ EXPRESS_PAYMENT: "EXPRESS_PAYMENT",
10
+ PAYMENT_SUCCESS: "PAYMENT_SUCCESS",
11
+ PAYMENT_ERROR: "PAYMENT_ERROR",
12
+ PAYMENT_CANCELLED: "PAYMENT_CANCELLED",
13
+ REDIRECT: "REDIRECT"
14
+ };
15
+
16
+ // src/utils.ts
17
+ var parseOrigin = (origin) => {
18
+ try {
19
+ return new URL(origin).origin;
20
+ } catch {
21
+ try {
22
+ return new URL(`https://${origin}`).origin;
23
+ } catch {
24
+ throw new Error(`[FlintN SDK] Invalid origin: ${origin}`);
25
+ }
26
+ }
27
+ };
28
+ var buildIframeSrc = (origin) => {
29
+ return parseOrigin(origin) + "/";
30
+ };
31
+ var createIframeElement = (src) => {
32
+ const iframe = document.createElement("iframe");
33
+ iframe.src = src;
34
+ iframe.title = "FlintN Checkout";
35
+ iframe.style.cssText = "width: 100%; height: 100%; border: none;";
36
+ iframe.setAttribute("sandbox", "allow-scripts allow-forms allow-popups allow-same-origin");
37
+ iframe.setAttribute("allow", "payment; clipboard-write");
38
+ return iframe;
39
+ };
40
+ var validateConfig = (config) => {
41
+ if (!config.clientToken) {
42
+ throw new Error("[FlintN SDK] clientToken is required");
43
+ }
44
+ };
45
+ var sanitizeToken = (token) => {
46
+ if (token.length <= 20) return token;
47
+ return token.substring(0, 20) + "...";
48
+ };
49
+
50
+ // src/flintn-payment.ts
51
+ var DEFAULT_ORIGIN = "https://pay.flintn.com/iframe";
52
+ var FlintNPayment = class {
53
+ constructor(options) {
54
+ this.iframe = null;
55
+ this.container = null;
56
+ this.isReady = false;
57
+ this.messageHandler = null;
58
+ validateConfig(options.config);
59
+ this.options = options;
60
+ this.origin = parseOrigin(options.origin || DEFAULT_ORIGIN);
61
+ this.log("Initialized with origin:", this.origin);
62
+ }
63
+ mount(selector) {
64
+ this.container = typeof selector === "string" ? document.querySelector(selector) : selector;
65
+ if (!this.container) {
66
+ const selectorDescription = typeof selector === "string" ? selector : `<${selector.tagName.toLowerCase()}${selector.id ? ` id="${selector.id}"` : ""}${selector.className ? ` class="${selector.className}"` : ""}>`;
67
+ throw new Error(`[FlintN SDK] Container not found: ${selectorDescription}`);
68
+ }
69
+ this.iframe = createIframeElement(buildIframeSrc(this.origin));
70
+ this.container.appendChild(this.iframe);
71
+ this.messageHandler = this.handleMessage.bind(this);
72
+ window.addEventListener("message", this.messageHandler);
73
+ this.log("Mounted to container");
74
+ }
75
+ unmount() {
76
+ if (this.messageHandler) {
77
+ window.removeEventListener("message", this.messageHandler);
78
+ this.messageHandler = null;
79
+ }
80
+ if (this.iframe && this.container && this.container.contains(this.iframe)) {
81
+ this.container.removeChild(this.iframe);
82
+ }
83
+ this.iframe = null;
84
+ this.isReady = false;
85
+ this.log("Unmounted");
86
+ }
87
+ getIsReady() {
88
+ return this.isReady;
89
+ }
90
+ handleMessage(event) {
91
+ if (event.origin !== this.origin) return;
92
+ const { type, payload } = event.data || {};
93
+ if (!type) return;
94
+ this.log("Received message:", type, payload ?? "");
95
+ switch (type) {
96
+ case EventType.WIDGET_READY:
97
+ this.isReady = true;
98
+ this.sendConfig();
99
+ this.options.onReady?.();
100
+ break;
101
+ case EventType.PAYMENT:
102
+ this.handlePaymentResult(payload, this.options.onPayment);
103
+ break;
104
+ case EventType.EXPRESS_PAYMENT:
105
+ this.handlePaymentResult(payload, this.options.onExpressPayment);
106
+ break;
107
+ case EventType.REDIRECT:
108
+ if (payload?.url && this.isValidRedirectUrl(payload.url)) {
109
+ window.location.href = payload.url;
110
+ }
111
+ break;
112
+ }
113
+ }
114
+ isValidRedirectUrl(url) {
115
+ try {
116
+ const parsed = new URL(url);
117
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
118
+ this.log("Invalid redirect URL protocol:", parsed.protocol);
119
+ return false;
120
+ }
121
+ if (parsed.origin === window.location.origin) {
122
+ return true;
123
+ }
124
+ const configRedirectUrl = this.options.config.successRedirectUrl;
125
+ if (configRedirectUrl) {
126
+ const configParsed = new URL(configRedirectUrl);
127
+ if (parsed.origin === configParsed.origin) {
128
+ return true;
129
+ }
130
+ }
131
+ this.log("Redirect URL not in allowed origins:", url);
132
+ return false;
133
+ } catch {
134
+ this.log("Invalid redirect URL:", url);
135
+ return false;
136
+ }
137
+ }
138
+ handlePaymentResult(payload, callback) {
139
+ if (!callback) return;
140
+ callback(payload);
141
+ }
142
+ sendConfig() {
143
+ if (!this.iframe?.contentWindow) return;
144
+ this.iframe.contentWindow.postMessage(
145
+ {
146
+ type: EventType.WIDGET_CONFIG,
147
+ payload: this.options.config
148
+ },
149
+ this.origin
150
+ );
151
+ this.log("Sent config:", sanitizeToken(this.options.config.clientToken));
152
+ }
153
+ log(...args) {
154
+ if (this.options.debug) {
155
+ console.log("[FlintN SDK]", ...args);
156
+ }
157
+ }
158
+ };
159
+
160
+ // src/react/use-flintn-payment.ts
161
+ function useFlintNPayment(options) {
162
+ const containerRef = useRef(null);
163
+ const paymentRef = useRef(null);
164
+ const onPaymentRef = useRef(options.onPayment);
165
+ const onExpressPaymentRef = useRef(options.onExpressPayment);
166
+ const [isReady, setIsReady] = useState(false);
167
+ const [paymentResult, setPaymentResult] = useState(null);
168
+ const [error, setError] = useState(null);
169
+ useEffect(() => {
170
+ onPaymentRef.current = options.onPayment;
171
+ }, [options.onPayment]);
172
+ useEffect(() => {
173
+ onExpressPaymentRef.current = options.onExpressPayment;
174
+ }, [options.onExpressPayment]);
175
+ useEffect(() => {
176
+ if (!containerRef.current) return;
177
+ setIsReady(false);
178
+ setPaymentResult(null);
179
+ setError(null);
180
+ try {
181
+ paymentRef.current = new FlintNPayment({
182
+ ...options,
183
+ onReady: () => setIsReady(true),
184
+ onPayment: (result) => {
185
+ setPaymentResult(result);
186
+ onPaymentRef.current?.(result);
187
+ },
188
+ onExpressPayment: (result) => {
189
+ setPaymentResult(result);
190
+ onExpressPaymentRef.current?.(result);
191
+ },
192
+ onError: (err) => setError(err)
193
+ });
194
+ paymentRef.current.mount(containerRef.current);
195
+ } catch (err) {
196
+ setError({
197
+ code: "INIT_ERROR",
198
+ message: err instanceof Error ? err.message : "Failed to initialize SDK"
199
+ });
200
+ }
201
+ return () => {
202
+ paymentRef.current?.unmount();
203
+ paymentRef.current = null;
204
+ };
205
+ }, [
206
+ options.origin,
207
+ options.debug,
208
+ options.config.clientToken,
209
+ options.config.maxWidth,
210
+ options.config.isCardHolderRequired,
211
+ options.config.successRedirectUrl
212
+ ]);
213
+ return {
214
+ containerRef,
215
+ isReady,
216
+ paymentResult,
217
+ error
218
+ };
219
+ }
220
+ export {
221
+ EventType,
222
+ useFlintNPayment
223
+ };
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "flintn-checkout",
3
+ "version": "0.0.1",
4
+ "description": "FlintN Payment SDK — drop-in iframe checkout for card payments and wallets with localization and theming.",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "keywords": [
9
+ "flintn",
10
+ "payments",
11
+ "payment",
12
+ "checkout",
13
+ "payment-gateway",
14
+ "payment-widget",
15
+ "payment-iframe",
16
+ "iframe",
17
+ "tokenization",
18
+ "card-payments",
19
+ "3ds",
20
+ "apple-pay",
21
+ "google-pay",
22
+ "web-sdk"
23
+ ],
24
+ "author": "FlintN (https://flintn.com), support@flintn.com",
25
+ "homepage": "https://tutorvue-group.gitbook.io/flintn-docs",
26
+ "repository": { "url": "https://github.com" },
27
+ "exports": {
28
+ ".": {
29
+ "import": "./dist/index.mjs",
30
+ "require": "./dist/index.js",
31
+ "types": "./dist/index.d.ts"
32
+ },
33
+ "./react": {
34
+ "import": "./dist/react.mjs",
35
+ "require": "./dist/react.js",
36
+ "types": "./dist/react.d.ts"
37
+ }
38
+ },
39
+ "files": ["dist"],
40
+ "scripts": {
41
+ "build": "tsup",
42
+ "dev": "tsup --watch",
43
+ "typecheck": "tsc --noEmit"
44
+ },
45
+ "peerDependencies": {
46
+ "react": ">=17.0.0"
47
+ },
48
+ "peerDependenciesMeta": {
49
+ "react": { "optional": true }
50
+ },
51
+ "devDependencies": {
52
+ "@types/react": "^19.0.0",
53
+ "react": "^19.0.0",
54
+ "tsup": "^8.0.0",
55
+ "typescript": "^5.3.0"
56
+ },
57
+ "license": "MIT"
58
+ }