n8n-nodes-ifood-completo 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/PUBLISHING.md +49 -0
- package/README.md +111 -0
- package/credentials/IfoodCompletoApi.credentials.js +34 -0
- package/helpers/api.js +52 -0
- package/index.js +1 -0
- package/nodes/IfoodCompleto/IfoodCompleto.node.js +227 -0
- package/nodes/IfoodCompleto/IfoodCompleto.node.json +18 -0
- package/package.json +57 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
- Versão inicial do pacote `n8n-nodes-ifood-completo`.
|
|
6
|
+
- Inclui credencial `iFood Completo API`.
|
|
7
|
+
- Inclui node `iFood Completo` com operações para Authentication, Merchant, Events, Order, Financial, Catalog, Logistics, Shipping e Review.
|
|
8
|
+
- Inclui operações customizadas por módulo para endpoints adicionais da Merchant API do iFood.
|
package/PUBLISHING.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Publicação no npm
|
|
2
|
+
|
|
3
|
+
## Pré-requisitos
|
|
4
|
+
|
|
5
|
+
- Conta no npm com permissão para publicar o pacote `n8n-nodes-ifood-completo`.
|
|
6
|
+
- Node.js e npm instalados no ambiente de publicação.
|
|
7
|
+
- Acesso ao diretório `lib/vendor/ifood_completo/n8n`.
|
|
8
|
+
|
|
9
|
+
## Validar localmente
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
cd lib/vendor/ifood_completo/n8n
|
|
13
|
+
npm run validate
|
|
14
|
+
npm run publish:dry-run
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Gerar pacote local
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm run release:pack
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Isso cria um arquivo `.tgz` que pode ser instalado manualmente no n8n para teste.
|
|
24
|
+
|
|
25
|
+
## Publicar no npm
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm login
|
|
29
|
+
npm publish --access public
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Se o pacote for privado ou restrito a uma organização, ajuste `publishConfig.access` no `package.json` antes da publicação.
|
|
33
|
+
|
|
34
|
+
## Atualizar versão
|
|
35
|
+
|
|
36
|
+
Antes de publicar uma nova versão:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm version patch
|
|
40
|
+
npm publish --access public
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Use `minor` ou `major` quando houver mudança funcional relevante ou quebra de compatibilidade.
|
|
44
|
+
|
|
45
|
+
## Observações
|
|
46
|
+
|
|
47
|
+
- Não publique tokens, credenciais, arquivos `.env` ou pacotes `.tgz` gerados localmente.
|
|
48
|
+
- A keyword `n8n-community-node-package` deve permanecer no `package.json` para o n8n reconhecer o pacote como community node.
|
|
49
|
+
- O nome do pacote deve continuar iniciando com `n8n-nodes-`.
|
package/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# n8n-nodes-ifood-completo
|
|
2
|
+
|
|
3
|
+
Pacote npm local com node customizado para usar a Merchant API do iFood no n8n.
|
|
4
|
+
|
|
5
|
+
## Módulos disponíveis
|
|
6
|
+
|
|
7
|
+
O node `iFood Completo` concentra operações para os módulos criados em `lib/vendor/ifood_completo`:
|
|
8
|
+
|
|
9
|
+
- `Authentication`: gerar token `client_credentials`, refresh token e user code.
|
|
10
|
+
- `Merchant`: listar lojas, consultar detalhes/status, horários, interrupções e QR code de check-in.
|
|
11
|
+
- `Events`: polling e acknowledgment de eventos.
|
|
12
|
+
- `Order`: detalhes do pedido, confirmação, preparo, retirada, despacho, cancelamento, motivos e rastreio.
|
|
13
|
+
- `Financial`: Sales, Reconciliation, Settlements, Financial Events, Reconciliation On Demand e Anticipations.
|
|
14
|
+
- `Catalog`: catálogos, categorias, itens e endpoint customizado.
|
|
15
|
+
- `Logistics`: consulta logística por pedido e endpoint customizado.
|
|
16
|
+
- `Shipping`: cotação, criação, consulta e cancelamento de entregas.
|
|
17
|
+
- `Review`: listagem, detalhe, resposta e endpoint customizado.
|
|
18
|
+
|
|
19
|
+
## Instalação local para desenvolvimento
|
|
20
|
+
|
|
21
|
+
Dentro deste diretório:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
cd lib/vendor/ifood_completo/n8n
|
|
25
|
+
npm install
|
|
26
|
+
npm run dev
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
O comando `npm run dev` usa o `@n8n/node-cli` para carregar o pacote em um n8n local de desenvolvimento.
|
|
30
|
+
|
|
31
|
+
## Gerar pacote `.tgz`
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
cd lib/vendor/ifood_completo/n8n
|
|
35
|
+
npm pack
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Depois instale o arquivo gerado no ambiente do n8n:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install /caminho/n8n-nodes-ifood-completo-0.1.0.tgz
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Em Docker, monte ou copie o pacote para o container e instale no diretório onde o n8n carrega community nodes.
|
|
45
|
+
|
|
46
|
+
## Credenciais
|
|
47
|
+
|
|
48
|
+
Crie uma credencial do tipo `iFood Completo API` com:
|
|
49
|
+
|
|
50
|
+
- `Access Token`: token Bearer retornado pelo endpoint OAuth do iFood.
|
|
51
|
+
- `Base URL`: por padrão `https://merchant-api.ifood.com.br`.
|
|
52
|
+
|
|
53
|
+
As operações do módulo `Authentication` não exigem credencial. Informe `clientId`, `clientSecret` ou demais dados no campo `Body JSON`.
|
|
54
|
+
|
|
55
|
+
Exemplo para `Authentication: Client Credentials`:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"clientId": "SEU_CLIENT_ID",
|
|
60
|
+
"clientSecret": "SEU_CLIENT_SECRET"
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Campos gerais do node
|
|
65
|
+
|
|
66
|
+
- `Módulo`: organiza visualmente a operação selecionada.
|
|
67
|
+
- `Operação`: ação executada.
|
|
68
|
+
- `Merchant ID`: ID da loja iFood.
|
|
69
|
+
- `Order ID / ID`: ID principal usado pela operação, como pedido, review, entrega, interrupção ou request.
|
|
70
|
+
- `Data Inicial` e `Data Final`: períodos financeiros ou consultas por data.
|
|
71
|
+
- `Competência`: competência mensal no formato `YYYY-MM`.
|
|
72
|
+
- `Query JSON`: query string adicional.
|
|
73
|
+
- `Body JSON`: payload da requisição.
|
|
74
|
+
- `Método Customizado` e `Path Customizado`: usados nas operações de endpoint customizado.
|
|
75
|
+
|
|
76
|
+
## Exemplos
|
|
77
|
+
|
|
78
|
+
Consultar vendas financeiras:
|
|
79
|
+
|
|
80
|
+
- Módulo: `Financial`
|
|
81
|
+
- Operação: `Financial: Sales`
|
|
82
|
+
- Merchant ID: `merchantId`
|
|
83
|
+
- Data Inicial: `2026-08-01`
|
|
84
|
+
- Data Final: `2026-08-31`
|
|
85
|
+
- Query JSON: `{"page":1,"size":100}`
|
|
86
|
+
|
|
87
|
+
Consultar arquivo de conciliação:
|
|
88
|
+
|
|
89
|
+
- Módulo: `Financial`
|
|
90
|
+
- Operação: `Financial: Reconciliation Arquivo`
|
|
91
|
+
- Merchant ID: `merchantId`
|
|
92
|
+
- Competência: `2026-08`
|
|
93
|
+
|
|
94
|
+
Polling de eventos:
|
|
95
|
+
|
|
96
|
+
- Módulo: `Events`
|
|
97
|
+
- Operação: `Events: Polling`
|
|
98
|
+
|
|
99
|
+
Confirmar pedido:
|
|
100
|
+
|
|
101
|
+
- Módulo: `Order`
|
|
102
|
+
- Operação: `Order: Confirmar`
|
|
103
|
+
- Order ID / ID: `orderId`
|
|
104
|
+
- Body JSON: `{}`
|
|
105
|
+
|
|
106
|
+
## Observações
|
|
107
|
+
|
|
108
|
+
- Este pacote não altera nenhum script PHP existente.
|
|
109
|
+
- O pacote usa JavaScript CommonJS direto, sem etapa obrigatória de build.
|
|
110
|
+
- Alguns endpoints podem depender de permissão liberada no app iFood e podem retornar `403` se o app não possuir escopo.
|
|
111
|
+
- Use operações customizadas quando a documentação do iFood adicionar parâmetros ou paths novos dentro do mesmo módulo.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
class IfoodCompletoApi {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.name = 'ifoodCompletoApi';
|
|
4
|
+
this.displayName = 'iFood Completo API';
|
|
5
|
+
this.documentationUrl = 'https://developer.ifood.com.br/';
|
|
6
|
+
this.authenticate = {
|
|
7
|
+
type: 'generic',
|
|
8
|
+
properties: {
|
|
9
|
+
headers: {
|
|
10
|
+
Authorization: '=Bearer {{$credentials.accessToken}}',
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
this.properties = [
|
|
15
|
+
{
|
|
16
|
+
displayName: 'Access Token',
|
|
17
|
+
name: 'accessToken',
|
|
18
|
+
type: 'string',
|
|
19
|
+
typeOptions: { password: true },
|
|
20
|
+
default: '',
|
|
21
|
+
description: 'Token Bearer gerado pela API de autenticação do iFood.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
displayName: 'Base URL',
|
|
25
|
+
name: 'baseUrl',
|
|
26
|
+
type: 'string',
|
|
27
|
+
default: 'https://merchant-api.ifood.com.br',
|
|
28
|
+
description: 'URL base da Merchant API do iFood.',
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
exports.IfoodCompletoApi = IfoodCompletoApi;
|
package/helpers/api.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
async function ifoodApiRequest(method, path, body, qs, authentication) {
|
|
2
|
+
const credentials = authentication === false ? { baseUrl: 'https://merchant-api.ifood.com.br' } : await this.getCredentials('ifoodCompletoApi');
|
|
3
|
+
const baseUrl = String(credentials.baseUrl || 'https://merchant-api.ifood.com.br').replace(/\/$/, '');
|
|
4
|
+
const options = {
|
|
5
|
+
method,
|
|
6
|
+
url: /^https?:\/\//i.test(path) ? path : `${baseUrl}/${String(path).replace(/^\//, '')}`,
|
|
7
|
+
json: true,
|
|
8
|
+
qs: qs || {},
|
|
9
|
+
headers: {
|
|
10
|
+
Accept: 'application/json',
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
if (body !== undefined && body !== null && method !== 'GET') {
|
|
15
|
+
options.body = body;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (authentication === false) {
|
|
19
|
+
if (body && typeof body === 'object') {
|
|
20
|
+
options.form = body;
|
|
21
|
+
delete options.body;
|
|
22
|
+
}
|
|
23
|
+
options.headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
24
|
+
return this.helpers.httpRequest(options);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return this.helpers.httpRequestWithAuthentication.call(this, 'ifoodCompletoApi', options);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function parseJson(value, fallback) {
|
|
31
|
+
if (value === undefined || value === null || value === '') return fallback;
|
|
32
|
+
if (typeof value === 'object') return value;
|
|
33
|
+
try {
|
|
34
|
+
return JSON.parse(value);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
throw new Error(`JSON inválido: ${error.message}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function compactQuery(query) {
|
|
41
|
+
const clean = {};
|
|
42
|
+
Object.keys(query || {}).forEach((key) => {
|
|
43
|
+
if (query[key] !== undefined && query[key] !== null && query[key] !== '') clean[key] = query[key];
|
|
44
|
+
});
|
|
45
|
+
return clean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = {
|
|
49
|
+
ifoodApiRequest,
|
|
50
|
+
parseJson,
|
|
51
|
+
compactQuery,
|
|
52
|
+
};
|
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = {};
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
const { ifoodApiRequest, parseJson, compactQuery } = require('../../helpers/api');
|
|
2
|
+
|
|
3
|
+
class IfoodCompleto {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.description = {
|
|
6
|
+
displayName: 'iFood Completo',
|
|
7
|
+
name: 'ifoodCompleto',
|
|
8
|
+
icon: 'fa:store',
|
|
9
|
+
group: ['transform'],
|
|
10
|
+
version: 1,
|
|
11
|
+
subtitle: '={{$parameter["resource"] + ": " + $parameter["operation"]}}',
|
|
12
|
+
description: 'Merchant API iFood: autenticação, lojas, eventos, pedidos, financeiro, catálogo, logística, shipping e avaliações.',
|
|
13
|
+
defaults: {
|
|
14
|
+
name: 'iFood Completo',
|
|
15
|
+
},
|
|
16
|
+
inputs: ['main'],
|
|
17
|
+
outputs: ['main'],
|
|
18
|
+
credentials: [
|
|
19
|
+
{
|
|
20
|
+
name: 'ifoodCompletoApi',
|
|
21
|
+
required: false,
|
|
22
|
+
displayOptions: {
|
|
23
|
+
hide: {
|
|
24
|
+
resource: ['authentication'],
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
properties: [
|
|
30
|
+
{
|
|
31
|
+
displayName: 'Módulo',
|
|
32
|
+
name: 'resource',
|
|
33
|
+
type: 'options',
|
|
34
|
+
default: 'financial',
|
|
35
|
+
options: [
|
|
36
|
+
{ name: 'Authentication', value: 'authentication' },
|
|
37
|
+
{ name: 'Catalog', value: 'catalog' },
|
|
38
|
+
{ name: 'Events', value: 'events' },
|
|
39
|
+
{ name: 'Financial', value: 'financial' },
|
|
40
|
+
{ name: 'Logistics', value: 'logistics' },
|
|
41
|
+
{ name: 'Merchant', value: 'merchant' },
|
|
42
|
+
{ name: 'Order', value: 'order' },
|
|
43
|
+
{ name: 'Review', value: 'review' },
|
|
44
|
+
{ name: 'Shipping', value: 'shipping' },
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
displayName: 'Operação',
|
|
49
|
+
name: 'operation',
|
|
50
|
+
type: 'options',
|
|
51
|
+
default: 'sales',
|
|
52
|
+
options: [
|
|
53
|
+
{ name: 'Authentication: Client Credentials', value: 'authClientCredentials' },
|
|
54
|
+
{ name: 'Authentication: Refresh Token', value: 'authRefreshToken' },
|
|
55
|
+
{ name: 'Authentication: User Code', value: 'authUserCode' },
|
|
56
|
+
{ name: 'Catalog: Endpoint Customizado', value: 'catalogCustom' },
|
|
57
|
+
{ name: 'Catalog: Listar Catálogos', value: 'catalogList' },
|
|
58
|
+
{ name: 'Catalog: Listar Categorias', value: 'catalogCategories' },
|
|
59
|
+
{ name: 'Catalog: Listar Itens', value: 'catalogItems' },
|
|
60
|
+
{ name: 'Events: Acknowledgment', value: 'eventsAck' },
|
|
61
|
+
{ name: 'Events: Endpoint Customizado', value: 'eventsCustom' },
|
|
62
|
+
{ name: 'Events: Polling', value: 'eventsPolling' },
|
|
63
|
+
{ name: 'Financial: Antecipações', value: 'financialAnticipations' },
|
|
64
|
+
{ name: 'Financial: Conciliação On Demand - Criar', value: 'financialReconciliationOnDemandCreate' },
|
|
65
|
+
{ name: 'Financial: Conciliação On Demand - Status', value: 'financialReconciliationOnDemandStatus' },
|
|
66
|
+
{ name: 'Financial: Endpoint Customizado', value: 'financialCustom' },
|
|
67
|
+
{ name: 'Financial: Eventos Financeiros', value: 'financialEvents' },
|
|
68
|
+
{ name: 'Financial: Reconciliation Arquivo', value: 'financialReconciliation' },
|
|
69
|
+
{ name: 'Financial: Sales', value: 'sales' },
|
|
70
|
+
{ name: 'Financial: Settlements', value: 'settlements' },
|
|
71
|
+
{ name: 'Logistics: Endpoint Customizado', value: 'logisticsCustom' },
|
|
72
|
+
{ name: 'Logistics: Pedido', value: 'logisticsOrder' },
|
|
73
|
+
{ name: 'Merchant: Criar Interrupção', value: 'merchantCreateInterruption' },
|
|
74
|
+
{ name: 'Merchant: Deletar Interrupção', value: 'merchantDeleteInterruption' },
|
|
75
|
+
{ name: 'Merchant: Detalhes', value: 'merchantDetails' },
|
|
76
|
+
{ name: 'Merchant: Endpoint Customizado', value: 'merchantCustom' },
|
|
77
|
+
{ name: 'Merchant: Horários', value: 'merchantOpeningHours' },
|
|
78
|
+
{ name: 'Merchant: Interrupções', value: 'merchantInterruptions' },
|
|
79
|
+
{ name: 'Merchant: Listar', value: 'merchantList' },
|
|
80
|
+
{ name: 'Merchant: QR Code Check-in', value: 'merchantCheckinQrCode' },
|
|
81
|
+
{ name: 'Merchant: Status', value: 'merchantStatus' },
|
|
82
|
+
{ name: 'Merchant: Atualizar Horários', value: 'merchantUpdateOpeningHours' },
|
|
83
|
+
{ name: 'Order: Cancellation Reasons', value: 'orderCancellationReasons' },
|
|
84
|
+
{ name: 'Order: Confirmar', value: 'orderConfirm' },
|
|
85
|
+
{ name: 'Order: Despachar', value: 'orderDispatch' },
|
|
86
|
+
{ name: 'Order: Detalhes', value: 'orderDetails' },
|
|
87
|
+
{ name: 'Order: Endpoint Customizado', value: 'orderCustom' },
|
|
88
|
+
{ name: 'Order: Iniciar Preparo', value: 'orderStartPreparation' },
|
|
89
|
+
{ name: 'Order: Pronto Para Retirada', value: 'orderReadyToPickup' },
|
|
90
|
+
{ name: 'Order: Rastreio', value: 'orderTracking' },
|
|
91
|
+
{ name: 'Order: Solicitar Cancelamento', value: 'orderRequestCancellation' },
|
|
92
|
+
{ name: 'Review: Detalhes', value: 'reviewGet' },
|
|
93
|
+
{ name: 'Review: Endpoint Customizado', value: 'reviewCustom' },
|
|
94
|
+
{ name: 'Review: Listar', value: 'reviewList' },
|
|
95
|
+
{ name: 'Review: Responder', value: 'reviewReply' },
|
|
96
|
+
{ name: 'Shipping: Cancelar Entrega', value: 'shippingCancel' },
|
|
97
|
+
{ name: 'Shipping: Consultar Entrega', value: 'shippingGet' },
|
|
98
|
+
{ name: 'Shipping: Cotação', value: 'shippingQuote' },
|
|
99
|
+
{ name: 'Shipping: Criar Entrega', value: 'shippingCreate' },
|
|
100
|
+
{ name: 'Shipping: Endpoint Customizado', value: 'shippingCustom' },
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
{ displayName: 'Merchant ID', name: 'merchantId', type: 'string', default: '', description: 'ID da loja iFood.' },
|
|
104
|
+
{ displayName: 'Order ID / ID', name: 'id', type: 'string', default: '', description: 'ID do pedido, entrega, review, request ou interrupção conforme a operação.' },
|
|
105
|
+
{ displayName: 'ID Secundário', name: 'secondaryId', type: 'string', default: '', description: 'ID auxiliar, como catalogId, categoryId, interruptionId ou reviewId.' },
|
|
106
|
+
{ displayName: 'Data Inicial', name: 'beginDate', type: 'string', default: '', placeholder: '2026-08-01' },
|
|
107
|
+
{ displayName: 'Data Final', name: 'endDate', type: 'string', default: '', placeholder: '2026-08-31' },
|
|
108
|
+
{ displayName: 'Competência', name: 'competence', type: 'string', default: '', placeholder: '2026-08' },
|
|
109
|
+
{ displayName: 'Método Customizado', name: 'customMethod', type: 'options', default: 'GET', options: [{ name: 'GET', value: 'GET' }, { name: 'POST', value: 'POST' }, { name: 'PUT', value: 'PUT' }, { name: 'PATCH', value: 'PATCH' }, { name: 'DELETE', value: 'DELETE' }] },
|
|
110
|
+
{ displayName: 'Path Customizado', name: 'customPath', type: 'string', default: '', placeholder: '/merchants ou /orders/{id}' },
|
|
111
|
+
{ displayName: 'Query JSON', name: 'queryJson', type: 'json', default: '{}', description: 'Parâmetros query string em JSON.' },
|
|
112
|
+
{ displayName: 'Body JSON', name: 'bodyJson', type: 'json', default: '{}', description: 'Payload JSON ou formulário para autenticação.' },
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async execute() {
|
|
118
|
+
const items = this.getInputData();
|
|
119
|
+
const returnData = [];
|
|
120
|
+
|
|
121
|
+
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
|
122
|
+
const operation = this.getNodeParameter('operation', itemIndex);
|
|
123
|
+
const merchantId = this.getNodeParameter('merchantId', itemIndex, '');
|
|
124
|
+
const id = this.getNodeParameter('id', itemIndex, '');
|
|
125
|
+
const secondaryId = this.getNodeParameter('secondaryId', itemIndex, '');
|
|
126
|
+
const beginDate = this.getNodeParameter('beginDate', itemIndex, '');
|
|
127
|
+
const endDate = this.getNodeParameter('endDate', itemIndex, '');
|
|
128
|
+
const competence = this.getNodeParameter('competence', itemIndex, '');
|
|
129
|
+
const customMethod = this.getNodeParameter('customMethod', itemIndex, 'GET');
|
|
130
|
+
const customPath = this.getNodeParameter('customPath', itemIndex, '');
|
|
131
|
+
const queryJson = parseJson(this.getNodeParameter('queryJson', itemIndex, '{}'), {});
|
|
132
|
+
const bodyJson = parseJson(this.getNodeParameter('bodyJson', itemIndex, '{}'), {});
|
|
133
|
+
|
|
134
|
+
const response = await this.callOperation(operation, {
|
|
135
|
+
merchantId,
|
|
136
|
+
id,
|
|
137
|
+
secondaryId,
|
|
138
|
+
beginDate,
|
|
139
|
+
endDate,
|
|
140
|
+
competence,
|
|
141
|
+
customMethod,
|
|
142
|
+
customPath,
|
|
143
|
+
queryJson,
|
|
144
|
+
bodyJson,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
returnData.push({ json: response });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return [returnData];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async callOperation(operation, p) {
|
|
154
|
+
const q = compactQuery(p.queryJson);
|
|
155
|
+
const body = p.bodyJson;
|
|
156
|
+
const replace = (path) => path
|
|
157
|
+
.replace('{merchantId}', encodeURIComponent(p.merchantId))
|
|
158
|
+
.replace('{id}', encodeURIComponent(p.id))
|
|
159
|
+
.replace('{secondaryId}', encodeURIComponent(p.secondaryId));
|
|
160
|
+
|
|
161
|
+
switch (operation) {
|
|
162
|
+
case 'authClientCredentials':
|
|
163
|
+
return ifoodApiRequest.call(this, 'POST', '/authentication/v1.0/oauth/token', Object.assign({ grantType: 'client_credentials' }, body), {}, false);
|
|
164
|
+
case 'authRefreshToken':
|
|
165
|
+
return ifoodApiRequest.call(this, 'POST', '/authentication/v1.0/oauth/token', Object.assign({ grantType: 'refresh_token' }, body), {}, false);
|
|
166
|
+
case 'authUserCode':
|
|
167
|
+
return ifoodApiRequest.call(this, 'POST', '/authentication/v1.0/oauth/userCode', body, {}, false);
|
|
168
|
+
|
|
169
|
+
case 'merchantList': return ifoodApiRequest.call(this, 'GET', '/merchant/v1.0/merchants', null, q);
|
|
170
|
+
case 'merchantDetails': return ifoodApiRequest.call(this, 'GET', replace('/merchant/v1.0/merchants/{merchantId}'), null, q);
|
|
171
|
+
case 'merchantStatus': return ifoodApiRequest.call(this, 'GET', replace('/merchant/v1.0/merchants/{merchantId}/status'), null, q);
|
|
172
|
+
case 'merchantInterruptions': return ifoodApiRequest.call(this, 'GET', replace('/merchant/v1.0/merchants/{merchantId}/interruptions'), null, q);
|
|
173
|
+
case 'merchantCreateInterruption': return ifoodApiRequest.call(this, 'POST', replace('/merchant/v1.0/merchants/{merchantId}/interruptions'), body, q);
|
|
174
|
+
case 'merchantDeleteInterruption': return ifoodApiRequest.call(this, 'DELETE', replace('/merchant/v1.0/merchants/{merchantId}/interruptions/{id}'), null, q);
|
|
175
|
+
case 'merchantOpeningHours': return ifoodApiRequest.call(this, 'GET', replace('/merchant/v1.0/merchants/{merchantId}/opening-hours'), null, q);
|
|
176
|
+
case 'merchantUpdateOpeningHours': return ifoodApiRequest.call(this, 'PUT', replace('/merchant/v1.0/merchants/{merchantId}/opening-hours'), body, q);
|
|
177
|
+
case 'merchantCheckinQrCode': return ifoodApiRequest.call(this, 'POST', '/merchant/v1.0/merchants/checkin-qrcode', body, q);
|
|
178
|
+
case 'merchantCustom': return ifoodApiRequest.call(this, p.customMethod, `/merchant/v1.0/${p.customPath.replace(/^\//, '')}`, body, q);
|
|
179
|
+
|
|
180
|
+
case 'eventsPolling': return ifoodApiRequest.call(this, 'GET', '/events/v1.0/events:polling', null, q);
|
|
181
|
+
case 'eventsAck': return ifoodApiRequest.call(this, 'POST', '/events/v1.0/events/acknowledgment', body, q);
|
|
182
|
+
case 'eventsCustom': return ifoodApiRequest.call(this, p.customMethod, `/events/v1.0/${p.customPath.replace(/^\//, '')}`, body, q);
|
|
183
|
+
|
|
184
|
+
case 'orderDetails': return ifoodApiRequest.call(this, 'GET', replace('/order/v1.0/orders/{id}'), null, q);
|
|
185
|
+
case 'orderConfirm': return ifoodApiRequest.call(this, 'POST', replace('/order/v1.0/orders/{id}/confirm'), body, q);
|
|
186
|
+
case 'orderStartPreparation': return ifoodApiRequest.call(this, 'POST', replace('/order/v1.0/orders/{id}/startPreparation'), body, q);
|
|
187
|
+
case 'orderReadyToPickup': return ifoodApiRequest.call(this, 'POST', replace('/order/v1.0/orders/{id}/readyToPickup'), body, q);
|
|
188
|
+
case 'orderDispatch': return ifoodApiRequest.call(this, 'POST', replace('/order/v1.0/orders/{id}/dispatch'), body, q);
|
|
189
|
+
case 'orderRequestCancellation': return ifoodApiRequest.call(this, 'POST', replace('/order/v1.0/orders/{id}/requestCancellation'), body, q);
|
|
190
|
+
case 'orderCancellationReasons': return ifoodApiRequest.call(this, 'GET', p.id ? replace('/order/v1.0/orders/{id}/cancellationReasons') : '/order/v1.0/cancellationReasons', null, q);
|
|
191
|
+
case 'orderTracking': return ifoodApiRequest.call(this, 'GET', replace('/order/v1.0/orders/{id}/tracking'), null, q);
|
|
192
|
+
case 'orderCustom': return ifoodApiRequest.call(this, p.customMethod, `/order/v1.0/${p.customPath.replace(/^\//, '')}`, body, q);
|
|
193
|
+
|
|
194
|
+
case 'sales': return ifoodApiRequest.call(this, 'GET', '/v3/sales', null, compactQuery(Object.assign({ merchantId: p.merchantId, beginSalesDate: p.beginDate, endSalesDate: p.endDate }, q)));
|
|
195
|
+
case 'financialReconciliation': return ifoodApiRequest.call(this, 'GET', replace('/financial/v3.0/merchants/{merchantId}/reconciliation'), null, compactQuery(Object.assign({ competence: p.competence }, q)));
|
|
196
|
+
case 'settlements': return ifoodApiRequest.call(this, 'GET', replace('/financial/v3.0/merchants/{merchantId}/settlements'), null, compactQuery(Object.assign({ beginPaymentDate: p.beginDate, endPaymentDate: p.endDate }, q)));
|
|
197
|
+
case 'financialEvents': return ifoodApiRequest.call(this, 'GET', '/v3/financial-events', null, compactQuery(Object.assign({ merchantId: p.merchantId, beginDate: p.beginDate, endDate: p.endDate }, q)));
|
|
198
|
+
case 'financialReconciliationOnDemandCreate': return ifoodApiRequest.call(this, 'POST', '/v3/reconciliation-on-demand', body, q);
|
|
199
|
+
case 'financialReconciliationOnDemandStatus': return ifoodApiRequest.call(this, 'GET', `/v3/reconciliation-on-demand/${encodeURIComponent(p.id)}`, null, q);
|
|
200
|
+
case 'financialAnticipations': return ifoodApiRequest.call(this, 'GET', '/v3/anticipations', null, compactQuery(Object.assign({ merchantId: p.merchantId }, q)));
|
|
201
|
+
case 'financialCustom': return ifoodApiRequest.call(this, p.customMethod, p.customPath, body, q);
|
|
202
|
+
|
|
203
|
+
case 'catalogList': return ifoodApiRequest.call(this, 'GET', replace('/catalog/v2.0/merchants/{merchantId}/catalogs'), null, q);
|
|
204
|
+
case 'catalogCategories': return ifoodApiRequest.call(this, 'GET', replace('/catalog/v2.0/merchants/{merchantId}/catalogs/{id}/categories'), null, q);
|
|
205
|
+
case 'catalogItems': return ifoodApiRequest.call(this, 'GET', replace('/catalog/v2.0/merchants/{merchantId}/catalogs/{id}/items'), null, q);
|
|
206
|
+
case 'catalogCustom': return ifoodApiRequest.call(this, p.customMethod, `/catalog/v2.0/${p.customPath.replace(/^\//, '')}`, body, q);
|
|
207
|
+
|
|
208
|
+
case 'logisticsOrder': return ifoodApiRequest.call(this, 'GET', replace('/logistics/v1.0/orders/{id}'), null, q);
|
|
209
|
+
case 'logisticsCustom': return ifoodApiRequest.call(this, p.customMethod, `/logistics/v1.0/${p.customPath.replace(/^\//, '')}`, body, q);
|
|
210
|
+
|
|
211
|
+
case 'shippingQuote': return ifoodApiRequest.call(this, 'POST', '/shipping/v1.0/quote', body, q);
|
|
212
|
+
case 'shippingCreate': return ifoodApiRequest.call(this, 'POST', '/shipping/v1.0/deliveries', body, q);
|
|
213
|
+
case 'shippingGet': return ifoodApiRequest.call(this, 'GET', replace('/shipping/v1.0/deliveries/{id}'), null, q);
|
|
214
|
+
case 'shippingCancel': return ifoodApiRequest.call(this, 'POST', replace('/shipping/v1.0/deliveries/{id}/cancel'), body, q);
|
|
215
|
+
case 'shippingCustom': return ifoodApiRequest.call(this, p.customMethod, `/shipping/v1.0/${p.customPath.replace(/^\//, '')}`, body, q);
|
|
216
|
+
|
|
217
|
+
case 'reviewList': return ifoodApiRequest.call(this, 'GET', replace('/review/v2.0/merchants/{merchantId}/reviews'), null, q);
|
|
218
|
+
case 'reviewGet': return ifoodApiRequest.call(this, 'GET', replace('/review/v2.0/merchants/{merchantId}/reviews/{id}'), null, q);
|
|
219
|
+
case 'reviewReply': return ifoodApiRequest.call(this, 'POST', replace('/review/v2.0/merchants/{merchantId}/reviews/{id}/reply'), body, q);
|
|
220
|
+
case 'reviewCustom': return ifoodApiRequest.call(this, p.customMethod, `/review/v2.0/${p.customPath.replace(/^\//, '')}`, body, q);
|
|
221
|
+
default:
|
|
222
|
+
throw new Error(`Operação não implementada: ${operation}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
exports.IfoodCompleto = IfoodCompleto;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"node": "n8n-nodes-ifood-completo.ifoodCompleto",
|
|
3
|
+
"nodeVersion": "1.0",
|
|
4
|
+
"codexVersion": "1.0",
|
|
5
|
+
"categories": ["Finance", "Sales", "Delivery"],
|
|
6
|
+
"resources": {
|
|
7
|
+
"credentialDocumentation": [
|
|
8
|
+
{
|
|
9
|
+
"url": "https://developer.ifood.com.br/"
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
"primaryDocumentation": [
|
|
13
|
+
{
|
|
14
|
+
"url": "https://developer.ifood.com.br/"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-nodes-ifood-completo",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Nós customizados do n8n para Merchant API iFood: autenticação, lojas, eventos, pedidos, financeiro, catálogo, logística, shipping e avaliações.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package",
|
|
7
|
+
"n8n",
|
|
8
|
+
"ifood",
|
|
9
|
+
"merchant-api",
|
|
10
|
+
"conciliacao",
|
|
11
|
+
"delivery"
|
|
12
|
+
],
|
|
13
|
+
"license": "UNLICENSED",
|
|
14
|
+
"homepage": "https://merchant-api.ifood.com.br/",
|
|
15
|
+
"author": "BK Manaus",
|
|
16
|
+
"main": "index.js",
|
|
17
|
+
"files": [
|
|
18
|
+
"credentials",
|
|
19
|
+
"helpers",
|
|
20
|
+
"nodes",
|
|
21
|
+
"README.md",
|
|
22
|
+
"CHANGELOG.md",
|
|
23
|
+
"PUBLISHING.md"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"lint": "n8n-node lint",
|
|
27
|
+
"build": "node -e \"console.log('Pacote JavaScript pronto para n8n. Nenhum build necessario.')\"",
|
|
28
|
+
"dev": "n8n-node dev",
|
|
29
|
+
"pack:local": "npm pack",
|
|
30
|
+
"validate": "node scripts/validate-package.js",
|
|
31
|
+
"publish:dry-run": "npm publish --dry-run",
|
|
32
|
+
"release:pack": "npm pack",
|
|
33
|
+
"prepublishOnly": "npm run validate && npm run build"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@n8n/node-cli": "latest"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"n8n-workflow": "*"
|
|
41
|
+
},
|
|
42
|
+
"n8n": {
|
|
43
|
+
"credentials": [
|
|
44
|
+
"credentials/IfoodCompletoApi.credentials.js"
|
|
45
|
+
],
|
|
46
|
+
"nodes": [
|
|
47
|
+
"nodes/IfoodCompleto/IfoodCompleto.node.js"
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=18.10",
|
|
52
|
+
"npm": ">=8"
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public"
|
|
56
|
+
}
|
|
57
|
+
}
|