@01.software/init 0.10.0 → 0.10.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.
@@ -49,6 +49,7 @@ type CartItemDoc = {
49
49
  quantity?: number | null
50
50
  unitPrice?: number | null
51
51
  discountedTotalPrice?: number | null
52
+ requiresShipping?: boolean | null
52
53
  }
53
54
 
54
55
  type CartDoc = {
@@ -127,7 +128,6 @@ export type SoftwareSdkClientLike = {
127
128
  where?: unknown
128
129
  joins?: boolean
129
130
  }) => Promise<{ docs: unknown[]; totalDocs?: number }>
130
- update: (id: string, data: Record<string, unknown>) => Promise<unknown>
131
131
  }
132
132
  }
133
133
  commerce: {
@@ -171,8 +171,17 @@ export type SoftwareSdkClientLike = {
171
171
  orders: {
172
172
  checkout: (params: {
173
173
  cartId: string
174
+ cartToken?: string
174
175
  orderNumber: string
175
176
  customerSnapshot: CustomerSnapshotInput
177
+ shippingAddress?: {
178
+ recipientName: string
179
+ phone: string
180
+ postalCode: string
181
+ address: string
182
+ detailAddress: string
183
+ deliveryMessage?: string
184
+ }
176
185
  idempotencyKey?: string
177
186
  }) => Promise<OrderDoc>
178
187
  confirmPaymentReturningOrder: (params: {
@@ -372,28 +381,12 @@ export function buildSoftwareCommerceProvider(
372
381
  if (!cartId || lines.length === 0) {
373
382
  throw new Error('Cart is empty')
374
383
  }
375
-
376
- // The order snapshots `shippingAddress` from the cart, and checkout only
377
- // accepts `customerSnapshot` — so persist the buyer-entered address on the
378
- // cart first. These fields are caller-writable (only the amount fields are
379
- // read-only). Shipping was already computed by the cart on each item
380
- // mutation; the base fee does not depend on the address for the default
381
- // policy (postalCode-driven zone surcharge recompute is owned by the
382
- // Console cart shipping-authority work, not the template).
383
- await client.collections.from('carts').update(cartId, {
384
- email: input.customerSnapshot.email,
385
- shippingAddress: {
386
- recipientName: input.shippingAddress.recipientName,
387
- phone: input.shippingAddress.phone,
388
- postalCode: input.shippingAddress.postalCode,
389
- address: input.shippingAddress.address,
390
- detailAddress: input.shippingAddress.detailAddress,
391
- deliveryMessage: input.shippingAddress.deliveryMessage ?? '',
392
- },
393
- ...(input.shippingAddress.deliveryMessage
394
- ? { customerNote: input.shippingAddress.deliveryMessage }
395
- : {}),
396
- })
384
+ const hasShippableItems = lines.some(
385
+ (item) => item.requiresShipping !== false,
386
+ )
387
+ if (hasShippableItems && !input.shippingAddress) {
388
+ throw new Error('Shipping address is required')
389
+ }
397
390
 
398
391
  const orderNumber = generateOrderNumber()
399
392
  // pgPaymentId is minted template-side AFTER checkout (the design's "mint
@@ -407,12 +400,25 @@ export function buildSoftwareCommerceProvider(
407
400
 
408
401
  const placed = await client.commerce.orders.checkout({
409
402
  cartId,
403
+ cartToken: input.cartToken,
410
404
  orderNumber,
411
405
  customerSnapshot: {
412
406
  name: input.customerSnapshot.name,
413
407
  email: input.customerSnapshot.email,
414
408
  phone: input.customerSnapshot.phone,
415
409
  },
410
+ ...(input.shippingAddress
411
+ ? {
412
+ shippingAddress: {
413
+ recipientName: input.shippingAddress.recipientName,
414
+ phone: input.shippingAddress.phone,
415
+ postalCode: input.shippingAddress.postalCode,
416
+ address: input.shippingAddress.address,
417
+ detailAddress: input.shippingAddress.detailAddress,
418
+ deliveryMessage: input.shippingAddress.deliveryMessage,
419
+ },
420
+ }
421
+ : {}),
416
422
  idempotencyKey: `checkout-${orderNumber}`,
417
423
  })
418
424
 
@@ -420,7 +426,9 @@ export function buildSoftwareCommerceProvider(
420
426
  fallbackOrderNumber: orderNumber,
421
427
  contact: {
422
428
  customerSnapshot: input.customerSnapshot,
423
- shippingAddress: input.shippingAddress,
429
+ ...(input.shippingAddress
430
+ ? { shippingAddress: input.shippingAddress }
431
+ : {}),
424
432
  },
425
433
  })
426
434
 
@@ -566,6 +574,9 @@ export function buildSoftwareCommerceProvider(
566
574
  quantity,
567
575
  unitAmount,
568
576
  lineAmount,
577
+ ...(item.requiresShipping !== undefined
578
+ ? { requiresShipping: item.requiresShipping }
579
+ : {}),
569
580
  productTitle: summary?.title ?? 'Product',
570
581
  variantTitle: variant?.title,
571
582
  image: variant?.images[0] ?? summary?.image ?? null,
@@ -635,7 +646,9 @@ function createSoftwareCustomerProvider(token: string): CommerceProvider {
635
646
  * client. Logged-in `createCart()` therefore mints a **customer-bound** cart
636
647
  * (the JWT auto-binds `customer`), never a guest cart.
637
648
  */
638
- export function createSoftwareCustomerCartProvider(token: string): CartProvider {
649
+ export function createSoftwareCustomerCartProvider(
650
+ token: string,
651
+ ): CartProvider {
639
652
  return createSoftwareCustomerProvider(token)
640
653
  }
641
654
 
@@ -758,7 +771,7 @@ function mapOrder(
758
771
  fallbackOrderNumber?: string
759
772
  contact?: {
760
773
  customerSnapshot: Order['customerSnapshot']
761
- shippingAddress: Order['shippingAddress']
774
+ shippingAddress?: Order['shippingAddress']
762
775
  }
763
776
  } = {},
764
777
  ): Order {
@@ -805,32 +818,36 @@ function mapOrder(
805
818
  opts.contact?.customerSnapshot.phone ??
806
819
  '',
807
820
  },
808
- shippingAddress: {
809
- recipientName:
810
- order.shippingAddress?.recipientName ??
811
- opts.contact?.shippingAddress.recipientName ??
812
- '',
813
- phone:
814
- order.shippingAddress?.phone ??
815
- opts.contact?.shippingAddress.phone ??
816
- '',
817
- postalCode:
818
- order.shippingAddress?.postalCode ??
819
- opts.contact?.shippingAddress.postalCode ??
820
- '',
821
- address:
822
- order.shippingAddress?.address ??
823
- opts.contact?.shippingAddress.address ??
824
- '',
825
- detailAddress:
826
- order.shippingAddress?.detailAddress ??
827
- opts.contact?.shippingAddress.detailAddress ??
828
- '',
829
- deliveryMessage:
830
- order.shippingAddress?.deliveryMessage ??
831
- opts.contact?.shippingAddress.deliveryMessage ??
832
- '',
833
- },
821
+ ...(order.shippingAddress || opts.contact?.shippingAddress
822
+ ? {
823
+ shippingAddress: {
824
+ recipientName:
825
+ order.shippingAddress?.recipientName ??
826
+ opts.contact?.shippingAddress?.recipientName ??
827
+ '',
828
+ phone:
829
+ order.shippingAddress?.phone ??
830
+ opts.contact?.shippingAddress?.phone ??
831
+ '',
832
+ postalCode:
833
+ order.shippingAddress?.postalCode ??
834
+ opts.contact?.shippingAddress?.postalCode ??
835
+ '',
836
+ address:
837
+ order.shippingAddress?.address ??
838
+ opts.contact?.shippingAddress?.address ??
839
+ '',
840
+ detailAddress:
841
+ order.shippingAddress?.detailAddress ??
842
+ opts.contact?.shippingAddress?.detailAddress ??
843
+ '',
844
+ deliveryMessage:
845
+ order.shippingAddress?.deliveryMessage ??
846
+ opts.contact?.shippingAddress?.deliveryMessage ??
847
+ '',
848
+ },
849
+ }
850
+ : {}),
834
851
  subtotalAmount: order.subtotalAmount ?? 0,
835
852
  shippingAmount: order.shippingAmount ?? 0,
836
853
  totalAmount: order.totalAmount ?? 0,
@@ -1,63 +1,64 @@
1
1
  export type CommerceImage = {
2
- url: string;
3
- alt?: string;
4
- width?: number;
5
- height?: number;
6
- };
2
+ url: string
3
+ alt?: string
4
+ width?: number
5
+ height?: number
6
+ }
7
7
 
8
8
  export type Product = {
9
- id: string;
10
- slug: string;
11
- title: string;
12
- description?: string;
13
- thumbnail?: CommerceImage | null;
14
- images: CommerceImage[];
15
- status: "draft" | "published" | "archived";
16
- };
9
+ id: string
10
+ slug: string
11
+ title: string
12
+ description?: string
13
+ thumbnail?: CommerceImage | null
14
+ images: CommerceImage[]
15
+ status: 'draft' | 'published' | 'archived'
16
+ }
17
17
 
18
18
  export type ProductDetail = {
19
- product: Product;
20
- variants: ProductVariant[];
21
- options: ProductOption[];
22
- };
19
+ product: Product
20
+ variants: ProductVariant[]
21
+ options: ProductOption[]
22
+ }
23
23
 
24
24
  export type ProductOption = {
25
- id: string;
26
- slug: string;
27
- title: string;
28
- values: ProductOptionValue[];
29
- };
25
+ id: string
26
+ slug: string
27
+ title: string
28
+ values: ProductOptionValue[]
29
+ }
30
30
 
31
31
  export type ProductOptionValue = {
32
- id: string;
33
- slug: string;
34
- value: string;
35
- };
32
+ id: string
33
+ slug: string
34
+ value: string
35
+ }
36
36
 
37
37
  export type ProductVariantOptionValue = {
38
- optionId: string;
39
- valueId: string;
40
- valueSlug: string;
41
- value: string;
42
- };
38
+ optionId: string
39
+ valueId: string
40
+ valueSlug: string
41
+ value: string
42
+ }
43
43
 
44
44
  export type ProductVariant = {
45
- id: string;
46
- productId: string;
47
- title?: string;
48
- sku?: string;
49
- price: number;
50
- stock: number;
51
- reservedStock: number;
52
- isUnlimited: boolean;
53
- optionValues: ProductVariantOptionValue[];
54
- images: CommerceImage[];
55
- };
45
+ id: string
46
+ productId: string
47
+ title?: string
48
+ sku?: string
49
+ price: number
50
+ stock: number
51
+ reservedStock: number
52
+ isUnlimited: boolean
53
+ requiresShipping?: boolean | null
54
+ optionValues: ProductVariantOptionValue[]
55
+ images: CommerceImage[]
56
+ }
56
57
 
57
58
  export type CartLine = {
58
- variantId: string;
59
- quantity: number;
60
- };
59
+ variantId: string
60
+ quantity: number
61
+ }
61
62
 
62
63
  /**
63
64
  * Reference used to add a line to the server cart. The catalog identity
@@ -65,11 +66,11 @@ export type CartLine = {
65
66
  * cart, not the browser, owns price and stock.
66
67
  */
67
68
  export type CartItemRef = {
68
- productId: string;
69
- variantId: string;
70
- optionId?: string;
71
- quantity: number;
72
- };
69
+ productId: string
70
+ variantId: string
71
+ optionId?: string
72
+ quantity: number
73
+ }
73
74
 
74
75
  /**
75
76
  * A single line of the server cart, enriched with catalog display fields so the
@@ -77,16 +78,17 @@ export type CartItemRef = {
77
78
  * handle used by update/remove.
78
79
  */
79
80
  export type CartItemView = {
80
- cartItemId: string;
81
- productId: string;
82
- variantId: string;
83
- quantity: number;
84
- unitAmount: number;
85
- lineAmount: number;
86
- productTitle: string;
87
- variantTitle?: string;
88
- image?: CommerceImage | null;
89
- };
81
+ cartItemId: string
82
+ productId: string
83
+ variantId: string
84
+ quantity: number
85
+ unitAmount: number
86
+ lineAmount: number
87
+ requiresShipping?: boolean | null
88
+ productTitle: string
89
+ variantTitle?: string
90
+ image?: CommerceImage | null
91
+ }
90
92
 
91
93
  /**
92
94
  * The server cart projected for the storefront. `id` is the cart's DB id used
@@ -96,95 +98,95 @@ export type CartItemView = {
96
98
  * computed and stored by the Console cart).
97
99
  */
98
100
  export type CartView = {
99
- id: string;
100
- currency: string;
101
- subtotalAmount: number;
102
- shippingAmount: number;
103
- discountAmount: number;
104
- totalAmount: number;
105
- discountCode?: string;
106
- items: CartItemView[];
107
- };
101
+ id: string
102
+ currency: string
103
+ subtotalAmount: number
104
+ shippingAmount: number
105
+ discountAmount: number
106
+ totalAmount: number
107
+ discountCode?: string
108
+ items: CartItemView[]
109
+ }
108
110
 
109
111
  export type ShippingPolicy = {
110
- currency: string;
111
- baseAmount: number;
112
- freeAboveAmount?: number;
113
- };
112
+ currency: string
113
+ baseAmount: number
114
+ freeAboveAmount?: number
115
+ }
114
116
 
115
117
  export type PricedOrderLine = CartLine & {
116
- unitAmount: number;
117
- lineAmount: number;
118
- variant: ProductVariant;
119
- };
118
+ unitAmount: number
119
+ lineAmount: number
120
+ variant: ProductVariant
121
+ }
120
122
 
121
123
  export type PricedOrder = {
122
- currency: string;
123
- lines: PricedOrderLine[];
124
- subtotalAmount: number;
125
- shippingAmount: number;
126
- totalAmount: number;
127
- };
124
+ currency: string
125
+ lines: PricedOrderLine[]
126
+ subtotalAmount: number
127
+ shippingAmount: number
128
+ totalAmount: number
129
+ }
128
130
 
129
131
  export type StockCheckLine = CartLine & {
130
- availableQuantity: number | null;
131
- ok: boolean;
132
- reason?: "variant_not_found" | "insufficient_stock";
133
- };
132
+ availableQuantity: number | null
133
+ ok: boolean
134
+ reason?: 'variant_not_found' | 'insufficient_stock'
135
+ }
134
136
 
135
137
  export type StockCheckResult = {
136
- ok: boolean;
137
- lines: StockCheckLine[];
138
- };
138
+ ok: boolean
139
+ lines: StockCheckLine[]
140
+ }
139
141
 
140
142
  export type CustomerSnapshot = {
141
- name: string;
142
- email: string;
143
- phone: string;
144
- };
143
+ name: string
144
+ email: string
145
+ phone: string
146
+ }
145
147
 
146
148
  export type ShippingAddress = {
147
- recipientName: string;
148
- phone: string;
149
- postalCode: string;
150
- address: string;
151
- detailAddress: string;
152
- deliveryMessage?: string;
153
- };
149
+ recipientName: string
150
+ phone: string
151
+ postalCode: string
152
+ address: string
153
+ detailAddress: string
154
+ deliveryMessage?: string
155
+ }
154
156
 
155
157
  export type OrderItem = {
156
- variantId: string;
157
- productTitle: string;
158
- variantTitle?: string;
159
- quantity: number;
160
- unitAmount: number;
161
- lineAmount: number;
162
- };
158
+ variantId: string
159
+ productTitle: string
160
+ variantTitle?: string
161
+ quantity: number
162
+ unitAmount: number
163
+ lineAmount: number
164
+ }
163
165
 
164
166
  export type PaymentTransaction = {
165
- paymentId: string;
166
- provider: string;
167
- status: "pending" | "paid" | "failed" | "canceled";
168
- amount: number;
169
- };
167
+ paymentId: string
168
+ provider: string
169
+ status: 'pending' | 'paid' | 'failed' | 'canceled'
170
+ amount: number
171
+ }
170
172
 
171
173
  export type Order = {
172
- id: string;
173
- orderNumber: string;
174
- displayStatus: "pending" | "paid" | "canceled";
175
- items: OrderItem[];
176
- customerSnapshot: CustomerSnapshot;
177
- shippingAddress: ShippingAddress;
178
- subtotalAmount: number;
179
- shippingAmount: number;
180
- totalAmount: number;
181
- transactions: PaymentTransaction[];
182
- };
174
+ id: string
175
+ orderNumber: string
176
+ displayStatus: 'pending' | 'paid' | 'canceled'
177
+ items: OrderItem[]
178
+ customerSnapshot: CustomerSnapshot
179
+ shippingAddress?: ShippingAddress
180
+ subtotalAmount: number
181
+ shippingAmount: number
182
+ totalAmount: number
183
+ transactions: PaymentTransaction[]
184
+ }
183
185
 
184
186
  export type ProductListResult = {
185
- products: ProductDetail[];
186
- total: number;
187
- };
187
+ products: ProductDetail[]
188
+ total: number
189
+ }
188
190
 
189
191
  /**
190
192
  * Checkout input. The cart contents live server-side (addressed by `cartToken`
@@ -192,15 +194,15 @@ export type ProductListResult = {
192
194
  * snapshot. No client-supplied lines or totals — the cart is the quote.
193
195
  */
194
196
  export type CheckoutCartInput = {
195
- cartToken: string;
196
- customerSnapshot: CustomerSnapshot;
197
- shippingAddress: ShippingAddress;
198
- };
197
+ cartToken: string
198
+ customerSnapshot: CustomerSnapshot
199
+ shippingAddress?: ShippingAddress
200
+ }
199
201
 
200
202
  export type CreatePendingOrderResult = {
201
- order: Order;
202
- paymentId: string;
203
- paymentName: string;
204
- amount: number;
205
- currency: string;
206
- };
203
+ order: Order
204
+ paymentId: string
205
+ paymentName: string
206
+ amount: number
207
+ currency: string
208
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ecommerce",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
@@ -12,7 +12,7 @@
12
12
  "check-types": "next typegen && tsc --noEmit"
13
13
  },
14
14
  "dependencies": {
15
- "@01.software/sdk": "^0.42.0",
15
+ "@01.software/sdk": "^0.43.0",
16
16
  "@portone/browser-sdk": "^0.1.8",
17
17
  "@portone/server-sdk": "^0.19.0",
18
18
  "@tosspayments/tosspayments-sdk": "^2.7.0",
@@ -254,8 +254,14 @@ function fakeCheckoutSdkClient(calls: string[]) {
254
254
  },
255
255
  },
256
256
  orders: {
257
- async checkout(params: { cartId: string }) {
258
- calls.push(`commerce.orders.checkout:${params.cartId}`);
257
+ async checkout(params: {
258
+ cartId: string;
259
+ cartToken?: string;
260
+ shippingAddress?: { postalCode?: string };
261
+ }) {
262
+ calls.push(
263
+ `commerce.orders.checkout:${params.cartId}:${params.cartToken ?? ""}:${params.shippingAddress?.postalCode ?? ""}`,
264
+ );
259
265
  if (params.cartId === "cart_other") {
260
266
  throw Object.assign(new Error("Forbidden"), { status: 403 });
261
267
  }
@@ -287,10 +293,6 @@ function fakeCheckoutSdkClient(calls: string[]) {
287
293
  calls.push(`collections.${slug}.find`);
288
294
  return { docs: [] };
289
295
  },
290
- async update(id: string) {
291
- calls.push(`collections.${slug}.update:${id}`);
292
- return {};
293
- },
294
296
  };
295
297
  },
296
298
  },
@@ -322,8 +324,11 @@ test("customer checkout provider: own cart checks out through the bound SDK clie
322
324
 
323
325
  assert.equal(pending.order.orderNumber, "ORD-1");
324
326
  assert.ok(calls.includes("commerce.cart.get:cart_owned_token"));
325
- assert.ok(calls.includes("collections.carts.update:cart_owned"));
326
- assert.ok(calls.includes("commerce.orders.checkout:cart_owned"));
327
+ assert.ok(
328
+ calls.includes(
329
+ "commerce.orders.checkout:cart_owned:cart_owned_token:04524",
330
+ ),
331
+ );
327
332
  });
328
333
 
329
334
  test("customer checkout provider: another customer's cart is rejected by the SDK boundary", async () => {
@@ -352,7 +357,11 @@ test("customer checkout provider: another customer's cart is rejected by the SDK
352
357
  Object.assign(new Error("Forbidden"), { status: 403 }),
353
358
  );
354
359
 
355
- assert.ok(calls.includes("commerce.orders.checkout:cart_other"));
360
+ assert.ok(
361
+ calls.includes(
362
+ "commerce.orders.checkout:cart_other:cart_other_token:04524",
363
+ ),
364
+ );
356
365
  });
357
366
 
358
367
  // ---- logout clears both the session JWT and the cart cookie ---------------