brainerce 1.0.1 → 1.0.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/README.md CHANGED
@@ -123,15 +123,11 @@ const { data: products } = await client.getProducts();
123
123
 
124
124
  > **AI Agents / Vibe-Coders:** Read this section carefully! These are common misunderstandings.
125
125
 
126
- ### 1. Guest Checkout - Don't Use createCheckout with Local Cart!
126
+ ### 1. Guest Checkout - Use `startGuestCheckout()` for Guests
127
127
 
128
- **This is the #1 cause of "Cart not found" errors!**
128
+ **For guest users, use `startGuestCheckout()` which creates a checkout from the session cart:**
129
129
 
130
130
  ```typescript
131
- // ❌ WRONG - Local cart ID "__local__" doesn't exist on server!
132
- const cart = await client.smartGetCart(); // Returns { id: "__local__", ... }
133
- const checkout = await client.createCheckout({ cartId: cart.id }); // 💥 ERROR: Cart not found
134
-
135
131
  // ✅ CORRECT - Use startGuestCheckout() for guest users
136
132
  const result = await client.startGuestCheckout();
137
133
  if (result.tracked) {
@@ -145,7 +141,7 @@ const order = await client.submitGuestOrder();
145
141
 
146
142
  **Rule of thumb:**
147
143
 
148
- - Guest user + Local cart → `startGuestCheckout()` or `submitGuestOrder()`
144
+ - Guest user + Session cart → `startGuestCheckout()` or `submitGuestOrder()`
149
145
  - Logged-in user + Server cart → `createCheckout({ cartId })`
150
146
 
151
147
  ### 2. ⛔ NEVER Create Local Interfaces - Use SDK Types!
@@ -381,13 +377,7 @@ const total = subtotal - discount;
381
377
  - Cart field is `discountAmount`, NOT `discount`
382
378
  - Cart has NO `total` field - use `getCartTotals()` or calculate
383
379
  - Checkout DOES have a `total` field, but Cart does not
384
- - `getCartTotals()` only works with **server Cart** (which has `subtotal` and `discountAmount` fields). It does **NOT** work with **LocalCart** (guest cart from localStorage). For LocalCart, calculate totals manually:
385
- ```typescript
386
- const subtotal = cart.items.reduce(
387
- (sum, item) => sum + parseFloat(item.price || '0') * item.quantity,
388
- 0
389
- );
390
- ```
380
+ - `getCartTotals()` works with all carts guests now use server-side session carts with full pricing fields.
391
381
 
392
382
  ### 15. SearchSuggestions - Products Have `price`, Not `basePrice`
393
383
 
@@ -432,91 +422,61 @@ export default function SuccessPage() {
432
422
 
433
423
  **Why is this needed?**
434
424
 
435
- - `completeGuestCheckout()` sends the order to the server AND clears the local cart
425
+ - `completeGuestCheckout()` sends the order to the server AND clears the session cart
436
426
  - Without it, the order is never created on the server (payment goes through but no order!)
437
427
  - For partial checkout (AliExpress-style), only the purchased items are removed
438
- - **WARNING:** Do NOT use `handlePaymentSuccess()` - it only clears localStorage and does NOT create the order
428
+ - After successful checkout, also call `client.onCheckoutComplete()` to clear the session cart reference
439
429
 
440
430
  ---
441
431
 
442
432
  ## Checkout: Guest vs Logged-In Customer
443
433
 
444
- > **⚠️ CRITICAL:** There are TWO different checkout flows. Using the wrong one will cause errors!
434
+ Both guests and logged-in customers now use the same `smart*` cart methods. The SDK handles server-side session carts for guests automatically.
445
435
 
446
- | Customer Type | Cart Type | With Payment (Stripe) | Without Payment UI |
447
- | ------------- | ------------------------- | ---------------------- | -------------------- |
448
- | **Guest** | Local Cart (localStorage) | `startGuestCheckout()` | `submitGuestOrder()` |
449
- | **Logged In** | Server Cart | `createCheckout()` | `completeCheckout()` |
436
+ | Customer Type | Cart Method | Checkout |
437
+ | ------------- | ----------- | -------- |
438
+ | **Guest** | `smartAddToCart()` (session cart) | `startGuestCheckout()` `createCheckout()` |
439
+ | **Logged In** | `smartAddToCart()` (server cart) | `createCheckout()` `completeCheckout()` |
450
440
 
451
- ### COMMON MISTAKE - Don't Do This!
441
+ ### Cart Usage (Same for Both)
452
442
 
453
443
  ```typescript
454
- // WRONG - This will FAIL with "Cart not found" error!
455
- const cart = client.getLocalCart(); // Returns cart with id: "__local__"
456
- const checkout = await client.createCheckout({ cartId: cart.id }); // ERROR!
457
-
458
- // The "__local__" ID is virtual - it doesn't exist on the server!
459
- ```
444
+ // Add to cart works for both guests and logged-in users
445
+ await client.smartAddToCart({ productId: 'prod_123', quantity: 2 });
460
446
 
461
- ### Correct Flow for Guest Checkout with Payment
447
+ // Get cart
448
+ const cart = await client.smartGetCart();
462
449
 
463
- ```typescript
464
- // CORRECT - Use startGuestCheckout() for guests with local cart
465
- const result = await client.startGuestCheckout();
450
+ // Update quantity
451
+ await client.smartUpdateCartItem('prod_123', 5);
466
452
 
467
- if (result.tracked) {
468
- // Now you have a REAL checkout on the server
469
- const checkout = await client.getCheckout(result.checkoutId);
453
+ // Remove item
454
+ await client.smartRemoveFromCart('prod_123');
470
455
 
471
- // Continue with shipping, payment, etc.
472
- await client.setShippingAddress(result.checkoutId, { ... });
473
- const intent = await client.createPaymentIntent(result.checkoutId);
474
- // ... Stripe payment ...
475
- }
456
+ // Get totals (works for all carts)
457
+ import { getCartTotals } from 'brainerce';
458
+ const totals = getCartTotals(cart); // { subtotal, discount, shipping, total }
476
459
  ```
477
460
 
478
- ### Decision Flow
461
+ ### On Login — Merge Guest Cart
479
462
 
480
463
  ```typescript
481
- // ALWAYS check this at checkout!
482
- if (isLoggedIn()) {
483
- // Logged-in customer Server Cart + Checkout flow
484
- // Orders will be linked to their account
485
- const order = await completeServerCheckout();
486
- } else {
487
- // ✅ Guest → Local Cart + submitGuestOrder
488
- // Orders are standalone (not linked to any account)
489
- const order = await client.submitGuestOrder();
490
- }
464
+ // After setting customer token
465
+ client.setCustomerToken(token);
466
+ await client.syncCartOnLogin(); // Merges session cart into customer cart
491
467
  ```
492
468
 
493
- ### Guest Checkout (for visitors without account)
469
+ ### Guest Checkout
494
470
 
495
471
  ```typescript
496
- // Cart stored locally - NO API calls until checkout!
497
-
498
- // Add to local cart (stored in localStorage)
499
- client.addToLocalCart({
500
- productId: products[0].id,
501
- quantity: 1,
502
- name: products[0].name,
503
- price: String(products[0].basePrice),
504
- });
505
-
506
- // Set customer info
507
- client.setLocalCartCustomer({ email: 'customer@example.com' });
508
- client.setLocalCartShippingAddress({
509
- firstName: 'John',
510
- lastName: 'Doe',
511
- line1: '123 Main St',
512
- city: 'New York',
513
- postalCode: '10001',
514
- country: 'US',
515
- });
472
+ // Guest checkout creates a checkout from the session cart
473
+ const result = await client.startGuestCheckout();
516
474
 
517
- // Submit order (single API call!)
518
- const order = await client.submitGuestOrder();
519
- console.log('Order created:', order.orderId);
475
+ if (result.tracked) {
476
+ const checkout = await client.getCheckout(result.checkoutId);
477
+ await client.setShippingAddress(result.checkoutId, shippingAddress);
478
+ // ... continue with shipping rates, payment, etc.
479
+ }
520
480
  ```
521
481
 
522
482
  ### Logged-In Customer Checkout (orders linked to account)
@@ -525,27 +485,24 @@ console.log('Order created:', order.orderId);
525
485
  // 1. Make sure customer token is set (after login)
526
486
  client.setCustomerToken(authResponse.token);
527
487
 
528
- // 2. Create server cart (auto-linked to customer!)
529
- const cart = await client.createCart();
530
- localStorage.setItem('cartId', cart.id);
531
-
532
- // 3. Add items to server cart
533
- await client.addToCart(cart.id, {
488
+ // 2. Add items to cart (smart methods handle server cart automatically)
489
+ await client.smartAddToCart({
534
490
  productId: products[0].id,
535
491
  quantity: 1,
536
492
  });
537
493
 
538
- // 4. Create checkout from cart
494
+ // 3. Get cart and create checkout
495
+ const cart = await client.smartGetCart();
539
496
  const checkout = await client.createCheckout({ cartId: cart.id });
540
497
 
541
- // 5. Set customer info (REQUIRED - email is needed for order!)
498
+ // 4. Set customer info (REQUIRED - email is needed for order!)
542
499
  await client.setCheckoutCustomer(checkout.id, {
543
500
  email: 'customer@example.com',
544
501
  firstName: 'John',
545
502
  lastName: 'Doe',
546
503
  });
547
504
 
548
- // 6. Set shipping address
505
+ // 5. Set shipping address
549
506
  await client.setShippingAddress(checkout.id, {
550
507
  firstName: 'John',
551
508
  lastName: 'Doe',
@@ -555,11 +512,11 @@ await client.setShippingAddress(checkout.id, {
555
512
  country: 'US',
556
513
  });
557
514
 
558
- // 7. Get shipping rates and select one
515
+ // 6. Get shipping rates and select one
559
516
  const rates = await client.getShippingRates(checkout.id);
560
517
  await client.selectShippingMethod(checkout.id, rates[0].id);
561
518
 
562
- // 8. Complete checkout - order is linked to customer!
519
+ // 7. Complete checkout - order is linked to customer!
563
520
  const { orderId } = await client.completeCheckout(checkout.id);
564
521
  console.log('Order created:', orderId);
565
522
 
@@ -570,62 +527,53 @@ console.log('Order created:', orderId);
570
527
 
571
528
  ---
572
529
 
573
- ## Two Ways to Handle Cart
574
-
575
- ### Option 1: Local Cart (Guest Users)
530
+ ## Cart (Unified for All Users)
576
531
 
577
- For guest users, the cart is stored in **localStorage** - exactly like Amazon, Shopify, and other major platforms do. This means:
532
+ The SDK uses **server-side carts for all users**. Guests get automatic session carts; logged-in customers get server carts linked to their account.
578
533
 
579
- - ✅ No API calls when browsing/adding to cart
580
- - ✅ Cart persists across page refreshes
581
- - ✅ Single API call at checkout
582
- - ✅ No server load for window shoppers
534
+ - ✅ Cart persists across page refreshes (via session token in localStorage)
535
+ - ✅ Server-side pricing, discounts, and totals
536
+ - ✅ Automatic migration from guest → customer cart on login
537
+ - ✅ Same API for both guests and logged-in users
583
538
 
584
539
  ```typescript
585
- // Add product to local cart
586
- client.addToLocalCart({ productId: 'prod_123', quantity: 2 });
540
+ // Add to cart (guest or logged-in — same code!)
541
+ await client.smartAddToCart({ productId: 'prod_123', quantity: 2 });
587
542
 
588
- // View cart
589
- const cart = client.getLocalCart();
543
+ // Get cart
544
+ const cart = await client.smartGetCart();
590
545
  console.log('Items:', cart.items.length);
546
+ console.log('Total:', getCartTotals(cart).total);
591
547
 
592
548
  // Update quantity
593
- client.updateLocalCartItem('prod_123', 5);
549
+ await client.smartUpdateCartItem('prod_123', 5);
594
550
 
595
551
  // Remove item
596
- client.removeFromLocalCart('prod_123');
597
-
598
- // At checkout - submit everything in ONE API call
599
- const order = await client.submitGuestOrder();
552
+ await client.smartRemoveFromCart('prod_123');
600
553
  ```
601
554
 
602
- ### Option 2: Server Cart (Logged-In Customers)
603
-
604
- For logged-in customers, **you MUST use server-side cart** to link orders to their account:
605
-
606
- - ✅ Cart syncs across devices
607
- - ✅ Abandoned cart recovery
608
- - ✅ Orders linked to customer account
609
- - ✅ Customer can see orders in "My Orders"
555
+ ### After Login Sync Cart
610
556
 
611
557
  ```typescript
612
- // 1. Set customer token (after login)
613
558
  client.setCustomerToken(token);
559
+ const mergedCart = await client.syncCartOnLogin();
560
+ // Guest session cart items are merged into the customer's server cart
561
+ ```
614
562
 
615
- // 2. Create cart (auto-linked to customer)
616
- const cart = await client.createCart();
617
- localStorage.setItem('cartId', cart.id);
618
-
619
- // 3. Add items
620
- await client.addToCart(cart.id, { productId: 'prod_123', quantity: 2 });
563
+ ### After Checkout Clear Cart
621
564
 
622
- // 4. At checkout - create checkout and complete
623
- const checkout = await client.createCheckout({ cartId: cart.id });
624
- // ... set shipping address, select shipping method ...
625
- const { orderId } = await client.completeCheckout(checkout.id);
565
+ ```typescript
566
+ client.onCheckoutComplete();
567
+ // Clears session cart reference so next visit starts fresh
626
568
  ```
627
569
 
628
- > **⚠️ CRITICAL:** If you use `submitGuestOrder()` for a logged-in customer, their order will NOT be linked to their account!
570
+ ### After Logout Preserve Guest Cart
571
+
572
+ ```typescript
573
+ client.clearCustomerToken();
574
+ client.onLogout();
575
+ // Session cart is preserved — guest can continue browsing
576
+ ```
629
577
 
630
578
  ---
631
579
 
@@ -642,29 +590,14 @@ export const client = new BrainerceClient({
642
590
  connectionId: 'vc_YOUR_CONNECTION_ID', // Your Connection ID from Brainerce
643
591
  });
644
592
 
645
- // ----- Guest Cart Helpers (localStorage) -----
646
-
647
- export function getCartItemCount(): number {
648
- return client.getLocalCartItemCount();
649
- }
650
-
651
- export function getCart() {
652
- return client.getLocalCart();
653
- }
654
-
655
- // ----- For Registered Users (server cart) -----
656
-
657
- export function getServerCartId(): string | null {
658
- if (typeof window === 'undefined') return null;
659
- return localStorage.getItem('cartId');
660
- }
593
+ // ----- Cart Helpers -----
661
594
 
662
- export function setServerCartId(id: string): void {
663
- localStorage.setItem('cartId', id);
595
+ export async function getCart() {
596
+ return client.smartGetCart();
664
597
  }
665
598
 
666
- export function clearServerCartId(): void {
667
- localStorage.removeItem('cartId');
599
+ export function getCartItemCount(): number {
600
+ return client.getSmartCartItemCount();
668
601
  }
669
602
 
670
603
  // ----- Customer Token Helpers -----
@@ -1096,164 +1029,86 @@ function ProductDescription({ product }: { product: Product }) {
1096
1029
 
1097
1030
  ---
1098
1031
 
1099
- ### Local Cart (Guest Users) - RECOMMENDED
1032
+ ### Cart Operations (All Users)
1100
1033
 
1101
- The local cart stores everything in **localStorage** until checkout. This is the recommended approach for most storefronts.
1034
+ The `smart*` methods work for both guests and logged-in users. Guests use server-side session carts; logged-in users use server carts linked to their account.
1102
1035
 
1103
- #### Add to Local Cart
1036
+ #### Add to Cart
1104
1037
 
1105
1038
  ```typescript
1106
- // Add item with product info (for display)
1107
- client.addToLocalCart({
1039
+ await client.smartAddToCart({
1108
1040
  productId: 'prod_123',
1109
1041
  variantId: 'var_456', // Optional: for products with variants
1110
1042
  quantity: 2,
1111
- name: 'Cool T-Shirt', // Optional: for cart display
1112
- price: '29.99', // Optional: for cart display
1113
- image: 'https://...', // Optional: for cart display
1114
1043
  });
1115
1044
  ```
1116
1045
 
1117
- #### Get Local Cart
1046
+ #### Get Cart
1118
1047
 
1119
1048
  ```typescript
1120
- const cart = client.getLocalCart();
1049
+ const cart = await client.smartGetCart();
1121
1050
 
1122
- console.log(cart.items); // Array of cart items
1123
- console.log(cart.customer); // Customer info (if set)
1124
- console.log(cart.shippingAddress); // Shipping address (if set)
1125
- console.log(cart.couponCode); // Applied coupon (if any)
1051
+ console.log(cart.items); // Array of CartItem
1052
+ console.log(cart.itemCount); // Total item count
1053
+ console.log(cart.couponCode); // Applied coupon (if any)
1126
1054
  ```
1127
1055
 
1128
1056
  #### Update Item Quantity
1129
1057
 
1130
1058
  ```typescript
1131
1059
  // Set quantity to 5
1132
- client.updateLocalCartItem('prod_123', 5);
1060
+ await client.smartUpdateCartItem('prod_123', 5);
1133
1061
 
1134
1062
  // For variant products
1135
- client.updateLocalCartItem('prod_123', 3, 'var_456');
1063
+ await client.smartUpdateCartItem('prod_123', 3, 'var_456');
1136
1064
 
1137
1065
  // Set to 0 to remove
1138
- client.updateLocalCartItem('prod_123', 0);
1066
+ await client.smartUpdateCartItem('prod_123', 0);
1139
1067
  ```
1140
1068
 
1141
1069
  #### Remove Item
1142
1070
 
1143
1071
  ```typescript
1144
- client.removeFromLocalCart('prod_123');
1145
- client.removeFromLocalCart('prod_123', 'var_456'); // With variant
1146
- ```
1147
-
1148
- #### Clear Cart
1149
-
1150
- ```typescript
1151
- client.clearLocalCart();
1072
+ await client.smartRemoveFromCart('prod_123');
1073
+ await client.smartRemoveFromCart('prod_123', 'var_456'); // With variant
1152
1074
  ```
1153
1075
 
1154
- #### Set Customer Info
1076
+ #### Get Cart Item Count (No API Call)
1155
1077
 
1156
1078
  ```typescript
1157
- client.setLocalCartCustomer({
1158
- email: 'customer@example.com', // Required
1159
- firstName: 'John', // Optional
1160
- lastName: 'Doe', // Optional
1161
- phone: '+1234567890', // Optional
1162
- });
1163
- ```
1164
-
1165
- #### Set Shipping Address
1166
-
1167
- ```typescript
1168
- client.setLocalCartShippingAddress({
1169
- firstName: 'John',
1170
- lastName: 'Doe',
1171
- line1: '123 Main St',
1172
- line2: 'Apt 4B', // Optional
1173
- city: 'New York',
1174
- region: 'NY', // Optional: State/Province
1175
- postalCode: '10001',
1176
- country: 'US',
1177
- phone: '+1234567890', // Optional
1178
- });
1179
- ```
1180
-
1181
- #### Set Billing Address (Optional)
1182
-
1183
- ```typescript
1184
- client.setLocalCartBillingAddress({
1185
- firstName: 'John',
1186
- lastName: 'Doe',
1187
- line1: '456 Business Ave',
1188
- city: 'New York',
1189
- postalCode: '10002',
1190
- country: 'US',
1191
- });
1079
+ // Returns cached count from session reference — instant, no API call
1080
+ const count = client.getSmartCartItemCount();
1081
+ console.log(`${count} items in cart`);
1082
+ // For accurate count, use: (await client.smartGetCart()).itemCount
1192
1083
  ```
1193
1084
 
1194
1085
  #### Apply Coupon
1195
1086
 
1196
1087
  ```typescript
1197
- client.setLocalCartCoupon('SAVE20');
1088
+ const cart = await client.smartGetCart();
1089
+ const updated = await client.applyCoupon(cart.id, 'SAVE20');
1090
+ console.log(updated.discountAmount); // "10.00"
1091
+ console.log(updated.couponCode); // "SAVE20"
1198
1092
 
1199
1093
  // Remove coupon
1200
- client.setLocalCartCoupon(undefined);
1201
- ```
1202
-
1203
- #### Get Cart Item Count
1204
-
1205
- ```typescript
1206
- const count = client.getLocalCartItemCount();
1207
- console.log(`${count} items in cart`);
1094
+ await client.removeCoupon(cart.id);
1208
1095
  ```
1209
1096
 
1210
- #### Local Cart Type Definition
1097
+ #### Cart Totals
1211
1098
 
1212
1099
  ```typescript
1213
- interface LocalCart {
1214
- items: LocalCartItem[];
1215
- couponCode?: string;
1216
- customer?: {
1217
- email: string;
1218
- firstName?: string;
1219
- lastName?: string;
1220
- phone?: string;
1221
- };
1222
- shippingAddress?: {
1223
- firstName: string;
1224
- lastName: string;
1225
- line1: string;
1226
- line2?: string;
1227
- city: string;
1228
- region?: string;
1229
- postalCode: string;
1230
- country: string;
1231
- phone?: string;
1232
- };
1233
- billingAddress?: {
1234
- /* same as shipping */
1235
- };
1236
- notes?: string;
1237
- updatedAt: string;
1238
- }
1100
+ import { getCartTotals } from 'brainerce';
1239
1101
 
1240
- interface LocalCartItem {
1241
- productId: string;
1242
- variantId?: string;
1243
- quantity: number;
1244
- name?: string;
1245
- sku?: string;
1246
- price?: string;
1247
- image?: string;
1248
- addedAt: string;
1249
- }
1102
+ const cart = await client.smartGetCart();
1103
+ const totals = getCartTotals(cart);
1104
+ // { subtotal: 59.98, discount: 10, shipping: 5.99, total: 55.97 }
1250
1105
  ```
1251
1106
 
1252
1107
  ---
1253
1108
 
1254
1109
  ### Guest Checkout (Submit Order)
1255
1110
 
1256
- Submit the local cart as an order with a **single API call**:
1111
+ > **Note:** `startGuestCheckout()` is the preferred method for guest checkout — it creates a full checkout session from the session cart. `submitGuestOrder()` still works as a simpler alternative for basic orders.
1257
1112
 
1258
1113
  ```typescript
1259
1114
  // Make sure cart has items, customer email, and shipping address
@@ -1279,7 +1134,7 @@ const order = await client.submitGuestOrder({ clearCartOnSuccess: false });
1279
1134
 
1280
1135
  #### Create Order with Custom Data
1281
1136
 
1282
- If you manage cart state yourself instead of using local cart:
1137
+ If you manage cart state yourself instead of using the smart cart methods:
1283
1138
 
1284
1139
  ```typescript
1285
1140
  const order = await client.createGuestOrder({
@@ -1384,15 +1239,14 @@ type GuestCheckoutStartResponse =
1384
1239
 
1385
1240
  ---
1386
1241
 
1387
- ### Server Cart (Registered Users)
1242
+ ### Server Cart (Low-Level API)
1388
1243
 
1389
- For logged-in customers who want cart sync across devices.
1244
+ These low-level methods are available for advanced use cases. For most storefronts, use the `smart*` methods above instead.
1390
1245
 
1391
1246
  #### Create Cart
1392
1247
 
1393
1248
  ```typescript
1394
1249
  const cart = await client.createCart();
1395
- setServerCartId(cart.id); // Save to localStorage
1396
1250
  ```
1397
1251
 
1398
1252
  #### Get Cart
@@ -1715,12 +1569,13 @@ For vibe-coded sites, the SDK provides payment integration with Stripe and PayPa
1715
1569
  Before creating a payment intent, you need a checkout ID. How you get it depends on the customer type:
1716
1570
 
1717
1571
  ```typescript
1718
- // For GUEST users (local cart in localStorage):
1572
+ // For GUEST users (session cart):
1719
1573
  const result = await client.startGuestCheckout();
1720
1574
  const checkoutId = result.checkoutId;
1721
1575
 
1722
1576
  // For LOGGED-IN users (server cart):
1723
- const checkout = await client.createCheckout({ cartId: serverCartId });
1577
+ const cart = await client.smartGetCart();
1578
+ const checkout = await client.createCheckout({ cartId: cart.id });
1724
1579
  const checkoutId = checkout.id;
1725
1580
 
1726
1581
  // Then continue with shipping and payment...
@@ -1941,11 +1796,12 @@ if (status.status === 'succeeded' && status.orderId) {
1941
1796
  **How to get a checkout_id:**
1942
1797
 
1943
1798
  ```typescript
1944
- // For GUEST users (local cart):
1799
+ // For GUEST users (session cart):
1945
1800
  const result = await client.startGuestCheckout();
1946
1801
  const checkoutId = result.checkoutId; // Use this!
1947
1802
 
1948
1803
  // For LOGGED-IN users (server cart):
1804
+ const cart = await client.smartGetCart();
1949
1805
  const checkout = await client.createCheckout({ cartId: cart.id });
1950
1806
  const checkoutId = checkout.id; // Use this!
1951
1807
  ```
@@ -2058,7 +1914,7 @@ function PaymentForm({ checkoutId }: { checkoutId: string }) {
2058
1914
 
2059
1915
  **CRITICAL:** After payment succeeds, you MUST call `completeGuestCheckout()` to create the order on the server and clear the cart.
2060
1916
 
2061
- > **WARNING:** Do NOT use `handlePaymentSuccess()` - it only clears the local cart (localStorage)
1917
+ > **WARNING:** Do NOT use `handlePaymentSuccess()` - it only clears cart state locally
2062
1918
  > and does NOT send the order to the server. Your customer will pay but no order will be created!
2063
1919
 
2064
1920
  ```typescript
@@ -2092,8 +1948,8 @@ export default function CheckoutSuccessPage() {
2092
1948
  **How it works:**
2093
1949
  | User Type | Cart Type | Behavior |
2094
1950
  |-----------|-----------|----------|
2095
- | Guest (partial checkout) | Local cart | Creates order + removes only purchased items |
2096
- | Guest (full checkout) | Local cart | Creates order + clears entire cart |
1951
+ | Guest (partial checkout) | Session cart | Creates order + removes only purchased items |
1952
+ | Guest (full checkout) | Session cart | Creates order + clears entire cart |
2097
1953
  | Logged-in | Server cart | Creates order + clears cart via SDK state |
2098
1954
 
2099
1955
  **Why is this needed?**
@@ -2452,10 +2308,7 @@ export default function LoginPage() {
2452
2308
  // Social login handler
2453
2309
  const handleSocialLogin = async (provider: string) => {
2454
2310
  try {
2455
- // Save cart ID before redirect
2456
- const cartId = localStorage.getItem('cartId');
2457
- if (cartId) sessionStorage.setItem('pendingCartId', cartId);
2458
-
2311
+ // Session cart is preserved automatically via localStorage session token
2459
2312
  const { authorizationUrl, state } = await client.getOAuthAuthorizeUrl(
2460
2313
  provider as 'GOOGLE' | 'FACEBOOK' | 'GITHUB',
2461
2314
  { redirectUrl: window.location.origin + '/auth/callback' }
@@ -2979,7 +2832,7 @@ export default function ProductsPage() {
2979
2832
  }
2980
2833
  ```
2981
2834
 
2982
- ### Product Detail with Add to Cart (Local Cart)
2835
+ ### Product Detail with Add to Cart
2983
2836
 
2984
2837
  ```typescript
2985
2838
  'use client';
@@ -3036,19 +2889,14 @@ export default function ProductPage({ params }: { params: { slug: string } }) {
3036
2889
  return product.salePrice || product.basePrice;
3037
2890
  };
3038
2891
 
3039
- const handleAddToCart = () => {
2892
+ const handleAddToCart = async () => {
3040
2893
  if (!product) return;
3041
2894
 
3042
- // Add to local cart (NO API call!)
3043
- client.addToLocalCart({
2895
+ // Add to cart (works for both guests and logged-in users)
2896
+ await client.smartAddToCart({
3044
2897
  productId: product.id,
3045
2898
  variantId: selectedVariant?.id,
3046
2899
  quantity,
3047
- name: selectedVariant?.name
3048
- ? `${product.name} - ${selectedVariant.name}`
3049
- : product.name,
3050
- price: getDisplayPrice(),
3051
- image: getDisplayImage(),
3052
2900
  });
3053
2901
 
3054
2902
  alert('Added to cart!');
@@ -3170,39 +3018,35 @@ export default function ProductPage({ params }: { params: { slug: string } }) {
3170
3018
  }
3171
3019
  ```
3172
3020
 
3173
- ### Cart Page (Local Cart)
3021
+ ### Cart Page
3174
3022
 
3175
3023
  ```typescript
3176
3024
  'use client';
3177
3025
  import { useState, useEffect } from 'react';
3178
3026
  import { client } from '@/lib/brainerce';
3179
- import type { LocalCart } from 'brainerce';
3027
+ import { getCartTotals, formatPrice } from 'brainerce';
3028
+ import type { Cart } from 'brainerce';
3180
3029
 
3181
3030
  export default function CartPage() {
3182
- // ⚠️ Do NOT use useState(client.getLocalCart()) — causes hydration mismatch!
3183
- // Server has no localStorage (empty cart) but client does (cart with items).
3184
- const [cart, setCart] = useState<LocalCart>({ items: [], updatedAt: '' });
3031
+ const [cart, setCart] = useState<Cart | null>(null);
3032
+ const [loading, setLoading] = useState(true);
3185
3033
 
3186
3034
  useEffect(() => {
3187
- setCart(client.getLocalCart());
3035
+ client.smartGetCart().then(setCart).finally(() => setLoading(false));
3188
3036
  }, []);
3189
3037
 
3190
- const updateQuantity = (productId: string, quantity: number, variantId?: string) => {
3191
- const updated = client.updateLocalCartItem(productId, quantity, variantId);
3038
+ const updateQuantity = async (productId: string, quantity: number, variantId?: string) => {
3039
+ const updated = await client.smartUpdateCartItem(productId, quantity, variantId);
3192
3040
  setCart(updated);
3193
3041
  };
3194
3042
 
3195
- const removeItem = (productId: string, variantId?: string) => {
3196
- const updated = client.removeFromLocalCart(productId, variantId);
3043
+ const removeItem = async (productId: string, variantId?: string) => {
3044
+ const updated = await client.smartRemoveFromCart(productId, variantId);
3197
3045
  setCart(updated);
3198
3046
  };
3199
3047
 
3200
- // Calculate subtotal from local cart
3201
- const subtotal = cart.items.reduce((sum, item) => {
3202
- return sum + (parseFloat(item.price || '0') * item.quantity);
3203
- }, 0);
3204
-
3205
- if (cart.items.length === 0) {
3048
+ if (loading) return <div>Loading cart...</div>;
3049
+ if (!cart || cart.items.length === 0) {
3206
3050
  return (
3207
3051
  <div className="text-center py-12">
3208
3052
  <h1 className="text-2xl font-bold">Your cart is empty</h1>
@@ -3211,43 +3055,46 @@ export default function CartPage() {
3211
3055
  );
3212
3056
  }
3213
3057
 
3058
+ const totals = getCartTotals(cart);
3059
+
3214
3060
  return (
3215
3061
  <div>
3216
3062
  <h1 className="text-2xl font-bold mb-6">Shopping Cart</h1>
3217
3063
 
3218
3064
  {cart.items.map((item) => (
3219
- <div key={`${item.productId}-${item.variantId || ''}`} className="flex items-center gap-4 py-4 border-b">
3065
+ <div key={item.id} className="flex items-center gap-4 py-4 border-b">
3220
3066
  <img
3221
- src={item.image || '/placeholder.jpg'}
3222
- alt={item.name || 'Product'}
3067
+ src={item.product.images?.[0] || '/placeholder.jpg'}
3068
+ alt={item.product.name}
3223
3069
  className="w-20 h-20 object-cover"
3224
3070
  />
3225
3071
  <div className="flex-1">
3226
- <h3 className="font-medium">{item.name || 'Product'}</h3>
3227
- <p className="font-bold">${item.price}</p>
3072
+ <h3 className="font-medium">{item.product.name}</h3>
3073
+ {item.variant && <p className="text-sm text-gray-500">{item.variant.name}</p>}
3074
+ <p className="font-bold">${item.unitPrice}</p>
3228
3075
  </div>
3229
3076
  <div className="flex items-center gap-2">
3230
3077
  <button
3231
- onClick={() => updateQuantity(item.productId, item.quantity - 1, item.variantId)}
3078
+ onClick={() => updateQuantity(item.productId, item.quantity - 1, item.variantId ?? undefined)}
3232
3079
  className="w-8 h-8 border rounded"
3233
3080
  >-</button>
3234
3081
  <span className="w-8 text-center">{item.quantity}</span>
3235
3082
  <button
3236
- onClick={() => updateQuantity(item.productId, item.quantity + 1, item.variantId)}
3083
+ onClick={() => updateQuantity(item.productId, item.quantity + 1, item.variantId ?? undefined)}
3237
3084
  className="w-8 h-8 border rounded"
3238
3085
  >+</button>
3239
3086
  </div>
3240
3087
  <button
3241
- onClick={() => removeItem(item.productId, item.variantId)}
3088
+ onClick={() => removeItem(item.productId, item.variantId ?? undefined)}
3242
3089
  className="text-red-600"
3243
3090
  >Remove</button>
3244
3091
  </div>
3245
3092
  ))}
3246
3093
 
3247
3094
  <div className="mt-6 text-right">
3248
- <p className="text-xl">Subtotal: <strong>${subtotal.toFixed(2)}</strong></p>
3249
- {cart.couponCode && (
3250
- <p className="text-green-600">Coupon applied: {cart.couponCode}</p>
3095
+ <p className="text-xl">Subtotal: <strong>${totals.subtotal.toFixed(2)}</strong></p>
3096
+ {totals.discount > 0 && (
3097
+ <p className="text-green-600">Discount: -${totals.discount.toFixed(2)}</p>
3251
3098
  )}
3252
3099
  <a
3253
3100
  href="/checkout"
@@ -3261,19 +3108,23 @@ export default function CartPage() {
3261
3108
  }
3262
3109
  ```
3263
3110
 
3264
- ### Universal Checkout (Handles Both Guest & Logged-In)
3111
+ ### Checkout Page
3265
3112
 
3266
- > **RECOMMENDED:** Use this pattern to properly handle both guest and logged-in customers in a single checkout page.
3113
+ > **RECOMMENDED:** Use this unified pattern the `smart*` methods handle both guest and logged-in users.
3267
3114
 
3268
3115
  ```typescript
3269
3116
  'use client';
3270
3117
  import { useState, useEffect } from 'react';
3271
- import { client, isLoggedIn, getServerCartId, setServerCartId, restoreCustomerToken } from '@/lib/brainerce';
3118
+ import { client, isLoggedIn, restoreCustomerToken } from '@/lib/brainerce';
3119
+ import type { Checkout, ShippingRate } from 'brainerce';
3272
3120
 
3273
3121
  export default function CheckoutPage() {
3274
3122
  const [loading, setLoading] = useState(true);
3275
3123
  const [submitting, setSubmitting] = useState(false);
3276
- const [customerLoggedIn, setCustomerLoggedIn] = useState(false);
3124
+ const [checkout, setCheckout] = useState<Checkout | null>(null);
3125
+ const [shippingRates, setShippingRates] = useState<ShippingRate[]>([]);
3126
+ const [selectedRate, setSelectedRate] = useState<string | null>(null);
3127
+ const customerLoggedIn = isLoggedIn();
3277
3128
 
3278
3129
  // Form state
3279
3130
  const [email, setEmail] = useState('');
@@ -3281,49 +3132,26 @@ export default function CheckoutPage() {
3281
3132
  firstName: '', lastName: '', line1: '', city: '', postalCode: '', country: 'US'
3282
3133
  });
3283
3134
 
3284
- // Server checkout state (for logged-in customers)
3285
- const [checkoutId, setCheckoutId] = useState<string | null>(null);
3286
- const [shippingRates, setShippingRates] = useState<any[]>([]);
3287
- const [selectedRate, setSelectedRate] = useState<string | null>(null);
3288
-
3289
3135
  useEffect(() => {
3290
3136
  restoreCustomerToken();
3291
- const loggedIn = isLoggedIn();
3292
- setCustomerLoggedIn(loggedIn);
3293
3137
 
3294
3138
  async function initCheckout() {
3295
- if (loggedIn) {
3296
- // Logged-in customer: Create server cart + checkout
3297
- let cartId = getServerCartId();
3298
-
3299
- if (!cartId) {
3300
- // Create new cart (auto-linked to customer)
3301
- const cart = await client.createCart();
3302
- cartId = cart.id;
3303
- setServerCartId(cartId);
3304
-
3305
- // Migrate local cart items to server cart
3306
- const localCart = client.getLocalCart();
3307
- for (const item of localCart.items) {
3308
- await client.addToCart(cartId, {
3309
- productId: item.productId,
3310
- variantId: item.variantId,
3311
- quantity: item.quantity,
3312
- });
3139
+ try {
3140
+ // startGuestCheckout() creates checkout from session cart (for guests)
3141
+ // For logged-in users, create checkout from their server cart
3142
+ if (customerLoggedIn) {
3143
+ const cart = await client.smartGetCart();
3144
+ const co = await client.createCheckout({ cartId: cart.id });
3145
+ setCheckout(co);
3146
+ } else {
3147
+ const result = await client.startGuestCheckout();
3148
+ if (result.tracked) {
3149
+ const co = await client.getCheckout(result.checkoutId);
3150
+ setCheckout(co);
3313
3151
  }
3314
- client.clearLocalCart(); // Clear local cart after migration
3315
- }
3316
-
3317
- // Create checkout from server cart
3318
- const checkout = await client.createCheckout({ cartId });
3319
- setCheckoutId(checkout.id);
3320
-
3321
- // Pre-fill from customer profile if available
3322
- try {
3323
- const profile = await client.getMyOrders({ limit: 1 }); // Just to check auth works
3324
- } catch (e) {
3325
- console.log('Could not fetch profile');
3326
3152
  }
3153
+ } catch (err) {
3154
+ console.error('Failed to initialize checkout:', err);
3327
3155
  }
3328
3156
  setLoading(false);
3329
3157
  }
@@ -3333,50 +3161,33 @@ export default function CheckoutPage() {
3333
3161
 
3334
3162
  const handleSubmit = async (e: React.FormEvent) => {
3335
3163
  e.preventDefault();
3164
+ if (!checkout) return;
3336
3165
  setSubmitting(true);
3337
3166
 
3338
3167
  try {
3339
- if (customerLoggedIn && checkoutId) {
3340
- // ===== LOGGED-IN CUSTOMER: Server Checkout =====
3341
-
3342
- // 1. Set customer info (REQUIRED - even for logged-in customers!)
3343
- await client.setCheckoutCustomer(checkoutId, {
3344
- email: email, // Get from form or customer profile
3345
- firstName: shippingAddress.firstName,
3346
- lastName: shippingAddress.lastName,
3347
- });
3348
-
3349
- // 2. Set shipping address
3350
- await client.setShippingAddress(checkoutId, shippingAddress);
3351
-
3352
- // 3. Get and select shipping rate
3353
- const rates = await client.getShippingRates(checkoutId);
3354
- if (rates.length > 0) {
3355
- await client.selectShippingMethod(checkoutId, selectedRate || rates[0].id);
3356
- }
3357
-
3358
- // 4. Complete checkout - ORDER IS LINKED TO CUSTOMER!
3359
- const { orderId } = await client.completeCheckout(checkoutId);
3360
-
3361
- // Clear cart ID
3362
- localStorage.removeItem('cartId');
3168
+ // 1. Set customer info
3169
+ await client.setCheckoutCustomer(checkout.id, {
3170
+ email,
3171
+ firstName: shippingAddress.firstName,
3172
+ lastName: shippingAddress.lastName,
3173
+ });
3363
3174
 
3364
- // Redirect to success page
3365
- window.location.href = `/order-success?orderId=${orderId}`;
3175
+ // 2. Set shipping address
3176
+ await client.setShippingAddress(checkout.id, shippingAddress);
3366
3177
 
3367
- } else {
3368
- // ===== GUEST: Local Cart + submitGuestOrder =====
3178
+ // 3. Get and select shipping rate
3179
+ const rates = await client.getShippingRates(checkout.id);
3180
+ if (rates.length > 0) {
3181
+ await client.selectShippingMethod(checkout.id, selectedRate || rates[0].id);
3182
+ }
3369
3183
 
3370
- // Set customer and shipping info on local cart
3371
- client.setLocalCartCustomer({ email });
3372
- client.setLocalCartShippingAddress(shippingAddress);
3184
+ // 4. Complete checkout
3185
+ const { orderId } = await client.completeCheckout(checkout.id);
3373
3186
 
3374
- // Submit guest order (single API call)
3375
- const order = await client.submitGuestOrder();
3187
+ // 5. Clean up
3188
+ client.onCheckoutComplete();
3376
3189
 
3377
- // Redirect to success page
3378
- window.location.href = `/order-success?orderId=${order.orderId}`;
3379
- }
3190
+ window.location.href = `/order-success?orderId=${orderId}`;
3380
3191
  } catch (error) {
3381
3192
  console.error('Checkout failed:', error);
3382
3193
  alert('Checkout failed. Please try again.');
@@ -3389,31 +3200,19 @@ export default function CheckoutPage() {
3389
3200
 
3390
3201
  return (
3391
3202
  <form onSubmit={handleSubmit}>
3392
- {/* Show email field only for guests */}
3393
3203
  {!customerLoggedIn && (
3394
- <input
3395
- type="email"
3396
- value={email}
3397
- onChange={(e) => setEmail(e.target.value)}
3398
- placeholder="Email"
3399
- required
3400
- />
3204
+ <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Email" required />
3401
3205
  )}
3402
-
3403
- {/* Shipping address fields */}
3404
3206
  <input value={shippingAddress.firstName} onChange={(e) => setShippingAddress({...shippingAddress, firstName: e.target.value})} placeholder="First Name" required />
3405
3207
  <input value={shippingAddress.lastName} onChange={(e) => setShippingAddress({...shippingAddress, lastName: e.target.value})} placeholder="Last Name" required />
3406
3208
  <input value={shippingAddress.line1} onChange={(e) => setShippingAddress({...shippingAddress, line1: e.target.value})} placeholder="Address" required />
3407
3209
  <input value={shippingAddress.city} onChange={(e) => setShippingAddress({...shippingAddress, city: e.target.value})} placeholder="City" required />
3408
3210
  <input value={shippingAddress.postalCode} onChange={(e) => setShippingAddress({...shippingAddress, postalCode: e.target.value})} placeholder="Postal Code" required />
3409
3211
 
3410
- {/* Shipping rates (for logged-in customers) */}
3411
- {customerLoggedIn && shippingRates.length > 0 && (
3212
+ {shippingRates.length > 0 && (
3412
3213
  <select value={selectedRate || ''} onChange={(e) => setSelectedRate(e.target.value)}>
3413
3214
  {shippingRates.map((rate) => (
3414
- <option key={rate.id} value={rate.id}>
3415
- {rate.name} - ${rate.price}
3416
- </option>
3215
+ <option key={rate.id} value={rate.id}>{rate.name} - ${rate.price}</option>
3417
3216
  ))}
3418
3217
  </select>
3419
3218
  )}
@@ -3421,12 +3220,6 @@ export default function CheckoutPage() {
3421
3220
  <button type="submit" disabled={submitting}>
3422
3221
  {submitting ? 'Processing...' : 'Place Order'}
3423
3222
  </button>
3424
-
3425
- {customerLoggedIn && (
3426
- <p className="text-sm text-green-600">
3427
- ✓ Logged in - Order will be saved to your account
3428
- </p>
3429
- )}
3430
3223
  </form>
3431
3224
  );
3432
3225
  }
@@ -3434,207 +3227,21 @@ export default function CheckoutPage() {
3434
3227
 
3435
3228
  > **Key Points:**
3436
3229
  >
3437
- > - `isLoggedIn()` determines which flow to use
3438
- > - Logged-in customers use `createCart()` `createCheckout()` `completeCheckout()`
3439
- > - Guests use local cart + `submitGuestOrder()`
3440
- > - Local cart items are migrated to server cart when customer logs in
3441
-
3442
- ---
3443
-
3444
- ### Guest Checkout (Single API Call)
3445
-
3446
- This checkout is for **guest users only**. All cart data is in localStorage, and we submit it in one API call.
3447
-
3448
- > **⚠️ WARNING:** Do NOT use this for logged-in customers! Use the Universal Checkout pattern above instead.
3449
-
3450
- ```typescript
3451
- 'use client';
3452
- import { useState, useEffect } from 'react';
3453
- import { client } from '@/lib/brainerce';
3454
- import type { LocalCart, GuestOrderResponse } from 'brainerce';
3455
-
3456
- type Step = 'info' | 'review' | 'complete';
3457
-
3458
- export default function CheckoutPage() {
3459
- // ⚠️ Do NOT use useState(client.getLocalCart()) — causes hydration mismatch!
3460
- const [cart, setCart] = useState<LocalCart>({ items: [], updatedAt: '' });
3461
- const [step, setStep] = useState<Step>('info');
3462
- const [order, setOrder] = useState<GuestOrderResponse | null>(null);
3463
- const [submitting, setSubmitting] = useState(false);
3464
- const [error, setError] = useState('');
3465
-
3466
- // Form state
3467
- const [email, setEmail] = useState('');
3468
- const [firstName, setFirstName] = useState('');
3469
- const [lastName, setLastName] = useState('');
3470
- const [address, setAddress] = useState('');
3471
- const [city, setCity] = useState('');
3472
- const [postalCode, setPostalCode] = useState('');
3473
- const [country, setCountry] = useState('US');
3474
-
3475
- // Load cart from localStorage after hydration
3476
- useEffect(() => {
3477
- const localCart = client.getLocalCart();
3478
- setCart(localCart);
3479
- if (localCart.customer?.email) setEmail(localCart.customer.email);
3480
- if (localCart.customer?.firstName) setFirstName(localCart.customer.firstName);
3481
- if (localCart.customer?.lastName) setLastName(localCart.customer.lastName);
3482
- if (localCart.shippingAddress?.line1) setAddress(localCart.shippingAddress.line1);
3483
- if (localCart.shippingAddress?.city) setCity(localCart.shippingAddress.city);
3484
- if (localCart.shippingAddress?.postalCode) setPostalCode(localCart.shippingAddress.postalCode);
3485
- if (localCart.shippingAddress?.country) setCountry(localCart.shippingAddress.country);
3486
- }, []);
3487
-
3488
- // Calculate subtotal
3489
- const subtotal = cart.items.reduce((sum, item) => {
3490
- return sum + (parseFloat(item.price || '0') * item.quantity);
3491
- }, 0);
3492
-
3493
- // Redirect if cart is empty
3494
- useEffect(() => {
3495
- if (cart.items.length === 0 && step !== 'complete') {
3496
- window.location.href = '/cart';
3497
- }
3498
- }, [cart.items.length, step]);
3499
-
3500
- const handleInfoSubmit = (e: React.FormEvent) => {
3501
- e.preventDefault();
3502
-
3503
- // Save to local cart
3504
- client.setLocalCartCustomer({ email, firstName, lastName });
3505
- client.setLocalCartShippingAddress({
3506
- firstName,
3507
- lastName,
3508
- line1: address,
3509
- city,
3510
- postalCode,
3511
- country,
3512
- });
3513
-
3514
- setStep('review');
3515
- };
3516
-
3517
- const handlePlaceOrder = async () => {
3518
- setSubmitting(true);
3519
- setError('');
3520
-
3521
- try {
3522
- // Single API call to create order!
3523
- const result = await client.submitGuestOrder();
3524
- setOrder(result);
3525
- setStep('complete');
3526
- } catch (err) {
3527
- setError(err instanceof Error ? err.message : 'Failed to place order');
3528
- } finally {
3529
- setSubmitting(false);
3530
- }
3531
- };
3532
-
3533
- if (step === 'complete' && order) {
3534
- return (
3535
- <div className="text-center py-12">
3536
- <h1 className="text-3xl font-bold text-green-600">Order Complete!</h1>
3537
- <p className="mt-4">Order Number: <strong>{order.orderNumber}</strong></p>
3538
- <p className="mt-2">Total: <strong>${order.total.toFixed(2)}</strong></p>
3539
- <p className="mt-4 text-gray-600">A confirmation email will be sent to {email}</p>
3540
- <a href="/" className="mt-6 inline-block text-blue-600">Continue Shopping</a>
3541
- </div>
3542
- );
3543
- }
3544
-
3545
- return (
3546
- <div className="max-w-2xl mx-auto">
3547
- <h1 className="text-2xl font-bold mb-6">Checkout</h1>
3548
-
3549
- {error && (
3550
- <div className="bg-red-100 text-red-600 p-3 rounded mb-4">{error}</div>
3551
- )}
3552
-
3553
- {step === 'info' && (
3554
- <form onSubmit={handleInfoSubmit} className="space-y-4">
3555
- <h2 className="text-lg font-bold">Contact Information</h2>
3556
- <input
3557
- type="email"
3558
- placeholder="Email"
3559
- value={email}
3560
- onChange={e => setEmail(e.target.value)}
3561
- required
3562
- className="w-full border p-2 rounded"
3563
- />
3564
-
3565
- <h2 className="text-lg font-bold mt-6">Shipping Address</h2>
3566
- <div className="grid grid-cols-2 gap-4">
3567
- <input placeholder="First Name" value={firstName} onChange={e => setFirstName(e.target.value)} required className="border p-2 rounded" />
3568
- <input placeholder="Last Name" value={lastName} onChange={e => setLastName(e.target.value)} required className="border p-2 rounded" />
3569
- </div>
3570
- <input placeholder="Address" value={address} onChange={e => setAddress(e.target.value)} required className="w-full border p-2 rounded" />
3571
- <div className="grid grid-cols-2 gap-4">
3572
- <input placeholder="City" value={city} onChange={e => setCity(e.target.value)} required className="border p-2 rounded" />
3573
- <input placeholder="Postal Code" value={postalCode} onChange={e => setPostalCode(e.target.value)} required className="border p-2 rounded" />
3574
- </div>
3575
- <select value={country} onChange={e => setCountry(e.target.value)} className="w-full border p-2 rounded">
3576
- <option value="US">United States</option>
3577
- <option value="IL">Israel</option>
3578
- <option value="GB">United Kingdom</option>
3579
- </select>
3580
-
3581
- <button type="submit" className="w-full bg-black text-white py-3 rounded">
3582
- Review Order
3583
- </button>
3584
- </form>
3585
- )}
3586
-
3587
- {step === 'review' && (
3588
- <div className="space-y-6">
3589
- {/* Order Summary */}
3590
- <div className="border p-4 rounded">
3591
- <h3 className="font-bold mb-4">Order Summary</h3>
3592
- {cart.items.map((item) => (
3593
- <div key={`${item.productId}-${item.variantId || ''}`} className="flex justify-between py-2">
3594
- <span>{item.name} x {item.quantity}</span>
3595
- <span>${(parseFloat(item.price || '0') * item.quantity).toFixed(2)}</span>
3596
- </div>
3597
- ))}
3598
- <hr className="my-2" />
3599
- <div className="flex justify-between font-bold">
3600
- <span>Total</span>
3601
- <span>${subtotal.toFixed(2)}</span>
3602
- </div>
3603
- </div>
3604
-
3605
- {/* Shipping Info */}
3606
- <div className="border p-4 rounded">
3607
- <h3 className="font-bold mb-2">Shipping To</h3>
3608
- <p>{firstName} {lastName}</p>
3609
- <p>{address}</p>
3610
- <p>{city}, {postalCode}, {country}</p>
3611
- <button onClick={() => setStep('info')} className="text-blue-600 text-sm mt-2">Edit</button>
3612
- </div>
3613
-
3614
- <button
3615
- onClick={handlePlaceOrder}
3616
- disabled={submitting}
3617
- className="w-full bg-green-600 text-white py-3 rounded text-lg"
3618
- >
3619
- {submitting ? 'Processing...' : 'Place Order'}
3620
- </button>
3621
- </div>
3622
- )}
3623
- </div>
3624
- );
3625
- }
3626
- ```
3230
+ > - Both guests and logged-in users go through `createCheckout()` `completeCheckout()`
3231
+ > - Guest session cart is created automatically by `smart*` methods
3232
+ > - Call `client.onCheckoutComplete()` after successful payment to clear the session cart
3233
+ > - Call `client.syncCartOnLogin()` when a user logs in to merge their guest cart
3627
3234
 
3628
3235
  ### Multi-Step Checkout (Server Cart - For Logged-In Customers Only)
3629
3236
 
3630
- > **IMPORTANT:** This checkout pattern is ONLY for logged-in customers. For a checkout page that handles both guests and logged-in customers, see the "Universal Checkout" example above.
3237
+ > **IMPORTANT:** This checkout pattern is ONLY for logged-in customers. For a checkout page that handles both guests and logged-in customers, see the "Checkout Page" example above.
3631
3238
 
3632
3239
  For logged-in users with server-side cart - orders will be linked to their account:
3633
3240
 
3634
3241
  ```typescript
3635
3242
  'use client';
3636
3243
  import { useEffect, useState } from 'react';
3637
- import { client, getServerCartId } from '@/lib/brainerce';
3244
+ import { client } from '@/lib/brainerce';
3638
3245
  import type { Checkout, ShippingRate } from 'brainerce';
3639
3246
 
3640
3247
  type Step = 'customer' | 'shipping' | 'payment' | 'complete';
@@ -3657,13 +3264,13 @@ export default function CheckoutPage() {
3657
3264
 
3658
3265
  useEffect(() => {
3659
3266
  async function initCheckout() {
3660
- const cartId = getServerCartId();
3661
- if (!cartId) {
3662
- window.location.href = '/cart';
3663
- return;
3664
- }
3665
3267
  try {
3666
- const c = await client.createCheckout({ cartId });
3268
+ const cart = await client.smartGetCart();
3269
+ if (!cart || cart.items.length === 0) {
3270
+ window.location.href = '/cart';
3271
+ return;
3272
+ }
3273
+ const c = await client.createCheckout({ cartId: cart.id });
3667
3274
  setCheckout(c);
3668
3275
  } finally {
3669
3276
  setLoading(false);
@@ -3995,7 +3602,7 @@ export default function AccountPage() {
3995
3602
  }
3996
3603
  ```
3997
3604
 
3998
- ### Header Component with Cart Count (Local Cart)
3605
+ ### Header Component with Cart Count
3999
3606
 
4000
3607
  ```typescript
4001
3608
  'use client';
@@ -4008,8 +3615,8 @@ export function Header() {
4008
3615
 
4009
3616
  useEffect(() => {
4010
3617
  setLoggedIn(isLoggedIn());
4011
- // Get cart count from local storage (NO API call!)
4012
- setCartCount(client.getLocalCartItemCount());
3618
+ // Get cart count from session reference (no API call!)
3619
+ setCartCount(client.getSmartCartItemCount());
4013
3620
  }, []);
4014
3621
 
4015
3622
  return (
@@ -4124,16 +3731,12 @@ import type {
4124
3731
  ProductQueryParams,
4125
3732
  PaginatedResponse,
4126
3733
 
4127
- // Local Cart (Guest Users)
4128
- LocalCart,
4129
- LocalCartItem,
4130
- CreateGuestOrderDto,
4131
- GuestOrderResponse,
4132
-
4133
- // Server Cart (Registered Users)
3734
+ // Cart (All Users)
4134
3735
  Cart,
4135
3736
  CartItem,
4136
3737
  AddToCartDto,
3738
+ CreateGuestOrderDto,
3739
+ GuestOrderResponse,
4137
3740
 
4138
3741
  // Checkout
4139
3742
  Checkout,
@@ -4246,7 +3849,7 @@ import { BrainerceError } from 'brainerce';
4246
3849
  // Add to cart with toast feedback
4247
3850
  const handleAddToCart = async (productId: string, quantity: number) => {
4248
3851
  try {
4249
- client.addToLocalCart({ productId, quantity });
3852
+ await client.smartAddToCart({ productId, quantity });
4250
3853
  toast.success('Added to cart!');
4251
3854
  } catch (error) {
4252
3855
  if (error instanceof BrainerceError) {