@flopay/react 1.0.3 → 1.1.3

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 CHANGED
@@ -215,7 +215,8 @@ Use `onButtonClick` to track button interactions, `onDecline` to track declines/
215
215
  layout="buttons"
216
216
  createSession={{
217
217
  clientId: 'your-client-id',
218
- items: [{ providerItemId: 'product-1', providerItemName: 'Widget', totalAmount: 29.99, currency: 'EUR' }],
218
+ currency: 'EUR',
219
+ items: [{ providerItemId: 'product-1', providerItemName: 'Widget', totalAmount: 29.99 }],
219
220
  account: { userId: 'user_1', email: 'test@email.com' },
220
221
  successUrl: '/success',
221
222
  cancelUrl: '/cancel',
@@ -258,7 +259,8 @@ Skip the backend API route — create the session directly in the component:
258
259
  layout="buttons"
259
260
  createSession={{
260
261
  clientId: 'your-client-id',
261
- items: [{ providerItemId: 'product-1', providerItemName: 'Widget', totalAmount: 29.99, currency: 'EUR' }],
262
+ currency: 'EUR',
263
+ items: [{ providerItemId: 'product-1', providerItemName: 'Widget', totalAmount: 29.99 }],
262
264
  account: { userId: 'user_1', email: 'user@example.com', firstName: 'Jane', lastName: 'Doe' },
263
265
  successUrl: '/success',
264
266
  cancelUrl: '/cancel',
@@ -269,6 +271,8 @@ Skip the backend API route — create the session directly in the component:
269
271
 
270
272
  The component POSTs to the billing API, gets the full session back, and renders the form — zero backend code needed.
271
273
 
274
+ Session-level `currency` is now **required**. The backend (#760) enforces `@IsNotEmpty` on the field; the SDK pre-validates and throws `FloPayError({ type: 'validation_error', code: 'CurrencyRequired' })` before issuing the request when neither the session nor any item/subscription/product carries a currency. Pass `currency` at the top of `createSession` (preferred), or rely on the legacy fallback to the first item/subscription/product `currency`.
275
+
272
276
  When you use `onBeforeButtonClick` with `createSession`, any returned `InlineSessionPatch` is merged into the draft session params before the selected buttons-layout flow continues. That lets you add tracking data or update account fields just in time without pre-creating a separate backend session.
273
277
 
274
278
  ### Advanced: Manual Provider Setup
@@ -389,13 +393,22 @@ The `SplitCardForm` layout:
389
393
 
390
394
  ### PayPal Handling
391
395
 
392
- PayPal requires its own Stripe Elements instance because it cannot share an Elements group that uses `paymentMethodCreation: 'manual'`. In addition, the billing API may return a dedicated Stripe account for PayPal via `gatewayData.paypalPublishableKey` — when set, every PayPal Stripe operation uses that key. When the field is `null` or missing, PayPal is disabled (the button is not rendered); there is no fallback to the primary `publishableKey`.
396
+ The SDK supports two PayPal paths, selected per-session based on what the billing API advertises under `gateways.*`:
397
+
398
+ - **Direct PayPal** (preferred where available — works inside Facebook, Instagram, and other in-app browsers): when the session exposes `gateways.paypal.publishableKey`, the SDK renders PayPal via the official PayPal JS SDK using `<DirectPayPalButton>`. The PayPal client ID and `environment` (`'sandbox'`/`'live'`) come directly from the backend.
399
+ - **Stripe-rendered PayPal fallback** (legacy): when only `gateways.stripe` is configured, PayPal renders through Stripe's `ExpressCheckoutElement` in its own Elements wrapper. This path can't render in Facebook/Instagram in-app browsers.
400
+
401
+ Renderer selection is mutually exclusive per session — direct PayPal takes priority over Stripe-rendered PayPal. Consumer props such as `showPayPal` continue to gate visibility on the client side.
402
+
403
+ **PayPal-only sessions:** When the backend advertises only `gateways.paypal` (no `gateways.stripe`), `FloPayCheckout` skips Stripe Elements entirely and renders `<DirectPayPalButton>` as the sole payment surface. Sessions that advertise no supported gateway at all throw a `validation_error` explaining the expected shape.
404
+
405
+ The SDK exposes the relevant pieces in three ways:
393
406
 
394
- The SDK handles this in two ways:
407
+ - **`SplitCardForm`** / **`FloPayCheckout`**: pick the renderer automatically based on `gateways.*`. No additional configuration needed.
395
408
 
396
- - **`SplitCardForm`**: Automatically manages two Elements instances internally. Card/wallet fields use the primary Stripe account with `paymentMethodCreation: 'manual'`. PayPal renders inside a separate `<Elements>` wrapper backed by the dedicated PayPal FloPay instance (exposed via the `usePayPalFloPay()` hook).
409
+ - **`FloPayProvider`** (manual composition): accepts an optional `paypalFlopay` prop used to drive Stripe's PayPal redirect leg. Load a second `FloPay` instance yourself only if you need to render PayPal manually. `usePayPalFloPay()` exposes it to descendants.
397
410
 
398
- - **`FloPayProvider`** (manual composition): accepts an optional `paypalFlopay` prop. Load a second `FloPay` instance with the PayPal publishable key yourself, then pass it in alongside the primary instance. `usePayPalFloPay()` exposes it to descendants.
411
+ - **`DirectPayPalButton`** (standalone): can be rendered outside `SplitCardForm` when you only need the PayPal button. Pass `clientId`, `currency`, `environment`, and `isSubscription`.
399
412
 
400
413
  - **`PayPalButton`** (standalone): Must be rendered inside its own `FloPayProvider`:
401
414