@lockerverse/react 0.2.132 → 0.2.133-dev.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.
Files changed (2) hide show
  1. package/README.md +158 -26
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -1,6 +1,26 @@
1
1
  # `@lockerverse/react`
2
2
 
3
- Tree-shakable React UI for Lockerverse payment and signup widgets. The Core SDK is installed automatically as a normal package dependency.
3
+ Tree-shakable React UI for Lockerverse payments, signup, auctions, and public Events. The Core SDK is installed automatically as a normal package dependency.
4
+
5
+ This README ships with the package and describes that release. For an installed
6
+ project, use its local README, `package.json` exports, and public `.d.ts` files.
7
+ Online examples can describe a newer release. Do not import private `dist`
8
+ modules or copy API details from a different SDK version.
9
+
10
+ ## Usage guide
11
+
12
+ - [Install](#install) and [payment quick start](#use)
13
+ - [Configuration and environment](#configuration-and-environment)
14
+ - [Server rendering](#server-rendering) and [resource identity](#resource-identity)
15
+ - [Custom product UI](#custom-product-ui) and [live availability](#live-product-availability)
16
+ - [Donate launcher](#donate-launcher)
17
+ - [Signup](#signup)
18
+ - [Styling](#styling)
19
+ - [Payment outcomes](#payment-outcomes) and [recovery](#payment-recovery)
20
+ - [Auctions](#auctions) and [direct auction pages](#direct-auction-pages)
21
+ - [Public event listings](#public-event-listings)
22
+
23
+ For custom non-React clients, use the README shipped with `@lockerverse/sdk`.
4
24
 
5
25
  ## Install
6
26
 
@@ -35,8 +55,6 @@ export function Checkout() {
35
55
  metadata={{ source: "community-site" }}
36
56
  onPaymentComplete={(payment: LockerversePaymentCompletion) => {
37
57
  console.info("Payment complete", payment.checkoutId);
38
- console.info("Customer", payment.email);
39
- console.info("Authoritative items", payment.lineItems);
40
58
  }}
41
59
  onPaymentUncertain={({ paymentReference, reason }) => {
42
60
  console.info("Payment needs status recovery", paymentReference, reason);
@@ -165,6 +183,38 @@ cadence and configures Stripe Elements for a subscription:
165
183
  Recurring checkout accepts one fixed Product. It does not accept quantities,
166
184
  custom amounts, or multiple Products.
167
185
 
186
+ ### Live product availability
187
+
188
+ Use the live `widget.catalog` for names, prices, and limits. Find the requested
189
+ product by its `listingId` or `slug`; do not keep a separate static price list.
190
+ Amounts are integer cents. Format them with `catalog.payment.currency`, not a
191
+ currency field on the product.
192
+
193
+ Before enabling a new purchase, check the selected product's `minimumQuantity`,
194
+ `maximumQuantity`, and `inventoryQuantity`; nullable limits mean no limit.
195
+ For custom amounts, use `getLockerverseMinimumCustomAmountCents(product)` from
196
+ `@lockerverse/sdk/checkout` to include the SDK minimum and the product minimum.
197
+ Also check:
198
+
199
+ - The matching `catalog.availability.listings` entry: `status`,
200
+ `maxPurchasableQuantity`, and `remainingQuantity`.
201
+ - Each `catalog.availability.sharedLimits` entry whose `listingIds` includes
202
+ the selected product's listing: `status` and `remainingQuantity`. Add the
203
+ quantities of all selected listings in that shared group before checking its
204
+ remaining quantity.
205
+
206
+ A missing listing availability entry means no listing-specific limit was
207
+ reported. Other product and shared limits still apply. These checks help the
208
+ host show an unavailable state; the authoritative quote decides whether the
209
+ selection can be purchased. Show loading, retry, and empty states. Keep an
210
+ unresolved payment available for recovery even if a new purchase is unavailable.
211
+
212
+ If an existing button already selects one product, open its checkout directly.
213
+ Use the donate launcher when the visitor needs a product picker. For a custom
214
+ dialog, use an accessible title, close button, focus handling, and mobile scroll
215
+ area. Keep recovery state in an owner that remains mounted when the dialog
216
+ closes. Closing a dialog does not complete or cancel a payment.
217
+
168
218
  ## Donate launcher
169
219
 
170
220
  `LockerverseDonateLauncher` turns an existing payment widget into a native
@@ -238,8 +288,19 @@ export function Signup() {
238
288
 
239
289
  Pass `googlePlacesApiKey` to enable US address suggestions when the signup asks for an address. Manual address entry always remains available.
240
290
 
241
- For a development HTTPS backend, provide `apiBaseUrl` to the hook. The backend
242
- supplies the correct Stripe public key.
291
+ ## Configuration and environment
292
+
293
+ Production is the default. Payment, signup, auction, and Events hooks accept
294
+ `apiBaseUrl` and `environment` together. A custom URL requires an explicit
295
+ environment; `environment: "development"` also requires a URL. Include `/api`
296
+ in the API base URL. Do not infer the environment from the host website's URL.
297
+ Use the public community and resource slugs from the intended integration.
298
+ Signup uses `signupSlug`; payment uses `widgetSlug`.
299
+
300
+ The backend supplies the Stripe publishable key and connected account. Do not
301
+ hard-code either value or pass a secret key. The default Stripe loader handles
302
+ initialization. If a host supplies `stripeLoader={loadStripe}`, preserve all
303
+ loader arguments, including the connected-account options.
243
304
 
244
305
  ```tsx
245
306
  const widget = useLockerverseWidget({
@@ -255,6 +316,25 @@ const widget = useLockerverseWidget({
255
316
  />
256
317
  ```
257
318
 
319
+ ## Server rendering
320
+
321
+ Use the host framework's client component or client-only boundary for browser
322
+ payment UI. Keep browser storage access in an effect, not at module scope or
323
+ during server rendering. For TanStack Router, import `ClientOnly` from
324
+ `@tanstack/react-router` and put the component that owns checkout inside it:
325
+
326
+ ```tsx
327
+ import { ClientOnly } from "@tanstack/react-router";
328
+
329
+ <ClientOnly fallback={<p>Loading checkout...</p>}>
330
+ <Checkout />
331
+ </ClientOnly>
332
+ ```
333
+
334
+ The host supplies this boundary; TanStack Router is not an SDK dependency.
335
+ Wait for saved recovery state to load before mounting payment. Remount the
336
+ recovery owner when its resource or recovery key changes.
337
+
258
338
  ## Styling
259
339
 
260
340
  Auction checkout, payment, and signup use common email, phone, field-error, and checkbox controls. Payment and signup share the custom-field renderer. Phone fields use the lightweight payment/signup country list, normalization, and Valibot format validation. The shared phone control is imported directly, with no separate phone chunk or metadata library. Each flow keeps its existing field configuration and backend rules.
@@ -303,31 +383,83 @@ An authoritative `failed` result is retryable and the next submit receives a new
303
383
 
304
384
  To abandon a locked attempt and intentionally start a new checkout, the host must remount `LockerversePayment` with a new React `key` after resolving the payment status through its own workflow.
305
385
 
306
- Persist `onPaymentRecoveryChange` synchronously before navigation and pass the saved value back through `resumePayment` after a refresh. The recovery value contains a Lockerverse reference and public connected-account ID, never a Stripe secret.
386
+ ### Payment recovery
307
387
 
308
- ```tsx
309
- import type { LockerversePaymentRecovery } from "@lockerverse/react";
388
+ Persist `onPaymentRecoveryChange` synchronously before navigation and pass the
389
+ saved value back through `resumePayment` after a refresh. Do not defer the save
390
+ to an effect. A recovery record has only `paymentReference: string` and
391
+ `connectedAccountId: string | null`; it contains no Stripe secret. Scope its
392
+ storage key to the environment, community, widget, and selection.
310
393
 
311
- const recoveryKey = "lockerverse:tailgate-party:payment";
312
- const savedRecovery = sessionStorage.getItem(recoveryKey);
313
- const recovery = savedRecovery
314
- ? (JSON.parse(savedRecovery) as LockerversePaymentRecovery)
315
- : null;
394
+ This hook reads storage after mount, validates the saved record, and keeps an
395
+ in-memory record if storage is unavailable:
316
396
 
317
- <LockerversePayment
318
- selection={selection}
319
- resumePayment={recovery}
320
- onPaymentRecoveryChange={(nextRecovery) => {
321
- if (nextRecovery) {
322
- sessionStorage.setItem(recoveryKey, JSON.stringify(nextRecovery));
323
- } else {
324
- sessionStorage.removeItem(recoveryKey);
397
+ ```tsx
398
+ import { useEffect, useState } from "react";
399
+ import type { LockerversePaymentRecovery } from "@lockerverse/react/payment";
400
+
401
+ function usePaymentRecovery(recoveryKey: string) {
402
+ const [recovery, setRecovery] = useState<LockerversePaymentRecovery | null>(null);
403
+ const [loaded, setLoaded] = useState(false);
404
+
405
+ useEffect(() => {
406
+ try {
407
+ const raw = sessionStorage.getItem(recoveryKey);
408
+ const saved: unknown = raw ? JSON.parse(raw) : null;
409
+ if (
410
+ saved !== null && typeof saved === "object" &&
411
+ "paymentReference" in saved && typeof saved.paymentReference === "string" &&
412
+ "connectedAccountId" in saved &&
413
+ (saved.connectedAccountId === null || typeof saved.connectedAccountId === "string")
414
+ ) {
415
+ setRecovery({
416
+ paymentReference: saved.paymentReference,
417
+ connectedAccountId: saved.connectedAccountId,
418
+ });
419
+ }
420
+ } catch {
421
+ // Invalid or unavailable storage must not break checkout.
325
422
  }
326
- }}
327
- widget={widget}
328
- />
423
+ setLoaded(true);
424
+ }, [recoveryKey]);
425
+
426
+ function saveRecovery(next: LockerversePaymentRecovery | null) {
427
+ setRecovery(next);
428
+ try {
429
+ if (next) sessionStorage.setItem(recoveryKey, JSON.stringify(next));
430
+ else sessionStorage.removeItem(recoveryKey);
431
+ } catch {
432
+ // Keep the in-memory record if storage is unavailable.
433
+ }
434
+ }
435
+
436
+ return { loaded, recovery, saveRecovery };
437
+ }
329
438
  ```
330
439
 
440
+ Use it in the payment owner, keyed by the recovery key. Render checkout only
441
+ after `loaded` is true:
442
+
443
+ ```tsx
444
+ const { loaded, recovery, saveRecovery } = usePaymentRecovery(recoveryKey);
445
+
446
+ return loaded ? (
447
+ <LockerversePayment
448
+ selection={selection}
449
+ resumePayment={recovery}
450
+ onPaymentRecoveryChange={saveRecovery}
451
+ widget={widget}
452
+ />
453
+ ) : <p>Loading checkout...</p>;
454
+ ```
455
+
456
+ Retain the record on `onPaymentUncertain` and let the component recover the
457
+ same payment. Do not create a replacement charge for an unknown result.
458
+ `onPaymentComplete` confirms success; its `paymentRequired` distinguishes a
459
+ paid purchase from a no-charge order. This browser callback does not grant paid
460
+ access; access checks require server verification. Keep private contact and
461
+ payment data out of URLs, analytics, logs, and public metadata.
462
+
331
463
  Malformed host selections and invalid custom tips render customer-safe validation messages and do not call the Lockerverse API. Product selection remains owned by the host application.
332
464
 
333
465
  ## Auctions
@@ -389,8 +521,8 @@ export function AuctionPage() {
389
521
  }
390
522
  ```
391
523
 
392
- Production is the default. Set `environment: "development"` on the hook only
393
- when you intend to use the development backend. Auction data supplies the
524
+ Production is the default. For development, set both `apiBaseUrl` and
525
+ `environment: "development"` as shown in [configuration](#configuration-and-environment). Auction data supplies the
394
526
  publishable key and connected account for Stripe initialization. The backend
395
527
  must include the auction `payment` configuration to use built-in checkout.
396
528
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lockerverse/react",
3
- "version": "0.2.132",
3
+ "version": "0.2.133-dev.1",
4
4
  "description": "Tree-shakable React payment and signup UI for Lockerverse",
5
5
  "repository": {
6
6
  "type": "git",
@@ -127,7 +127,7 @@
127
127
  "embla-carousel-react": "8.6.0",
128
128
  "lucide-react": "0.536.0",
129
129
  "valibot": "1.4.2",
130
- "@lockerverse/sdk": "0.2.132"
130
+ "@lockerverse/sdk": "0.2.133-dev.1"
131
131
  },
132
132
  "devDependencies": {
133
133
  "@size-limit/file": "13.0.3",