@hanzo/commerce 2.0.0 → 3.0.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.
Files changed (56) hide show
  1. package/components/add-to-cart-widget.tsx +3 -3
  2. package/components/buy-item/buy-item-button.tsx +28 -0
  3. package/components/buy-item/buy-item-card.tsx +82 -0
  4. package/components/buy-item/buy-item-popup.tsx +38 -0
  5. package/components/{select-category-and-item-widget.tsx → buy-item/select-category-and-item-widget.tsx} +4 -4
  6. package/components/buy-item/select-category-item-card.tsx +111 -0
  7. package/components/{cart-line-item.tsx → cart-panel/cart-line-item.tsx} +4 -3
  8. package/components/{cart.tsx → cart-panel/index.tsx} +24 -17
  9. package/components/category-item-ios-wheel-selector.tsx +60 -0
  10. package/components/category-item-radio-selector.tsx +55 -0
  11. package/components/{checkout → checkout-panel}/confirm-order.tsx +3 -2
  12. package/components/{checkout → checkout-panel}/index.tsx +26 -69
  13. package/components/checkout-panel/payment/contact-info.tsx +59 -0
  14. package/components/checkout-panel/payment/index.tsx +118 -0
  15. package/components/checkout-panel/payment/pay-with-bank-transfer.tsx +106 -0
  16. package/components/checkout-panel/payment/pay-with-card.tsx +198 -0
  17. package/components/{checkout → checkout-panel/payment}/pay-with-crypto.tsx +52 -30
  18. package/components/checkout-panel/payment/payment-methods/amex.tsx +32 -0
  19. package/components/checkout-panel/payment/payment-methods/diners-club.tsx +13 -0
  20. package/components/checkout-panel/payment/payment-methods/discover.tsx +25 -0
  21. package/components/checkout-panel/payment/payment-methods/index.tsx +24 -0
  22. package/components/checkout-panel/payment/payment-methods/jcb.tsx +26 -0
  23. package/components/checkout-panel/payment/payment-methods/mastercard.tsx +27 -0
  24. package/components/checkout-panel/payment/payment-methods/visa.tsx +25 -0
  25. package/components/{checkout → checkout-panel}/shipping-info.tsx +4 -10
  26. package/components/{facet-toggles-widget → facet-values-widget}/facet-image.tsx +7 -3
  27. package/components/{facet-toggles-widget → facet-values-widget}/index.tsx +7 -5
  28. package/components/index.ts +11 -9
  29. package/components/{select-item-in-category-view.tsx → select-category-item-panel.tsx} +72 -76
  30. package/index.ts +2 -2
  31. package/package.json +11 -4
  32. package/service/context.tsx +4 -4
  33. package/service/impls/standalone/index.ts +4 -4
  34. package/service/impls/standalone/orders/index.ts +42 -20
  35. package/service/impls/standalone/standalone-service.ts +128 -44
  36. package/types/category.ts +3 -2
  37. package/types/checkout.ts +6 -0
  38. package/types/commerce-service.ts +20 -7
  39. package/types/facet.ts +1 -3
  40. package/types/index.ts +4 -4
  41. package/types/item-selector.ts +14 -0
  42. package/util/index.ts +31 -1
  43. package/util/square-payment.ts +43 -0
  44. package/util/use-sync-sku-param-w-current-item.ts +4 -1
  45. package/blocks/components/commerce-cat-and-item-block.tsx +0 -13
  46. package/blocks/def/commerce-cat-and-item-block.ts +0 -9
  47. package/components/checkout/contact-info.tsx +0 -83
  48. package/components/checkout/pay-by-bank-transfer.tsx +0 -76
  49. package/components/facets-widget.tsx +0 -55
  50. package/components/product-selection-mobile-picker.tsx +0 -78
  51. package/components/product-selection-radio-group.tsx +0 -38
  52. /package/components/{checkout → checkout-panel}/countries.ts +0 -0
  53. /package/components/{checkout → checkout-panel}/icons/btc.tsx +0 -0
  54. /package/components/{checkout → checkout-panel}/icons/eth.tsx +0 -0
  55. /package/components/{checkout → checkout-panel}/icons/usdt.tsx +0 -0
  56. /package/components/{checkout → checkout-panel}/thank-you.tsx +0 -0
@@ -6,35 +6,36 @@ import { observer } from 'mobx-react-lite'
6
6
  import { cn } from '@hanzo/ui/util'
7
7
  import { Skeleton } from '@hanzo/ui/primitives'
8
8
 
9
- import type { Category, ObsLineItemRef } from '../types'
9
+ import type { ItemSelector } from '../types'
10
10
  import { formatPrice } from '../util'
11
11
  import { Icons } from './Icons'
12
12
 
13
13
  import AddToCartWidget from './add-to-cart-widget'
14
- import ProductSelectionRadioGroup from './product-selection-radio-group'
15
- import ProductSelectionMobilePicker from './product-selection-mobile-picker'
16
-
17
- const SelectItemInCategoryView: React.FC<React.HTMLAttributes<HTMLDivElement> & {
18
- category: Category
19
- lineItemRef: ObsLineItemRef
20
- handleItemSelected: (sku: string) => void
21
- isLoading?: boolean
22
- mobile?: boolean
23
- }> = ({
14
+ import CategoryItemRadioSelector from './category-item-radio-selector'
15
+ import CategoryItemIOSWheelSelector from './category-item-ios-wheel-selector'
16
+
17
+ const SelectCategoryItemPanel: React.FC<
18
+ React.HTMLAttributes<HTMLDivElement> & ItemSelector &
19
+ {
20
+ isLoading?: boolean
21
+ mobile?: boolean
22
+ }
23
+ > = /* NOT observer */ ({
24
24
  category,
25
- lineItemRef,
26
- handleItemSelected,
25
+ selectedItemRef,
26
+ selectSku,
27
27
  className,
28
+ showQuantity=true,
28
29
  isLoading = false,
29
30
  mobile = false,
30
31
  ...props
31
32
  }) => {
32
33
 
33
- const waiting = (): boolean => (isLoading) // keep for convenience for now
34
+ const soleOption = category.products.length === 1
34
35
 
35
36
  const CategoryImage: React.FC<{ className?: string }> = ({ className = '' }) => {
36
37
 
37
- if (waiting()) {
38
+ if (isLoading) {
38
39
  // deliberately not Skeleton to have a better overall pulse effect.
39
40
  return <div className={cn(
40
41
  'bg-level-1 rounded-xl aspect-square w-full min-h-1 ', // +
@@ -42,25 +43,13 @@ const SelectItemInCategoryView: React.FC<React.HTMLAttributes<HTMLDivElement> &
42
43
  className)} />
43
44
  }
44
45
 
45
- if (!category.img) {
46
- return (
47
- <div
48
- aria-label='Placeholder'
49
- role='img'
50
- aria-roledescription='placeholder'
51
- className={cn('w-full flex items-center justify-center aspect-square' , className)}
52
- >
53
- <Icons.barcode className='h-9 w-9 text-muted' aria-hidden='true' />
54
- </div>
55
- )
56
- }
57
-
58
- return (
46
+ return category.img ? (
47
+ // TODO: Why so many div's?
59
48
  <div className={cn('flex flex-col justify-start', className)}>
60
49
  <div className={cn('w-full border rounded-xl p-6 ')}>
61
50
  <div className={cn('w-full aspect-square relative')}>
62
51
  <Image
63
- src={category.img!}
52
+ src={category.img}
64
53
  fill
65
54
  sizes="(max-width: 480px) 100vw, (max-width: 768px) 50vw, (max-width: 1200px) 50vw, 20vw"
66
55
  alt={category.title}
@@ -70,81 +59,88 @@ const SelectItemInCategoryView: React.FC<React.HTMLAttributes<HTMLDivElement> &
70
59
  </div>
71
60
  </div>
72
61
  </div>
62
+ ) : (
63
+ <div
64
+ aria-label='Placeholder'
65
+ role='img'
66
+ aria-roledescription='placeholder'
67
+ className={cn('w-full flex items-center justify-center aspect-square' , className)}
68
+ >
69
+ <Icons.barcode className='h-9 w-9 text-muted' aria-hidden='true' />
70
+ </div>
73
71
  )
74
72
  }
75
73
 
76
74
  const AvailableAmounts: React.FC<{ className?: string }> = observer(({ className = '' }) => {
77
75
 
78
- const soleOption = !waiting() && category.products.length === 1
79
- const mobilePicker = !waiting() && (mobile && category.products.length > 8)
76
+ if (soleOption) return null
77
+ const mobilePicker = (mobile && category.products.length > 8)
80
78
 
81
- return waiting() ? (
79
+ return isLoading ? (
82
80
  <Skeleton className={'min-h-[120px] w-pr-60 mx-auto ' + className} />
83
81
  ) : (
84
82
  <div /* id='CV_AVAIL_AMOUNTS' */ className={cn(
85
83
  'sm:w-pr-80 sm:mx-auto md:w-full flex flex-col justify-start items-center',
86
- (soleOption ? 'gap-2' : mobilePicker ? 'gap-4' : 'gap-8'),
84
+ (mobilePicker ? 'gap-4' : 'gap-8'),
87
85
  className
88
86
  )}>
89
87
  <div className='w-full flex flex-col justify-start items-center'>
90
- <h6 className='text-center font-semibold'>{`Available size${soleOption ? '' : 's'}`}</h6>
91
- <div className={'h-[1px] bg-muted-3 ' + (mobilePicker ? 'w-pr-55' : 'w-pr-80') } />
88
+ <h6 className='text-center font-semibold'>Available options</h6>
89
+ <div className={'h-[1px] bg-muted-3 ' + (mobilePicker ? 'w-pr-55' : 'w-pr-60') } />
92
90
  </div>
93
- {soleOption ? (
94
- <p>{category.products[0].titleAsOption}</p>
95
- ) : ( mobilePicker ? (
96
- <ProductSelectionMobilePicker
97
- products={category.products}
98
- selectedSku={lineItemRef.item?.sku ?? undefined}
99
- onValueChange={handleItemSelected}
100
- height={180}
101
- itemHeight={30}
102
- outerClx='mb-4'
103
- />
104
- ) : (
105
- <ProductSelectionRadioGroup
106
- products={category.products}
107
- selectedSku={lineItemRef.item?.sku ?? undefined}
108
- onValueChange={handleItemSelected}
109
- groupClx='grid grid-cols-2 gap-0 gap-y-3 gap-x-8 '
110
- itemClx='flex flex-row gap-2 items-center'
111
- />
112
- )
91
+ {mobilePicker ? (
92
+ <CategoryItemIOSWheelSelector
93
+ category={category}
94
+ selectedItemRef={selectedItemRef}
95
+ selectSku={selectSku}
96
+ showQuantity={showQuantity}
97
+ height={180}
98
+ itemHeight={30}
99
+ outerClx='mb-4'
100
+ />
101
+ ) : (
102
+ <CategoryItemRadioSelector
103
+ category={category}
104
+ selectedItemRef={selectedItemRef}
105
+ selectSku={selectSku}
106
+ showQuantity={showQuantity}
107
+ groupClx='block columns-2 gap-4'
108
+ itemClx='flex flex-row gap-2 items-center mb-2.5'
109
+ />
113
110
  )}
114
111
  </div>
115
112
  )
116
113
  })
117
114
 
118
115
  const AddToCartArea: React.FC<{ className?: string }> = observer(({ className = '' }) => (
119
- (lineItemRef.item && !isLoading) ? (
120
- <AddToCartWidget size='default' item={lineItemRef.item} className={className}/>
116
+ (selectedItemRef.item && !isLoading) ? (
117
+ <AddToCartWidget size='default' item={selectedItemRef.item} className={className}/>
121
118
  ) : (
122
119
  <div className={cn('h-6 w-12 invisible', className)} />
123
120
  )
124
121
  ))
125
122
 
126
123
  const TitleArea: React.FC<{ className?: string }> = observer(({ className = '' }) => (
127
- waiting() ? (<Skeleton className={'h-12 w-pr-80 mx-auto ' + className} />) : (
128
-
129
- <div className={cn('flex flex-col justify-start items-center mb-6', className)}>
130
- <h3 className='text-base md:text-lg lg:text-2xl md:mt-[2vw] font-nav text-center'>
131
- <span className='xs:inline md:hidden'>
132
- {category.title.split(', ').map((s, i) => (<p key={i}>{s}</p>))}
133
- </span>
134
- <span className='xs:hidden md:inline '>
135
- {category.title}
136
- </span>
124
+
125
+ isLoading ? (<Skeleton className={'h-12 w-pr-80 mx-auto ' + className} />) : (
126
+
127
+ <div className={cn('flex flex-col justify-start items-center', className)}>
128
+ <h3 className='text-base md:text-lg lg:text-2xl font-nav text-center'>
129
+ {category.parentTitle && (
130
+ <span>{category.parentTitle}<br className='md:hidden' /><span className='xs:hidden md:inline '>,&nbsp;</span></span>
131
+ )}
132
+ <span>{category.title}</span>
137
133
  </h3>
138
- {lineItemRef.item?.sku ? (
134
+ {selectedItemRef.item?.sku ? (
139
135
  <h6 className='text-center font-semibold'>
140
- {lineItemRef.item.titleAsOption + ': ' + formatPrice(lineItemRef.item.price)}
136
+ {(soleOption ? '' : (selectedItemRef.item.titleAsOption + ': ')) + formatPrice(selectedItemRef.item.price)}
141
137
  </h6>
142
138
  ) : ''}
143
139
  </div>
144
140
  )))
145
141
 
146
142
  const Desc: React.FC<{ className?: string }> = ({ className = '' }) => (
147
- waiting() ? (
143
+ isLoading ? (
148
144
  <Skeleton className={'min-h-20 w-full grow mx-auto ' + className} />
149
145
  ) : (
150
146
  <p className={cn('text-base lg:text-lg mb-6 xs:mb-0', className)}>{category.desc}</p>
@@ -161,7 +157,7 @@ const SelectItemInCategoryView: React.FC<React.HTMLAttributes<HTMLDivElement> &
161
157
  {...props}
162
158
  >
163
159
  <div /* id='CV_TITLE_AND_IMAGE_ROW' */ className='flex flex-row justify-between items-start w-full'>
164
- {waiting() ? ( <Skeleton className={'min-h-30 w-full '} /> ) : (<>
160
+ {isLoading ? ( <Skeleton className={'min-h-30 w-full '} /> ) : (<>
165
161
  <CategoryImage className='w-pr-33' />
166
162
  <TitleArea className='grow pt-3 mb-0' />
167
163
  </>)}
@@ -176,17 +172,17 @@ const SelectItemInCategoryView: React.FC<React.HTMLAttributes<HTMLDivElement> &
176
172
  <div /* id='CV_IMAGE_COL_SEP' */ className={'relative ' + (!isLoading ? 'md:hidden sm:min-w-[185px] xl:flex xl:w-pr-40' : '')}>
177
173
  <CategoryImage className='w-full' />
178
174
  </div>
179
- <div /* id='CV_CONTENT_COL */ className='flex flex-col gap-y-6 xl:w-pr-60'>
175
+ <div /* id='CV_CONTENT_COL */ className={'flex flex-col xl:w-pr-60 justify-center ' + (soleOption ? '' : 'gap-6') }>
180
176
  <div /* id='CV_MD_IMAGE_AND_TITLE */ className='hidden md:flex xl:hidden flex-row gap-x-3'>
181
177
  <CategoryImage className='min-w-pr-30' />
182
178
  <TitleArea className='grow' />
183
179
  </div>
184
-
180
+
185
181
  <div /* id='CV_CONTENT' */ className={'flex flex-col gap-2.5 ' + (isLoading ? 'justify-between h-full' : '')}>
186
182
  <TitleArea className='md:hidden xl:flex' />
187
183
  <Desc className='' />
188
184
  </div>
189
- <div /* id='CV_CTA_AREA_BIG' */ className='hidden lg:flex p-4 flex-col justify-start items-center gap-6'>
185
+ <div /* id='CV_CTA_AREA_BIG' */ className='hidden lg:flex flex-col justify-start items-center gap-6'>
190
186
  <AvailableAmounts />
191
187
  <AddToCartArea className='' />
192
188
  </div>
@@ -201,4 +197,4 @@ const SelectItemInCategoryView: React.FC<React.HTMLAttributes<HTMLDivElement> &
201
197
  }
202
198
 
203
199
 
204
- export default SelectItemInCategoryView
200
+ export default SelectCategoryItemPanel
package/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export * from './types'
2
1
  export * from './service/context'
2
+ export * from './components'
3
3
  export type { StandaloneServiceOptions as ServiceOptions } from './service/impls/standalone/standalone-service'
4
- export { useSyncSkuParamWithCurrentItem } from './util'
4
+ export { useSyncSkuParamWithCurrentItem, getFacetValuesMutator, formatPrice } from './util'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanzo/commerce",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "Library with shopping cart components.",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/",
@@ -26,19 +26,26 @@
26
26
  "tc": "tsc",
27
27
  "clean": "rm -rf dist && rm -rf node_modules"
28
28
  },
29
+ "exports": {
30
+ ".": "./index.ts",
31
+ "./types": "./types/index.ts"
32
+ },
29
33
  "dependencies": {
30
34
  "ethers": "^6.11.1",
31
35
  "next-usequerystate": "^1.17.0",
32
- "react-mobile-picker": "^1.0.0"
36
+ "react-mobile-picker": "^1.0.0",
37
+ "react-square-web-payments-sdk": "^3.2.1",
38
+ "square": "^35.0.0"
33
39
  },
34
40
  "peerDependencies": {
35
- "@hanzo/auth": "^2.0.0",
36
- "@hanzo/ui": "^3.0.0",
41
+ "@hanzo/auth": "^2.0.2",
42
+ "@hanzo/ui": "^3.0.2",
37
43
  "@hookform/resolvers": "^3.3.4",
38
44
  "firebase": "^10.8.0",
39
45
  "lucide-react": "^0.307.0",
40
46
  "mobx": "^6.12.0",
41
47
  "mobx-react-lite": "^4.0.5",
48
+ "mobx-utils": "^6.0.8",
42
49
  "next": "^14.1.0",
43
50
  "react": "^18.2.0",
44
51
  "react-dom": "^18.2.0",
@@ -9,7 +9,7 @@ enableStaticRendering(typeof window === "undefined")
9
9
  import type CommerceService from '../types/commerce-service'
10
10
  import type { ServiceOptions } from '..'
11
11
  import getServiceSingleton from './impls'
12
- import type { Category, FacetsDesc } from '../types'
12
+ import type { Category, FacetValueDesc } from '../types'
13
13
 
14
14
  const CommerceServiceContext = createContext<CommerceService | undefined>(undefined)
15
15
 
@@ -20,16 +20,16 @@ const useCommerce = (): CommerceService => {
20
20
 
21
21
  const CommerceServiceProvider: React.FC<PropsWithChildren & {
22
22
  productsByCategory: Category[]
23
- facets: FacetsDesc
23
+ rootFacet: FacetValueDesc
24
24
  options?: ServiceOptions
25
25
  }> = ({
26
26
  children,
27
27
  productsByCategory,
28
- facets,
28
+ rootFacet,
29
29
  options
30
30
  }) => {
31
31
 
32
- const serviceRef = useRef<CommerceService>(getServiceSingleton(productsByCategory, facets, options))
32
+ const serviceRef = useRef<CommerceService>(getServiceSingleton(productsByCategory, rootFacet, options))
33
33
  return (
34
34
  <CommerceServiceContext.Provider value={serviceRef.current}>
35
35
  {children}
@@ -1,6 +1,6 @@
1
1
  import { enableStaticRendering } from 'mobx-react-lite'
2
2
 
3
- import type { CommerceService, Category, FacetsDesc } from '../../../types'
3
+ import type { CommerceService, Category, FacetValueDesc } from '../../../types'
4
4
  import StandaloneService, {type StandaloneServiceOptions} from './standalone-service'
5
5
 
6
6
  import { readSnapshot, listenAndWriteSnapshots } from './localStorage'
@@ -19,7 +19,7 @@ let instance: StandaloneService | undefined = undefined
19
19
 
20
20
  export const getInstance = (
21
21
  categories: Category[],
22
- facets: FacetsDesc,
22
+ rootFacet: FacetValueDesc,
23
23
  options?: StandaloneServiceOptions
24
24
  ): CommerceService => {
25
25
 
@@ -31,7 +31,7 @@ export const getInstance = (
31
31
  //_log("NEW INSTANCE FOR SERVER")
32
32
  return new StandaloneService(
33
33
  categories,
34
- facets,
34
+ rootFacet,
35
35
  options
36
36
  )
37
37
  }
@@ -42,7 +42,7 @@ export const getInstance = (
42
42
  const snapShot = readSnapshot()
43
43
  instance = new StandaloneService(
44
44
  categories,
45
- facets,
45
+ rootFacet,
46
46
  options,
47
47
  snapShot
48
48
  )
@@ -23,9 +23,10 @@ const getDBInstance = (name: string): Firestore => {
23
23
 
24
24
  interface SavedOrder {
25
25
  email: string
26
- paymentMethod: string
26
+ name: string
27
27
  // TODO: add shippingInfo type
28
28
  shippingInfo?: any
29
+ paymentInfo?: any
29
30
  status: string
30
31
  timestamp: FieldValue
31
32
  items: ActualLineItemSnapshot[]
@@ -33,16 +34,16 @@ interface SavedOrder {
33
34
 
34
35
  const createOrder = async (
35
36
  email: string,
36
- paymentMethod: string,
37
37
  items: ActualLineItemSnapshot[],
38
38
  options: {
39
39
  dbName: string
40
40
  ordersTable: string
41
- }
41
+ },
42
+ name?: string
42
43
  ): Promise<{
43
44
  success: boolean,
44
45
  error: any,
45
- id?: string
46
+ id?: string,
46
47
  }> => {
47
48
 
48
49
  let error: any | null = null
@@ -52,7 +53,7 @@ const createOrder = async (
52
53
  try {
53
54
  await setDoc(doc(ordersRef, orderId), {
54
55
  email,
55
- paymentMethod,
56
+ name: name ?? '',
56
57
  status: 'open',
57
58
  timestamp: serverTimestamp(),
58
59
  items,
@@ -67,17 +68,13 @@ const createOrder = async (
67
68
  return { success: !error, error }
68
69
  }
69
70
 
70
- const updateOrder = async (
71
+ const updateOrderShippingInfo = async (
71
72
  orderId: string,
72
- email: string,
73
- paymentMethod: string,
74
- // TODO: add shippingInfo type
75
- items: ActualLineItemSnapshot[],
73
+ shippingInfo: any,
76
74
  options: {
77
75
  dbName: string
78
76
  ordersTable: string
79
- },
80
- shippingInfo?: any
77
+ }
81
78
  ): Promise<{
82
79
  success: boolean,
83
80
  error: any
@@ -88,20 +85,45 @@ const updateOrder = async (
88
85
 
89
86
  try {
90
87
  await setDoc(doc(ordersRef, orderId), {
91
- email,
92
- paymentMethod,
93
88
  shippingInfo,
94
- status: 'open',
95
89
  timestamp: serverTimestamp(),
96
- items,
97
- } satisfies SavedOrder)
90
+ }, { merge: true })
98
91
  }
99
92
  catch (e) {
100
- console.error('Error writing item document: ', e)
101
- error = e
93
+ console.error('Error writing item document: ', e)
94
+ error = e
95
+ }
96
+
97
+ return { success: !error, error }
98
+ }
99
+
100
+ const updateOrderPaymentInfo = async (
101
+ orderId: string,
102
+ paymentInfo: any,
103
+ options: {
104
+ dbName: string
105
+ ordersTable: string
106
+ }
107
+ ): Promise<{
108
+ success: boolean,
109
+ error: any
110
+ }> => {
111
+
112
+ let error: any | null = null
113
+ const ordersRef = collection(getDBInstance(options.dbName), options.ordersTable)
114
+
115
+ try {
116
+ await setDoc(doc(ordersRef, orderId), {
117
+ paymentInfo,
118
+ timestamp: serverTimestamp(),
119
+ }, { merge: true })
120
+ }
121
+ catch (e) {
122
+ console.error('Error writing item document: ', e)
123
+ error = e
102
124
  }
103
125
 
104
126
  return { success: !error, error }
105
127
  }
106
128
 
107
- export { createOrder, updateOrder }
129
+ export { createOrder, updateOrderShippingInfo, updateOrderPaymentInfo }