[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 salesforce-connector/node/__tests__/service/SalesforceOrderServiceTest.ts from the salesforce-connector VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { AxiosInstance } from 'axios'
import axios from 'axios'

import SalesforceOrderService from '../../service/SalesforceOrderService'
import type { Item, OrderVtexResponse } from '../../schemas/orderVtexResponse'
import type { Parameter } from '../../schemas/Parameter'
import { ParameterList } from '../../schemas/Parameter'
import { ACCOUNT_ID, LIST_PRICE_ID } from '../../utils/constans'

describe('SalesforceOrderService', () => {
  let service: SalesforceOrderService
  let mockAxiosInstance: AxiosInstance
  let order: OrderVtexResponse
  let parameterList: ParameterList
  let item: Item

  beforeEach(() => {
    service = new SalesforceOrderService()
    mockAxiosInstance = axios.create()
    order = {
      orderId: '',
      sequence: '',
      status: '',
      value: 0,
      discounts: 0,
      ratesAndBenefitsData: [
        {
          name: 'promo 1',
        },
        {
          name: 'promo 2',
        },
      ],
      creationDate: '',
      paymentSystemName: '',
      items: [],
      clientProfileData: {
        id: '',
        firtsName: '',
        lastName: '',
        document: '',
        phone: '',
        userProfileId: '',
      },
      address: {
        street: '',
        city: '',
        state: '',
        country: '',
        postalCode: '',
      },
    }
    const parameterAccount: Parameter = {
      id: ACCOUNT_ID,
      parameterValue: 'ACCOUNT_ID',
    }

    const parameterListPrice = {
      id: LIST_PRICE_ID,
      parameterValue: 'LIST_PRICE_ID',
    }

    const parameters: Parameter[] = []

    parameters.push(parameterAccount)
    parameters.push(parameterListPrice)
    parameterList = new ParameterList(parameters)
    item = {
      id: '',
      productId: '',
      uniqueId: '',
      name: '',
      quantity: 0,
      measurementUnit: '',
      price: 0,
      imageUrl: '',
      refId: '',
      sellingPrice: 0,
    }
  })

  describe('createOrder', () => {
    test('Create order in Saleforce', async () => {
      jest.spyOn(mockAxiosInstance, 'post').mockResolvedValue({ status: 201 })
      const response = await service.createOrder(
        order,
        '',
        mockAxiosInstance,
        parameterList
      )

      expect(response).toEqual({
        status: 200,
        message: 'OK',
        data: undefined,
      })
    })

    test('Create order without promotions in Saleforce', async () => {
      jest.spyOn(mockAxiosInstance, 'post').mockResolvedValue({ status: 201 })
      order.ratesAndBenefitsData = []
      const response = await service.createOrder(
        order,
        '',
        mockAxiosInstance,
        parameterList
      )

      expect(response).toEqual({
        status: 200,
        message: 'OK',
        data: undefined,
      })
    })

    test('Not found account id in MTDT', async () => {
      parameterList.remove(ACCOUNT_ID)
      jest.spyOn(mockAxiosInstance, 'post').mockResolvedValue({ status: 201 })
      const response = await service.createOrder(
        order,
        '',
        mockAxiosInstance,
        parameterList
      )

      expect(response).toEqual({
        status: 500,
        message: `Parameter not found: ${ACCOUNT_ID}`,
        data: undefined,
      })
    })

    test('Not found list price id in MTDT', async () => {
      parameterList.remove(LIST_PRICE_ID)
      jest.spyOn(mockAxiosInstance, 'post').mockResolvedValue({ status: 201 })
      const response = await service.createOrder(
        order,
        '',
        mockAxiosInstance,
        parameterList
      )

      expect(response).toEqual({
        status: 500,
        message: `Parameter not found: ${LIST_PRICE_ID}`,
        data: undefined,
      })
    })

    test('Failed to create order in salesforce', async () => {
      jest.spyOn(mockAxiosInstance, 'post').mockResolvedValue({ status: 500 })
      const response = await service.createOrder(
        order,
        '',
        mockAxiosInstance,
        parameterList
      )

      expect(response).toEqual({
        status: 500,
        message: 'order could not be created in salesforce',
        data: undefined,
      })
    })

    test('Error to create order in salesforce', async () => {
      jest
        .spyOn(mockAxiosInstance, 'post')
        .mockRejectedValue(new Error('Request failed with status code 400'))
      const response = await service.createOrder(
        order,
        '',
        mockAxiosInstance,
        parameterList
      )

      expect(response).toEqual({
        status: 500,
        message: 'an error occurred while processing order',
        data: new Error('Request failed with status code 400'),
      })
    })
  })

  describe('getProductByExternalId', () => {
    test('Get product by external id in Salesforce', async () => {
      jest.spyOn(mockAxiosInstance, 'get').mockResolvedValue({ status: 200 })
      const response = await service.getProductByExternalId(
        '',
        mockAxiosInstance
      )

      expect(response).toEqual({
        status: 200,
        message: 'OK',
        data: undefined,
      })
    })

    test('Failed to get product by external id in Salesforce', async () => {
      jest.spyOn(mockAxiosInstance, 'get').mockResolvedValue({ status: 404 })
      const response = await service.getProductByExternalId(
        '',
        mockAxiosInstance
      )

      expect(response).toEqual({
        status: 404,
        message: 'product could not be queried in salesforce',
        data: undefined,
      })
    })

    test('Error to get product by external id in Salesforce', async () => {
      jest
        .spyOn(mockAxiosInstance, 'get')
        .mockRejectedValue(new Error('Request failed with status code 400'))
      const response = await service.getProductByExternalId(
        '',
        mockAxiosInstance
      )

      expect(response).toEqual({
        status: 500,
        message: 'an error occurred while viewing the product',
        data: new Error('Request failed with status code 400'),
      })
    })
  })

  describe('createProduct', () => {
    test('Create product in Salesforce', async () => {
      jest.spyOn(mockAxiosInstance, 'post').mockResolvedValue({ status: 201 })
      const response = await service.createProduct(item, mockAxiosInstance)

      expect(response).toEqual({
        status: 200,
        message: 'OK',
        data: undefined,
      })
    })

    test('Failed to create product in Salesforce', async () => {
      jest.spyOn(mockAxiosInstance, 'post').mockResolvedValue({ status: 500 })
      const response = await service.createProduct(item, mockAxiosInstance)

      expect(response).toEqual({
        status: 500,
        message: 'product could not be created in salesforce',
        data: undefined,
      })
    })

    test('Error to create product in Salesforce', async () => {
      jest
        .spyOn(mockAxiosInstance, 'post')
        .mockRejectedValue(new Error('Request failed with status code 400'))
      const response = await service.createProduct(item, mockAxiosInstance)

      expect(response).toEqual({
        status: 500,
        message: 'an error occurred when creating product in salesforce',
        data: new Error('Request failed with status code 400'),
      })
    })
  })

  describe('createPricebookEntry', () => {
    test('Create pricebookEntry in Salesforce', async () => {
      jest.spyOn(mockAxiosInstance, 'post').mockResolvedValue({ status: 201 })
      const response = await service.createPricebookEntry(
        '',
        '',
        item,
        mockAxiosInstance
      )

      expect(response).toEqual({
        status: 200,
        message: 'OK',
        data: undefined,
      })
    })

    test('Failed to create pricebookEntry in Salesforce', async () => {
      jest.spyOn(mockAxiosInstance, 'post').mockResolvedValue({ status: 500 })
      const response = await service.createPricebookEntry(
        '',
        '',
        item,
        mockAxiosInstance
      )

      expect(response).toEqual({
        status: 500,
        message: 'PricebookEntry could not be created in salesforce',
        data: undefined,
      })
    })

    test('Error to create pricebookEntry in Salesforce', async () => {
      jest
        .spyOn(mockAxiosInstance, 'post')
        .mockRejectedValue(new Error('Request failed with status code 400'))
      const response = await service.createPricebookEntry(
        '',
        '',
        item,
        mockAxiosInstance
      )

      expect(response).toEqual({
        status: 500,
        message: 'an error occurred when creating PricebookEntry in salesforce',
        data: new Error('Request failed with status code 400'),
      })
    })
  })

  describe('associateOrderAndProduct', () => {
    test('Create associate order and product in Salesforce', async () => {
      jest.spyOn(mockAxiosInstance, 'post').mockResolvedValue({ status: 201 })
      const response = await service.associateOrderAndProduct(
        '',
        '',
        item,
        mockAxiosInstance
      )

      expect(response).toEqual({
        status: 200,
        message: 'OK',
        data: undefined,
      })
    })

    test('Failed to create associate order and product in Salesforce', async () => {
      jest.spyOn(mockAxiosInstance, 'post').mockResolvedValue({ status: 500 })
      const response = await service.associateOrderAndProduct(
        '',
        '',
        item,
        mockAxiosInstance
      )

      expect(response).toEqual({
        status: 500,
        message: 'OrderItem could not be created in salesforce',
        data: undefined,
      })
    })

    test('Error to create associate order and product in Salesforce', async () => {
      jest
        .spyOn(mockAxiosInstance, 'post')
        .mockRejectedValue(new Error('Request failed with status code 400'))
      const response = await service.associateOrderAndProduct(
        '',
        '',
        item,
        mockAxiosInstance
      )

      expect(response).toEqual({
        status: 500,
        message: 'an error occurred when creating OrderItem in salesforce',
        data: new Error('Request failed with status code 400'),
      })
    })
  })

  describe('updateUnitPricePricebookEntry', () => {
    test('Update unit price pricebookEntry in Salesforce', async () => {
      jest.spyOn(mockAxiosInstance, 'patch').mockResolvedValue({ status: 200 })
      const response = await service.updateUnitPricePricebookEntry(
        '',
        item,
        mockAxiosInstance
      )

      expect(response).toEqual({
        status: 200,
        message: 'OK',
        data: undefined,
      })
    })

    test('Failed to update unit price pricebookEntry in Salesforce', async () => {
      jest.spyOn(mockAxiosInstance, 'patch').mockResolvedValue({ status: 500 })
      const response = await service.updateUnitPricePricebookEntry(
        '',
        item,
        mockAxiosInstance
      )

      expect(response).toEqual({
        status: 500,
        message: 'PricebookEntry could not be updating in salesforce',
        data: undefined,
      })
    })

    test('Error to update unit price pricebookEntry in Salesforce', async () => {
      jest
        .spyOn(mockAxiosInstance, 'patch')
        .mockRejectedValue(new Error('Request failed with status code 400'))
      const response = await service.updateUnitPricePricebookEntry(
        '',
        item,
        mockAxiosInstance
      )

      expect(response).toEqual({
        status: 500,
        message: 'an error occurred when updating PricebookEntry in salesforce',
        data: new Error('Request failed with status code 400'),
      })
    })
  })

  describe('getOrderById', () => {
    test('Get order by id in Salesforce', async () => {
      jest.spyOn(mockAxiosInstance, 'get').mockResolvedValue({ status: 200 })
      const response = await service.getOrderById('', mockAxiosInstance)

      expect(response).toEqual({
        status: 200,
        message: 'OK',
        data: undefined,
      })
    })

    test('Failed to get order by id in Salesforce', async () => {
      jest.spyOn(mockAxiosInstance, 'get').mockResolvedValue({ status: 404 })
      const response = await service.getOrderById('', mockAxiosInstance)

      expect(response).toEqual({
        status: 404,
        message: 'order could not be queried in salesforce',
        data: undefined,
      })
    })

    test('Error to get order by id in Salesforce', async () => {
      jest
        .spyOn(mockAxiosInstance, 'get')
        .mockRejectedValue(new Error('Request failed with status code 400'))
      const response = await service.getOrderById('', mockAxiosInstance)

      expect(response).toEqual({
        status: 500,
        message: 'an error occurred while viewing the order',
        data: new Error('Request failed with status code 400'),
      })
    })
  })

  describe('updateStatusOrder', () => {
    test('Update status order in Salesforce', async () => {
      jest.spyOn(mockAxiosInstance, 'patch').mockResolvedValue({ status: 200 })
      const response = await service.updateStatusOrder(
        '',
        '',
        mockAxiosInstance
      )

      expect(response).toEqual({
        status: 200,
        message: 'OK',
        data: undefined,
      })
    })

    test('Failed to update status order in Salesforce', async () => {
      jest.spyOn(mockAxiosInstance, 'patch').mockResolvedValue({ status: 500 })
      const response = await service.updateStatusOrder(
        '',
        '',
        mockAxiosInstance
      )

      expect(response).toEqual({
        status: 500,
        message: 'updateStatusOrder could not be updating in salesforce',
        data: undefined,
      })
    })

    test('Error to update status order in Salesforce', async () => {
      jest
        .spyOn(mockAxiosInstance, 'patch')
        .mockRejectedValue(new Error('Request failed with status code 400'))
      const response = await service.updateStatusOrder(
        '',
        '',
        mockAxiosInstance
      )

      expect(response).toEqual({
        status: 500,
        message:
          'an error occurred when updating updateStatusOrder in salesforce',
        data: new Error('Request failed with status code 400'),
      })
    })
  })
})

```


[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 salesforce-connector/node/__tests__/service/CreateEntitiesMasterDataV2ServiceTest.ts from the salesforce-connector VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { AxiosInstance } from 'axios'
import axios from 'axios'

import CreateEntitiesMasterDataV2Service from '../../service/CreateEntitiesMasterDataV2Service'

describe('CreateEntitiesMasterDataV2Service', () => {
  let service: CreateEntitiesMasterDataV2Service
  let mockAxiosInstance: AxiosInstance

  beforeEach(() => {
    service = new CreateEntitiesMasterDataV2Service()
    mockAxiosInstance = axios.create()
  })
  describe('createEntity', () => {
    test('registers entities successfully in MTDT', async () => {
      jest.spyOn(mockAxiosInstance, 'put').mockResolvedValue({ status: 200 })
      const response = await service.createEntity('', mockAxiosInstance)

      expect(response).toEqual({
        status: 200,
        message: 'OK',
        data: 'Entities registered in MTDT',
      })
    })

    test('No register entities in MTDT', async () => {
      jest.spyOn(mockAxiosInstance, 'put').mockResolvedValue({ status: 500 })
      const response = await service.createEntity('', mockAxiosInstance)

      expect(response).toEqual({
        status: 200,
        message: 'OK',
        data: 'Entities registered in MTDT',
      })
    })
  })
})

```


[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 salesforce-connector/node/__tests__/service/ConfigurationServiceTest.ts from the salesforce-connector VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { Parameter } from '../../schemas/Parameter'
import { ParameterList } from '../../schemas/Parameter'
import { Result } from '../../schemas/Result'
import ConfigurationService from '../../service/ConfigurationService'
import { LIST_PRICE_ID } from '../../utils/constans'

jest.mock('co-body', () => ({
  json: jest.fn().mockReturnValue({ email: 'email' }),
}))

let mockSalesforceConfigurationService: any
let mockSalesforceClientService: any
let mockMasterDataService: any

jest.mock('../../service/SalesforceConfigurationService', () =>
  jest.fn().mockImplementation(() => mockSalesforceConfigurationService)
)

jest.mock('../../service/SalesforceClientService', () =>
  jest.fn().mockImplementation(() => mockSalesforceClientService)
)

jest.mock('../../service/MasterDataService', () =>
  jest.fn().mockImplementation(() => mockMasterDataService)
)

describe('ConfigurationService', () => {
  let service: ConfigurationService
  let ctx: any
  let parameterList: ParameterList
  const fields = [1, 1, 1, 1, 0]

  beforeEach(() => {
    service = new ConfigurationService()
    ctx = {
      vtex: {
        authToken: 'token',
      },
      req: {},
    }
    const parameterListPrice = {
      id: LIST_PRICE_ID,
      parameterValue: 'LIST_PRICE_ID',
    }

    const parameters: Parameter[] = []

    parameters.push(parameterListPrice)
    parameterList = new ParameterList(parameters)
  })

  test('Execute success proccess configuration', async () => {
    mockSalesforceConfigurationService = {
      createPricebook: jest.fn().mockReturnValue(Result.TaskOk({})),
      createAccount: jest.fn().mockReturnValue(Result.TaskOk({})),
      createCustomField: jest.fn().mockReturnValue({}),
    }
    mockSalesforceClientService = {
      login: jest
        .fn()
        .mockResolvedValue(Result.TaskOk({ access_token: 'token' })),
    }
    mockMasterDataService = {
      saveUpdateParameter: jest.fn().mockReturnValue({}),
    }
    const response = await service.proccessConfiguration(
      ctx,
      parameterList,
      fields
    )

    expect(response).toEqual({
      data: 'Configuration completed successfully',
      status: 200,
      message: 'OK',
    })
  })

  test('Create pricebook in Salesforce', async () => {
    parameterList.remove(LIST_PRICE_ID)
    mockSalesforceConfigurationService = {
      createPricebook: jest.fn().mockReturnValue(Result.TaskOk({})),
      createAccount: jest.fn().mockReturnValue(Result.TaskOk({})),
      createCustomField: jest.fn().mockReturnValue({}),
    }
    mockMasterDataService = {
      saveUpdateParameter: jest.fn().mockReturnValue({}),
    }
    const response = await service.proccessConfiguration(
      ctx,
      parameterList,
      fields
    )

    expect(response).toEqual({
      data: 'Configuration completed successfully',
      status: 200,
      message: 'OK',
    })
  })

  test('Error to execute success proccess configuration', async () => {
    mockSalesforceClientService = {
      login: jest.fn().mockRejectedValue(Result.TaskError('error')),
    }
    const response = await service.proccessConfiguration(
      ctx,
      parameterList,
      fields
    )

    expect(response).toEqual({
      status: 500,
      message: 'an error occurred while processing the configuration',
      data: Result.TaskResult(500, 'error', undefined),
    })
  })
})

```


[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 salesforce-connector/react/utils/constans.ts from the salesforce-connector VTEX app.

[Assistant]
Here's the implementation:

```typescript
export const URI_ADD_CREDENTIALS_SALESFORCE = '/v1/vtex/add-credentials'
export const URI_CONFIGURATION_SALESFORCE = '/v1/vtex/configuration'
export const URI_GET_PARAMETERS = '/v1/vtex/get-parameters'
export const URI_GET_CONFIGURATION = '/v1/vtex/get-configuration'

```


[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 salesforce-connector/react/components/settings/SalesforceSettingsComponent.tsx from the salesforce-connector VTEX app.

[Assistant]
Here's the implementation:

```tsx
import axios from 'axios'
import React, { useEffect, useState } from 'react'
import { Button, Modal, Input } from 'vtex.styleguide'

import {
  URI_ADD_CREDENTIALS_SALESFORCE,
  URI_CONFIGURATION_SALESFORCE,
  URI_GET_CONFIGURATION,
  URI_GET_PARAMETERS,
} from '../../utils/constans'

const SalesforceSettingsComponent: React.FC = () => {
  const [data, setData] = useState({
    isModalOpen: false,
    accountSalesforce: '',
    clientId: '',
    clientSecret: '',
  })

  const [responseSettings, setresponseSettings] = useState('')
  const [responseAddCredentials, setresponseAddCredentials] = useState('')

  useEffect(() => {
    axios
      .get(URI_GET_PARAMETERS)
      .then((response) => {
        setData({
          ...response.data,
          isModalOpen: false,
        })
        setresponseAddCredentials('Credentials have been added successfully')
      })
      .catch((error) => {
        console.error(error)
      })

    axios
      .get(URI_GET_CONFIGURATION)
      .then((response) => {
        if (response.data === true) {
          setresponseSettings('Configuration process executed successfully')
        }
      })
      .catch((error) => {
        console.error(error)
      })
  }, [])

  const handleOpenModal = () => {
    setData({
      ...data,
      isModalOpen: true,
    })
  }

  const handleCloseModal = () => {
    setData({
      ...data,
      isModalOpen: false,
    })
  }

  const handleInputChange = (e: {
    target: { name: string; value: string }
  }) => {
    const { name, value } = e.target

    setData({
      ...data,
      [name]: value,
    })
  }

  const handleCredentials = () => {
    axios
      .post(URI_ADD_CREDENTIALS_SALESFORCE, data)
      .then(() => {
        setresponseAddCredentials('Credentials have been added successfully')
      })
      .catch((error) => {
        console.error(error)
      })

    setData({
      ...data,
      isModalOpen: false,
    })
  }

  const handleSettings = () => {
    axios
      .post(URI_CONFIGURATION_SALESFORCE, data)
      .then(() => {
        setresponseSettings('Configuration process executed successfully')
      })
      .catch((error) => {
        console.error(error)
      })
  }

  return (
    <div>
      <div className="mb6">
        <Button onClick={handleOpenModal}>Add Salesforce Credentials</Button>
        <p>{responseAddCredentials}</p>
      </div>

      <hr />

      <div className="mt6">
        <Button onClick={handleSettings}>Create Salesforce Settings</Button>
        <p>{responseSettings}</p>
      </div>

      <Modal
        isOpen={data.isModalOpen}
        title="Returns Settings"
        responsiveFullScreen
        bottomBar={
          <div className="nowrap">
            <span className="mr4">
              <Button variation="tertiary" onClick={handleCloseModal}>
                Cancel
              </Button>
            </span>
            <span>
              <Button variation="primary" onClick={handleCredentials}>
                Add Credentials
              </Button>
            </span>
          </div>
        }
        onClose={handleCloseModal}
      >
        <div>
          <div className="w-100 mv6">
            <Input
              type="text"
              value={data.accountSalesforce || ''}
              name="accountSalesforce"
              placeholder="Account Salesforce"
              size="large"
              label="Account Salesforce"
              onChange={handleInputChange}
            />
          </div>
          <div className="w-100 mv6">
            <Input
              type="text"
              value={data.clientId || ''}
              name="clientId"
              placeholder="Client Id"
              size="large"
              label="Client Id"
              onChange={handleInputChange}
            />
          </div>
          <div className="w-100 mv6">
            <Input
              type="password"
              value={data.clientSecret || ''}
              name="clientSecret"
              placeholder="Client Secret"
              size="large"
              label="Client Secret"
              onChange={handleInputChange}
            />
          </div>
        </div>
      </Modal>
    </div>
  )
}

export default SalesforceSettingsComponent

```


[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 sandbox/messages/pt.json from the sandbox VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin/editor.sandbox.title": "Sandbox",
  "admin/editor.sandbox.description": "Um componente sandbox que renderiza conteúdo HTML dentro de um iframe seguro",
  "admin/editor.sandbox.initialContent.title": "HTML de conteúdo inicial",
  "admin/editor.sandbox.initialContent.description": "Qualquer HTML válido. Pode incluir scripts. As propriedades passadas para este bloco estão disponíveis em `window.props`.",
  "admin/editor.sandbox.width.title": "Largura",
  "admin/editor.sandbox.width.description": "Largura do Iframe (e.g. `400px`)",
  "admin/editor.sandbox.height.title": "Altura",
  "admin/editor.sandbox.height.description": "Altura do Iframe (e.g. `20px`)",
  "admin/editor.sandbox.allowCookies.title": "Permitir cookies",
  "admin/editor.sandbox.allowCookies.description": "Permitir que scripts no iframe leiam e escrevam cookies"
}

```


[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 sandbox/messages/en.json from the sandbox VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin/editor.sandbox.title": "Sandbox",
  "admin/editor.sandbox.description": "A sandbox component that renders HTML content inside a secure iframe",
  "admin/editor.sandbox.initialContent.title": "Initial content HTML",
  "admin/editor.sandbox.initialContent.description": "Any valid HTML. May include scripts. The props passed to this block are available in `window.props`.",
  "admin/editor.sandbox.width.title": "Width",
  "admin/editor.sandbox.width.description": "Iframe width (e.g. `400px`)",
  "admin/editor.sandbox.height.title": "Height",
  "admin/editor.sandbox.height.description": "Iframe height (e.g. `20px`)",
  "admin/editor.sandbox.allowCookies.title": "Allow cookies",
  "admin/editor.sandbox.allowCookies.description": "Allow scripts in iframe to read and write cookies"
}

```


[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 sandbox/messages/context.json from the sandbox VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin/editor.sandbox.title": "Sandbox",
  "admin/editor.sandbox.description": "A sandbox component that renders HTML content inside a secure iframe",
  "admin/editor.sandbox.initialContent.title": "Initial content HTML",
  "admin/editor.sandbox.initialContent.description": "Any valid HTML. May include scripts. The props passed to this block are available in `window.props`.",
  "admin/editor.sandbox.width.title": "Width",
  "admin/editor.sandbox.width.description": "Iframe width (e.g. `400px`)",
  "admin/editor.sandbox.height.title": "Height",
  "admin/editor.sandbox.height.description": "Iframe height (e.g. `20px`)",
  "admin/editor.sandbox.allowCookies.title": "Allow cookies",
  "admin/editor.sandbox.allowCookies.description": "Allow scripts in iframe to read and write cookies"
}

```


[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 sandbox/messages/es.json from the sandbox VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin/editor.sandbox.title": "Sandbox",
  "admin/editor.sandbox.description": "Un componente sandbox que renderiza contenido HTML dentro de un iframe seguro",
  "admin/editor.sandbox.initialContent.title": "HTML de contenido inicial",
  "admin/editor.sandbox.initialContent.description": "Cualquier HTML válido. Puede incluir scripts. Las propiedades pasadas a este bloque están disponibles en `window.props`.",
  "admin/editor.sandbox.width.title": "Largura",
  "admin/editor.sandbox.width.description": "Largura do Iframe (e.g. `400px`)",
  "admin/editor.sandbox.height.title": "Altura",
  "admin/editor.sandbox.height.description": "Altura do Iframe (e.g. `20px`)",
  "admin/editor.sandbox.allowCookies.title": "Permitir cookies",
  "admin/editor.sandbox.allowCookies.description": "Permitir que scripts en el iframe leen y escriba cookies"
}

```


[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 sandbox/docs/README.md from the sandbox VTEX app.

[Assistant]
Here's the implementation:

```markdown
# VTEX IO Sandbox Component
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-0-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->

Allows mounting arbitrary HTML content in extension points from the comfort and safety of an iframe.

### Example block

```json
  "sandbox.product": {
    "props": {
      "width": "200px",
      "height": "60px",
      "initialContent": "
initial
",
      "allowCookies": true
    }
  },
  "sandbox.order": {
    "props": {
      "width": "200px",
      "height": "60px",
      "initialContent": "",
      "allowCookies": true
    }
  },
  "sandbox#home": {
    "props": {
      "width": "200px",
      "height": "60px",
      "initialContent": "
home
",
      "allowCookies": true
    }
  },
  "store.home": {
    "blocks": ["carousel#home", "shelf#home", "sandbox#home"]
  },
  "store.product": {
    "blocks": [
      "product-details#default",
      "sandbox#product"
    ]
  },
```

## Contributors ✨

Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):

<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->

This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
```


[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 sandbox/react/SandboxProduct.tsx from the sandbox VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { useProduct } from 'vtex.product-context'
import { Props, schema } from './modules/schema'
import Sandbox from './Sandbox'

const SandboxProduct: StorefrontFunctionComponent<Props> = ({
  initialContent,
  width,
  height,
  allowCookies,
  allowStyles,
  hidden,
}) => {
  const productContext = useProduct()

  return (
    <Sandbox
      {...productContext}
      initialContent={initialContent}
      width={width}
      height={height}
      allowCookies={allowCookies}
      allowStyles={allowStyles}
      hidden={hidden} />
  )
}

SandboxProduct.schema = schema

export default SandboxProduct
```


[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 sandbox/react/package.json from the sandbox VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "scripts": {
    "test": "vtex-test-tools test",
    "lint": "tsc --noEmit && eslint --ext ts ."
  },
  "dependencies": {
    "@vtex/css-handles": "^1.0.0",
    "classnames": "^2.2.6",
    "iframe-resizer": "^4.1.1",
    "ramda": "^0.26.1",
    "react": "^16.8.3",
    "react-dom": "^16.8.3",
    "react-intl": "^2.7.2",
    "safe-json-stringify": "^1.2.0"
  },
  "devDependencies": {
    "@types/classnames": "^2.2.7",
    "@types/graphql": "^14.2.0",
    "@types/iframe-resizer": "^3.5.7",
    "@types/jest": "^24.0.11",
    "@types/node": "^11.13.0",
    "@types/prop-types": "^15.7.0",
    "@types/ramda": "^0.26.5",
    "@types/react": "^16.8.10",
    "@types/react-intl": "^2.3.17",
    "@types/safe-json-stringify": "^1.0.1",
    "@vtex/test-tools": "^0.2.0",
    "apollo-client": "^2.5.1",
    "eslint": "^5.16.0",
    "eslint-config-vtex": "^10.1.0",
    "prettier": "^1.16.4",
    "tslint-eslint-rules": "^5.4.0",
    "typescript": "3.7.3"
  },
  "version": "0.5.1"
}

```


[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 sandbox/react/SandboxOrder.tsx from the sandbox VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { Props, schema } from './modules/schema'
import { useOrderForm } from 'vtex.store-resources/OrderFormContext'
import Sandbox from './Sandbox'

const SandboxOrder: StorefrontFunctionComponent<Props> = ({
  initialContent,
  width,
  height,
  allowCookies,
  allowStyles,
  hidden,
}) => {
  const orderForm = useOrderForm()

  return (
    <Sandbox
      orderForm={orderForm}
      initialContent={initialContent}
      width={width}
      height={height}
      allowCookies={allowCookies}
      allowStyles={allowStyles}
      hidden={hidden} />
  )
}

SandboxOrder.schema = schema

export default SandboxOrder
```


[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 sandbox/react/Sandbox.tsx from the sandbox VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { Component } from 'react'
import { NoSSR, canUseDOM } from 'vtex.render-runtime'
import stringify from 'safe-json-stringify'
import { schema, Props } from './modules/schema'

// Avoid breaking SSR by loading this only in the client side
let iFrameResizer: any = null
if (canUseDOM) {
  iFrameResizer = require('iframe-resizer/js/iframeResizer')
}

interface StyleContainer {
  href?: string
  rules?: string
}

interface IframeOptions {
  cookieEventType: string
  propsEventType: string
  cookie: string
  styles: StyleContainer[]
}

// Create objects representing styles, either with href or the actual rules, to be passed to the iframe.
function getExistingStyles() {
  const styles: StyleContainer[] = []
  for (let i = 0; i < document.styleSheets.length; i++) {
    const stylesheet: any = document.styleSheets.item(i)
    if (stylesheet !== null) {
      if (stylesheet.href) {
        styles.push({href: stylesheet.href})
      } else {
        styles.push({rules: Array.prototype.map.call(stylesheet.rules, (r: any) => r.cssText).join('\n') as string })
      }
    }
  }
  return styles
}

const appId = process.env.VTEX_APP_ID
const cookieEventType = `${appId}:iframeCookieSet`
const propsEventType = `${appId}:props`

if (canUseDOM) {
  window.addEventListener('message', function(event: MessageEvent) {
    if (event.data && event.data.type === cookieEventType) {
      document.cookie = event.data.value
    }
  })
}

function initIframe (options: IframeOptions) {
  const {styles, cookie, cookieEventType, propsEventType} = options

  // Mount cookie
  if (cookie) {
    (window as any).__cookie = cookie
    Object.defineProperty(document, 'cookie', {
      get: () => (window as any).__cookie,
      set: (value: string) => {
        if (!value) {
          return
        }
        const clean = value.split(';')[0];
        (window as any).__cookie = `${clean}; ` + (window as any).__cookie
        window.parent.postMessage({type: cookieEventType, value}, '*')
      },
    })
  }

  // Add styles
  if (styles && Array.isArray(styles)) {
    styles.forEach((style: StyleContainer) => {
      if (style.href) {
        const link = document.createElement('link')
        link.href = style.href
        link.type = 'text/css'
        link.rel = 'stylesheet'
        document.head.appendChild(link)
      } else if (style.rules) {
        const sheet = document.createElement('style')
        sheet.innerHTML = style.rules
        document.head.appendChild(sheet)
      }
    })
  }

  window.addEventListener('message', function(event: MessageEvent) {
    // Add props to window
    if (event.data && event.data.type === propsEventType) {
      (window as any).props = JSON.parse(event.data.props)
    }
  })
}

class Sandbox extends Component<Props> {
  public iframeRef: HTMLIFrameElement | null = null

  private injectedDocument: string = ''
  static schema: object;

  public constructor(props: Props) {
    super(props)

    const { initialContent, allowCookies, allowStyles: allowStylesProp } = props
    const allowStyles = allowStylesProp === undefined ? true : allowStylesProp

    if (!canUseDOM) {
      return
    }

    const cookie = allowCookies && document.cookie
    const styles = allowStyles && getExistingStyles()
    this.injectedDocument = `<html><head><script>window.props=${
      this.safeProps
    };(${
      initIframe.toString()
    })(${
      stringify({cookieEventType, propsEventType, styles, cookie})
    });</script><script type="text/javascript" src="https://unpkg.com/iframe-resizer@4.1.1/js/iframeResizer.contentWindow.js"></script></head><body>${initialContent}</body></html>`
  }

  public componentDidMount() {
    this.updateIframe()
  }

  public componentDidUpdate() {
    this.updateIframe()
  }

  setRef = (element: HTMLIFrameElement | null) => {
    if (!element) {
      return
    }

    this.iframeRef = element
    iFrameResizer({
      checkOrigin: false,
    }, this.iframeRef)
  }

  public render() {
    const { width = '100%', height, hidden } = this.props
    return (
      <NoSSR>
        <iframe
          ref={this.setRef}
          frameBorder={0}
          style={hidden ? {display: 'none'} : {width, height}}
          sandbox="allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-top-navigation"
          className="vtex-sandbox-iframe"
          hidden={hidden}
          src={`data:text/html;charset=UTF-8,${encodeURIComponent(this.injectedDocument)}`}>
        </iframe>
      </NoSSR>
    )
  }

  private get safeProps () {
    // We don't use this vars because we are interested in the `rest`
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const {runtime, iframeRef, initialContent, allowCookies, height, width, treePath, ...rest} = this.props as any
    return stringify(rest)
  }

  private updateIframe() {
    if (this.iframeRef && this.iframeRef.contentWindow) {
      const props = this.safeProps
      this.iframeRef.contentWindow.postMessage({type: propsEventType, props}, '*')
    }
  }
}

Sandbox.schema = schema

export default Sandbox
```


[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 sandbox/store/interfaces.json from the sandbox VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "sandbox": {
    "component": "Sandbox"
  },
  "sandbox.product": {
    "component": "SandboxProduct"
  },
  "sandbox.order": {
    "component": "SandboxOrder"
  }
}
```


[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 sandbox/react/typings/storefront.d.ts from the sandbox VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { FunctionComponent, ReactElement } from 'react'

declare global {
  interface StorefrontFunctionComponent<P = {}> extends FunctionComponent<P> {
    getSchema?(props: P): object
    schema?: object
  }

  interface StorefrontComponent<P = {}, S = {}> extends Component<P, S> {
    getSchema?(props: P): object
    schema: object
  }
}

```


[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 sandbox/react/typings/vtex.product-context.d.ts from the sandbox VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.product-context' {
  interface ProductContext {
    product: object
  }

  export const useProduct = ProductContext
}
```


[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 sandbox/react/typings/vtex.render-runtime.d.ts from the sandbox VTEX app.

[Assistant]
Here's the implementation:

```typescript
/* Typings for `render-runtime` */
declare module 'vtex.render-runtime' {
  import { Component, ComponentType, ReactElement, ReactType } from 'react'

  export interface NavigationOptions {
    page: string
    params?: any
  }

  export interface RenderContextProps {
    runtime: {
      navigate: (options: NavigationOptions) => void
    }
  }

  interface ExtensionPointProps {
    id: string
    [key: string]: any
  }

  export const ExtensionPoint: ComponentType<ExtensionPointProps>

  interface ChildBlockProps {
    id: string
  }

  export const ChildBlock: ComponentType<ChildBlockProps>
  export const useChildBlock = function({ id: string }): object {}

  export const Helmet: ReactElement
  export const Link: ReactType
  export const NoSSR: ReactType
  export const RenderContextConsumer: ReactElement
  export const canUseDOM: boolean
  export const withRuntimeContext: <TOriginalProps extends {}>(
    Component: ComponentType<TOriginalProps & RenderContextProps>
  ) => ComponentType<TOriginalProps>
}

```


[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 sandbox/react/typings/css.d.ts from the sandbox VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module '*.css' {
  const css: any
  export default css
}

```


[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 sandbox/react/typings/graphql.d.ts from the sandbox VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module '*.graphql' {
  import { DocumentNode } from 'graphql'

  const value: DocumentNode
  export default value
}
```


[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 sandbox/react/modules/schema.ts from the sandbox VTEX app.

[Assistant]
Here's the implementation:

```typescript
export interface Props {
  initialContent?: string
  width?: string
  height?: string
  allowCookies?: boolean
  allowStyles?: boolean
  hidden?: boolean
  [key: string]: any
}

export const schema = {
  title: 'admin/editor.sandbox.title',
  description: 'admin/editor.sandbox.description',
  type: 'object',
  properties: {
    width: {
      title: 'admin/editor.sandbox.width.title',
      description: 'admin/editor.sandbox.width.description',
      type: 'string',
      default: null,
    },
    height: {
      title: 'admin/editor.sandbox.height.title',
      description: 'admin/editor.sandbox.height.description',
      type: 'string',
      default: null,
    },
    initialContent: {
      title: 'admin/editor.sandbox.initialContent.title',
      description: 'admin/editor.sandbox.initialContent.description',
      type: 'string',
      default: null,
    },
    allowCookies: {
      title: 'admin/editor.sandbox.allowCookies.title',
      description: 'admin/editor.sandbox.allowCookies.description',
      type: 'boolean',
      default: false,
    },
  },
}
```


[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 adyen-platforms/messages/en.json from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "adyen.navigation.label-group": "Adyen for Platforms",
  "adyen.navigation.search.kws": "adyen platforms"
}

```


[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 adyen-platforms/messages/context.json from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "adyen.navigation.label-group": "admin-example.navigation.label-group",
  "adyen.navigation.search.kws": "adyen.navigation.search.kws"
}

```


[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 adyen-platforms/admin/navigation.json from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```json
[
  {
    "section": "marketplace",
    "LMProductId": "2",
    "titleId": "adyen.navigation.label-group",
    "searchKeyWordsHelpers": "adyen.navigation.search.kws",
    "path": "/admin/adyen-for-platforms"
  }
]

```


[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 adyen-platforms/admin/routes.json from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin.app.adyen-menu": {
    "component": "AdyenMenu",
    "path": "/admin/app/adyen-for-platforms"
  },
  "admin.app.seller-detail": {
    "component": "SellerDetail",
    "path": "/admin/app/adyen-for-platforms/:seller_id"
  }
}

```


[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 adyen-platforms/docs/README.md from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```markdown
📢 Use this project, [contribute](https://github.com/vtex-apps/connector-adyen) to it or open issues to help evolve it using [Store Discussion](https://github.com/vtex-apps/store-discussion).

# Adyen Platforms

<!-- DOCS-IGNORE:start -->
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->

[![All Contributors](https://img.shields.io/badge/all_contributors-0-orange.svg?style=flat-square)](#contributors-)

<!-- ALL-CONTRIBUTORS-BADGE:END -->
<!-- DOCS-IGNORE:end -->

This app integrates the [Adyen for Platforms](https://docs.adyen.com/platforms) service with your VTEX marketplace.

## Configuration

### Adyen Payment Setup

⚠ Before you can use the Adyen Platforms app, the [Adyen Payment connector](https://github.com/vtex-apps/connector-adyen) must be installed and configured in you VTEX account.

### Contacting Adyen

Users must first contact Adyen to get their account enabled to use Adyen for Platforms. To check if Adyen for Platforms is activated, look for the presence of a `Platform` tab in the Adyen merchant account admin sidebar. Further information: [Adyen for Platforms Quick Start Guide](https://docs.adyen.com/platforms/quick-start)

### Installing the App

1. Install this app in the desired account using the CLI command `vtex install vtex.adyen-platforms`.
2. In your admin sidebar, search for `Adyen for Platforms`. This will bring you to the Adyen for Platforms settings page.
3. Select the `Settings` tab:
   - Enter your Platform API credentials. To find them, log in to Adyen's admin interface and do the following:
      - Switch from `Company` account to `Merchant` account
      - Under the **Developers** menu, select `API credentials`
      - Select the username that follows this format `ws\_[123456]@MarketPlace.[YourPlatformAccount]`, if there is no username in that format, see **Creating New Web Service User**
      - Use `Generate New API Key` to create a new key for Adyen and store it somewhere safe. This can be regenerated if lost
   - Enter your production Adyen for Platforms API endpoint. For testing, you can enter the test endpoint: `https://cal-test.adyen.com/cal/services`. This is also found under the **Developers** menu in your Adyen account.
   - Enter the URL sub-merchants will be directed to when they complete their onboarding. This is optional, see the Onboarding section for details on this process.

### Managing Seller Accounts

The Sellers tab in the Adyen for Platforms menu in your VTEX admin will display all Seller accounts in your VTEX marketplace.
- Users can `Create Adyen Account` for specific sellers
  1. Enter a unique Adyen Account Holder Code (this can be the seller account name within VTEX or another string of your choosing)
      - Note: To view any Adyen Account Holder Codes that are already in use:
        - Ensure you're on Adyen's `Merchant` account in Adyen's website
        - Under the `Platform` tab, select `Sub-merchants`
  2. Enter the Country, Entity Type, Business Name, and Business Email
  3. Set the desired payout schedule. See section `Payout Schedule` for more information
- Users can create a new `Onboarding Link` and use that link to onboard users.
  - Note: After onboarding, it may take awhile before the user is activated for use.

#### Onboarding New Sellers

If a seller has not yet been onboarded, you will have the option to create an Adyen account for the selected seller. This option creates a unique URL for that seller. The URL needs to be provided to the seller, which will direct them to the Adyen [hosted onboarding page](https://docs.adyen.com/platforms/hosted-onboarding-page).

You will be able to accept payment on behalf of a seller immediately after created a Adyen account for them, but payouts of collected payments will be disabled until the seller completes the Adyen onboarding process.

ℹ After completing the Adyen hosted onboarding, users will be redirecting to the URL you entered in the app settings. If no URL was entered, they are redirected back to a default onboarding success page that is provided when you install the app, `https://[Store]/marketplace/onboard-complete/`

#### Payout Schedule

The Adyen default payout schedule for a seller is `daily`. You can change this setting to the desired interval

<!-- DOCS-IGNORE:start -->

## Contributors ✨

Thanks goes to these wonderful people:

<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->

<!-- ALL-CONTRIBUTORS-LIST:END -->

This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind are welcome!

<!-- DOCS-IGNORE:end -->

```


[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 adyen-platforms/graphql/schema.graphql from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```graphql
type Seller {
  id: String
  name: String
  logo: String
  isActive: Boolean
  fulfillmentEndpoint: String
  allowHybridPayments: Boolean
  taxCode: String
  email: String
  description: String
  sellerCommissionConfiguration: String
  catalogSystemEndpoint: String
  isBetterScope: Boolean
  sellerType: Int
  availableSalesChannels: [SalesChannel]
  CSCIdentification: String
  account: String
  channel: String
  salesChannel: String
  isVtex: Boolean
  score: Int
  exchangeReturnPolicy: String
  deliveryPolicy: String
  securityPrivacyPolicy: String
  fulfillmentSellerId: String
  trustPolicy: String
  adyenOnboarding: Onboarding
  adyenAccount: AdyenAccount
  adyenAccountHolder: AdyenAccountHolder
}

type AdyenAccount {
  sellerId: String
  accountCode: String
  status: String
}
type GetAdyenAccount {
  onboarding: Onboarding
  accountHolder: AdyenAccountHolder
}

type Onboarding {
  accountHolderCode: String
  urlToken: String
  invalidFields: [InvalidFields]
}

type InvalidFields {
  errorDescription: String
}

type SalesChannel {
  isSelected: Boolean
  id: Int
  name: String
}

type AdyenAccountUpdate {
  accountCode: String
  schedule: String
}

type AdyenAccountStatus {
  adyenAccountHolder: AdyenAccountHolder
  onboarding: Onboarding
  invalidFields: [InvalidFields]
}

type AdyenAccountHolder {
  accountHolderCode: String
  accountHolderStatus: AccountHolderStatus
  accountHolderDetails: AccountHolderDetails
  accounts: [Account]
  urlToken: String
}

type AccountHolderStatus {
  status: String
  processingState: AdyenProcessingState
  payoutState: AdyenPayoutState
}

type AccountHolderDetails {
  email: String
}

type AdyenProcessingState {
  disabled: Boolean
}

type AdyenPayoutState {
  allowPayout: Boolean
  disabled: Boolean
}

type Account {
  accountCode: String
  description: String
  payoutSchedule: PayoutSchedule
  payoutSpeed: String
  status: String
}

type PayoutSchedule {
  schedule: String
}

input AccountInput {
  sellerId: String
  country: String
  legalBusinessName: String
  email: String
  legalEntity: String
}

type Query {
  sellers: [Seller] @cacheControl(scope: PUBLIC, maxAge: SHORT)
  seller(sellerId: String): Seller @cacheControl(scope: PUBLIC, maxAge: SHORT)
  adyenAccountHolder(sellerId: String): AdyenAccountHolder
  getAdyenAccount(accountHolderCode: String): AdyenAccountHolder
    @cacheControl(scope: PUBLIC, maxAge: MEDIUM)
  onboarding(accountHolderCode: String): Onboarding
    @cacheControl(scope: PUBLIC, maxAge: MEDIUM)

  appSettings: String @cacheControl(scope: PRIVATE) @checkAdminAccess
}

type Mutation {
  createAccountHolder(
    accountHolderCode: String!
    sellerId: String!
    country: String!
    legalBusinessName: String!
    email: String!
    legalEntity: String!
    processingTier: Int!
  ): AdyenAccountStatus @cacheControl(scope: PRIVATE) @checkAdminAccess
  closeAccountHolder(accountHolderCode: String!): Onboarding
    @cacheControl(scope: PRIVATE)
    @checkAdminAccess
  onboardingComplete(accountHolderCode: String!): Onboarding
    @cacheControl(scope: PRIVATE)
    @checkAdminAccess
  refreshOnboarding(accountHolderCode: String!): Onboarding
    @cacheControl(scope: PRIVATE)
    @checkAdminAccess
  updateAccount(accountCode: String!, schedule: String!): AdyenAccountUpdate
    @cacheControl(scope: PRIVATE)
    @checkAdminAccess
}

```


[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 adyen-platforms/graphql/directives.graphql from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```graphql
directive @checkAdminAccess on FIELD | FIELD_DEFINITION

```


[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 adyen-platforms/node/service.json from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "stack": "nodejs",
  "memory": 512,
  "ttl": 43200,
  "timeout": 40,
  "minReplicas": 2,
  "maxReplicas": 12,
  "workers": 1,
  "routes": {
    "account": {
      "path": "/_v/api/adyen-platforms/v0/account",
      "public": true
    },
    "adyenWebhook": {
      "path": "/_v/api/adyen-platforms/v0/hook",
      "public": true
    },
    "onboarding": {
      "path": "/_v/api/adyen-platforms/v0/onboarding",
      "public": true
    }
  }
}

```


[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 adyen-platforms/node/package.json from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "dependencies": {
    "co-body": "^6.0.0",
    "uuid": "^8.3.1",
    "graphql": "^14.5.0",
    "graphql-tools": "^4.0.6"
  },
  "devDependencies": {
    "@types/bluebird": "^3.5.25",
    "@types/co-body": "^0.0.3",
    "@types/node": "^12.0.0",
    "@types/uuid": "^8.3.0",
    "@vtex/api": "6.45.12",
    "tslint": "^5.12.0",
    "tslint-config-vtex": "^2.1.0",
    "typescript": "3.9.7",
    "vtex.apps-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.apps-graphql@2.8.0/public/@types/vtex.apps-graphql",
    "vtex.css-handles": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.css-handles@0.4.4/public/@types/vtex.css-handles",
    "vtex.flex-layout": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.flex-layout@0.19.0/public/@types/vtex.flex-layout",
    "vtex.render-runtime": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.render-runtime@8.132.4/public/@types/vtex.render-runtime",
    "vtex.rich-text": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.rich-text@0.15.0/public/@types/vtex.rich-text",
    "vtex.store": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.store@2.125.1/public/@types/vtex.store",
    "vtex.store-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.store-graphql@2.152.2/public/@types/vtex.store-graphql",
    "vtex.store-sitemap": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.store-sitemap@2.14.0/public/@types/vtex.store-sitemap",
    "vtex.styleguide": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.146.1/public/@types/vtex.styleguide"
  },
  "scripts": {
    "lint": "tsc --noEmit && tslint -c tslint.json './**/*.ts'"
  },
  "version": "0.4.1"
}

```


[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 adyen-platforms/node/index.ts from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { ParamsContext, RecorderState, ServiceContext } from '@vtex/api'
import { Service } from '@vtex/api'

import { Clients } from './clients'
import { resolvers } from './api/resolvers'
import { routes } from './api/routes'
import { schemaDirectives } from './directives'

const MEDIUM_TIMEOUT_MS = 2 * 1000

declare global {
  type Context = ServiceContext<Clients>
}

export default new Service<Clients, RecorderState, ParamsContext>({
  clients: {
    implementation: Clients,
    options: {
      default: {
        timeout: MEDIUM_TIMEOUT_MS,
      },
    },
  },
  graphql: {
    resolvers,
    schemaDirectives,
  },
  routes,
})

```


[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 adyen-platforms/react/AdyenMenu.tsx from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { FC } from 'react'
import React from 'react'
import { useQuery } from 'react-apollo'
import {
  Columns,
  ThemeProvider,
  DataGrid,
  Tag,
  Heading,
  Text,
  useDataGridState,
  Skeleton,
  Tabs,
  Card,
  useTabState,
} from '@vtex/admin-ui'
import { useRuntime } from 'vtex.render-runtime'

import './styles.global.css'
import Settings from './components/Settings'
import Sellers from './graphql/Sellers.graphql'

const AdyenMenu: FC = () => {
  const { navigate } = useRuntime()
  const { loading, data } = useQuery(Sellers)

  const tabState = useTabState()

  const handleRowClick = (item: any) => {
    navigate({
      page: `admin.app.seller-detail`,
      params: { seller_id: item.id },
    })
  }

  const items: any[] = !loading && data ? data.sellers : []
  const gridState = useDataGridState({
    density: 'compact',
    onRowClick: (item: any) => handleRowClick(item),
    columns: [
      {
        id: 'account',
        header: 'Account',
      },
      {
        id: 'name',
        header: 'Name',
      },
      {
        id: 'status',
        header: 'Status',
        resolver: {
          type: 'root',
          render: function Description({ item, context }) {
            if (context.status === 'loading') {
              return <Skeleton csx={{ height: 24 }} />
            }

            return (
              <Tag
                size="small"
                palette={
                  item?.adyenAccount?.status === 'Active' ? 'green' : undefined
                }
                label={
                  !item.adyenAccount ? 'No Account' : item.adyenAccount.status
                }
              />
            )
          },
        },
      },
    ],
    items,
  })

  if (gridState.status !== 'loading' && !data && loading) {
    gridState.setStatus({
      type: 'loading',
    })
  }

  if (gridState.status === 'loading' && !loading) {
    gridState.setStatus({
      type: 'ready',
    })
  }

  return (
    <ThemeProvider>
      <Columns
        csx={{
          padding: 80,
        }}
      >
        <Columns.Item>
          <Heading>Adyen for Platforms</Heading>
          <Tabs state={tabState}>
            <Tabs.List csx={{ marginTop: 6 }} aria-label="behavior-tabs">
              <Tabs.Tab label="Sellers" id="1" />
              <Tabs.Tab label="Settings" id="2" />
            </Tabs.List>
            <Tabs.Content id="1">
              <Card csx={{ marginTop: 2 }}>
                <Text variant="subtitle" csx={{ marginBottom: 9 }}>
                  Manage Adyen account onboarding and payouts for your connected
                  seller accounts
                </Text>
                <DataGrid state={gridState} />
              </Card>
            </Tabs.Content>
            <Tabs.Content id="2">
              <Settings />
            </Tabs.Content>
          </Tabs>
        </Columns.Item>
      </Columns>
    </ThemeProvider>
  )
}

export default AdyenMenu

```


[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 adyen-platforms/react/SellerDetail.tsx from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { FC } from 'react'
import React, { useState } from 'react'
import { ThemeProvider, Card, Divider } from '@vtex/admin-ui'
import { useQuery } from 'react-apollo'
import { useRuntime } from 'vtex.render-runtime'

import Seller from './graphql/Seller.graphql'
import Onboarding from './graphql/Onboarding.graphql'
import AdyenAccountHolder from './graphql/AdyenAccountHolder.graphql'
import SellerDetailTitle from './components/sellerDetailTitle'
import SellerPayouts from './components/sellerPayouts'
import SellerOnboarding from './components/sellerOnboarding'
import SellerAccountDetails from './components/sellerAccountDetails'
import { StateContext } from './context/StateContext'

import './styles.global.css'

const SellerDetail: FC = () => {
  const {
    route: { params },
  } = useRuntime()

  const [state, setState] = useState<any>({
    onboarding: null,
    seller: null,
    adyenAccountHolder: null,
    setContextState: (inputState: any) => {
      setState((prevState: any) => ({
        ...prevState,
        ...inputState,
      }))
    },
  })

  const { loading: loadingS, data: sellerData } = useQuery(Seller, {
    variables: { sellerId: params.seller_id },
    onCompleted: () => {
      setState((prevState: any) => ({
        ...prevState,
        seller: sellerData.seller,
      }))
    },
  })

  const { loading: loadingA, data: adyenData } = useQuery(AdyenAccountHolder, {
    variables: { sellerId: params.seller_id },
    onCompleted: () => {
      setState((prevState: any) => ({
        ...prevState,
        adyenAccountHolder: adyenData?.adyenAccountHolder,
      }))
    },
  })

  const { accountHolderCode } = adyenData?.adyenAccountHolder || {}
  const { loading: loadingO, data: onboardingData } = useQuery(Onboarding, {
    variables: {
      accountHolderCode,
    },
    skip: !accountHolderCode,
    onCompleted: () => {
      setState((prevState: any) => ({
        ...prevState,
        onboarding: onboardingData.onboarding,
      }))
    },
  })

  const isLoading = loadingS && loadingO && loadingA

  if (isLoading || !sellerData) return null

  return (
    <ThemeProvider>
      <StateContext.Provider value={state}>
        <Card
          csx={{
            padding: 60,
            height: '100%',
          }}
        >
          <SellerDetailTitle />
          <SellerOnboarding />
          <Divider csx={{ marginY: 6 }} />
          <SellerPayouts />
          <Divider csx={{ marginY: 6 }} />
          <SellerAccountDetails />
        </Card>
      </StateContext.Provider>
    </ThemeProvider>
  )
}

export default SellerDetail

```


[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 adyen-platforms/react/package.json from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "name": "vtex.admin-platforms",
  "version": "0.4.1",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {},
  "devDependencies": {
    "@types/node": "^11.12.1",
    "@types/react": "^16.8.10",
    "@types/react-intl": "^2.3.17",
    "@vtex/tsconfig": "^0.4.4",
    "@vtex/admin-ui-icons": "^0.12.4",
    "@vtex/admin-ui": "^0.106.5",
    "apollo-client": "^2.4.7",
    "graphql": "^14.0.2",
    "prop-types": "^15.7.2",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-intl": "^2.7.2",
    "react-apollo": "^3.1.3",
    "typescript": "3.9.7",
    "vtex.render-runtime": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.render-runtime@8.130.0/public/@types/vtex.render-runtime",
    "vtex.styleguide": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.142.0/public/@types/vtex.styleguide"
  }
}

```


[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 adyen-platforms/react/styles.global.css from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```css
html,
body,
#render-container,
#render-provider,
.render-container,
.render-provider {
  height: 100%;
}

```


[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 adyen-platforms/react/OnboardSuccess.tsx from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { FC } from 'react'
import React, { useEffect } from 'react'
import { useMutation } from 'react-apollo'
import { useRuntime } from 'vtex.render-runtime'
import { PageBlock, Layout, PageHeader } from 'vtex.styleguide'

import OnboardingComplete from './graphql/OnboardingComplete.graphql'

const OnboardSuccess: FC = () => {
  const [updateOnboarding] = useMutation(OnboardingComplete)
  const {
    route: { queryString },
  } = useRuntime()

  useEffect(() => {
    if (!queryString?.account) return

    updateOnboarding({
      variables: { accountHolderCode: queryString.account },
    })
  }, [queryString?.account, updateOnboarding])

  return (
    <Layout pageHeader={<PageHeader title="Adyen Onboarding" />}>
      <PageBlock>
        <div className="mb4">Adyen onboarding is complete.</div>

        <div>
          Once Adyen has finished with the verification process, payouts on your
          account will be enabled.
        </div>
      </PageBlock>
    </Layout>
  )
}

export default OnboardSuccess

```


[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 adyen-platforms/store/blocks.json from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "store.adyen-onboarding": {
    "blocks": ["onboardSuccess"]
  }
}

```


[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 adyen-platforms/store/interfaces.json from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "store.adyen-onboarding": {},
  "onboardSuccess": {
    "component": "OnboardSuccess"
  }
}

```


[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 adyen-platforms/store/routes.json from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "store.adyen-onboarding": {
    "path": "/marketplace/onboard-complete",
    "isSitemapEntry": false
  }
}

```


[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 adyen-platforms/node/directives/checkAdminAccess.ts from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { GraphQLField } from 'graphql'
import { defaultFieldResolver } from 'graphql'
import { SchemaDirectiveVisitor } from 'graphql-tools'
import { AuthenticationError, ForbiddenError } from '@vtex/api'

const validateUser = async (ctx: Context) => {
  const {
    vtex: { adminUserAuthToken, logger },
    clients: { identity },
  } = ctx

  if (!adminUserAuthToken) {
    throw new AuthenticationError('No Token Was Provided')
  }

  try {
    await identity.validateToken(adminUserAuthToken)
  } catch (error) {
    logger.warn({
      error,
      message: 'adyenPlatform-UnauthorizedAccess',
    })

    throw new ForbiddenError('Unauthorized Access')
  }
}

export class CheckAdminAccess extends SchemaDirectiveVisitor {
  public visitFieldDefinition(field: GraphQLField<any, any>) {
    const { resolve = defaultFieldResolver } = field

    field.resolve = async (
      root: any,
      args: any,
      context: Context,
      info: any
    ) => {
      await validateUser(context)

      return resolve(root, args, context, 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 adyen-platforms/node/directives/index.ts from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { CheckAdminAccess } from './checkAdminAccess'

export const schemaDirectives = {
  checkAdminAccess: CheckAdminAccess as any,
}

```


[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 adyen-platforms/node/clients/identity.ts from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

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

export class IdentityClient extends JanusClient {
  constructor(ctx: IOContext, options?: InstanceOptions) {
    super(ctx, {
      ...options,
      headers: {
        ...options?.headers,
      },
    })
  }

  public async validateToken(token: string): Promise<any> {
    return this.http.post(`/api/vtexid/credential/validate`, { token })
  }
}

```


[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 adyen-platforms/node/clients/sellers.ts from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

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

const SELLERS_URL = '/api/seller-register/pvt/sellers' as const

interface SellersResponse {
  paging: {
    from: number
    to: number
    total: number
  }
  items: Seller[]
}

interface Seller {
  id: string
  name: string
  logo: string
  isActive: boolean
  fulfillmentEndpoint: string
  allowHybridPayments: boolean
  taxCode: string
  email: string
  description: string
  sellerCommissionConfiguration: string
  catalogSystemEndpoint: string
  isBetterScope: boolean
  sellerType: number
  availableSalesChannels: SalesChannel[]
  CSCIdentification: string
  account: string
  channel: string
  salesChannel: number
  isVtex: boolean
  score: number
  exchangeReturnPolicy: string
  deliveryPolicy: string
  securityPrivacyPolicy: string
  fulfillmentSellerId: string
  trustPolicy: string
}

interface SalesChannel {
  isSelected: boolean
  id: number
  name: string
}

export class SellersClient extends JanusClient {
  constructor(context: IOContext, options?: InstanceOptions) {
    super(context, {
      ...options,
      headers: {
        ...options?.headers,
        VtexIdClientAutCookie: context.authToken,
      },
    })
  }

  public sellers = async () => {
    return this.http.get<SellersResponse>(SELLERS_URL, {
      metric: 'adyen-getSellers',
    })
  }

  public seller = async (sellerId: string) => {
    return this.http.get<SellersResponse>(`${SELLERS_URL}/${sellerId}`, {
      metric: 'adyen-getSeller',
    })
  }
}

```


[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 adyen-platforms/node/clients/adyen.ts from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```typescript
/* eslint-disable @typescript-eslint/consistent-type-imports */
import { ExternalClient, InstanceOptions, IOContext } from '@vtex/api'

const TEST_URL = 'https://cal-test.adyen.com/cal/services' as const

export default class Adyen extends ExternalClient {
  constructor(protected context: IOContext, options?: InstanceOptions) {
    super('', context, options)
  }

  private getEndpoint(settings: any) {
    return settings.productionAPI ?? TEST_URL
  }

  public async createAccountHolder(
    accountHolder: CreateAccountHolderRequest,
    settings: any
  ): Promise<CreateAccountHolderResponse> {
    return this.http.post(
      `${this.getEndpoint(settings)}/Account/v6/createAccountHolder
      `,
      accountHolder,
      {
        headers: {
          'X-API-Key': settings.apiKey,
          'X-Vtex-Use-Https': 'true',
          'Content-Type': 'application/json',
        },
        metric: 'adyen-createAccountHolder',
      }
    )
  }

  public async closeAccountHolder(
    accountHolderCode: string,
    settings: any
  ): Promise<any> {
    return this.http.post<any>(
      `${this.getEndpoint(settings)}/Account/v6/closeAccountHolder
      `,
      { accountHolderCode },
      {
        headers: {
          'X-API-Key': settings.apiKey,
          'X-Vtex-Use-Https': 'true',
          'Content-Type': 'application/json',
        },
        metric: 'adyen-deleteAccountHolder',
      }
    )
  }

  public async getAccountHolder(
    accountHolderCode: string,
    settings: any
  ): Promise<GetAccountHolderResponse> {
    return this.http.post(
      `${this.getEndpoint(settings)}/Account/v6/getAccountHolder
      `,
      { accountHolderCode },
      {
        headers: {
          'X-API-Key': settings.apiKey,
          'X-Vtex-Use-Https': 'true',
          'Content-Type': 'application/json',
        },
        metric: 'adyen-getAccountHolder',
      }
    )
  }

  public async updateAccount({ data, settings }: any): Promise<any> {
    const { accountCode, schedule } = data

    return this.http.post(
      `${this.getEndpoint(settings)}/Account/v6/updateAccount`,
      {
        accountCode,
        payoutSchedule: {
          action: 'UPDATE',
          reason: 'Update the payout schedule',
          schedule,
        },
      },
      {
        headers: {
          'X-API-Key': settings.apiKey,
          'X-Vtex-Use-Https': 'true',
          'Content-Type': 'application/json',
        },
        metric: 'adyen-updateAccount',
      }
    )
  }

  public async getOnboardingUrl({
    data,
    settings,
  }: any): Promise<GetOnboardingUrlResponse> {
    return this.http.post(
      `${this.getEndpoint(settings)}/Hop/v6/getOnboardingUrl`,
      data,
      {
        headers: {
          'X-API-Key': settings.apiKey,
          'X-Vtex-Use-Https': 'true',
          'Content-Type': 'application/json',
        },
        metric: 'adyen-getOnboardingURL',
      }
    )
  }
}

```


[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 adyen-platforms/node/clients/index.ts from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

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

import Adyen from './adyen'
import { Onboarding } from './masterdata/onboarding'
import { Account } from './masterdata/account'
import { SellersClient } from './sellers'
import { IdentityClient } from './identity'

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

  public get sellersClient() {
    return this.getOrSet('sellersClient', SellersClient)
  }

  public get onboarding() {
    return this.getOrSet('onboarding', Onboarding)
  }

  public get account() {
    return this.getOrSet('account', Account)
  }

  public get identity() {
    return this.getOrSet('identity', IdentityClient)
  }
}

```


[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 adyen-platforms/node/typings/adyen.d.ts from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```typescript
interface CreateAccountHolderRequest {
  accountHolderCode: string
  accountHolderDetails: AccountHolderDetails
  legalEntity: string
}

interface GetAccountHolderResponse {
  accountHolderCode: string
  accountHolderDetails: AccountHolderDetails
  accountHolderStatus: AccountHolderStatus
  legalEntity: string
  verification: {
    accountHolder: {
      checks: Check[]
    }
  }
  accounts: Account[]
}

interface Account {
  accountCode: string
  payoutSchedule: {
    schedule: string
  }
}

interface GetOnboardingUrlRequest {
  accountHolderCode: string
}

interface GetOnboardingUrlResponse {
  invalidFields: InvalidField[]
  pspReference: string
  resultCode: string
  redirectUrl: string
}

interface AccountHolderDetails {
  address: Address
  businessDetails: BusinessDetails
  email: string
}

interface Address {
  country: string
}

interface BusinessDetails {
  legalBusinessName: string
}

interface CreateAccountHolderResponse {
  invalidFields: string[]
  pspReference: string
  accountCode: string
  accountHolderCode: string
  accountHolderDetails: AccountHolderDetails
  accountHolderStatus: AccountHolderStatus
  legalEntity: string
  verification: {
    accountHolder: {
      checks: Check[]
    }
  }
}

interface AccountHolderStatus {
  status: 'Active' | 'Inactive' | 'Suspended' | 'Closed'
  processingState: {
    disabled: boolean
    processedFrom: {
      currency: string
      value: number
    }
    processedTo: {
      currency: string
      value: number
    }
    tierNumber: number
  }
  payoutState: {
    allowPayout: boolean
    disabled: boolean
  }
  events: Event[]
}

interface Event {
  event: string
  executionDate: string
  reason: string
}

interface Check {
  type: string
  status: string
  requiredFields?: string[]
}

interface InvalidField {
  errorCode: string
  errorDescription: string
  fieldType: {
    field: string
    fieldName: string
    shareholderCode: 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 adyen-platforms/node/typings/sellers.d.ts from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```typescript
interface SellerAccount {
  sellerId: string
  accountHolderCode: string
  accountCode: string
  status: 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 adyen-platforms/node/services/onboardingService.ts from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { v4 as uuidv4 } from 'uuid'

import { settings } from './utils'

const ONE_DAY = 86400000 as const

const buildUrl = (ctx: any, accountHolderCode: any) => {
  return `https://${ctx.vtex.production ? `` : `${ctx.vtex.workspace}--`}${
    ctx.vtex.account
  }.myvtex.com/marketplace/onboard-complete/?account=${accountHolderCode}`
}

export default {
  getOnboardingUrl: async (ctx: Context, urlToken: string) => {
    const {
      clients: { onboarding: onboardingClient, adyenClient },
    } = ctx

    const [onboarding] = (await onboardingClient.find({ urlToken })) ?? []

    if (!onboarding) return null

    if (onboarding.expirationTimestamp < Date.now()) {
      throw new Error('Link has expired')
    }

    const appSettings = await settings(ctx)

    const onboardingCompleteRedirect = appSettings?.onboardingRedirectUrl
      ? appSettings.onboardingRedirectUrl
      : buildUrl(ctx, onboarding.accountHolderCode)

    const adyenOnboarding = await adyenClient.getOnboardingUrl({
      data: {
        accountHolderCode: onboarding.accountHolderCode,
        returnUrl: onboardingCompleteRedirect,
      },
      settings: appSettings,
    })

    return adyenOnboarding.redirectUrl ?? null
  },
  getOnboarding: async (ctx: Context, accountHolderCode: string) => {
    const {
      clients: { onboarding: onboardingClient },
      vtex: { logger },
    } = ctx

    try {
      const [onboarding] =
        (await onboardingClient.find({
          accountHolderCode,
        })) ?? []

      if (!onboarding) return null

      return onboarding
    } catch (error) {
      logger.error({
        error,
        message: 'adyenPlatform-getOnboardingError',
      })

      return null
    }
  },
  create: async (ctx: Context, accountHolderCode: string) => {
    const {
      clients: { onboarding: onboardingClient },
      vtex: { logger },
    } = ctx

    const urlToken = uuidv4()
    const expirationTimestamp = Date.now() + 7 * ONE_DAY

    try {
      await onboardingClient.create({
        data: {
          accountHolderCode,
          urlToken,
          expirationTimestamp,
        },
      })

      return {
        accountHolderCode,
        urlToken,
      }
    } catch (error) {
      logger.error({
        error,
        message: 'adyenPlatform-createOnboardingError',
      })

      return null
    }
  },
  refreshOnboarding: async (
    ctx: Context,
    data: { accountHolderCode: string }
  ) => {
    const {
      clients: { onboarding: onboardingClient },
      vtex: { logger },
    } = ctx

    try {
      const [onboarding] = (await onboardingClient.find(data)) ?? []

      if (!onboarding) return null

      const { id, ...update } = onboarding

      update.urlToken = uuidv4()

      await onboardingClient.update(id, {
        ...update,
        expirationTimestamp: Date.now() + 7 * ONE_DAY,
      })

      return update
    } catch (error) {
      logger.error({
        error,
        message: 'adyenPlatform-refreshOnboardingError',
      })

      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 adyen-platforms/node/services/sellerService.ts from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { replaceDefaultSchedule, settings } from './utils'

function getAccount({
  seller,
  adyenAccounts,
}: {
  seller: any
  adyenAccounts: any
}): SellerAccount | null {
  let adyenAccount: SellerAccount | null = null

  for (const account of adyenAccounts) {
    if (
      account.sellerId === seller.id &&
      (!adyenAccount || account.status === 'Active')
    ) {
      adyenAccount = account
      if (account.status === 'Active') {
        break
      }
    }
  }

  return adyenAccount
}

export default {
  all: async ({ ctx }: { ctx: Context }) => {
    const {
      clients: { sellersClient, account: accountClient, adyenClient },
      vtex: { logger },
    } = ctx

    try {
      const { items: sellers } = await sellersClient.sellers()
      const { data: adyenAccounts } = await accountClient.all()
      const settingsFetch = await settings(ctx)

      const response = await Promise.all(
        sellers
          .reduce((prev, seller) => {
            if (seller.account === ctx.vtex.account) return prev

            prev.push({
              ...seller,
              adyenAccount: getAccount({
                seller,
                adyenAccounts,
              }),
            })

            return prev
          }, [] as any)
          .map(async (seller: any) => {
            if (seller?.adyenAccount) {
              return adyenClient
                .getAccountHolder(
                  seller.adyenAccount.accountHolderCode,
                  settingsFetch
                )
                .then(accountHolder => {
                  seller.adyenAccount.status =
                    accountHolder?.accountHolderStatus.status ??
                    seller.adyenAccount.status

                  return seller
                })
            }

            return seller
          })
      )

      return response
    } catch (error) {
      logger.error({
        error,
        message: 'adyenPlatform-getAllSellersError',
      })

      return []
    }
  },
  findOne: async (sellerId: string, ctx: Context) => {
    const {
      clients: {
        sellersClient,
        account: accountClient,
        adyenClient,
        onboarding: onboardingClient,
      },
      vtex: { logger },
    } = ctx

    try {
      const seller = await sellersClient.seller(sellerId)
      const accounts = await accountClient.find({ sellerId })

      if (!accounts.length) return seller

      const account = accounts.find(a => a.status === 'Active') ?? accounts[0]

      const adyenAccountHolder = await adyenClient.getAccountHolder(
        account.accountHolderCode,
        await settings(ctx)
      )

      adyenAccountHolder.accounts = await replaceDefaultSchedule(
        ctx,
        adyenAccountHolder.accounts,
        account.accountCode
      )

      const [onboarding] =
        (await onboardingClient.find({
          accountHolderCode: account.accountHolderCode,
        })) ?? []

      const adyenOnboarding = onboarding ?? null

      return {
        ...seller,
        adyenOnboarding,
        adyenAccountHolder,
      }
    } catch (error) {
      if (error.response.status === 404) {
        logger.warn({
          error,
          message: 'adyenPlatform-findOneSellerNotFound',
        })
      } else {
        logger.error({
          error,
          message: 'adyenPlatform-findOneSellerError',
        })
      }

      return {}
    }
  },
}

```


[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 adyen-platforms/node/services/accountService.ts from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```typescript
export default {
  findBySellerId: async ({
    ctx,
    sellerIds,
  }: {
    ctx: Context
    sellerIds: string[]
  }) => {
    const {
      clients: { account },
      vtex: { logger },
    } = ctx

    try {
      return await account.findBySellerId(sellerIds)
    } catch (error) {
      if (error.response.status === 404) {
        logger.warn({
          error,
          message: 'adyenPlatforms-findBySellerIdNotFound',
        })
      } else {
        logger.error({
          error,
          message: 'adyenPlatforms-findBySellerIdError',
        })
      }

      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 adyen-platforms/node/services/utils.ts from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```typescript
const appId = process.env.VTEX_APP_ID as string

export const settings = async (context: Context): Promise<any> => {
  const {
    clients: { apps },
    vtex: { logger },
  } = context

  try {
    return await apps.getAppSettings(appId)
  } catch (error) {
    logger.error({
      error,
      message: 'adyenPlatforms-getAppSettingsError',
    })

    return null
  }
}

export const replaceDefaultSchedule = async (
  context: Context,
  accounts: Account[],
  accountCode: string
): Promise<Account[]> => {
  const {
    clients: { vbase },
  } = context

  const savedPayoutSchedules = await vbase.getJSON<{
    [accountCode: string]: string
  }>('adyen-platforms', 'updatedPayoutSchedule', true)

  if (
    !savedPayoutSchedules ||
    !Object.prototype.hasOwnProperty.call(savedPayoutSchedules, accountCode)
  ) {
    return accounts
  }

  return accounts.map(account => {
    if (
      accountCode === account.accountCode &&
      account.payoutSchedule?.schedule === 'DEFAULT'
    ) {
      return {
        ...account,
        payoutSchedule: {
          schedule: savedPayoutSchedules[accountCode],
        },
      }
    }

    return account
  })
}

```


[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 adyen-platforms/node/services/adyenService.ts from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type {
  CreateAccountHolderDTO,
  AccountUpdateDTO,
} from '../api/resolvers/adyen'
import { service } from '.'
import { settings, replaceDefaultSchedule } from './utils'

export default {
  createAccountHolder: async ({
    ctx,
    data,
  }: {
    ctx: Context
    data: CreateAccountHolderDTO
  }) => {
    const {
      clients: { adyenClient, account },
      vtex: { logger },
    } = ctx

    const {
      country,
      legalBusinessName,
      legalEntity,
      email,
      sellerId,
      processingTier,
    } = data

    const accountHolder = {
      accountHolderCode: data.accountHolderCode,
      legalEntity,
      processingTier,
      accountHolderDetails: {
        address: {
          country,
        },
        businessDetails: {
          legalBusinessName,
        },
        email,
      },
    }

    try {
      const adyenAccountHolder = await adyenClient.createAccountHolder(
        accountHolder,
        await settings(ctx)
      )

      if (!adyenAccountHolder) return

      const { accountHolderCode, accountHolderStatus, accountCode } =
        adyenAccountHolder

      const onboarding = await service.onboarding.create(ctx, accountHolderCode)

      await account.save({
        data: {
          sellerId,
          accountHolderCode,
          accountCode,
          status: accountHolderStatus.status,
        },
      })

      return {
        onboarding,
        adyenAccountHolder: accountHolder,
        invalidFields: null,
      }
    } catch (error) {
      if (error.response?.data?.invalidFields?.length) {
        logger.warn({
          error,
          message: 'adyenPlatforms-createAccountHolderInvalidFields',
        })

        return { invalidFields: error.response?.data?.invalidFields }
      }

      logger.error({
        error,
        message: 'adyenPlatforms-createAccountHolderError',
      })

      return null
    }
  },
  getAccountHolder: async ({
    ctx,
    data,
  }: {
    ctx: Context
    data: { sellerId: string }
  }) => {
    const {
      clients: { adyenClient },
      vtex: { logger },
    } = ctx

    const accounts = await service.account.findBySellerId({
      ctx,
      sellerIds: [data.sellerId],
    })

    if (!accounts) return null

    let [{ accountHolderCode, accountCode }] = accounts

    for (const account of accounts) {
      if (account.status !== 'Closed') {
        accountHolderCode = account.accountHolderCode
        accountCode = account.accountCode
        break
      }
    }

    try {
      const accountHolder = await adyenClient.getAccountHolder(
        accountHolderCode,
        await settings(ctx)
      )

      accountHolder.accounts = await replaceDefaultSchedule(
        ctx,
        accountHolder.accounts,
        accountCode
      )

      return accountHolder
    } catch (error) {
      logger.error({
        error,
        message: 'adyenPlatforms-getAccountHolderError',
      })

      return null
    }
  },
  updateAccount: async ({
    ctx,
    data,
  }: {
    ctx: Context
    data: AccountUpdateDTO
  }) => {
    const {
      clients: { adyenClient, vbase },
      vtex: { logger },
    } = ctx

    try {
      const response = await adyenClient.updateAccount({
        settings: await settings(ctx),
        data,
      })

      const {
        accountCode,
        payoutSchedule: { schedule },
      } = response

      const savedPayoutSchedules = await vbase.getJSON<{
        [accountCode: string]: string
      }>('adyen-platforms', 'updatedPayoutSchedule', true)

      await vbase.saveJSON('adyen-platforms', 'updatedPayoutSchedule', {
        ...savedPayoutSchedules,
        [accountCode]: schedule,
      })

      return {
        accountCode,
        schedule,
      }
    } catch (error) {
      logger.error({
        error,
        message: 'adyenPlatforms-updateAccountError',
      })

      return null
    }
  },
  closeAccountHolder: async ({
    ctx,
    data: { accountHolderCode },
  }: {
    ctx: Context
    data: { accountHolderCode: string }
  }) => {
    const {
      clients: { adyenClient, account: accountClient },
      vtex: { logger },
    } = ctx

    try {
      const response = await adyenClient.closeAccountHolder(
        accountHolderCode,
        await settings(ctx)
      )

      if (
        !response?.accountHolderStatus ||
        response.accountHolderStatus.status !== 'Closed'
      ) {
        return null
      }

      const accounts = await accountClient.find({ accountHolderCode })

      if (!accounts.length) return

      const account = accounts.find(a => a.status === 'Active') ?? accounts[0]

      const { id, ...update } = account

      await accountClient.update(id, { ...update, status: 'Closed' })

      return response.accountHolderStatus
    } catch (error) {
      logger.error({
        error,
        message: 'adyenPlatforms-closeAccountHolderError',
      })
    }
  },
}

```


[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 adyen-platforms/node/services/index.ts from the adyen-platforms VTEX app.

[Assistant]
Here's the implementation:

```typescript
import adyen from './adyenService'
import account from './accountService'
import seller from './sellerService'
import onboarding from './onboardingService'

export const service = { adyen, seller, account, onboarding }

```