[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/graphql/types/Item.graphql from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```graphql
scalar InputValues

"""
ProductCategoriesObject is a Record<string, string>, in the following format:
{ '<categoryNumber>': '<categoryName>' }
"""
scalar ProductCategoriesObject

type Item {
  additionalInfo: ItemAdditionalInfo
  assemblyOptions: AssemblyOptionItem
  availability: String
  attachmentOfferings: [AttachmentOffering!]!
  attachments: [Attachment!]!
  bundleItems: [Item!]!
  detailUrl: String
  id: ID!
  imageUrls: ImageUrls
  listPrice: Float
  manualPrice: Float
  measurementUnit: String
  modalType: String
  name: String @translatableV2
  offerings: [Offering!]!
  options: [AssemblyOptionType]
  parentAssemblyBinding: String
  parentItemIndex: Int
  price: Float
  productCategories: ProductCategoriesObject
  productCategoryIds: String
  productSpecificationGroups: [ProductSpecificationGroups]!
  """
  Id of the product. Can be null if included inside bundle items.
  """
  productId: String
  productRefId: String
  quantity: Float!
  refId: String
  """
  Product's seller. Can be null if included inside bundle items.
  """
  seller: String
  sellingPrice: Float
  sellingPriceWithAssemblies: Float
  skuName: String @translatableV2
  skuSpecifications: [SKUSpecification!]!
  uniqueId: String!
  unitMultiplier: Float
  priceTags: [PriceTag!]!
  isGift: Boolean!
  priceDefinition: PriceDefinition
}

type PriceDefinition {
  calculatedSellingPrice: Float!
  sellingPrices: [SellingPrice!]!
  total: Float!
}

type SellingPrice {
  quantity: Float!
  value: Float!
}

type ImageUrls {
  at1x: String!
  at2x: String!
  at3x: String!
}

type AssemblyOptionItem {
  added: [AddedOptionItem]!
  removed: [RemovedOptionItem]!
  parentPrice: Float
}

type RemovedOptionItem {
  initialQuantity: Int
  removedQuantity: Int
  name: String @translatableV2
}

type AddedOptionItem {
  item: Item
  compositionItem: CompositionItem
  normalizedQuantity: Int
  choiceType: String
  extraQuantity: Int
}

type CompositionItem {
  id: ID
  minQuantity: Int
  maxQuantity: Int
  initialQuantity: Int
  priceTable: String
  seller: String
}

type ItemAdditionalInfo {
  brandName: String @translatableV2
}

type SKUSpecification {
  fieldName: String @translatableV2
  fieldValues: [String]! @translatableV2
}

type ProductSpecification {
  name: String @translatableV2
  originalName: String @translatableV2
  values: [String]! @translatableV2
}

type ProductSpecificationGroups {
  name: String @translatableV2
  originalName: String @translatableV2
  specifications: [ProductSpecification]!
}

type AssemblyOptionType {
  assemblyId: String!
  id: ID
  quantity: Int
  seller: String
  inputValues: InputValues
  options: [AssemblyOptionType!]
}

type Offering {
  id: String!
  name: String! @translatableV2
  price: Int!
  type: String!
  attachmentOfferings: [AttachmentOffering!]!
}

scalar AttachmentSchema

type AttachmentOffering {
  name: String @translatableV2
  required: Boolean
  schema: AttachmentSchema
}

scalar AttachmentContent

type Attachment {
  name: String @translatableV2
  content: AttachmentContent
}

input ItemInput {
  id: Int
  index: Int
  quantity: Float
  seller: ID
  uniqueId: String
  inputValues: InputValues
  options: [AssemblyOptionInput]
}

input AssemblyOptionInput {
  assemblyId: String!
  id: ID
  quantity: Int
  seller: String
  inputValues: InputValues
  options: [AssemblyOptionInput!]
}

scalar OfferingInfo

input OfferingInput {
  itemIndex: Int!
  offeringId: String!
  offeringInfo: OfferingInfo
}

input BundleItemAttachmentInput {
  itemIndex: Int!
  bundleItemId: String!
  attachmentName: String!
  attachmentContent: AttachmentContent!
}

input ManualPriceInput {
  itemIndex: Int!
  price: Float!
}

type PriceTag {
  identifier: String
  isPercentual: Boolean
  name: String
  ratesAndBenefitsIdentifier: RatesAndBenefitsIdentifier
  rawValue: Float
  value: Float
}

type RatesAndBenefitsIdentifier {
  description: String
  featured: Boolean
  id: String
  matchedParameters: MatchedParameters
  name: String
}

scalar MatchedParameters

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/graphql/types/Payment.graphql from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```graphql
type Installment {
  count: Int!
  hasInterestRate: Boolean
  interestRate: Int
  value: Float
  total: Float!
}

type InstallmentOption {
  paymentSystem: String!
  bin: String
  paymentName: String
  paymentGroupName: String
  value: Float!
  installments: [Installment!]!
}

type Validator {
  regex: String
  mask: String
  cardCodeRegex: String
  cardCodeMask: String
  weights: [Int]
  useCvv: Boolean
  useExpirationDate: Boolean
  useCardHolderName: Boolean
  useBillingAddress: Boolean
}

type PaymentSystem {
  id: String!
  name: String!
  groupName: String!
  validator: Validator
  stringId: String!
  requiresDocument: Boolean!
  isCustom: Boolean!
  description: String
  requiresAuthentication: Boolean
  dueDate: String
}

type Payment {
  paymentSystem: String
  bin: String
  accountId: String
  tokenId: String
  installments: Int
  referenceValue: Float
  value: Float
}

type AvailableAccount {
  accountId: String!
  paymentSystem: String!
  paymentSystemName: String!
  cardNumber: String!
  bin: String!
}

type PaymentData {
  installmentOptions: [InstallmentOption!]!
  paymentSystems: [PaymentSystem!]!
  payments: [Payment!]!
  availableAccounts: [AvailableAccount!]!
  isValid: Boolean!
}

type SavePaymentTokenPayload {
  status: String
}

input PaymentInput {
  hasDefaultBillingAddress: Boolean
  installmentsInterestRate: Int
  paymentSystem: String
  bin: String
  accountId: String
  tokenId: String
  installments: Int
  referenceValue: Int
  value: Int
}

input PaymentDataInput {
  payments: [PaymentInput!]!
}

input PaymentToken {
  creditCardToken: String!
  paymentSystem: String!
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/graphql/types/ClientProfile.graphql from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```graphql
type ClientData {
  email: String
  firstName: String
  lastName: String
  document: String
  documentType: String
  phone: String
  corporateName: String
  tradeName: String
  corporateDocument: String
  stateInscription: String
  corporatePhone: String
  isCorporate: Boolean
  profileCompleteOnLoading: Boolean
  profileErrorOnLoading: Boolean
  customerClass: String
  isValid: Boolean!
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/graphql/types/Shipping.graphql from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```graphql
type Shipping {
  countries: [String]
  deliveryOptions: [DeliveryOption!]!
  pickupOptions: [PickupOption!]!
  selectedAddress: Address
  availableAddresses: [Address]
  isValid: Boolean!
}

type DeliveryOption {
  id: String!
  deliveryChannel: String!
  price: Int!
  estimate: String!
  isSelected: Boolean!
}

type PickupOption {
  id: String!
  address: Address!
  deliveryChannel: String!
  price: Int!
  estimate: String!
  isSelected: Boolean!
  friendlyName: String! @translatableV2
  additionalInfo: String @translatableV2
  storeDistance: Float
  transitTime: String
  businessHours: [BusinessHour!]!
}

type BusinessHour {
  dayNumber: String!
  closed: Boolean!
  closingTime: String!
  openingTime: String!
}

type Address {
  addressId: ID
  addressType: AddressType
  city: String
  complement: String
  country: String
  geoCoordinates: [Float]
  neighborhood: String
  number: String
  postalCode: String
  receiverName: String
  reference: String
  state: String
  street: String
  isDisposable: Boolean
}

input AddressInput {
  addressId: ID
  addressType: AddressType
  addressQuery: String
  city: String
  complement: String
  country: String
  geoCoordinates: [Float]
  neighborhood: String
  number: String
  postalCode: String
  receiverName: String
  reference: String
  state: String
  street: String
  isDisposable: Boolean
}

input ShippingItem {
  id: String
  quantity: String
  seller: String
}

enum AddressType {
  residential
  commercial
  inStore
  giftRegistry
  pickup
  search
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/directives/withOwnerId.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { defaultFieldResolver, GraphQLField } from 'graphql'
import { SchemaDirectiveVisitor } from 'graphql-tools'

import { getOwnerIdFromCookie } from '../utils'

export class WithOwnerId extends SchemaDirectiveVisitor {
  public visitFieldDefinition(field: GraphQLField<any, any>) {
    const { resolve = defaultFieldResolver } = field
    field.resolve = async (root: any, args: any, ctx: Context, info: any) => {
      const checkoutOwnerId = getOwnerIdFromCookie(ctx.cookies)
      ctx.vtex.ownerId = checkoutOwnerId
      return resolve(root, args, ctx, info)
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/directives/authorization.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { defaultFieldResolver, GraphQLField, GraphQLResolveInfo } from 'graphql'
import { SchemaDirectiveVisitor } from 'graphql-tools'

function checkForAuthorization(ctx: any, info: GraphQLResolveInfo) {
  const {
    request: {
      headers: {
        'user-agent': userAgent,
        'x-forwarded-for': forwardedFor,
        'x-forwarded-host': forwardedHost,
        'x-forwarded-path': forwardedPath,
        'x-vtex-caller': vtexCaller,
        origin,
        Authorization,
      },
    },
    vtex: { logger, adminUserAuthToken, storeUserAuthToken },
  } = ctx

  const vtexIdToken =
    ctx.cookies.get('VtexIdclientAutCookie') ?? ctx.get('VtexIdclientAutCookie')

  if (!vtexIdToken && Math.floor(Math.random() * 100) === 0) {
    logger.warn({
      message: 'Private route being accessed by unauthorized user',
      userAgent,
      forwardedFor,
      forwardedHost,
      forwardedPath,
      origin,
      vtexCaller,
      Authorization,
      adminUserAuthToken,
      storeUserAuthToken,
      queryName: info?.fieldName,
      queryParentType: info?.parentType.name,
      queryReturnType: info?.returnType.toString(),
    })
  }

  const vtexRCSessionIdv7 =
    ctx.cookies.get('VtexRCSessionIdv7') ?? ctx.get('VtexRCSessionIdv7')

  if (!vtexRCSessionIdv7) {
    logger.warn({
      message: 'No RCSessionIdv7 cookie found',
      userAgent,
      forwardedFor,
      forwardedHost,
      forwardedPath,
      origin,
      vtexCaller,
    })
  }
}

export class AuthorizationMetrics extends SchemaDirectiveVisitor {
  public visitFieldDefinition(field: GraphQLField<any, any>) {
    const { resolve = defaultFieldResolver } = field
    field.resolve = async (root, args, ctx, info) => {
      checkForAuthorization(ctx, info)

      return resolve(root, args, ctx, info)
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/directives/withSegment.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { defaultFieldResolver, GraphQLField } from 'graphql'
import { SchemaDirectiveVisitor } from 'graphql-tools'
import atob from 'atob'

/**
 * Warning: we stopped using the segment client to decode the segment token for us because it added unnecessary overhead for now.
 * If the getSegment API evolves to do more than a decode we must stop decoding it here and start calling the API once again.
 */
export class WithSegment extends SchemaDirectiveVisitor {
  public visitFieldDefinition(field: GraphQLField<any, any>) {
    const { resolve = defaultFieldResolver } = field
    field.resolve = async (root, args, ctx: Context, info) => {
      const {
        vtex: { segmentToken },
        clients: { segment },
      } = ctx

      ctx.vtex.segment = segmentToken
        ? JSON.parse(atob(segmentToken))
        : await segment.getSegment()

      return resolve(root, args, ctx, info)
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/directives/withOrderFormId.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { defaultFieldResolver, GraphQLField } from 'graphql'
import { SchemaDirectiveVisitor } from 'graphql-tools'

import { getOrderFormIdFromCookie } from '../utils'

export class WithOrderFormId extends SchemaDirectiveVisitor {
  public visitFieldDefinition(field: GraphQLField<any, any>) {
    const { resolve = defaultFieldResolver } = field
    field.resolve = async (root: any, args: any, ctx: Context, info: any) => {
      const checkoutOrderFormId = getOrderFormIdFromCookie(ctx.cookies)
      ctx.vtex.orderFormId = checkoutOrderFormId
      return resolve(root, args, ctx, info)
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/directives/noCache.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript

import { defaultFieldResolver, GraphQLField } from 'graphql'
import { SchemaDirectiveVisitor } from 'graphql-tools'

export class NoCache extends SchemaDirectiveVisitor {
  public visitFieldDefinition(field: GraphQLField<any, any>) {
    const { resolve = defaultFieldResolver } = field
    field.resolve = async (root: any, args: any, ctx: Context, info: any) => {
      const {
        graphql: { cacheControl }
      } = ctx

      cacheControl.noCache = true
      cacheControl.noStore = true

      return resolve(root, args, ctx, info)
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/directives/index.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { AuthorizationMetrics } from './authorization'
import { WithSegment } from './withSegment'
import { WithOrderFormId } from './withOrderFormId'
import { NoCache } from './noCache'
import { WithOwnerId } from './withOwnerId'

export const schemaDirectives = {
  withAuthMetrics: AuthorizationMetrics,
  withSegment: WithSegment,
  withOrderFormId: WithOrderFormId,
  withOwnerId: WithOwnerId,
  noCache: NoCache,
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/clients/countryDataSettings.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { InstanceOptions, IOContext, AppClient } from '@vtex/api'

export class CountryDataSettings extends AppClient {
  constructor(ctx: IOContext, options?: InstanceOptions) {
    super('vtex.country-data-settings@0.x', ctx, {
      ...options,
      headers: {
        ...options?.headers,
        ...(ctx.storeUserAuthToken
          ? { VtexIdclientAutCookie: ctx.storeUserAuthToken }
          : null),
      },
    })
  }

  public getCountrySettings(
    country: string
  ): Promise<CountryDataSchema | null> {
    return this.http.get(`/country-settings/${country}`)
  }

  public getAllCountriesSettings(): Promise<CountryDataSchema[]> {
    return this.http.get('/country-settings/')
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/clients/graphqlServer.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  AppClient,
  GraphQLClient,
  InstanceOptions,
  IOContext,
  RequestConfig,
  Serializable,
} from '@vtex/api'

export class GraphQLServer extends AppClient {
  protected graphql: GraphQLClient

  constructor(ctx: IOContext, opts?: InstanceOptions) {
    super('vtex.graphql-server@1.x', ctx, opts)
    this.graphql = new GraphQLClient(this.http)
  }

  public query = async <TResponse extends Serializable, TArgs extends object>(
    query: string,
    variables: any,
    extensions: any,
    config: RequestConfig
  ) => {
    return this.graphql.query<TResponse, TArgs>(
      {
        extensions,
        query,
        variables,
      },
      {
        ...config,
        params: {
          ...config.params,
          locale: this.context.locale,
        },
        url: '/graphql',
      }
    )
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/clients/checkout.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  InstanceOptions,
  IOContext,
  IOResponse,
  JanusClient,
  RequestConfig,
} from '@vtex/api'
import { UserProfileInput } from 'vtex.checkout-graphql'
import { OWNERSHIP_COOKIE } from '../constants'
import { forwardCheckoutCookies } from '../resolvers/orderForm'

import { checkoutCookieFormat, ownershipCookieFormat, statusToError } from '../utils'

export interface SimulationData {
  country: string
  items: Array<{ id: string; quantity: number | string; seller: string }>
  postalCode?: string
  isCheckedIn?: boolean
  priceTables?: string[]
  marketingData?: Record<string, string>
}

export class Checkout extends JanusClient {
  constructor(ctx: IOContext, options?: InstanceOptions) {
    super(ctx, {
      ...options,
      headers: {
        ...options?.headers,
        ...(ctx.storeUserAuthToken
          ? { VtexIdclientAutCookie: ctx.storeUserAuthToken }
          : null),
      },
    })
  }

  public savePaymentToken = (paymentTokens: any) => {
    return this.post(
      this.routes.savePaymentToken(this.getChannelQueryString()),
      paymentTokens,
      { metric: 'checkout-save-payment' }
    )
  }

  public getPaymentSession = () => {
    return this.get<PaymentSession>(this.routes.getPaymentSession())
  }

  public addItem = (
    orderFormId: string,
    items: Array<Omit<OrderFormItemInput, 'uniqueId' | 'index' | 'options'>>,
    salesChannel?: string,
    allowedOutdatedData?: string[]
  ) =>
    this.patch<CheckoutOrderForm>(
      this.routes.addItem(
        orderFormId,
        this.getChannelQueryString(salesChannel)
      ),
      {
        orderItems: items,
        allowedOutdatedData,
      },
      { metric: 'checkout-addItem' }
    )

  public cancelOrder = (orderFormId: string, reason: string) =>
    this.post(
      this.routes.cancelOrder(orderFormId),
      { reason },
      { metric: 'checkout-cancelOrder' }
    )

  public setOrderFormCustomData = (
    orderFormId: string,
    appId: string,
    field: string,
    value: any
  ) =>
    this.put(
      this.routes.orderFormCustomData(orderFormId, appId, field),
      { value },
      { metric: 'checkout-setOrderFormCustomData' }
    )

  public updateItems = (
    orderFormId: string,
    orderItems: Array<Omit<OrderFormItemInput, 'id'>>,
    splitItem: boolean,
    allowedOutdatedData?: string[]
  ) =>
    this.patch<CheckoutOrderForm>(
      this.routes.addItem(orderFormId, this.getChannelQueryString(undefined)),
      {
        orderItems,
        noSplitItem: !splitItem,
        allowedOutdatedData,
      },
      { metric: 'checkout-updateItems' }
    )

  public updateOrderFormIgnoreProfile = (
    orderFormId: string,
    ignoreProfileData: boolean
  ) =>
    this.patch(
      this.routes.orderFormProfile(orderFormId),
      { ignoreProfileData },
      { metric: 'checkout-updateOrderFormIgnoreProfile' }
    )

  public updateOrderFormPayment = (
    orderFormId: string,
    paymentData: PaymentDataInput
  ) =>
    this.post<CheckoutOrderForm>(
      this.routes.attachmentsData(orderFormId, 'paymentData'),
      paymentData,
      { metric: 'checkout-updateOrderFormPayment' }
    )

  public updateOrderFormProfile = async (
    orderFormId: string,
    fields: UserProfileInput,
    ctx: Context,
  ) => {
    const { data, headers } = await this.postRaw<CheckoutOrderForm>(
      this.routes.attachmentsData(orderFormId, 'clientProfileData'),
      fields,
      { metric: 'checkout-updateOrderFormProfile' }
    )
    forwardCheckoutCookies(headers, ctx, [OWNERSHIP_COOKIE])
    return data
  }

  public updateOrderFormShipping = (orderFormId: string, shipping: any) =>
    this.post<CheckoutOrderForm>(
      this.routes.attachmentsData(orderFormId, 'shippingData'),
      shipping,
      { metric: 'checkout-updateOrderFormShipping' }
    )

  public updateOrderFormMarketingData = (
    orderFormId: string,
    marketingData: any
  ) =>
    this.post<CheckoutOrderForm>(
      this.routes.attachmentsData(orderFormId, 'marketingData'),
      marketingData,
      { metric: 'checkout-updateOrderFormMarketingData' }
    )

  public updateOrderFormClientPreferencesData = (
    orderFormId: string,
    clientPreferencesData: CheckoutClientPreferencesData
  ) => {
    // The API default value of `optinNewsLetter` is `null`, but it doesn't accept a POST with its value as `null`
    const filteredClientPreferencesData =
      clientPreferencesData.optinNewsLetter === null
        ? { locale: clientPreferencesData.locale }
        : clientPreferencesData

    return this.post<CheckoutOrderForm>(
      this.routes.attachmentsData(orderFormId, 'clientPreferencesData'),
      filteredClientPreferencesData,
      { metric: 'checkout-updateOrderFormClientPreferencesData' }
    )
  }

  public updateOrderFromOpenTextField = (
    orderFromId: string,
    openTextField: OpenTextField
  ) =>
    this.post<CheckoutOrderForm>(
      this.routes.attachmentsData(orderFromId, 'openTextField'),
      openTextField,
      { metric: 'checkout-updateOrderFromOpenTextField' }
    )

  public updateSubscriptionDataField = (
    orderFormId: string,
    subscriptionData: SubscriptionData
  ) =>
    this.post<CheckoutOrderForm>(
      this.routes.attachmentsData(orderFormId, 'subscriptionData'),
      subscriptionData,
      { metric: 'checkout-updateSubscriptionDataField' }
    )

  public addAssemblyOptions = async (
    orderFormId: string,
    itemId: string | number,
    assemblyOptionsId: string,
    body: any
  ) =>
    this.post(
      this.routes.assemblyOptions(orderFormId, itemId, assemblyOptionsId),
      body,
      { metric: 'checkout-addAssemblyOptions' }
    )

  public removeAssemblyOptions = async (
    orderFormId: string,
    itemId: string | number,
    assemblyOptionsId: string,
    body: any
  ) =>
    this.delete(
      this.routes.assemblyOptions(orderFormId, itemId, assemblyOptionsId),
      { metric: 'checkout-removeAssemblyOptions', data: body }
    )

  public addItemOffering = async (
    orderFormId: string,
    itemIndex: number,
    offeringId: string,
    offeringInfo?: any
  ) =>
    this.post<CheckoutOrderForm>(
      this.routes.offering(orderFormId, itemIndex),
      { id: offeringId, offeringInfo },
      { metric: 'checkout-addItemOffering' }
    )

  public removeItemOffering = async (
    orderFormId: string,
    itemIndex: number,
    offeringId: string
  ) =>
    this.post<CheckoutOrderForm>(
      this.routes.removeOffering(orderFormId, itemIndex, offeringId),
      { Id: offeringId },
      { metric: 'checkout-removeItemOffering' }
    )

  public addBundleItemAttachment = async (
    orderFormId: string,
    itemIndex: number,
    bundleItemId: string,
    attachmentName: string,
    attachmentContent: any
  ) =>
    this.post<CheckoutOrderForm>(
      this.routes.bundleItemAttachment(
        orderFormId,
        itemIndex,
        bundleItemId,
        attachmentName
      ),
      { content: attachmentContent },
      { metric: 'checkout-addBundleItemAttachment' }
    )

  public removeBundleItemAttachment = async (
    orderFormId: string,
    itemIndex: number,
    bundleItemId: string,
    attachmentName: string,
    attachmentContent: any
  ) =>
    this.delete<CheckoutOrderForm>(
      this.routes.bundleItemAttachment(
        orderFormId,
        itemIndex,
        bundleItemId,
        attachmentName
      ),
      {
        metric: 'checkout-removeBundleItemAttachment',
        data: { content: attachmentContent },
      }
    )

  public updateOrderFormCheckin = (orderFormId: string, checkinPayload: any) =>
    this.post(this.routes.checkin(orderFormId), checkinPayload, {
      metric: 'checkout-updateOrderFormCheckin',
    })

  public orderForm = (orderFormId: string, refreshOutdatedData = false) => {
    return this.post<CheckoutOrderForm>(
      this.routes.orderForm(
        orderFormId,
        this.getOrderFormQueryString(refreshOutdatedData)
      ),
      {},
      { metric: 'checkout-orderForm' }
    )
  }

  public orderFormRaw = (orderFormId?: string, refreshOutdatedData = false) => {
    return this.postRaw<CheckoutOrderForm>(
      this.routes.orderForm(
        orderFormId,
        this.getOrderFormQueryString(refreshOutdatedData)
      ),
      {},
      { metric: 'checkout-orderForm' }
    )
  }

  public orders = () =>
    this.get(this.routes.orders, { metric: 'checkout-orders' })

  public simulation = (simulation: SimulationData) =>
    this.post(
      this.routes.simulation(this.getChannelQueryString()),
      simulation,
      {
        metric: 'checkout-simulation',
      }
    )

  public insertCoupon = (orderFormId: string, coupon: string) =>
    this.post<CheckoutOrderForm>(this.routes.insertCoupon(orderFormId), {
      text: coupon,
    })

  public clearMessages = (orderFormId: string) =>
    this.post<CheckoutOrderForm>(this.routes.clearMessages(orderFormId), {})

  public getProfile = (email: string) =>
    this.get<CheckoutProfile>(this.routes.profile(email))

  public updateItemsOrdination = (
    orderFormId: string,
    ascending: boolean,
    criteria: string
  ) =>
    this.post<CheckoutOrderForm>(
      this.routes.updateItemsOrdination(orderFormId),
      {
        ascending,
        criteria,
      },
      { metric: 'checkout-orderForm' }
    )

  protected get = <T>(url: string, config: RequestConfig = {}) => {
    config.headers = {
      ...config.headers,
      ...this.getCommonHeaders(),
    }
    return this.http.get<T>(url, config).catch(statusToError) as Promise<T>
  }

  protected post = <T>(url: string, data?: any, config: RequestConfig = {}) => {
    config.headers = {
      ...config.headers,
      ...this.getCommonHeaders(),
    }
    return this.http.post<T>(url, data, config).catch(statusToError) as Promise<
      T
    >
  }

  protected postRaw = async <T>(
    url: string,
    data?: any,
    config: RequestConfig = {}
  ) => {
    config.headers = {
      ...config.headers,
      ...this.getCommonHeaders(),
    }
    return this.http
      .postRaw<T>(url, data, config)
      .catch(statusToError) as Promise<IOResponse<T>>
  }

  protected delete = <T>(url: string, config: RequestConfig = {}) => {
    config.headers = {
      ...config.headers,
      ...this.getCommonHeaders(),
    }
    return this.http.delete<T>(url, config).catch(statusToError) as Promise<
      IOResponse<T>
    >
  }

  protected patch = <T>(
    url: string,
    data?: any,
    config: RequestConfig = {}
  ) => {
    config.headers = {
      ...config.headers,
      ...this.getCommonHeaders(),
    }
    return this.http
      .patch<T>(url, data, config)
      .catch(statusToError) as Promise<T>
  }

  protected put = <T>(url: string, data?: any, config: RequestConfig = {}) => {
    config.headers = {
      ...config.headers,
      ...this.getCommonHeaders(),
    }
    return this.http.put<T>(url, data, config).catch(statusToError) as Promise<
      T
    >
  }

  private getCommonHeaders = () => {
    const { orderFormId, ownerId } = (this.context as unknown) as CustomIOContext
    const checkoutCookie = orderFormId ? checkoutCookieFormat(orderFormId) : ''
    const ownershipCookie = ownerId ? ownershipCookieFormat(ownerId) : ''
    return {
      Cookie: `${checkoutCookie}${ownershipCookie}vtex_segment=${this.context.segmentToken};vtex_session=${this.context.sessionToken};`,
    }
  }

  private getChannelQueryString = (salesChannel?: string) => {
    const { segment } = (this.context as unknown) as CustomIOContext
    const channel = salesChannel ?? segment?.channel
    const queryString = channel ? `?sc=${channel}` : ''
    return queryString
  }

  private getOrderFormQueryString = (refreshOutdatedData?: boolean) => {
    if (refreshOutdatedData)
      return `?refreshOutdatedData=${refreshOutdatedData}`

    return ''
  }

  private get routes() {
    const base = '/api/checkout/pub'
    return {
      addItem: (orderFormId: string, queryString: string) =>
        `${base}/orderForm/${orderFormId}/items${queryString}`,
      assemblyOptions: (
        orderFormId: string,
        itemId: string | number,
        assemblyOptionsId: string
      ) =>
        `${base}/orderForm/${orderFormId}/items/${itemId}/assemblyOptions/${encodeURI(
          assemblyOptionsId
        )}`,
      attachmentsData: (orderFormId: string, field: string) =>
        `${base}/orderForm/${orderFormId}/attachments/${field}`,
      cancelOrder: (orderFormId: string) =>
        `${base}/orders/${orderFormId}/user-cancel-request`,
      checkin: (orderFormId: string) =>
        `${base}/orderForm/${orderFormId}/checkIn`,
      clearMessages: (orderFormId: string) =>
        `${base}/orderForm/${orderFormId}/messages/clear`,
      insertCoupon: (orderFormId: string) =>
        `${base}/orderForm/${orderFormId}/coupons`,
      orderForm: (orderFormId?: string, queryString?: string) =>
        `${base}/orderForm/${orderFormId ?? ''}${queryString}`,
      orderFormCustomData: (
        orderFormId: string,
        appId: string,
        field: string
      ) => `${base}/orderForm/${orderFormId}/customData/${appId}/${field}`,
      orders: `${base}/orders`,
      orderFormProfile: (orderFormId: string) =>
        `${base}/orderForm/${orderFormId}/profile`,
      profile: (email: string) => `${base}/profiles/?email=${email}`,
      simulation: (queryString: string) =>
        `${base}/orderForms/simulation${queryString}`,
      updateItems: (orderFormId: string) =>
        `${base}/orderForm/${orderFormId}/items/update`,
      offering: (orderFormId: string, itemIndex: number) =>
        `${base}/orderForm/${orderFormId}/items/${itemIndex}/offerings`,
      removeOffering: (
        orderFormId: string,
        itemIndex: number,
        offeringId: string
      ) =>
        `${base}/orderForm/${orderFormId}/items/${itemIndex}/offerings/${offeringId}/remove`,
      bundleItemAttachment: (
        orderFormId: string,
        itemIndex: number,
        bundleItemId: string,
        attachmentName: string
      ) =>
        `${base}/orderForm/${orderFormId}/items/${itemIndex}/bundles/${bundleItemId}/attachments/${attachmentName}`,
      savePaymentToken: (queryString: string) =>
        `${base}/current-user/payment-tokens/${queryString}`,
      getPaymentSession: () => `${base}/payment-session`,
      updateItemsOrdination: (orderFormId: string) =>
        `${base}/orderForm/${orderFormId}/itemsOrdination`,
    }
  }
}

export class CheckoutNoCookies extends Checkout {
  constructor(ctx: IOContext, options?: InstanceOptions) {
    super({ ...ctx, orderFormId: null, ownerId: null } as any, { ...options, headers: {} })
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/clients/checkoutAdmin.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  InstanceOptions,
  IOContext,
  JanusClient,
  RequestConfig,
} from '@vtex/api'

import { checkoutCookieFormat, statusToError } from '../utils'

export interface SimulationData {
  country: string
  items: Array<{ id: string; quantity: number | string; seller: string }>
  postalCode?: string
  isCheckedIn?: boolean
  priceTables?: string[]
  marketingData?: Record<string, string>
}

export class CheckoutAdmin extends JanusClient {
  constructor(ctx: IOContext, options?: InstanceOptions) {
    super(ctx, {
      ...options,
      headers: {
        ...options?.headers,
        ...(ctx.adminUserAuthToken
          ? { VtexIdClientAutCookie: ctx.adminUserAuthToken }
          : null),
      },
    })
  }

  public orderForm = () => {
    return this.post<CheckoutOrderForm>(
      this.routes.orderForm,
      {},
      { metric: 'checkout-orderForm' }
    )
  }

  public setManualPrice = (
    orderFormId: string,
    itemIndex: number,
    price: number
  ) =>
    this.put<CheckoutOrderForm>(
      this.routes.setManualPrice(orderFormId, itemIndex),
      {
        price,
      }
    )

  protected post = <T>(url: string, data?: any, config: RequestConfig = {}) => {
    config.headers = {
      ...config.headers,
      ...this.getCommonHeaders(),
    }
    return this.http.post<T>(url, data, config).catch(statusToError) as Promise<
      T
    >
  }

  protected put = <T>(url: string, data?: any, config: RequestConfig = {}) => {
    config.headers = {
      ...config.headers,
      ...this.getCommonHeaders(),
    }
    return this.http.put<T>(url, data, config).catch(statusToError) as Promise<
      T
    >
  }

  private getCommonHeaders = () => {
    const { orderFormId } = (this.context as unknown) as CustomIOContext
    const checkoutCookie = orderFormId ? checkoutCookieFormat(orderFormId) : ''
    return {
      Cookie: `${checkoutCookie}vtex_segment=${this.context.segmentToken};vtex_session=${this.context.sessionToken};`,
    }
  }

  private get routes() {
    const base = '/api/checkout/pub'
    return {
      orderForm: `${base}/orderForm`,
      setManualPrice: (orderFormId: string, itemIndex: number) =>
        `${base}/orderForm/${orderFormId}/items/${itemIndex}/price`,
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/clients/session.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { JanusClient } from '@vtex/api'
import parseCookie from 'set-cookie-parser'

interface VtexIdCookies {
  account: string | null
  id: string | null
}

const SESSION_COOKIE = 'vtex_session'

const routes = {
  base: '/api/sessions',
}

export class Session extends JanusClient {
  /**
   * Get the session data using the given token
   */
  public getSession = async (token: string, items: string[]) => {
    const { data: sessionData, headers } = await this.http.getRaw(routes.base, {
      headers: {
        'Content-Type': 'application/json',
        Cookie: `vtex_session=${token};`,
      },
      metric: 'session-get',
      params: {
        items: items.join(','),
      },
    })

    return {
      sessionData,
      sessionToken: extractSessionCookie(headers) ?? token,
    }
  }

  /**
   * Update the public portion of this session
   */
  public updateSession = (
    key: string,
    value: any,
    items: string[],
    token: any,
    vtexIdCookies: VtexIdCookies
  ) => {
    const data = { public: { [key]: { value } } }
    let cookies = `vtex_session=${token};`
    if (vtexIdCookies.account) {
      cookies += `${vtexIdCookies.account};`
    }
    if (vtexIdCookies.id) {
      cookies += `${vtexIdCookies.id};`
    }

    const config = {
      headers: {
        'Content-Type': 'application/json',
        Cookie: cookies,
      },
      metric: 'session-update',
      params: {
        items: items.join(','),
      },
    }

    return this.http.post(routes.base, data, config)
  }
}

function extractSessionCookie(headers: Record<string, string>) {
  for (const setCookie of headers['set-cookie'] ?? []) {
    const parsedSetCookies = parseCookie.parse(setCookie)
    for (const parsed of parsedSetCookies) {
      if (parsed.name === SESSION_COOKIE) {
        return parsed.value
      }
    }
  }

  return null
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/clients/index.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { IOClients } from '@vtex/api'

import { Checkout, CheckoutNoCookies } from './checkout'
import { SearchGraphQL } from './searchGraphQL'
import { Session } from './session'
import { CountryDataSettings } from './countryDataSettings'
import { CheckoutAdmin } from './checkoutAdmin'

export class Clients extends IOClients {
  public get checkout() {
    return this.getOrSet('checkout', Checkout)
  }

  public get checkoutNoCookies() {
    return this.getOrSet('checkoutNoCookies', CheckoutNoCookies)
  }

  public get searchGraphQL() {
    return this.getOrSet('searchGraphQL', SearchGraphQL)
  }

  public get customSession() {
    return this.getOrSet('customSession', Session)
  }

  public get countryDataSettings() {
    return this.getOrSet('countryDataSettings', CountryDataSettings)
  }

  public get checkoutAdmin() {
    return this.getOrSet('checkoutAdmin', CheckoutAdmin)
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/__fixtures__/shipping.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { AddressType, DELIVERY, PICKUP_IN_POINT } from '../constants'
import { EMPTY_ORDER_FORM } from './orderForm'
import { Clients } from '../clients'

const SLA = {
  deliveryIds: [],
  deliveryWindow: null,
  listPrice: 0,
  lockTTL: null,
  pickupDistance: 0,
  pickupPointId: null,
  pickupStoreInfo: {
    additionalInfo: null,
    address: null,
    dockId: null,
    friendlyName: null,
    isPickupStore: false,
  },
  polygonName: null,
  shippingEstimateDate: null,
  tax: 0,
}

const pickupSLA = {
  ...SLA,
  availableDeliveryWindows: [],
  deliveryChannel: PICKUP_IN_POINT,
  id: 'pickup-SLA',
  name: 'pickup-SLA',
  price: 100,
  shippingEstimate: '1d',
}

const deliverySLA = {
  ...SLA,
  availableDeliveryWindows: [],
  deliveryChannel: DELIVERY,
  id: 'delivery-SLA',
  name: 'delivery-SLA',
  price: 100,
  shippingEstimate: '1db',
}

const deliveryIdOne = {
  courierId: '1',
  warehouseId: '1',
  dockId: '1',
  courierName: 'PAC',
  quantity: 1,
}

const deliveryId = {
  courierId: '1',
  warehouseId: '1a105fe',
  dockId: '1fd8f86',
  courierName: 'PAC',
  quantity: 1,
}

const deliverySLAWithDeliveryIdOne = {
  ...SLA,
  availableDeliveryWindows: [],
  deliveryChannel: DELIVERY,
  id: 'delivery-SLA',
  name: 'delivery-SLA',
  price: 100,
  shippingEstimate: '1db',
  deliveryIds: [deliveryIdOne],
}

const deliverySLAWithDeliveryId = {
  ...SLA,
  availableDeliveryWindows: [],
  deliveryChannel: DELIVERY,
  id: 'delivery-SLA',
  name: 'delivery-SLA',
  price: 100,
  shippingEstimate: '1db',
  deliveryIds: [deliveryId],
}

const deliverySLAExpress = {
  ...SLA,
  availableDeliveryWindows: [],
  deliveryChannel: DELIVERY,
  id: 'delivery-SLA-Express',
  name: 'delivery-SLA-Express',
  price: 600,
  shippingEstimate: '1h',
}

const scheduledDeliverySLA = {
  ...SLA,
  availableDeliveryWindows: [{}],
  deliveryChannel: DELIVERY,
  id: 'scheduled-delivery-SLA',
  name: 'scheduled-delivery-SLA',
  price: 100,
  shippingEstimate: '1bd',
}

export const deliveryAddress = {
  addressId: 'test',
  addressType: AddressType.RESIDENTIAL,
  city: '',
  complement: '',
  country: 'BRA',
  geoCoordinates: [],
  neighborhood: '',
  number: '',
  postalCode: '222071060',
  receiverName: '',
  reference: null,
  state: '',
  street: '',
}

export const ORDER_FORM_WITH_PICKUPS = {
  ...EMPTY_ORDER_FORM,
  shippingData: {
    address: null,
    availableAddresses: [deliveryAddress],
    logisticsInfo: [
      {
        addressId: deliveryAddress.addressId,
        deliveryChannels: [{ id: DELIVERY }, { id: PICKUP_IN_POINT }],
        itemId: 'testId',
        itemIndex: 0,
        selectedDeliveryChannel: DELIVERY,
        selectedSla: deliverySLA.id,
        shipsTo: ['BRA', 'GBR'],
        slas: [deliverySLA, pickupSLA],
      },
    ],
    pickupPoints: [],
    selectedAddresses: [deliveryAddress],
  },
}

export const ORDER_FORM_WITH_SCHEDULED_DELIVERY = {
  ...EMPTY_ORDER_FORM,
  shippingData: {
    address: null,
    availableAddresses: [deliveryAddress],
    logisticsInfo: [
      {
        addressId: deliveryAddress.addressId,
        deliveryChannels: [{ id: DELIVERY }, { id: PICKUP_IN_POINT }],
        itemId: 'testId',
        itemIndex: 0,
        selectedDeliveryChannel: DELIVERY,
        selectedSla: deliverySLA.id,
        shipsTo: ['BRA', 'GBR'],
        slas: [deliverySLA, scheduledDeliverySLA],
      },
    ],
    pickupPoints: [],
    selectedAddresses: [deliveryAddress],
  },
}

export const ORDER_FORM_WITH_SCHEDULED_DELIVERY_AND_PICKUPS = {
  ...EMPTY_ORDER_FORM,
  shippingData: {
    address: null,
    availableAddresses: [deliveryAddress],
    logisticsInfo: [
      {
        addressId: deliveryAddress.addressId,
        deliveryChannels: [{ id: DELIVERY }, { id: PICKUP_IN_POINT }],
        itemId: 'testId',
        itemIndex: 0,
        selectedDeliveryChannel: DELIVERY,
        selectedSla: deliverySLA.id,
        shipsTo: ['BRA', 'GBR'],
        slas: [deliverySLA, scheduledDeliverySLA, pickupSLA],
      },
    ],
    pickupPoints: [],
    selectedAddresses: [deliveryAddress],
  },
}

export const ORDER_FORM_WITH_DIFFERENT_SLAS_BETWEEN_LOGISTICS_INFO = {
  ...EMPTY_ORDER_FORM,
  shippingData: {
    address: null,
    availableAddresses: [deliveryAddress],
    logisticsInfo: [
      {
        addressId: deliveryAddress.addressId,
        deliveryChannels: [{ id: DELIVERY }, { id: PICKUP_IN_POINT }],
        itemId: 'testId',
        itemIndex: 0,
        selectedDeliveryChannel: DELIVERY,
        selectedSla: deliverySLA.id,
        shipsTo: ['BRA', 'GBR'],
        slas: [deliverySLA],
      },
      {
        addressId: deliveryAddress.addressId,
        deliveryChannels: [{ id: DELIVERY }, { id: PICKUP_IN_POINT }],
        itemId: 'testId',
        itemIndex: 1,
        selectedDeliveryChannel: DELIVERY,
        selectedSla: deliverySLA.id,
        shipsTo: ['BRA', 'GBR'],
        slas: [deliverySLA, deliverySLAExpress],
      },
    ],
    pickupPoints: [],
    selectedAddresses: [deliveryAddress],
  },
}

export const ORDER_FORM_WITH_DUPLICATED_SLAS_WITH_DIFFERENT_DELIVERY_IDS = {
  ...EMPTY_ORDER_FORM,
  shippingData: {
    address: null,
    availableAddresses: [deliveryAddress],
    logisticsInfo: [
      {
        addressId: deliveryAddress.addressId,
        deliveryChannels: [{ id: DELIVERY }, { id: PICKUP_IN_POINT }],
        itemId: 'testId',
        itemIndex: 0,
        selectedDeliveryChannel: DELIVERY,
        selectedSla: deliverySLA.id,
        shipsTo: ['BRA', 'GBR'],
        slas: [deliverySLAWithDeliveryId],
      },
      {
        addressId: deliveryAddress.addressId,
        deliveryChannels: [{ id: DELIVERY }, { id: PICKUP_IN_POINT }],
        itemId: 'testId',
        itemIndex: 1,
        selectedDeliveryChannel: DELIVERY,
        selectedSla: deliverySLA.id,
        shipsTo: ['BRA', 'GBR'],
        slas: [deliverySLAWithDeliveryIdOne, deliverySLAExpress],
      },
    ],
    pickupPoints: [],
    selectedAddresses: [deliveryAddress],
  },
}

export const ORDER_FORM_WITH_EMPTY_LOGISTICS_INFO = {
  ...EMPTY_ORDER_FORM,
  shippingData: {
    address: null,
    availableAddresses: [deliveryAddress],
    logisticsInfo: [
      {
        addressId: deliveryAddress.addressId,
        deliveryChannels: [{ id: DELIVERY }, { id: PICKUP_IN_POINT }],
        itemId: 'testId',
        itemIndex: 0,
        selectedDeliveryChannel: DELIVERY,
        selectedSla: null,
        shipsTo: ['BRA', 'GBR'],
        slas: [],
      },
      {
        addressId: deliveryAddress.addressId,
        deliveryChannels: [{ id: DELIVERY }, { id: PICKUP_IN_POINT }],
        itemId: 'testId',
        itemIndex: 1,
        selectedDeliveryChannel: DELIVERY,
        selectedSla: null,
        shipsTo: ['BRA', 'GBR'],
        slas: [],
      },
    ],
    pickupPoints: [],
    selectedAddresses: [deliveryAddress],
  },
}

export const ORDER_FORM_WITH_UNAVAILABLE_ITEM_LOGISTICS_INFO = {
  ...EMPTY_ORDER_FORM,
  shippingData: {
    address: null,
    availableAddresses: [deliveryAddress],
    logisticsInfo: [
      {
        addressId: deliveryAddress.addressId,
        deliveryChannels: [{ id: DELIVERY }, { id: PICKUP_IN_POINT }],
        itemId: 'testId',
        itemIndex: 0,
        selectedDeliveryChannel: DELIVERY,
        selectedSla: deliverySLA.id,
        shipsTo: ['BRA', 'GBR'],
        slas: [],
      },
      {
        addressId: deliveryAddress.addressId,
        deliveryChannels: [{ id: DELIVERY }, { id: PICKUP_IN_POINT }],
        itemId: 'testId2',
        itemIndex: 0,
        selectedDeliveryChannel: DELIVERY,
        selectedSla: deliverySLA.id,
        shipsTo: ['BRA', 'GBR'],
        slas: [deliverySLA, pickupSLA],
      },
    ],
    pickupPoints: [],
    selectedAddresses: [deliveryAddress],
  },
}

export const ORDER_FORM_WITH_EMPTY_SHIPPING_DATA = {
  ...EMPTY_ORDER_FORM,
}

export const clients = ({
  checkout: {
    updateOrderFormShipping: jest.fn(),
  },
} as unknown) as Clients

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/__fixtures__/orderForm.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
export const EMPTY_ORDER_FORM = {
  allowManualPrice: false,
  canEditData: true,
  checkedInPickupPointId: null,
  clientPreferencesData: {
    locale: 'en',
    optinNewsLetter: null,
  },
  clientProfileData: null,
  commercialConditionData: null,
  customData: null,
  giftRegistryData: null,
  hooksData: null,
  ignoreProfileData: false,
  invoiceData: null,
  isCheckedIn: false,
  itemMetadata: {
    items: [],
  },
  items: [],
  itemsOrdination: null,
  loggedIn: false,
  marketingData: null,
  messages: [],
  openTextField: null,
  orderFormId: 'test',
  paymentData: {
    availableAccounts: [],
    availableTokens: [],
    giftCardMessages: [],
    giftCards: [],
    installmentOptions: [],
    paymentSystems: [],
    payments: [],
  },
  ratesAndBenefitsData: {
    rateAndBenefitsIdentifiers: [],
    teaser: [],
  },
  salesChannel: '1',
  selectableGifts: [],
  sellers: [],
  shippingData: {
    address: null,
    availableAddresses: [],
    logisticsInfo: [],
    pickupPoints: [],
    selectedAddresses: [],
  },
  storeId: '1',
  storePreferencesData: {
    countryCode: 'BRA',
    currencyCode: 'BRL',
    currencyFormatInfo: {
      currencyDecimalDigits: 2,
      currencyDecimalSeparator: ',',
      currencyGroupSeparator: '.',
      currencyGroupSize: 3,
      startsWithCurrencySymbol: true,
    },
    currencyLocale: 'pt-BR',
    currencySymbol: 'R$',
    saveUserData: false,
    timeZone: 'test',
  },
  subscriptionData: null,
  totalizers: [],
  userProfileId: null,
  userType: null,
  value: 1200,
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/constants/index.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
export enum AddressType {
  RESIDENTIAL = 'residential',
  COMMERCIAL = 'commercial',
  INSTORE = 'inStore',
  GIFT_REGISTRY = 'giftRegistry',
  PICKUP = 'pickup',
  SEARCH = 'search',
}

// Cookies
export const CHECKOUT_COOKIE = 'checkout.vtex.com'
export const ASPXAUTH_COOKIE = '.ASPXAUTH'
export const OWNERSHIP_COOKIE = 'CheckoutOrderFormOwnership'

// Delivery channels
export const DELIVERY = 'delivery'
export const PICKUP_IN_POINT = 'pickup-in-point'

export const VTEX_SESSION = 'vtex_session'

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/typings/global.d.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ServiceContext, SegmentData, ParamsContext } from '@vtex/api'
import { Attachment } from 'vtex.checkout-graphql'

import { Clients } from '../clients'

declare global {
  type Context = ServiceContext<Clients, RecorderState, CustomContext>

  interface CustomContext extends ParamsContext {
    cookie: string
    dataSources: SearchGraphQLDataSources
    originalPath: string
    vtex: CustomIOContext
    graphql: {
      cacheControl: {
        noStore: boolean
        noCache: boolean
      }
    }
  }

  interface CustomIOContext extends IOContext {
    segment?: SegmentData
    orderFormId?: string
    ownerId?: string
  }

  interface OrderFormMarketingData {
    utmCampaign?: string
    utmMedium?: string
    utmSource?: string
    utmiCampaign?: string
    utmiPart?: string
    utmipage?: string
    marketingTags?: string
    coupon?: string
  }

  interface CheckoutAddress {
    addressId: string
    addressType: string
    city: string | null
    complement: string | null
    country: string
    geoCoordinates: number[]
    neighborhood: string | null
    number: string | null
    postalCode: string | null
    receiverName: string | null
    reference: string | null
    state: string | null
    street: string | null
    isDisposable: boolean
  }

  type AddressFields = keyof CheckoutAddress

  interface CountryDataSchema {
    countryISO: string
    addressFields: AddressFieldsSchema
    phone: PhoneSchema
  }

  interface AddressFieldsSchema {
    [field in AddressField]: AddressFieldSchema
    postalCode?: AddressFieldSchema & PostalCodeSchema
  }

  interface PostalCodeSchema {
    forgottenURL?: string
  }

  interface AddressFieldSchema {
    label: string
    name?: AddressField | 'addressType'
    hidden?: boolean
    maxLength?: number
    size?: string
    required?: boolean
    autoComplete?: string
    optionsCaption?: string
    options?: AddressOption[]
    elementName?: string
    mask?: string
  }

  interface AddressOption {
    label: string
    value: string
  }

  interface BusinessHour {
    DayOfWeek: number
    ClosingTime: string
    OpeningTime: string
  }

  interface NormalizedBusinessHour {
    dayNumber: string | number
    closed: boolean
    closingTime: string
    openingTime: string
  }

  interface PhoneSchema {
    countryCode: string
    mask?: string
    pattern: string
  }

  interface OrderFormItem {
    id: string
    name: string
    detailUrl: string
    imageUrl: string
    skuName: string
    quantity: number
    uniqueId: string
    productId: string
    refId: string
    ean: string
    priceValidUntil: string
    price: number
    tax: number
    listPrice: number
    sellingPrice: number
    rewardValue: number
    isGift: boolean
    parentItemIndex: number | null
    parentAssemblyBinding: string | null
    productCategoryIds: string
    priceTags: string[]
    manualPrice: number
    measurementUnit: string
    additionalInfo: {
      brandName: string
      brandId: string
      offeringInfo: any | null
      offeringType: any | null
      offeringTypeId: any | null
    }
    productCategories: Record<string, string>
    productSpecificationGroups: ProductSpecificationGroup[]
    productRefId: string
    seller: string
    sellerChain: string[]
    availability: string
    unitMultiplier: number
    skuSpecifications: SKUSpecification[]
    priceDefinition: {
      calculatedSellingPrice: number
      sellingPrices: SellingPrice[]
      total: number
    }
    attachments?: Attachment[]
  }

  interface SellingPrice {
    quantity: number
    value: number
  }

  interface SKUSpecification {
    fieldName: string
    fieldValues: string[]
  }

  interface ProductSpecification {
    name: string
    originalName: string
    values: string[]
  }

  interface ProductSpecificationGroup {
    name: string
    originalName: string
    specifications: ProductSpecification[]
  }

  interface CompositionItem {
    id: string
    minQuantity: number
    maxQuantity: number
    initialQuantity: number
    priceTable: string
    seller: string
  }

  interface Composition {
    minQuantity: number
    maxQuantity: number
    items: CompositionItem[]
  }

  interface AssemblyOption {
    id: string
    name: string
    composition: Composition | null
  }

  interface SubscriptionDataEntry {
    executionCount: number
    itemIndex: number
    plan: {
      frequency: {
        interval: number
        periodicity: 'YEAR' | 'MONTH' | 'WEEK' | 'DAY'
      }
      type: string
      validity: {}
    }
  }

  interface SubscriptionData {
    subscriptions: SubscriptionDataEntry[]
  }

  interface MetadataItem {
    id: string
    name: string
    imageUrl: string
    detailUrl: string
    seller: string
    assemblyOptions: AssemblyOption[]
    skuName: string
    productId: string
    refId: string
    ean: string | null
  }
  interface AddedItem {
    choiceType: string
    compositionItem: CompositionItem
    extraQuantity: number
    item: OrderFormItem
    normalizedQuantity: number
  }

  interface RemovedItem {
    initialQuantity: number
    name: string
    removedQuantity: number
  }

  interface CheckoutOrderForm {
    orderFormId: string
    salesChannel: string
    loggedIn: boolean
    isCheckedIn: boolean
    storeId: string | null
    checkedInPickupPointId: string | null
    allowManualPrice: boolean
    canEditData: boolean
    userProfileId: string | null
    userType: string | null
    ignoreProfileData: boolean
    value: number
    messages: any[]
    items: OrderFormItem[]
    selectableGifts: any[]
    totalizers: Array<{
      id: string
      name: string
      value: number
    }>
    shippingData: ShippingData | null
    clientProfileData: ClientProfileData | null
    paymentData: PaymentData
    marketingData: OrderFormMarketingData | null
    sellers: Array<{
      id: string
      name: string
      logo: string
    }>
    clientPreferencesData: {
      locale: string
      optinNewsLetter: any | null
    }
    commercialConditionData: any | null
    storePreferencesData: {
      countryCode: string
      currencyCode: string
      currencyFormatInfo: {
        currencyDecimalDigits: number
        currencyDecimalSeparator: string
        currencyGroupSeparator: string
        currencyGroupSize: number
        startsWithCurrencySymbol: boolean
      }
      currencyLocale: string
      currencySymbol: string
      saveUserData: boolean
      timeZone: string
    }
    giftRegistryData: any | null
    openTextField: any | null
    invoiceData: any | null
    customData: any | null
    itemMetadata: {
      items: MetadataItem[]
    }
    hooksData: any | null
    ratesAndBenefitsData: {
      rateAndBenefitsIdentifiers: any[]
      teaser: any[]
    }
    subscriptionData: SubscriptionData | null
    itemsOrdination: any | null
  }

  interface CheckoutProfile {
    userProfileId: string
    profileProvider: string
    availableAccounts: string[]
    availableAddresses: CheckoutAddress[]
    userProfile: any
  }

  interface ClientProfileData {
    email: string
    firstName: string
    lastName: string
    document: string
    documentType: string
    phone: string
    corporateName: string
    tradeName: string
    corporateDocument: string
    stateInscription: string
    corporatePhone: string
    isCorporate: boolean
    profileCompleteOnLoading: boolean
    profileErrorOnLoading: boolean
    customerClass: string
  }

  interface PaymentData {
    installmentOptions: Array<{
      paymentSystem: string
      bin: string | null
      paymentName: string | null
      paymentGroupName: string | null
      value: number
      installments: Array<{
        count: number
        hasInterestRate: false
        interestRate: number
        value: number
        total: number
        sellerMerchantInstallments: Array<{
          count: number
          hasInterestRate: false
          interestRate: number
          value: number
          total: number
        }>
      }>
    }>
    paymentSystems: Array<{
      id: string
      name: string
      groupName: string
      validator: {
        regex: string
        mask: string
        cardCodeRegex: string
        cardCodeMask: string
        weights: number[]
        useCvv: boolean
        useExpirationDate: boolean
        useCardHolderName: boolean
        useBillingAddress: boolean
      }
      stringId: string
      template: string
      requiresDocument: boolean
      isCustom: boolean
      description: string | null
      requiresAuthentication: boolean
      dueDate: string
      availablePayments: any | null
    }>
    payments: any[]
    giftCards: any[]
    giftCardMessages: any[]
    availableAccounts: any[]
    availableTokens: any[]
  }

  interface ShippingData {
    address: CheckoutAddress | null
    logisticsInfo: LogisticsInfo[]
    selectedAddresses: CheckoutAddress[]
    availableAddresses: CheckoutAddress[]
    pickupPoints: PickupPoint[]
  }

  interface PickupPoint {
    friendlyName: string
    address: CheckoutAddress
    additionalInfo: string
    id: string
    businessHours: BusinessHour[]
  }

  interface LogisticsInfo {
    addressId: string | null
    deliveryChannels: DeliveryChannel[]
    itemId: string
    itemIndex: number
    shipsTo: string[]
    slas: SLA[]
    selectedDeliveryChannel: string | null
    selectedSla: string | null
  }

  interface DeliveryChannel {
    id: string
  }

  interface SLA {
    id: string
    deliveryChannel: string
    name: string
    deliveryIds: DeliveryId[]
    shippingEstimate: string
    shippingEstimateDate: string | null
    lockTTL: string | null
    availableDeliveryWindows: any[]
    deliveryWindow: string | null
    price: number
    listPrice: number
    tax: number
    pickupStoreInfo: {
      isPickupStore: boolean
      friendlyName: string | null
      address: CheckoutAddress | null
      additionalInfo: any | null
      dockId: string | null
    }
    pickupPointId: string | null
    pickupDistance: number | null
    polygonName: string | null
    transitTime: string | null
  }

  interface DeliveryId {
    courierId: string
    warehouseId: string
    dockId: string
    courierName: string
    quantity: number
  }

  interface ShippingDataRequest {
    logisticsInfo: LogisticsInfo[]
    selectedAddresses: CheckoutAddress[]
    clearAddressIfPostalCodeNotFound?: boolean
  }

  interface OrderFormItemInput {
    id?: number
    index?: number
    quantity?: number
    seller?: string
    uniqueId?: string
    options?: AssemblyOptionInput[]
  }

  interface AssemblyOptionInput {
    id: string
    quantity: number
    assemblyId: string
    seller: string
    inputValues: Record<string, string>
    options?: AssemblyOptionInput[]
  }

  interface Item {
    additionalInfo: {
      brandName: string
      brandId: string
      offeringInfo: any | null
      offeringType: any | null
      offeringTypeId: any | null
    }
    availability: string
    detailUrl: string
    id: string
    imageUrls?: {
      at1x: string
      at2x: string
      at3x: string
    }
    isGift: boolean
    listPrice: number
    measurementUnit: string
    name: string
    price: number
    productId: string
    productCategories: Record<string, string>
    productCategoryIds: string
    productSpecificationGroups: ProductSpecificationGroup[]
    productRefId: string
    quantity: number
    sellingPrice: number
    skuName: string
    skuSpecifications: SKUSpecification[]
    uniqueId: string
  }

  interface OrderForm {
    items: Item[]
    canEditData: boolean
    shipping?: Shipping
    marketingData: OrderFormMarketingData | null
    totalizers: Array<{
      id: string
      name: string
      value: number
    }>
    value: number
    messages: OrderFormMessages
  }

  interface Shipping {
    availableAddresses: CheckoutAddress[]
    countries: string[]
    deliveryOptions: DeliveryOption[]
    pickupOptions: PickupOption[]
    selectedAddress: CheckoutAddress | null
  }

  interface DeliveryOption {
    id: string
    price: number
    estimate: string
    isSelected: boolean
  }

  interface PickupOption {
    id: string
    price: number
    estimate: string
    isSelected: boolean
  }

  interface OrderFormMessages {
    couponMessages: Message[]
    generalMessages: Message[]
  }

  interface Message {
    code: string
    text: string
    status: string
  }

  interface PaymentSession {
    id: string
    name: string
    expiresAt: string
  }

  interface SavePaymentTokenPayload {
    status: string
  }

  interface UserProfileInput {
    email?: string
    firstName?: string
    lastName?: string
    document?: string
    phone?: string
    documentType?: string
    isCorporate?: boolean
    corporateName?: string
    tradeName?: string
    corporateDocument?: string
    stateInscription?: string
  }

  interface ClientPreferencesDataInput {
    optInNewsletter?: boolean
    locale?: string
  }

  interface CheckoutClientPreferencesData {
    optinNewsLetter?: boolean
    locale?: string
  }

  interface PaymentInput {
    paymentSystem?: string
    bin?: string
    accountId?: string
    tokenId?: string
    installments?: number
    referenceValue?: number
    value?: number
  }

  interface PaymentDataInput {
    payments: PaymentInput[]
  }

  interface ItemsOrdinationArgs {
    ascending: boolean
    criteria: ItemsOrdinationCriteria
  }

  interface OpenTextField {
    value?: string
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/utils/validation.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
/**
 * Validates the shipping portion of the orderForm
 */
export const isShippingValid = async (
  orderForm: CheckoutOrderForm,
  shipping: Omit<Shipping, 'selectedAddress'> &
    Partial<Pick<Shipping, 'selectedAddress'>>,
  ctx: Context
) => {
  const selectedShippingOption =
    shipping.deliveryOptions?.some(
      deliveryOption => deliveryOption?.isSelected
    ) || shipping.pickupOptions?.some(pickupOption => pickupOption?.isSelected)

  if (!(shipping.selectedAddress && selectedShippingOption)) {
    return false
  }

  if (!orderForm.canEditData && !shipping.selectedAddress?.isDisposable) {
    return true
  }

  const address = shipping.selectedAddress

  if (address == null) {
    return false
  }

  if (!address.country) {
    return false
  }

  const countrySettings = await ctx.clients.countryDataSettings.getCountrySettings(
    address.country
  )

  if (!countrySettings) {
    return false
  }

  const fields = countrySettings.addressFields

  for (const [field, fieldSchema] of Object.entries(fields) as Array<
    [AddressFields, AddressFieldSchema]
  >) {
    const fieldValue = address[field]

    if (fieldSchema.required && !fieldValue) {
      return false
    }

    if (
      fieldSchema.maxLength &&
      fieldValue != null &&
      typeof fieldValue !== 'boolean' &&
      fieldValue.length > fieldSchema.maxLength
    ) {
      return false
    }
  }

  return true
}

/**
 * Validates the profile portion of the orderForm
 */
export const isProfileValid = async (
  orderForm: CheckoutOrderForm,
  profile: ClientProfileData,
  ctx: Context
) => {
  if (
    !profile ||
    !profile.firstName ||
    !profile.lastName ||
    !profile.phone ||
    !profile.document ||
    !profile.documentType
  ) {
    return false
  }

  if (!orderForm.canEditData) {
    return true
  }

  const countriesSettings = await ctx.clients.countryDataSettings.getAllCountriesSettings()

  const phoneCountry = countriesSettings.find(countrySetting => {
    return profile.phone.startsWith(`+${countrySetting.phone.countryCode}`)
  })

  if (!phoneCountry) {
    return false
  }

  return profile.phone.match(phoneCountry.phone.pattern) !== null
}

/**
 * Validates the payment portion of the orderForm
 */
export const isPaymentValid = (payment: PaymentData) => {
  return !!(payment.payments.length > 0)
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/utils/pickup.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
const DAYS_ORDER = [1, 2, 3, 4, 5, 6, 0]
const MONDAY_INDEX = 0
const FRIDAY_INDEX = 4

const normalizeBusinessHours = (businessHours: BusinessHour[]) => {
  return DAYS_ORDER.map(dayNumber => {
    const day = businessHours.find(bh => dayNumber === bh.DayOfWeek)

    const dayObject = day && {
      closed: false,
      openingTime: day.OpeningTime,
      closingTime: day.ClosingTime,
    }

    return {
      dayNumber,
      closed: true,
      openingTime: '',
      closingTime: '',
      ...(dayObject ?? {}),
    }
  })
}

const doesWeekDaysHaveTheSameHours = (
  businessHours: NormalizedBusinessHour[]
) => {
  const weekDays = businessHours.slice(MONDAY_INDEX, FRIDAY_INDEX)

  const firstOpeningTime = weekDays[0].openingTime
  const firstClosingTime = weekDays[0].closingTime

  for (let i = 1; i < weekDays.length; i++) {
    const currentDay = weekDays[i]

    if (
      currentDay.openingTime !== firstOpeningTime ||
      currentDay.closingTime !== firstClosingTime
    ) {
      return false
    }
  }

  return true
}

const condenseWeekDaysHours = (businessHours: NormalizedBusinessHour[]) => {
  return businessHours.reduce(
    (
      acc: NormalizedBusinessHour[],
      businessHour: NormalizedBusinessHour,
      index: number
    ) => {
      if (index >= MONDAY_INDEX && index <= FRIDAY_INDEX) {
        if (index === MONDAY_INDEX) {
          return acc.concat({
            dayNumber: '1to5',
            closed: businessHour.closed,
            openingTime: businessHour.openingTime,
            closingTime: businessHour.closingTime,
          })
        }
        return acc
      }

      return acc.concat(businessHour)
    },
    []
  )
}

export function formatBusinessHoursList(businessHours: BusinessHour[]) {
  const normalizedBusinessHours = normalizeBusinessHours(businessHours)

  const weekDaysHaveTheSameHours = doesWeekDaysHaveTheSameHours(
    normalizedBusinessHours
  )

  if (weekDaysHaveTheSameHours) {
    return condenseWeekDaysHours(normalizedBusinessHours)
  }

  return normalizedBusinessHours
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/utils/args.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
export interface OrderFormIdArgs {
  orderFormId?: string
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/utils/shipping.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
// eslint-disable-next-line no-restricted-imports
import uniqBy from 'lodash/uniqBy'

import {
  addressHasGeocoordinates,
  filterDeliveryOptions,
  getSelectedAddress,
} from './address'
import {
  getFormattedDeliveryOptions,
  hasDeliveryOption,
} from './delivery-options'
import { Clients } from '../clients'
import { DELIVERY, PICKUP_IN_POINT } from '../constants'
import { formatBusinessHoursList } from './pickup'

export const getShippingData = (
  address: CheckoutAddress,
  logisticsInfo: LogisticsInfo[] | null
) => {
  const selectedAddresses = [address]
  const hasGeocoordinates = addressHasGeocoordinates(address)
  const logisticsInfoWithAddress =
    logisticsInfo?.map((li: LogisticsInfo) => ({
      ...li,
      addressId: address.addressId,
    })) ?? []

  const requestPayload = {
    logisticsInfo: logisticsInfoWithAddress,
    selectedAddresses,
    ...(hasGeocoordinates ? { clearAddressIfPostalCodeNotFound: false } : {}),
  }

  return requestPayload
}

export const selectShippingOption = ({
  shippingData,
  slaId,
  itemId,
  deliveryChannel,
}: {
  shippingData: ShippingData | null
  slaId: string
  itemId?: string
  deliveryChannel: string
}) => {
  const logisticsInfoWithSelectedDeliveryOption =
    shippingData?.logisticsInfo.map((li: LogisticsInfo) => {
      return {
        ...li,
        selectedDeliveryChannel: deliveryChannel,
        selectedSla:
          hasDeliveryOption(li.slas, slaId) &&
          (itemId != null ? li.itemId === itemId : true)
            ? slaId
            : li.selectedSla,
      }
    }) ?? []

  const deliveryAddress = getSelectedAddress(
    shippingData?.selectedAddresses ?? []
  )

  if (!deliveryAddress) {
    return shippingData
  }

  return getShippingData(
    deliveryAddress,
    logisticsInfoWithSelectedDeliveryOption
  )
}

export const selectAddress = ({
  shippingData,
  address,
}: {
  shippingData: ShippingData | null
  address: CheckoutAddress
}): ShippingData | null => {
  if (!shippingData) {
    return null
  }

  return {
    ...shippingData,
    selectedAddresses: [address],
  }
}

export const getShippingInfo = async ({
  clients,
  orderForm,
}: {
  clients: Clients
  orderForm: Pick<
    CheckoutOrderForm,
    'shippingData' | 'totalizers' | 'orderFormId' | 'value'
  >
}) => {
  const logisticsInfo = orderForm.shippingData?.logisticsInfo ?? []
  const pickupPoints = orderForm.shippingData?.pickupPoints ?? []

  const countries = Array.from(
    new Set(logisticsInfo.flatMap(item => item.shipsTo)).values()
  )

  const availableAddresses = orderForm.shippingData?.availableAddresses ?? []

  const selectedAddress =
    orderForm.shippingData &&
    getSelectedAddress(orderForm.shippingData.selectedAddresses)

  const availableItemsLogisticsInfo = logisticsInfo
    ? logisticsInfo.filter((item: LogisticsInfo) => item.slas.length > 0)
    : []

  const deliveryOptions = uniqBy(
    availableItemsLogisticsInfo.flatMap((item: LogisticsInfo) => item.slas),
    'id'
  )

  // Since at this time shipping does not show scheduled delivery/pickup
  // we are filtering from them results.
  // Also we will filter deliveryOptions which does not apply to all LogisticsInfo.
  const filteredDeliveryOptions = filterDeliveryOptions(
    deliveryOptions,
    availableItemsLogisticsInfo
  )

  const updatedDeliveryOptions = getFormattedDeliveryOptions(
    filteredDeliveryOptions,
    availableItemsLogisticsInfo
  )

  const selectedDeliveryOption = updatedDeliveryOptions.find(
    option => option.isSelected && option.deliveryChannel === DELIVERY
  )

  const shippingTotalizer = orderForm.totalizers.find(
    totalizer => totalizer.id === 'Shipping'
  )

  if (
    selectedDeliveryOption &&
    shippingTotalizer &&
    selectedDeliveryOption.price !== shippingTotalizer.value
  ) {
    const newShippingData = selectShippingOption({
      slaId: selectedDeliveryOption.id,
      deliveryChannel: DELIVERY,
      shippingData: orderForm.shippingData,
    })

    await clients.checkout.updateOrderFormShipping(
      orderForm.orderFormId,
      newShippingData
    )

    const difference = selectedDeliveryOption.price - shippingTotalizer.value
    shippingTotalizer.value = selectedDeliveryOption.price
    orderForm.value += difference
  }

  return {
    availableAddresses,
    countries,
    deliveryOptions: updatedDeliveryOptions.filter(
      ({ deliveryChannel }) => deliveryChannel === DELIVERY
    ),
    pickupOptions: updatedDeliveryOptions
      .filter(({ deliveryChannel }) => deliveryChannel === PICKUP_IN_POINT)
      .map(pickupOption => {
        return {
          id: pickupOption.id,
          address: pickupOption.sla.pickupStoreInfo.address,
          deliveryChannel: pickupOption.sla.deliveryChannel,
          price: pickupOption.price,
          estimate: pickupOption.estimate,
          isSelected: pickupOption.isSelected,
          friendlyName: pickupOption.sla.pickupStoreInfo.friendlyName,
          additionalInfo: pickupOption.sla.pickupStoreInfo.additionalInfo,
          storeDistance: pickupOption.sla.pickupDistance,
          transitTime: pickupOption.sla.transitTime,
          businessHours: formatBusinessHoursList(
            pickupPoints.find(
              (pp: PickupPoint) => pp.id === pickupOption.pickupPointId
            )?.businessHours ?? []
          ),
        }
      }),
    selectedAddress,
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/utils/delivery-options.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { PICKUP_IN_POINT } from '../constants/index'

export function getFormattedDeliveryOptions(
  deliveryOptions: SLA[],
  logisticsInfo: LogisticsInfo[] | null
) {
  return deliveryOptions.map(sla => {
    let price = 0

    const isSelected =
      logisticsInfo?.some(li => li.selectedSla === sla.id) ?? false

    logisticsInfo?.forEach(li => {
      const currentSla = li.slas.find(liSla => liSla.id === sla.id)

      if (currentSla) {
        price += currentSla.price
      }
    })

    return {
      estimate: sla.shippingEstimate,
      id: sla.id,
      isSelected,
      price,
      deliveryChannel: sla.deliveryChannel,
      sla,
      ...(sla.deliveryChannel === PICKUP_IN_POINT
        ? { pickupPointId: sla.pickupPointId }
        : {}),
    }
  })
}

export function hasDeliveryOption(
  deliveryOptions: SLA[],
  deliveryOptionId: string
) {
  return deliveryOptions.some(
    (deliveryOption: SLA) => deliveryOption.id === deliveryOptionId
  )
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/utils/subscriptions.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
const FREQUENCY_PATTERN = /(\d{1,3})?\s*(\w*?)(?:$|s|ly)/i

const SUBSCRIPTION_PREFIX = `vtex.subscription`
const SUBSCRIPTION_KEY_PREFIX = `${SUBSCRIPTION_PREFIX}.key`

const SUBSCRIPTION_KEY_FREQUENCY = `${SUBSCRIPTION_KEY_PREFIX}.frequency`

export function parseFrequency(frequency: string) {
  const match = frequency.trim().match(FREQUENCY_PATTERN)

  if (!match) {
    return
  }

  const [, count = '1', type] = match

  if (!type) return

  return {
    interval: +count,
    periodicity: type.toUpperCase(),
  } as SubscriptionDataEntry['plan']['frequency']
}

export function generateSubscriptionDataEntry(
  subscriptionsFromAssemblyOptions: Array<{
    itemIndex: number
    options: AssemblyOptionInput[]
  }>
): SubscriptionDataEntry[] {
  const planType = 'RECURRING_PAYMENT'
  const defaultExecutionCount = 0

  const subscriptionDataEntries = subscriptionsFromAssemblyOptions.map(item => {
    const { itemIndex, options } = item

    const subscriptionFrequencyOption = options.find(option =>
      Boolean(option.inputValues[SUBSCRIPTION_KEY_FREQUENCY])
    )

    if (!subscriptionFrequencyOption) {
      return null
    }

    const planFrequency = parseFrequency(
      subscriptionFrequencyOption.inputValues[SUBSCRIPTION_KEY_FREQUENCY]
    )

    if (!planFrequency) {
      return null
    }

    const subscriptionPlan = {
      frequency: planFrequency,
      type: planType,
      validity: {},
    }

    return {
      executionCount: defaultExecutionCount,
      itemIndex,
      plan: subscriptionPlan,
    }
  })

  // Remove null entries caused by invalid frequencies
  const validSubscriptionDataEntries = subscriptionDataEntries.filter(Boolean)

  return validSubscriptionDataEntries as SubscriptionDataEntry[]
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/utils/index.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { AuthenticationError, ForbiddenError, UserInputError } from '@vtex/api'
import { AxiosError } from 'axios'
import { parse } from 'set-cookie-parser'
import { SetOption } from 'cookies'
import { CHECKOUT_COOKIE, OWNERSHIP_COOKIE } from '../constants'

export function generateRandomName() {
  return (1 + Math.random()).toString(36).substring(2)
}

export function getFileExtension(fileName: string) {
  return fileName.match(/\.[0-9a-z]+$/i)
    ? (fileName.match(/\.[0-9a-z]+$/i) as any[])[0]
    : undefined
}

export function statusToError(e: any) {
  if (!e.response) {
    throw e
  }

  const { response } = e as AxiosError
  const { status } = response!

  if (status === 401) {
    throw new AuthenticationError(e)
  }
  if (status === 403) {
    throw new ForbiddenError(e)
  }
  if (status === 400) {
    throw new UserInputError(e)
  }

  throw e
}

export const parseCookie = (cookie: string): ParsedCookie => {
  const [parsed] = parse(cookie)

  const cookieName = parsed.name
  const cookieValue = parsed.value

  const extraOptions: SetOption = {
    path: parsed.path,
    domain: parsed.domain,
    expires: parsed.expires,
    httpOnly: true,
    secure: parsed.secure,
    sameSite: parsed.sameSite as "strict" | "lax" | undefined,
  }

  return {
    name: cookieName,
    value: cookieValue,
    options: extraOptions,
  }
}

export function checkoutCookieFormat(orderFormId: string) {
  return `${CHECKOUT_COOKIE}=__ofid=${orderFormId};`
}

export function ownershipCookieFormat(ownerId: string) {
  return `${OWNERSHIP_COOKIE}=${ownerId};`
}

export function getOrderFormIdFromCookie(cookies: Context['cookies']) {
  const cookie = cookies.get(CHECKOUT_COOKIE)
  return cookie?.split('=')[1]
}

export function getOwnerIdFromCookie(cookies: Context['cookies']) {
  return cookies.get(OWNERSHIP_COOKIE)
}

interface ParsedCookie {
  name: string
  value: string
  options: SetOption
}
```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/utils/attachmentsHelpers.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
/* eslint-disable no-shadow */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable no-loop-func */
/* eslint-disable @typescript-eslint/no-use-before-define */
/* eslint-disable no-await-in-loop */
import { partition, propEq, omit, eqProps } from 'ramda'

type OptionItems = Array<Omit<AssemblyOptionInput, 'assemblyId'>>

interface OptionRequestUnit {
  items: OptionItems
  inputValues: Record<string, string | boolean>
}

interface AddOptionsLogicInput {
  checkout: Context['clients']['checkout']
  orderForm: CheckoutOrderForm
  parentUniqueId?: string
  options?: AssemblyOptionInput[]
  oldItems: OrderFormItem[]
}

interface AssemblyOptionBody {
  noSplitItem?: boolean
  composition?: {
    items: Array<{
      id: string
      quantity: number
      seller: string
    }>
  }
  inputValues?: Record<string, string>
}

const getNewItemsOnly = (
  previousItems: OrderFormItem[],
  allItems: OrderFormItem[]
) => {
  return allItems.filter(item => !previousItems.find(eqProps('uniqueId', item)))
}

const findRecentlyAddedParent = (
  recentlyAdded: OrderFormItem[],
  id: string,
  assemblyId: string | null
) =>
  recentlyAdded.find(i => i.id === id && i.parentAssemblyBinding === assemblyId)

export const addOptionsForItems = async (
  items: OrderFormItemInput[],
  checkout: Context['clients']['checkout'],
  orderForm: CheckoutOrderForm,
  oldItems: OrderFormItem[]
) => {
  const recentlyAdded =
    items.length > 0 ? getNewItemsOnly(oldItems, orderForm.items) : []

  for (const item of items) {
    if (!item.options || item.options.length === 0) {
      continue
    }

    const parentItem = findRecentlyAddedParent(
      recentlyAdded,
      item.id!.toString(),
      null
    )

    await addOptionsLogic({
      checkout,
      parentUniqueId: parentItem?.uniqueId,
      options: item.options,
      orderForm,
      oldItems,
    })
  }
}

const joinOptionsWithType = (options: AssemblyOptionInput[]) => {
  const result: Record<string, OptionRequestUnit> = {}

  for (const option of options) {
    const { assemblyId, ...rest } = option
    const currentArray = (result[assemblyId] && result[assemblyId].items) || []

    if (rest.id) {
      currentArray.push(rest)
    }

    result[assemblyId] = {
      inputValues: rest.inputValues,
      items: currentArray,
    }
  }

  return result
}

const removeAssemblyBody = (option: OptionRequestUnit) => ({
  composition: {
    items: option.items.map(omit(['quantity', 'options'])),
  },
})

const addAssemblyBody = (option: OptionRequestUnit) => {
  const body: AssemblyOptionBody = {}

  if (option.items.length > 0) {
    body.noSplitItem = true
    body.composition = {
      items: option.items.map(omit(['options', 'inputValues'])),
    }
  }

  if (option.inputValues) {
    body.noSplitItem = true
    body.inputValues = Object.keys(option.inputValues).reduce<
      Record<string, string>
    >((acc, key) => {
      // Transforming boolean values to string
      acc[key] = `${option.inputValues[key]}`
      return acc
    }, {})
  }

  return body
}

const addOptionsLogic = async (input: AddOptionsLogicInput) => {
  const { checkout, orderForm, parentUniqueId, options, oldItems } = input
  if (!options || options.length === 0) {
    return
  }

  let parentIndex = orderForm.items.findIndex(
    propEq('uniqueId', parentUniqueId)
  )

  if (parentIndex == null || parentIndex < 0) {
    return
  }

  const isRemove = (option: AssemblyOptionInput) => option.quantity === 0
  const [toRemove, toAdd] = partition<AssemblyOptionInput>(isRemove, options)
  const joinedToAdd = joinOptionsWithType(toAdd)
  const joinedToRemove = joinOptionsWithType(toRemove)
  const idsToAdd = Object.keys(joinedToAdd)
  const idsToRemove = Object.keys(joinedToRemove)
  let recentOrderForm: any = orderForm

  for (const assemblyId of idsToRemove) {
    parentIndex = recentOrderForm.items.findIndex(
      propEq('uniqueId', parentUniqueId)
    )
    const parsedOptions = joinedToRemove[assemblyId]
    const response = await checkout
      .removeAssemblyOptions(
        orderForm.orderFormId!,
        parentIndex,
        assemblyId,
        removeAssemblyBody(parsedOptions)
      )
      .catch(() => ({ data: recentOrderForm }))
    recentOrderForm = response.data
  }

  for (const assemblyId of idsToAdd) {
    parentIndex = recentOrderForm.items.findIndex(
      propEq('uniqueId', parentUniqueId)
    )
    const parsedOptions = joinedToAdd[assemblyId]
    recentOrderForm = await checkout
      .addAssemblyOptions(
        orderForm.orderFormId!,
        parentIndex,
        assemblyId,
        addAssemblyBody(parsedOptions)
      )
      .catch(() => recentOrderForm)
  }

  for (const assemblyId of idsToAdd) {
    const parsedOptions = joinedToAdd[assemblyId]
    const itemsWithRecursiveOptions = parsedOptions.items.filter(
      ({ options }) => !!options
    )

    if (itemsWithRecursiveOptions.length > 0) {
      await addOptionsRecursive(
        itemsWithRecursiveOptions,
        assemblyId,
        recentOrderForm,
        oldItems,
        checkout
      )
    }
  }
}

const addOptionsRecursive = async (
  items: OptionItems,
  assemblyId: string,
  orderForm: CheckoutOrderForm,
  oldItems: OrderFormItem[],
  checkout: Context['clients']['checkout']
) => {
  const recentlyAdded = getNewItemsOnly(oldItems, orderForm.items)

  for (const item of items) {
    const parentItem = findRecentlyAddedParent(
      recentlyAdded,
      item.id!.toString(),
      assemblyId
    )

    await addOptionsLogic({
      checkout,
      parentUniqueId: parentItem?.uniqueId,
      options: item.options,
      orderForm,
      oldItems,
    })
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/utils/address.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { AddressType, DELIVERY, PICKUP_IN_POINT } from '../constants'

export function getSelectedAddress(selectedAddresses: CheckoutAddress[]) {
  return selectedAddresses.find((address: CheckoutAddress) => {
    const isPickup = address.addressType === AddressType.PICKUP
    const isInStore = address.addressType === AddressType.INSTORE

    return !isPickup && !isInStore
  })
}

export function addressHasGeocoordinates(address: CheckoutAddress) {
  return address?.geoCoordinates && address.geoCoordinates.length > 0
}

export function filterDeliveryOptions(
  deliveryOptions: SLA[],
  logisticsInfo: LogisticsInfo[]
) {
  return deliveryOptions.filter(deliveryOption => {
    const deliveryOptionIsInEveryLogisticsInfo = logisticsInfo.every(
      (li: LogisticsInfo) => li.slas.some(sla => sla.id === deliveryOption.id)
    )

    return (
      (deliveryOption.deliveryChannel === DELIVERY ||
        deliveryOption.deliveryChannel === PICKUP_IN_POINT) &&
      deliveryOption.availableDeliveryWindows.length === 0 &&
      deliveryOptionIsInEveryLogisticsInfo
    )
  })
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of checkout-graphql/node/resolvers/payment.ts from the checkout-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
export const queries = {
  getCardSessionId: async (_: {}, __: {}, ctx: Context): Promise<string> => {
    const { clients } = ctx
    const { id: cardSessionId } = await clients.checkout.getPaymentSession()
    return cardSessionId
  },
}

export const mutations = {
  savePaymentToken: async (
    _: {},
    {
      paymentTokens,
    }: {
      paymentTokens: Array<{ creditCardToken: string; paymentSystem: string }>
    },
    ctx: Context
  ): Promise<SavePaymentTokenPayload> => {
    const { clients } = ctx
    const { checkout } = clients
    await checkout.savePaymentToken(paymentTokens)
    return { status: 'OK' }
  },
}

```