@flopay/react 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +378 -0
- package/dist/index.cjs +1828 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +402 -0
- package/dist/index.d.ts +402 -0
- package/dist/index.mjs +1783 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +56 -0
package/README.md
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
# @flopay/react
|
|
2
|
+
|
|
3
|
+
React bindings for the FloPay SDK. Provides a context provider, drop-in checkout form components, individual element components, and hooks.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @flopay/react @flopay/js @flopay/shared react react-dom
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Peer dependencies: `react >= 18.0.0`, `react-dom >= 18.0.0`.
|
|
12
|
+
|
|
13
|
+
## Environment Setup
|
|
14
|
+
|
|
15
|
+
Configure which billing API the SDK uses. Choose one:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Option A: Environment variable (recommended)
|
|
19
|
+
# .env.production
|
|
20
|
+
NEXT_PUBLIC_FLOPAY_ENV=production
|
|
21
|
+
|
|
22
|
+
# .env.development
|
|
23
|
+
NEXT_PUBLIC_FLOPAY_ENV=staging
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
// Option B: Call at app startup
|
|
28
|
+
import { configureFlopay } from '@flopay/shared';
|
|
29
|
+
configureFlopay({ environment: 'production' });
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
If neither is set, the SDK defaults to staging.
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
### Recommended: FloPayCheckout
|
|
37
|
+
|
|
38
|
+
The simplest integration — one component, one prop:
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import { FloPayCheckout } from '@flopay/react';
|
|
42
|
+
|
|
43
|
+
function CheckoutPage() {
|
|
44
|
+
return (
|
|
45
|
+
<FloPayCheckout
|
|
46
|
+
sessionId="sess_abc123"
|
|
47
|
+
onComplete={(result) => {
|
|
48
|
+
window.location.href = '/success';
|
|
49
|
+
}}
|
|
50
|
+
onError={(err) => console.error(err)}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`FloPayCheckout` automatically fetches the session, initializes the correct payment provider, and renders a `SplitCardForm` with card fields, Apple Pay, Google Pay, and PayPal. Customer data (email, userId, name) is injected from the session.
|
|
57
|
+
|
|
58
|
+
#### Checkout Modes
|
|
59
|
+
|
|
60
|
+
`FloPayCheckout` supports three checkout modes matching the billing API's `checkoutMode` field:
|
|
61
|
+
|
|
62
|
+
- **`full`** (default) — Shows the full payment form with card fields + wallet buttons.
|
|
63
|
+
- **`confirm`** — Hides the payment form and shows a "Confirm Purchase" button. Uses a saved payment method on the backend.
|
|
64
|
+
- **`auto`** — Auto-submits with a saved payment method after the session loads. Falls back to `full` mode on failure.
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
// Confirm mode — one-click purchase with saved card
|
|
68
|
+
<FloPayCheckout
|
|
69
|
+
sessionId="sess_abc123"
|
|
70
|
+
checkoutMode="confirm"
|
|
71
|
+
confirmLabel="Complete Purchase"
|
|
72
|
+
onComplete={(result) => router.push('/success')}
|
|
73
|
+
/>
|
|
74
|
+
|
|
75
|
+
// Auto mode — instant checkout, falls back to full form
|
|
76
|
+
<FloPayCheckout
|
|
77
|
+
sessionId="sess_abc123"
|
|
78
|
+
checkoutMode="auto"
|
|
79
|
+
onComplete={(result) => router.push('/success')}
|
|
80
|
+
onSessionCompleted={(successUrl) => router.push(successUrl)}
|
|
81
|
+
/>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The mode can also be set on the session itself via the billing API's `checkoutMode` field. The `checkoutMode` prop overrides the session value.
|
|
85
|
+
|
|
86
|
+
#### Wallet Buttons
|
|
87
|
+
|
|
88
|
+
Apple Pay, Google Pay, and PayPal are enabled by default. Toggle them with props:
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
<FloPayCheckout
|
|
92
|
+
sessionId="sess_abc123"
|
|
93
|
+
showApplePay={true} // default: true
|
|
94
|
+
showGooglePay={true} // default: true
|
|
95
|
+
showPayPal={true} // default: true
|
|
96
|
+
onComplete={handleSuccess}
|
|
97
|
+
/>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Apple Pay and Google Pay only render on supported devices (Apple Pay on Safari/macOS/iOS, Google Pay on Chrome).
|
|
101
|
+
|
|
102
|
+
### Advanced: Manual Provider Setup
|
|
103
|
+
|
|
104
|
+
For full control over initialization:
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
import { loadFloPay } from '@flopay/js';
|
|
108
|
+
import { FloPayProvider, CheckoutForm } from '@flopay/react';
|
|
109
|
+
|
|
110
|
+
const floPayPromise = loadFloPay('pk_test_...');
|
|
111
|
+
|
|
112
|
+
function CheckoutPage() {
|
|
113
|
+
return (
|
|
114
|
+
<FloPayProvider
|
|
115
|
+
flopay={floPayPromise}
|
|
116
|
+
options={{
|
|
117
|
+
amount: 2999,
|
|
118
|
+
currency: 'usd',
|
|
119
|
+
}}
|
|
120
|
+
>
|
|
121
|
+
<CheckoutForm
|
|
122
|
+
sessionId="session_uuid"
|
|
123
|
+
email="user@example.com"
|
|
124
|
+
userId="user_1"
|
|
125
|
+
onComplete={(result) => {
|
|
126
|
+
if (result.status === 'succeeded') {
|
|
127
|
+
window.location.href = '/success';
|
|
128
|
+
}
|
|
129
|
+
}}
|
|
130
|
+
onError={(err) => console.error(err)}
|
|
131
|
+
/>
|
|
132
|
+
</FloPayProvider>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
`CheckoutForm` handles the full payment lifecycle by default:
|
|
138
|
+
|
|
139
|
+
1. Validate elements via `submitElements()`
|
|
140
|
+
2. Tokenize card via `createPaymentMethod()` -> `pm_xxx`
|
|
141
|
+
3. Create PaymentIntent via billing API
|
|
142
|
+
4. Confirm card payment (handles 3D Secure)
|
|
143
|
+
5. Submit tokenized body to `POST /v1/checkouts/sessions/process`
|
|
144
|
+
6. If backend returns `3ds_required`, re-confirm with new client secret
|
|
145
|
+
7. Resume wallet payments after redirect (PayPal)
|
|
146
|
+
|
|
147
|
+
### Override Mode (Custom Backend)
|
|
148
|
+
|
|
149
|
+
Pass `onTokenizedBody` to handle backend submission yourself:
|
|
150
|
+
|
|
151
|
+
```tsx
|
|
152
|
+
<CheckoutForm
|
|
153
|
+
sessionId="session_uuid"
|
|
154
|
+
billingApiUrl="https://billing.example.com"
|
|
155
|
+
email="user@example.com"
|
|
156
|
+
onTokenizedBody={(body) => {
|
|
157
|
+
// body = { id: 'pm_xxx', type: 'card', threeDSecureActionResultTokenId: 'pi_xxx' }
|
|
158
|
+
myCustomProcessPayment(body);
|
|
159
|
+
}}
|
|
160
|
+
/>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### SplitCardForm (Split Card Fields + PayPal)
|
|
164
|
+
|
|
165
|
+
`SplitCardForm` renders separate CardNumber, CardExpiry, and CardCVC fields with an integrated PayPal button. It matches the existing checkout/StripeCardForm layout.
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
import { FloPayProvider, SplitCardForm } from '@flopay/react';
|
|
169
|
+
import { loadFloPay } from '@flopay/js';
|
|
170
|
+
|
|
171
|
+
const floPayPromise = loadFloPay('pk_test_...');
|
|
172
|
+
|
|
173
|
+
function CheckoutPage() {
|
|
174
|
+
return (
|
|
175
|
+
<FloPayProvider
|
|
176
|
+
flopay={floPayPromise}
|
|
177
|
+
options={{
|
|
178
|
+
amount: 2999,
|
|
179
|
+
currency: 'usd',
|
|
180
|
+
}}
|
|
181
|
+
>
|
|
182
|
+
<SplitCardForm
|
|
183
|
+
sessionId="session_uuid"
|
|
184
|
+
billingApiUrl="https://billing.example.com"
|
|
185
|
+
email="user@example.com"
|
|
186
|
+
userId="user_1"
|
|
187
|
+
totalAmount={29.99} // dollars (for PayPal Elements config)
|
|
188
|
+
currency="usd"
|
|
189
|
+
showPayPal={true} // default: true
|
|
190
|
+
onComplete={(result) => {
|
|
191
|
+
if (result.status === 'succeeded') {
|
|
192
|
+
window.location.href = '/success';
|
|
193
|
+
}
|
|
194
|
+
}}
|
|
195
|
+
onError={(err) => console.error(err)}
|
|
196
|
+
onFirstNameChange={(name) => setFirstName(name)}
|
|
197
|
+
onLastNameChange={(name) => setLastName(name)}
|
|
198
|
+
/>
|
|
199
|
+
</FloPayProvider>
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
The `SplitCardForm` layout:
|
|
205
|
+
1. Wallet buttons — Apple Pay + Google Pay (via ExpressCheckoutElement in the main Elements instance)
|
|
206
|
+
2. PayPal button (via ExpressCheckoutElement in a separate Elements instance)
|
|
207
|
+
3. "or pay with card" divider
|
|
208
|
+
4. Card Number input
|
|
209
|
+
5. Card Expiry + CVC side by side
|
|
210
|
+
6. Full Name input
|
|
211
|
+
7. Submit button
|
|
212
|
+
|
|
213
|
+
### PayPal Handling
|
|
214
|
+
|
|
215
|
+
PayPal requires its own Stripe Elements instance because it cannot share an Elements group that uses `paymentMethodCreation: 'manual'`. The SDK handles this in two ways:
|
|
216
|
+
|
|
217
|
+
- **`SplitCardForm`**: Automatically manages two Elements instances internally. Card fields use the provider's elements with `paymentMethodCreation: 'manual'`. PayPal renders inside a separate `<Elements>` wrapper with `captureMethod: 'manual'` and no `paymentMethodCreation`.
|
|
218
|
+
|
|
219
|
+
- **`PayPalButton`** (standalone): Must be rendered inside its own `FloPayProvider`:
|
|
220
|
+
|
|
221
|
+
```tsx
|
|
222
|
+
{/* Card fields provider */}
|
|
223
|
+
<FloPayProvider flopay={flopay} options={{ amount, currency }}>
|
|
224
|
+
<SplitCardForm ... />
|
|
225
|
+
</FloPayProvider>
|
|
226
|
+
|
|
227
|
+
{/* PayPal provider (separate instance) */}
|
|
228
|
+
<FloPayProvider flopay={flopay} options={{ amount, currency, paymentMethodCreation: 'auto' }}>
|
|
229
|
+
<PayPalButton
|
|
230
|
+
sessionId="session_uuid"
|
|
231
|
+
billingApiUrl="https://billing.example.com"
|
|
232
|
+
email="user@example.com"
|
|
233
|
+
onComplete={() => router.push('/success')}
|
|
234
|
+
/>
|
|
235
|
+
</FloPayProvider>
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Using Individual Elements
|
|
239
|
+
|
|
240
|
+
```tsx
|
|
241
|
+
import { FloPayProvider, PaymentElement, CardElement, useFloPay } from '@flopay/react';
|
|
242
|
+
|
|
243
|
+
function CustomForm() {
|
|
244
|
+
const flopay = useFloPay();
|
|
245
|
+
|
|
246
|
+
const handleSubmit = async () => {
|
|
247
|
+
if (!flopay) return;
|
|
248
|
+
const { error } = await flopay.submitElements();
|
|
249
|
+
if (error) return console.error(error);
|
|
250
|
+
const { paymentMethodId } = await flopay.createPaymentMethod();
|
|
251
|
+
// Use paymentMethodId...
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
return (
|
|
255
|
+
<div>
|
|
256
|
+
<PaymentElement options={{ layout: 'tabs' }} />
|
|
257
|
+
<button onClick={handleSubmit}>Pay</button>
|
|
258
|
+
</div>
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Using Hooks
|
|
264
|
+
|
|
265
|
+
```tsx
|
|
266
|
+
import { useFloPay, useElements, useCheckout } from '@flopay/react';
|
|
267
|
+
|
|
268
|
+
function PaymentStatus() {
|
|
269
|
+
const flopay = useFloPay(); // FloPay | null
|
|
270
|
+
const elements = useElements(); // FloPayElements | null
|
|
271
|
+
const checkout = useCheckout(); // { session, loading, error }
|
|
272
|
+
|
|
273
|
+
if (!flopay) return <div>Loading SDK...</div>;
|
|
274
|
+
// ...
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## API Reference
|
|
279
|
+
|
|
280
|
+
### Components
|
|
281
|
+
|
|
282
|
+
| Component | Description |
|
|
283
|
+
|-----------|-------------|
|
|
284
|
+
| `FloPayProvider` | Context provider. Accepts `flopay` (instance or promise), `options?`, and `children`. Creates the elements group automatically. |
|
|
285
|
+
| `CheckoutForm` | Drop-in form with unified PaymentElement. Self-contained by default, or override with `onTokenizedBody`. Supports `ref` for imperative `handleNextAction()`. |
|
|
286
|
+
| `SplitCardForm` | Split card form (CardNumber + CardExpiry + CardCvc + Full Name). Integrates PayPal via separate Elements instance. Supports `ref` for imperative `handleNextAction()`. |
|
|
287
|
+
| `PayPalButton` | Standalone PayPal button. Requires its own `FloPayProvider` with `paymentMethodCreation` set to something other than `'manual'`. |
|
|
288
|
+
| `PaymentElement` | Unified payment element (cards, wallets, etc.) |
|
|
289
|
+
| `CardElement` | Combined card input |
|
|
290
|
+
| `CardNumberElement` | Card number field |
|
|
291
|
+
| `CardExpiryElement` | Card expiry field |
|
|
292
|
+
| `CardCvcElement` | Card CVC field |
|
|
293
|
+
| `AddressElement` | Address input element |
|
|
294
|
+
|
|
295
|
+
### Hooks
|
|
296
|
+
|
|
297
|
+
| Hook | Returns | Description |
|
|
298
|
+
|------|---------|-------------|
|
|
299
|
+
| `useFloPay()` | `FloPay \| null` | Current FloPay instance from context. `null` while loading. |
|
|
300
|
+
| `useElements()` | `FloPayElements \| null` | Current elements group from context. `null` while loading. |
|
|
301
|
+
| `useCheckout()` | `CheckoutState` | `{ session, loading, error }` from CheckoutContext |
|
|
302
|
+
|
|
303
|
+
### FloPayProviderProps
|
|
304
|
+
|
|
305
|
+
| Prop | Type | Description |
|
|
306
|
+
|------|------|-------------|
|
|
307
|
+
| `flopay` | `Promise<FloPay> \| FloPay` | SDK instance or promise from `loadFloPay()` |
|
|
308
|
+
| `options.locale` | `string?` | Locale |
|
|
309
|
+
| `options.appearance` | `FloPayAppearance?` | Theme appearance |
|
|
310
|
+
| `options.clientSecret` | `string?` | PaymentIntent client secret (if intent already exists) |
|
|
311
|
+
| `options.amount` | `number?` | Amount in cents (used when no `clientSecret`) |
|
|
312
|
+
| `options.currency` | `string?` | ISO 4217 currency code (used when no `clientSecret`) |
|
|
313
|
+
| `options.paymentMethodCreation` | `'manual' \| 'auto'` | How payment methods are created |
|
|
314
|
+
|
|
315
|
+
### CheckoutFormProps
|
|
316
|
+
|
|
317
|
+
| Prop | Type | Description |
|
|
318
|
+
|------|------|-------------|
|
|
319
|
+
| `sessionId` | `string` | Checkout session UUID |
|
|
320
|
+
| `billingApiUrl` | `string` | Billing API base URL |
|
|
321
|
+
| `email` | `string?` | User email |
|
|
322
|
+
| `userId` | `string?` | User ID |
|
|
323
|
+
| `onComplete` | `(result: PaymentResult) => void` | Success callback |
|
|
324
|
+
| `onError` | `(error: FloPayError) => void` | Error callback |
|
|
325
|
+
| `onTokenizedBody` | `(body: TokenizedBody) => void` | Override: handle backend submission yourself |
|
|
326
|
+
| `layout` | `'tabs' \| 'accordion' \| 'auto'` | PaymentElement layout (default: `'auto'`) |
|
|
327
|
+
| `submitLabel` | `string` | Button text (default: `'Pay'`) |
|
|
328
|
+
| `showAddress` | `boolean \| 'billing' \| 'shipping'` | Show address element (default: `false`) |
|
|
329
|
+
| `className` | `string?` | CSS class for form wrapper |
|
|
330
|
+
| `children` | `ReactNode?` | Custom submit button |
|
|
331
|
+
| `firstName` | `string?` | Billing first name |
|
|
332
|
+
| `lastName` | `string?` | Billing last name |
|
|
333
|
+
| `chv` | `string?` | Checkout version for A/B tracking |
|
|
334
|
+
| `isProcessing` | `boolean?` | External processing state |
|
|
335
|
+
| `error` | `string?` | External error message |
|
|
336
|
+
| `onErrorChange` | `(error: string \| null) => void` | Error state change callback |
|
|
337
|
+
|
|
338
|
+
### SplitCardFormProps
|
|
339
|
+
|
|
340
|
+
Shares most props with `CheckoutFormProps`, plus:
|
|
341
|
+
|
|
342
|
+
| Prop | Type | Description |
|
|
343
|
+
|------|------|-------------|
|
|
344
|
+
| `showPayPal` | `boolean` | Show PayPal button (default: `true`) |
|
|
345
|
+
| `showApplePay` | `boolean` | Show Apple Pay button (default: `true`) |
|
|
346
|
+
| `showGooglePay` | `boolean` | Show Google Pay button (default: `true`) |
|
|
347
|
+
| `totalAmount` | `number` | Amount in dollars for PayPal config |
|
|
348
|
+
| `currency` | `string` | Currency code for PayPal config (default: `'usd'`) |
|
|
349
|
+
| `onFirstNameChange` | `(value: string) => void` | First name change callback |
|
|
350
|
+
| `onLastNameChange` | `(value: string) => void` | Last name change callback |
|
|
351
|
+
| `submitLabel` | `string` | Button text (default: `'CONFIRM PAYMENT'`) |
|
|
352
|
+
|
|
353
|
+
### ElementComponentProps (shared by all element components)
|
|
354
|
+
|
|
355
|
+
| Prop | Type | Description |
|
|
356
|
+
|------|------|-------------|
|
|
357
|
+
| `className` | `string?` | CSS class for wrapper div |
|
|
358
|
+
| `id` | `string?` | HTML id for wrapper div |
|
|
359
|
+
| `style` | `CSSProperties?` | Inline styles for wrapper div |
|
|
360
|
+
| `options` | `Partial<ElementOptions>?` | Options for the underlying element |
|
|
361
|
+
| `onChange` | `(event: ElementChangeEvent) => void` | Value change handler |
|
|
362
|
+
| `onReady` | `() => void` | Element ready handler |
|
|
363
|
+
| `onFocus` | `() => void` | Focus handler |
|
|
364
|
+
| `onBlur` | `() => void` | Blur handler |
|
|
365
|
+
| `onEscape` | `() => void` | Escape key handler |
|
|
366
|
+
|
|
367
|
+
### Types
|
|
368
|
+
|
|
369
|
+
| Type | Description |
|
|
370
|
+
|------|-------------|
|
|
371
|
+
| `FloPayProviderProps` | Props for `FloPayProvider` |
|
|
372
|
+
| `CheckoutFormProps` | Props for `CheckoutForm` |
|
|
373
|
+
| `CheckoutFormRef` | Ref type: `{ handleNextAction(clientSecret) }` |
|
|
374
|
+
| `SplitCardFormProps` | Props for `SplitCardForm` |
|
|
375
|
+
| `SplitCardFormRef` | Ref type: `{ handleNextAction(clientSecret) }` |
|
|
376
|
+
| `PayPalButtonProps` | Props for `PayPalButton` |
|
|
377
|
+
| `ElementComponentProps` | Shared props for all element components |
|
|
378
|
+
| `CheckoutState` | `{ session, loading, error }` |
|