funifier-mcp 0.2.26 → 0.2.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/.cursor/rules/funifier.mdc +38 -41
  2. package/.github/copilot-instructions.md +38 -41
  3. package/AGENTS.md +56 -49
  4. package/README.md +40 -22
  5. package/datasource-funifier-docs/.coverage.json +326 -0
  6. package/datasource-funifier-docs/.validation.json +593 -0
  7. package/datasource-funifier-docs/knowledge/guides/aggregates.md +182 -70
  8. package/datasource-funifier-docs/knowledge/guides/database-access.md +174 -88
  9. package/datasource-funifier-docs/knowledge/guides/java-entities.md +294 -204
  10. package/datasource-funifier-docs/knowledge/guides/java-libraries.md +202 -226
  11. package/datasource-funifier-docs/knowledge/guides/java-managers.md +343 -265
  12. package/datasource-funifier-docs/knowledge/guides/trigger-examples.md +180 -236
  13. package/datasource-funifier-docs/knowledge/guides/triggers-guide.md +273 -191
  14. package/datasource-funifier-docs/knowledge/index.md +4 -1
  15. package/datasource-funifier-docs/knowledge/modules/achievement.md +1126 -28
  16. package/datasource-funifier-docs/knowledge/modules/action-log.md +469 -62
  17. package/datasource-funifier-docs/knowledge/modules/action.md +522 -70
  18. package/datasource-funifier-docs/knowledge/modules/auth.md +718 -69
  19. package/datasource-funifier-docs/knowledge/modules/avatar.md +483 -18
  20. package/datasource-funifier-docs/knowledge/modules/backup.md +603 -25
  21. package/datasource-funifier-docs/knowledge/modules/challenge.md +1048 -220
  22. package/datasource-funifier-docs/knowledge/modules/compact.md +469 -26
  23. package/datasource-funifier-docs/knowledge/modules/competition.md +811 -109
  24. package/datasource-funifier-docs/knowledge/modules/crossword.md +504 -28
  25. package/datasource-funifier-docs/knowledge/modules/csv-data.md +645 -20
  26. package/datasource-funifier-docs/knowledge/modules/custom-object.md +701 -36
  27. package/datasource-funifier-docs/knowledge/modules/database.md +730 -164
  28. package/datasource-funifier-docs/knowledge/modules/folder.md +935 -280
  29. package/datasource-funifier-docs/knowledge/modules/kpi-formulas.md +410 -15
  30. package/datasource-funifier-docs/knowledge/modules/lastmile.md +568 -29
  31. package/datasource-funifier-docs/knowledge/modules/leaderboard.md +595 -126
  32. package/datasource-funifier-docs/knowledge/modules/level.md +536 -54
  33. package/datasource-funifier-docs/knowledge/modules/lottery.md +809 -76
  34. package/datasource-funifier-docs/knowledge/modules/marketplace.md +688 -17
  35. package/datasource-funifier-docs/knowledge/modules/mystery.md +662 -52
  36. package/datasource-funifier-docs/knowledge/modules/notification.md +564 -26
  37. package/datasource-funifier-docs/knowledge/modules/patterns.md +519 -814
  38. package/datasource-funifier-docs/knowledge/modules/player.md +773 -73
  39. package/datasource-funifier-docs/knowledge/modules/point.md +380 -83
  40. package/datasource-funifier-docs/knowledge/modules/public.md +508 -178
  41. package/datasource-funifier-docs/knowledge/modules/question.md +619 -99
  42. package/datasource-funifier-docs/knowledge/modules/quiz.md +565 -120
  43. package/datasource-funifier-docs/knowledge/modules/scheduler.md +1092 -39
  44. package/datasource-funifier-docs/knowledge/modules/security.md +674 -112
  45. package/datasource-funifier-docs/knowledge/modules/staging.md +742 -19
  46. package/datasource-funifier-docs/knowledge/modules/story.md +565 -29
  47. package/datasource-funifier-docs/knowledge/modules/studio-page.md +470 -144
  48. package/datasource-funifier-docs/knowledge/modules/swap.md +552 -84
  49. package/datasource-funifier-docs/knowledge/modules/team.md +563 -45
  50. package/datasource-funifier-docs/knowledge/modules/trigger.md +876 -134
  51. package/datasource-funifier-docs/knowledge/modules/upload.md +468 -95
  52. package/datasource-funifier-docs/knowledge/modules/virtual-good.md +510 -63
  53. package/datasource-funifier-docs/knowledge/modules/webhook.md +375 -28
  54. package/datasource-funifier-docs/knowledge/modules/websocket.md +459 -26
  55. package/datasource-funifier-docs/knowledge/modules/widget.md +613 -27
  56. package/dist/cli/init.d.ts.map +1 -1
  57. package/dist/cli/init.js +42 -1
  58. package/dist/cli/init.js.map +1 -1
  59. package/dist/cli/init.test.js +74 -3
  60. package/dist/cli/init.test.js.map +1 -1
  61. package/dist/cli/persona.d.ts +3 -0
  62. package/dist/cli/persona.d.ts.map +1 -0
  63. package/dist/cli/persona.js +25 -0
  64. package/dist/cli/persona.js.map +1 -0
  65. package/dist/mcp/bundle.js +119 -93
  66. package/dist/mcp/check-update.d.ts +5 -0
  67. package/dist/mcp/check-update.d.ts.map +1 -1
  68. package/dist/mcp/check-update.js +21 -10
  69. package/dist/mcp/check-update.js.map +1 -1
  70. package/dist/mcp/check-update.test.d.ts +2 -0
  71. package/dist/mcp/check-update.test.d.ts.map +1 -0
  72. package/dist/mcp/check-update.test.js +33 -0
  73. package/dist/mcp/check-update.test.js.map +1 -0
  74. package/dist/mcp/index.js +2 -2
  75. package/dist/mcp/index.js.map +1 -1
  76. package/dist/mcp/prompts/templates.d.ts.map +1 -1
  77. package/dist/mcp/prompts/templates.js +35 -0
  78. package/dist/mcp/prompts/templates.js.map +1 -1
  79. package/dist/mcp/resources/documentation.d.ts +1 -1
  80. package/dist/mcp/resources/documentation.d.ts.map +1 -1
  81. package/dist/mcp/resources/documentation.js +39 -3
  82. package/dist/mcp/resources/documentation.js.map +1 -1
  83. package/dist/mcp/tools/connect.d.ts.map +1 -1
  84. package/dist/mcp/tools/connect.js +18 -8
  85. package/dist/mcp/tools/connect.js.map +1 -1
  86. package/dist/mcp/tools/database.d.ts.map +1 -1
  87. package/dist/mcp/tools/database.js +59 -47
  88. package/dist/mcp/tools/database.js.map +1 -1
  89. package/dist/mcp/tools/database.test.js +2 -2
  90. package/dist/mcp/tools/database.test.js.map +1 -1
  91. package/dist/mcp/tools/delete.d.ts.map +1 -1
  92. package/dist/mcp/tools/delete.js +13 -3
  93. package/dist/mcp/tools/delete.js.map +1 -1
  94. package/dist/mcp/tools/execute.d.ts.map +1 -1
  95. package/dist/mcp/tools/execute.js +20 -9
  96. package/dist/mcp/tools/execute.js.map +1 -1
  97. package/dist/mcp/tools/folder.d.ts.map +1 -1
  98. package/dist/mcp/tools/folder.js +22 -12
  99. package/dist/mcp/tools/folder.js.map +1 -1
  100. package/dist/mcp/tools/get.d.ts.map +1 -1
  101. package/dist/mcp/tools/get.js +16 -6
  102. package/dist/mcp/tools/get.js.map +1 -1
  103. package/dist/mcp/tools/index.d.ts +1 -1
  104. package/dist/mcp/tools/index.d.ts.map +1 -1
  105. package/dist/mcp/tools/index.js +28 -1
  106. package/dist/mcp/tools/index.js.map +1 -1
  107. package/dist/mcp/tools/list.d.ts.map +1 -1
  108. package/dist/mcp/tools/list.js +38 -14
  109. package/dist/mcp/tools/list.js.map +1 -1
  110. package/dist/mcp/tools/logs.d.ts.map +1 -1
  111. package/dist/mcp/tools/logs.js +15 -5
  112. package/dist/mcp/tools/logs.js.map +1 -1
  113. package/dist/mcp/tools/save.d.ts.map +1 -1
  114. package/dist/mcp/tools/save.js +14 -4
  115. package/dist/mcp/tools/save.js.map +1 -1
  116. package/dist/mcp/tools/save.test.js +3 -3
  117. package/dist/mcp/tools/save.test.js.map +1 -1
  118. package/dist/mcp/tools/search-docs.d.ts +3 -0
  119. package/dist/mcp/tools/search-docs.d.ts.map +1 -0
  120. package/dist/mcp/tools/search-docs.js +102 -0
  121. package/dist/mcp/tools/search-docs.js.map +1 -0
  122. package/package.json +6 -2
  123. package/skills/acquire-funifier-knowledge/SKILL.md +155 -0
  124. package/skills/acquire-funifier-knowledge/assets/templates/CONCERNS.md +25 -0
  125. package/skills/acquire-funifier-knowledge/assets/templates/CUSTOM_ENDPOINTS.md +24 -0
  126. package/skills/acquire-funifier-knowledge/assets/templates/CUSTOM_PAGES.md +24 -0
  127. package/skills/acquire-funifier-knowledge/assets/templates/GAME_MECHANICS.md +35 -0
  128. package/skills/acquire-funifier-knowledge/assets/templates/INTEGRATIONS.md +35 -0
  129. package/skills/acquire-funifier-knowledge/assets/templates/LEADERBOARDS.md +24 -0
  130. package/skills/acquire-funifier-knowledge/assets/templates/OVERVIEW.md +86 -0
  131. package/skills/acquire-funifier-knowledge/assets/templates/PLAYER_MODEL.md +31 -0
  132. package/skills/acquire-funifier-knowledge/assets/templates/SCHEDULERS.md +25 -0
  133. package/skills/acquire-funifier-knowledge/assets/templates/TECHNIQUES_AND_PATTERNS.md +26 -0
  134. package/skills/acquire-funifier-knowledge/assets/templates/TRIGGERS.md +27 -0
  135. package/skills/acquire-funifier-knowledge/references/funifier-inventory-checklist.md +81 -0
  136. package/skills/acquire-funifier-knowledge/references/game-techniques-taxonomy.md +62 -0
  137. package/skills/acquire-funifier-knowledge/references/mcp-call-patterns.md +118 -0
  138. package/skills/funifier/SKILL.md +88 -0
  139. package/skills/funifier/references/configure-security.md +96 -0
  140. package/skills/{funifier-create-action/SKILL.md → funifier/references/create-action.md} +0 -33
  141. package/skills/funifier/references/create-aggregate.md +144 -0
  142. package/skills/funifier/references/create-challenge.md +116 -0
  143. package/skills/funifier/references/create-competition.md +98 -0
  144. package/skills/funifier/references/create-crossword.md +574 -0
  145. package/skills/funifier/references/create-custom-object.md +91 -0
  146. package/skills/funifier/references/create-custom-page.md +135 -0
  147. package/skills/funifier/references/create-folder.md +104 -0
  148. package/skills/funifier/references/create-lastmile.md +643 -0
  149. package/skills/{funifier-create-leaderboard/SKILL.md → funifier/references/create-leaderboard.md} +0 -33
  150. package/skills/funifier/references/create-level.md +94 -0
  151. package/skills/funifier/references/create-lottery.md +913 -0
  152. package/skills/funifier/references/create-mystery.md +769 -0
  153. package/skills/funifier/references/create-notification.md +75 -0
  154. package/skills/{funifier-create-point/SKILL.md → funifier/references/create-point.md} +0 -33
  155. package/skills/funifier/references/create-quiz.md +98 -0
  156. package/skills/funifier/references/create-scheduler.md +141 -0
  157. package/skills/funifier/references/create-story.md +636 -0
  158. package/skills/funifier/references/create-swap.md +95 -0
  159. package/skills/{funifier-create-trigger/SKILL.md → funifier/references/create-trigger.md} +0 -33
  160. package/skills/funifier/references/create-virtual-good.md +96 -0
  161. package/skills/funifier/references/create-webhook.md +72 -0
  162. package/skills/funifier/references/create-websocket.md +71 -0
  163. package/skills/funifier/references/create-widget.md +76 -0
  164. package/skills/funifier/references/debug.md +87 -0
  165. package/skills/funifier/references/help.md +81 -0
  166. package/skills/funifier/references/implement-frontend.md +106 -0
  167. package/skills/funifier/references/import-csv.md +75 -0
  168. package/skills/funifier/references/manage-player.md +82 -0
  169. package/skills/funifier/references/manage-team.md +76 -0
  170. package/skills/funifier/references/upload-file.md +91 -0
  171. package/skills/funifier-create-aggregate/SKILL.md +0 -127
  172. package/skills/funifier-create-challenge/SKILL.md +0 -88
  173. package/skills/funifier-create-custom-page/SKILL.md +0 -127
  174. package/skills/funifier-create-level/SKILL.md +0 -87
  175. package/skills/funifier-create-quiz/SKILL.md +0 -87
  176. package/skills/funifier-create-scheduler/SKILL.md +0 -127
  177. package/skills/funifier-create-virtual-good/SKILL.md +0 -87
  178. package/skills/funifier-debug/SKILL.md +0 -92
  179. package/skills/funifier-help/SKILL.md +0 -86
  180. package/skills/funifier-implement-frontend/SKILL.md +0 -90
  181. package/skills/funifier-index/SKILL.md +0 -58
@@ -1,27 +1,698 @@
1
- # Marketplace (Mercado)
1
+ # `marketplace`
2
2
 
3
3
  **Acesso Studio:** `/market`
4
+ **API Endpoint:** `/v3/package` (+ `/v3/partner/package`, `/v3/system/package`)
5
+ **Coleções MongoDB:** `package`, `installed_package`, `package_install_log`
4
6
 
5
- ## O que é
7
+ > Não existe nenhuma classe `Marketplace` no código. O "marketplace" é a **face pública/cross-gamificação do recurso `Package`**. Toda a funcionalidade vive no módulo `com.funifier.engine.packages` (gerência por gamificação), `com.funifier.engine.system.packages` (gerência global) e `com.funifier.engine.system.checkout` (cobrança). Os endpoints `/v3/package/marketplace/*` apenas expõem os mesmos pacotes sem o filtro de `apikey`. Esta documentação descreve o recurso `Package` por inteiro, que é o que sustenta o marketplace.
6
8
 
7
- Compartilhamento e reutilização de componentes prontos para gamificações. Disponibiliza um catálogo de componentes já configurados — como desafios, rankings, widgets, modelos de gamificação — que podem ser reutilizados em diferentes projetos, acelerando a implementação.
9
+ ---
8
10
 
9
- ## Quando usar
11
+ ## 1. Visão Geral
10
12
 
11
- - Para reutilizar modelos de gamificação validados
12
- - Para compartilhar componentes entre departamentos
13
- - Para acelerar implementação com templates prontos
14
- - Para padronizar gamificações na organização
13
+ O recurso `package` empacota componentes prontos de uma gamificação (challenges, widgets, levels, leaderboards, triggers, mystery boxes, lotteries, point categories, actions, etc.) em um único objeto `Pack` reutilizável e instalável em **outras** gamificações.
15
14
 
16
- ## Checklist de Configuração
15
+ Papel arquitetural:
16
+
17
+ - Um `Pack` é um **snapshot** dos componentes no momento da publicação — não uma referência viva. Ao publicar via `/v3/package`, o conteúdo JSON completo de cada componente é serializado e armazenado em `Pack.components[].content` (`engine.packages.PackageManager.insert`, linhas 84-152).
18
+ - A **instalação** (`install`) reidrata cada componente gravando seu JSON de volta na coleção de destino (nomeada por `Component.type`), preservando o `_id` original.
19
+ - O marketplace é apenas a leitura global desses pacotes: `/v3/package/marketplace/{id}` lê o `Pack` da coleção global `package` (sem escopo de `apikey`) e enriquece com dados do publicador.
20
+ - Pacotes pagos têm um fluxo de checkout (Stripe/PayPal) com comissão da Funifier de 15% (`CheckoutManager.FUNIFIER_COMISSION`).
21
+ - Pacotes `managed` propagam atualizações automaticamente para todas as instalações ativas quando republicados.
22
+
23
+ Problemas que resolve:
24
+
25
+ - Reuso e distribuição de configurações de gamificação entre gamificações/contas distintas.
26
+ - Monetização de pacotes (one-time e subscription) com split de pagamento via contas Stripe conectadas.
27
+ - Catálogo de widgets disponível para a IA (`PackAI`) sugerir e instalar componentes automaticamente.
28
+
29
+ Relação com outros módulos:
30
+
31
+ - `trigger` — componentes do tipo `trigger` são **re-registrados** (`triggerManager.add`) na instalação. Isto compila/registra código server-side do trigger na gamificação que instala (vide seção 8).
32
+ - `lottery` — único componente com tratamento especial na instalação/remoção (usa `lotteryManager` em vez de gravação raw).
33
+ - `checkout` / `account` / `game` / `partner` — fluxo de cobrança, identificação de vendedor/comprador e publicação por parceiros.
34
+ - `widget` (Widget_V3) — alvo principal dos pacotes de IA (`findPackagesByArtificialIntelligenceModule("widget")` em `AIRest`).
35
+ - `technique_field` — fonte dos códigos GT em `Pack.techniques`.
36
+
37
+ ---
38
+
39
+ ## 2. Arquitetura e Fluxos
40
+
41
+ ### 2.1 Classes envolvidas
42
+
43
+ | Classe | Papel |
44
+ |---|---|
45
+ | `com.funifier.engine.packages.Pack` | Entidade raiz — documento da coleção `package` / `installed_package` |
46
+ | `com.funifier.engine.packages.Component` | Subentidade — um componente snapshotado (`type` + `content` JSON) |
47
+ | `com.funifier.engine.packages.PackMedia` | Subentidade — mídia de apresentação (image/video) |
48
+ | `com.funifier.engine.packages.PackAI` | Subentidade — marca o pacote como utilizável pela IA |
49
+ | `com.funifier.engine.packages.PackCheckout` | Subentidade — referência ao checkout + lista de preços |
50
+ | `com.funifier.engine.packages.PackageManager` | Manager **por gamificação** (via `ManagerFactory`): insert (com snapshot), install, uninstall, find, findAll, delete, findInstalled, findOptionsByEntity |
51
+ | `com.funifier.engine.system.packages.PackageManager` | Manager **global** (via `SystemFactory`): insert (sem snapshot), find/delete sem escopo, aggregates, queries por conta/IA, instalações ativas |
52
+ | `com.funifier.engine.system.packages.PackageInstallationLog` | Entidade da coleção `package_install_log` |
53
+ | `com.funifier.engine.system.checkout.Checkout` | Config de provedor de pagamento (stripe/paypal/pagarme) |
54
+ | `com.funifier.engine.system.checkout.CheckoutPricing` | Definição de preço (one_time/subscription, unit, currency, interval) |
55
+ | `com.funifier.engine.system.checkout.CheckoutManager` | Sessões de checkout, callbacks, faturas, cancelamento, uso medido |
56
+ | `com.funifier.rest.v3.rest.PackageRest` | Controller principal `/v3/package` (gamificação) |
57
+ | `com.funifier.rest.v3.rest.partner.PackageRest` | Controller `/v3/partner/package` (parceiros) |
58
+ | `com.funifier.rest.v3.rest.system.PackageRest` | Controller `/v3/system/package` (admin) |
59
+
60
+ Não há `Dao`/`Repository` dedicado — toda manipulação MongoDB é feita diretamente via Jongo dentro dos managers.
61
+
62
+ ### 2.2 Pipeline de publicação — `POST /v3/package` → `engine.packages.PackageManager.insert(Pack)`
63
+
64
+ Sequência exata (linhas 84-151):
65
+
66
+ ```
67
+ [1] Se pack.id vazio:
68
+ pack.id = Guid.newShortGuid()
69
+ pack.creation = now
70
+ [2] pack.updated = now
71
+ [3] pack.apikey = manager.getApiKey() ← FORÇADO; ignora qualquer apikey enviado pelo cliente
72
+ [4] Materializa componentes (substitui pack.components inteiro):
73
+ para cada item em pack.components com id e type não-vazios:
74
+ se item.id == "*":
75
+ copia até 1000 docs da coleção item.type:
76
+ cria Component { id, title=getTitleByEntity(...), type, content=BSON strict do doc }
77
+ senão:
78
+ busca 1 doc por _id na coleção item.type
79
+ se existe: item.content = BSON strict do doc; adiciona à lista
80
+ (exceções por item são engolidas → System.out.println, item é descartado silenciosamente)
81
+ [5] pack.components = lista materializada
82
+ [6] salva na coleção `package` (Jongo save → upsert por _id)
83
+ ```
84
+
85
+ Pseudocódigo da derivação de título (`getTitleByEntity`, linhas 41-80) — usado só no caso `"*"`:
86
+
87
+ ```
88
+ se type == action → title = doc.action
89
+ senão se type == point_category → title = doc.category
90
+ senão se type == challenge → title = doc.challenge
91
+ senão se type ∈ {widget_v3, leaderboard, mystery_box, lottery} → title = doc.title
92
+ senão se type == level → title = doc.level
93
+ senão se type == trigger → title = doc.name
94
+ senão → primeiro existente entre doc.title, doc.name, doc.label, ou "_id"
95
+ ```
96
+
97
+ Após o `insert`, o controller (`PackageRest.insert`, linhas 113-128) executa a propagação `managed` (vide seção 2.7).
98
+
99
+ ### 2.3 Pipeline de instalação — `GET /v3/package/install/{id}` → `engine.packages.PackageManager.install(id)`
100
+
101
+ ```mermaid
102
+ flowchart LR
103
+ A["install(id)"] --> B{"existe em<br/>installed_package?"}
104
+ B -->|sim| C["operation = update<br/>se pre.apikey != installer:<br/>remove componentes antigos"]
105
+ B -->|não| D["operation = install"]
106
+ C --> E["carrega Pack da<br/>coleção `package`"]
107
+ D --> E
108
+ E --> F{"pack.apikey ==<br/>installer apikey?"}
109
+ F -->|sim| G["throw UNAUTHORIZED<br/>'só instala pacotes de<br/>outras gamificações'"]
110
+ F -->|não| H["para cada component:<br/>insertContentByComponent"]
111
+ H --> I["grava PackageInstallationLog<br/>(install ou update)"]
112
+ I --> J["salva Pack em<br/>installed_package (log=logId)"]
113
+ J --> K{"?session= presente?"}
114
+ K -->|sim| L["applyStripePackageCheckoutCallback<br/>→ grava subscription no log"]
115
+ K -->|não| M["fim"]
116
+ L --> M
117
+ ```
118
+
119
+ Detalhamento (linhas 161-221):
120
+
121
+ 1. Busca registro anterior em `installed_package` por `_id`. Se existe → `operation = update`; e se `pre.apikey != installer` → remove todos os componentes antigos via `deleteContentByComponent` (cleanup de referências removidas em versões anteriores).
122
+ 2. Carrega o `Pack` da coleção global `package`.
123
+ 3. **Guard de propriedade**: se `pack.apikey == installer.apikey` → lança `FunifierException` UNAUTHORIZED. Só é possível instalar pacotes criados em **outra** gamificação.
124
+ 4. Para cada `Component`, chama `insertContentByComponent` (síncrono).
125
+ 5. Cria `PackageInstallationLog` e salva em `package_install_log`.
126
+ 6. Salva o `Pack` na coleção `installed_package` com `pack.log = log.id`.
127
+
128
+ `insertContentByComponent` (linhas 416-469):
129
+
130
+ ```
131
+ doc = BasicDBObject.parse(component.content)
132
+ collection(component.type).save(doc) ← upsert por _id; sobrescreve doc local de mesmo _id
133
+ se component.type == trigger:
134
+ triggerManager.add( triggerManager.findById(component.id) ) ← re-registra o trigger
135
+ ```
136
+
137
+ `deleteContentByComponent` (linhas 471-513):
138
+
139
+ ```
140
+ se component.type == lottery:
141
+ lotteryManager.delete(component.id)
142
+ senão:
143
+ collection(component.type).remove("{_id:#}", component.id) ← remoção raw por _id
144
+ ```
145
+
146
+ ### 2.4 Pipeline de desinstalação — `DELETE /v3/package/uninstall/{id}` → `uninstall(id)`
147
+
148
+ ```
149
+ [1] carrega Pack de installed_package por _id (se null → no-op)
150
+ [2] para cada component: deleteContentByComponent
151
+ [3] tenta cancelStripePackageSubscription (try/catch engolido)
152
+ [4] remove Pack de installed_package
153
+ [5] grava PackageInstallationLog operation=uninstall
154
+ ```
155
+
156
+ Não há verificação de integridade: se outro pacote ou configuração local compartilhar o mesmo `_id` de um componente, ele é removido junto (seções 5 e 8).
157
+
158
+ ### 2.5 Marketplace / descoberta
159
+
160
+ - `GET /v3/package/marketplace/{id}` → `system.packages.PackageManager.find(id)` (coleção `package`, **sem escopo de apikey**). Em seguida, se `pack.apikey != null`, enriquece `pack.provider` com `account.extra` do publicador (`PackageRest.findMarketplace`, linhas 246-256). Retorna o `Pack` completo, incluindo `components` (todo o JSON snapshotado) e o hash `password`.
161
+ - `POST /v3/package/marketplace/aggregate` → `system.packages.PackageManager.findAllAggregate(aggregations, range)`. Executa um **pipeline de agregação fornecido cru pelo cliente** sobre a coleção `package`, **sem nenhum filtro server-side** (`PackageRest.findAllAggregate`, linhas 258-272 + `system PackageManager.findAllAggregate`, linhas 75-102). Paginação via header `Range` (default `0-100`).
162
+
163
+ A descoberta do catálogo é inteiramente dirigida pelo cliente: o frontend do `/market` envia seu próprio `$match` (ex.: `visibility: globally`, `status: approved`). O servidor não impõe esses filtros (vide seção 7).
164
+
165
+ ### 2.6 Fluxo de checkout de pacote pago
166
+
167
+ ```mermaid
168
+ sequenceDiagram
169
+ participant C as Cliente (/market)
170
+ participant API as PackageRest
171
+ participant CM as CheckoutManager
172
+ participant S as Stripe (conta conectada)
173
+ C->>API: GET /v3/package/checkout?pack&price
174
+ API->>CM: createStripePackageCheckoutSession(checkout, pack, pricing, seller, buyer)
175
+ CM->>S: Session.create(line_items|subscription_data, application_fee, connected acct)
176
+ S-->>CM: session (url, id)
177
+ CM-->>C: session + pk + CONNECTED_STRIPE_ACCOUNT_ID
178
+ C->>S: paga na sessão Stripe
179
+ S-->>C: redirect success_url ...?session_id={ID}
180
+ C->>API: GET /v3/package/install/{id}?session={ID}
181
+ API->>API: install(id) (instala componentes + cria install log)
182
+ API->>CM: applyStripePackageCheckoutCallback(checkout, pack, session, logId, buyer)
183
+ CM->>S: Session.retrieve(session)
184
+ CM->>CM: grava PartnerSubscription no install log
185
+ CM-->>C: payment=OK
186
+ ```
187
+
188
+ Notas (`CheckoutManager`, linhas 155-355):
189
+
190
+ - `one_time` → `line_items` com `amount = price.price * 100` e `application_fee_amount = 15%` (linhas 170-186).
191
+ - `subscription` → `subscription_data` com `application_fee_percent = 15` (linhas 190-201).
192
+ - `success_url`/`cancel_url` apontam para `https://my.funifier.com/market/install/{id}/payment/...` (linhas 208-209) — confirma o Studio em `/market`.
193
+ - Uso medido (`updateStripePackageTotalUsage`): reporta ao Stripe o total de jogadores ativos (`unit=player`) ou de requests (`unit=request`) da conta.
194
+
195
+ ### 2.7 Propagação de pacote `managed`
196
+
197
+ ```mermaid
198
+ flowchart LR
199
+ A["POST /v3/package<br/>(managed=true)"] --> B["insert()"]
200
+ B --> C["findPackageActiveInstalls(pack.id)"]
201
+ C --> D{"para cada install ativo"}
202
+ D --> E["gameManager.findByApiKey(install.apikey)"]
203
+ E --> F{"game existe?"}
204
+ F -->|sim| G["FrontController(install.apikey)<br/>.getPackageManager().install(pack.id)"]
205
+ F -->|não| D
206
+ G --> D
207
+ ```
208
+
209
+ `PackageRest.insert` (linhas 113-128): se `pack.managed`, após salvar, busca todas as instalações ativas (`system.PackageManager.findPackageActiveInstalls` — agrega `package_install_log`, pega o último `operation` por (pack, account, apikey) e filtra `install|update`) e re-instala o pacote em cada gamificação que ainda existe. **Síncrono**, com `try/catch` que engole exceções e um `//TODO colocar atualizacao para ser feita assincronamente`.
210
+
211
+ ---
212
+
213
+ ## 3. Estrutura dos Objetos
214
+
215
+ ### 3.1 `Pack` — documento raiz (coleções `package` e `installed_package`)
216
+
217
+ | Campo | Tipo | Padrão | Obrigatório | Descrição |
218
+ |---|---|---|---|---|
219
+ | `_id` | String | short GUID auto | — | Gerado em `insert` se vazio (campo Java `id`, `@JsonProperty("_id")`) |
220
+ | `title` | String | — | sim (comentário) | Título do pacote. Não validado server-side |
221
+ | `image` | String | — | não | Ícone do pacote |
222
+ | `screenshot` | String | — | não | Screenshot |
223
+ | `medias` | List<PackMedia> | `[]` | não | Imagens/vídeos de apresentação |
224
+ | `description` | String | — | sim (comentário) | Descrição. Não validado server-side |
225
+ | `tags` | List<String> | `[]` | não | Tags livres |
226
+ | `techniques` | List<String> | `[]` | não | Códigos GT (técnicas de jogo) — vide 3.7 |
227
+ | `apikey` | String | forçado | sim | ApiKey da gamificação que criou o pacote. **Sobrescrito** no `insert` de `/v3/package`; **não** sobrescrito nos endpoints partner/system |
228
+ | `priority` | int | `0` | não | Quanto maior, maior a prioridade; 0 = nenhuma |
229
+ | `instructions` | String | — | não | Instruções de uso |
230
+ | `instructions_url` | String | — | não | URL de instruções |
231
+ | `password` | String | — | não | Hash BCrypt. Definido só via `/password`. **Não é verificado em find/install** e **vaza** na resposta do marketplace (seção 8) |
232
+ | `visibility` | String | `"private"` | não | `private` ou `globally`. **Não lido pelo servidor** |
233
+ | `status` | String | `null` | não | `waiting` / `approved` / `denied`. **Não lido pelo servidor** |
234
+ | `components` | List<Component> | `[]` | não | Componentes snapshotados. Substituído na publicação via `/v3/package` |
235
+ | `creation` | Date | now no insert | — | Data de criação |
236
+ | `updated` | Date | now no insert | — | Última atualização |
237
+ | `managed` | boolean | `false` | não | Se `true`, atualizações se propagam às instalações ativas |
238
+ | `provider` | Map<String,String> | runtime | — | `account.extra` do publicador. **Só preenchido em tempo de execução** no `findMarketplace`; não persiste |
239
+ | `ai` | PackAI | `null` | não | Marca o pacote como utilizável pela IA |
240
+ | `price_type` | String | `null` | não | `free` ou `checkout` |
241
+ | `checkout` | PackCheckout | `null` | não | Config de cobrança (pacotes pagos) |
242
+ | `log` | String | `null` | — | Id do `PackageInstallationLog` da instalação corrente (preenchido em `install`) |
243
+
244
+ **Campos computados (não persistem):** `provider` (preenchido só no `findMarketplace`).
245
+
246
+ **Campos sobrescritos silenciosamente no save:** `apikey` (forçado para a gamificação corrente no `insert` de `/v3/package`), `id`/`creation`/`updated` (gerados), `components` (substituído pela materialização).
247
+
248
+ **Campos aceitos mas ignorados pelo runtime:** `visibility`, `status`, `password` (no fluxo de leitura/instalação), `priority` (armazenado mas não usado em nenhuma ordenação server-side; ordenação fica a cargo do pipeline do cliente).
249
+
250
+ #### Enums / constantes do `Pack`
251
+
252
+ | Constante | Valor | Significado |
253
+ |---|---|---|
254
+ | `VISIBILITY_PRIVATE` | `private` | Visibilidade privada (convenção de cliente) |
255
+ | `VISIBILITY_GLOBALLY` | `globally` | Visível no marketplace global (convenção de cliente) |
256
+ | `STATUS_WAITING` | `waiting` | Aguardando aprovação (não enforçado) |
257
+ | `STATUS_APPROVED` | `approved` | Aprovado (não enforçado) |
258
+ | `STATUS_DENIED` | `denied` | Negado (não enforçado) |
259
+ | `PRICE_TYPE_FREE` | `free` | Pacote gratuito |
260
+ | `PRICE_TYPE_CHECKOUT` | `checkout` | Pacote pago (usa `checkout`) |
261
+
262
+ ### 3.2 `Component` — componente snapshotado
263
+
264
+ | Campo | Tipo | Padrão | Obrigatório | Descrição |
265
+ |---|---|---|---|---|
266
+ | `_id` | String | — | sim | Id do documento de origem; preservado na reidratação (campo Java `id`) |
267
+ | `type` | String | — | sim | **Nome da coleção MongoDB** de destino (ex.: `challenge`, `widget_v3`, `trigger`). Aceita `"*"` na publicação para copiar a coleção inteira (até 1000 docs) |
268
+ | `title` | String | — | não | Título legível (derivado por `getTitleByEntity` no caso `"*"`) |
269
+ | `content` | String | — | sim (após publicação) | JSON (BSON strict-mode) completo do documento. Gerado server-side na publicação via `/v3/package` |
270
+
271
+ > Na publicação via `/v3/package`, o cliente envia apenas `{ _id, type }` (ou `{ _id: "*", type }`); o `content` é preenchido pelo servidor. Nos endpoints partner/system, o `content` **não** é materializado — o cliente deve fornecê-lo.
272
+
273
+ ### 3.3 `PackMedia`
274
+
275
+ | Campo | Tipo | Descrição |
276
+ |---|---|---|
277
+ | `title` | String | Título da mídia |
278
+ | `type` | String | `image` ou `video` |
279
+ | `url` | String | URL do conteúdo |
280
+
281
+ ### 3.4 `PackAI`
282
+
283
+ | Campo | Tipo | Descrição |
284
+ |---|---|---|
285
+ | `module` | String | Módulo-alvo da IA (ex.: `widget`) |
286
+ | `info` | String | Texto auxiliar fornecido à IA |
287
+
288
+ > Comentário no código: pacotes de IA devem ser **gratuitos** e ter **apenas um componente** (ex.: 1 widget). Não há validação que imponha isso. Consumido por `findPackagesByArtificialIntelligenceModule(module)`, que projeta `{_id, title, info, module}`.
289
+
290
+ ### 3.5 `PackCheckout` / `CheckoutPricing`
291
+
292
+ `PackCheckout`:
293
+
294
+ | Campo | Tipo | Descrição |
295
+ |---|---|---|
296
+ | `_id` | String | Id da configuração `Checkout` (provedor) |
297
+ | `pricing` | List<CheckoutPricing> | Lista de preços; `getPricing(id)` retorna o item por `id` |
298
+
299
+ `CheckoutPricing`:
300
+
301
+ | Campo | Tipo | Padrão | Descrição |
302
+ |---|---|---|---|
303
+ | `id` | String | `null` | Id do preço (ex.: id de Plan Stripe para subscription) |
304
+ | `model` | String | `null` | `one_time` ou `subscription` |
305
+ | `unit` | String | `null` | `gamification` ou `player` (e `request` no cálculo de uso) |
306
+ | `currency` | String | `null` | Moeda |
307
+ | `price` | double | `0` | Valor (multiplicado por 100 para centavos no Stripe) |
308
+ | `interval` | String | `null` | Ex.: `month` |
309
+
310
+ `Checkout` (config de provedor): constantes `PROVIDER_STRIPE` (`stripe`), `PROVIDER_PAYPAL` (`paypal`), `PROVIDER_PAGARME` (`pagarme`). No fluxo de pacote só stripe e paypal são roteados (`PackageRest.createCheckoutSession`).
311
+
312
+ ### 3.6 `PackageInstallationLog` (coleção `package_install_log`)
313
+
314
+ | Campo | Tipo | Descrição |
315
+ |---|---|---|
316
+ | `_id` | String | short GUID (campo Java `id`) |
317
+ | `pack` | String | Id do pacote |
318
+ | `account` | String | Conta que solicitou a instalação |
319
+ | `apikey` | String | Gamificação onde o pacote foi instalado |
320
+ | `operation` | String | `install` / `update` / `uninstall` |
321
+ | `time` | Date | Quando a operação ocorreu |
322
+ | `subscription` | PartnerSubscription | Assinatura do gateway (preenchida no callback de pagamento); `null` para pacotes gratuitos |
323
+
324
+ ### 3.7 Técnicas de jogo (`techniques`)
325
+
326
+ `Pack.techniques` é uma `List<String>` de **códigos GT** (game techniques, Octalysis). As opções vêm da coleção `technique_field` (`Entity.GAME_TECHNIQUE_FIELD`), expostas via `GET /v3/package/component/type/technique_field` → `findOptionsByEntity` (linhas 340-344), que retorna cada `_id` como value e label. Os códigos são armazenados como texto livre; não há validação server-side de que pertençam ao catálogo.
327
+
328
+ ---
329
+
330
+ ## 4. Endpoints
331
+
332
+ > Autenticação em todos: **Bearer token** (`AuthBean`). Os endpoints `/partner/*` e `/system/*` exigem ainda permissão de sistema (`checkSystemPermission`).
333
+
334
+ ### Controller principal `/v3/package` (escopo: gamificação)
17
335
 
18
- - [ ] Acessar o marketplace via /market
19
- - [ ] Selecionar componente desejado
20
- - [ ] Importar para a gamificação atual
21
- - [ ] Customizar conforme necessidade
336
+ **`GET /v3/package/{id}`** `find`
22
337
 
23
- ## Validações e Testes
338
+ | Aspecto | Detalhe |
339
+ |---|---|
340
+ | Finalidade | Retorna 1 pacote por id |
341
+ | Escopo | **Global, sem filtro de apikey** (lê coleção `package` via `SystemFactory`). Qualquer gamificação lê qualquer pacote por id |
342
+ | Resposta | `Pack` com `null` fields removidos (inclui `components` e `password`) |
343
+
344
+ **`GET /v3/package`** — `findAll`
345
+ Lista apenas pacotes da gamificação corrente (`find("{apikey:#}", apiKey)`).
346
+
347
+ **`POST /v3/package`** — `insert`
348
+
349
+ | Aspecto | Detalhe |
350
+ |---|---|
351
+ | Finalidade | Publica/atualiza um pacote |
352
+ | Body | `Pack` (componentes como `{_id, type}` ou `{_id:"*", type}`) |
353
+ | Full replace | `Pack` inteiro; `components` é re-materializado (substituição total) |
354
+ | Comportamento | Força `apikey`, gera `id/creation/updated`, snapshota componentes; se `managed`, propaga às instalações ativas (síncrono) |
355
+
356
+ **`DELETE /v3/package/{id}`** — `delete`
357
+ Remove só se `_id` **e** `apikey` baterem (`remove("{_id:#, apikey:#}", ...)`). Não toca em `installed_package`.
358
+
359
+ **`PUT /v3/package/password`** — `changePassword`
360
+ Body `{ pack, password }`. Só o dono (`pack.apikey == apiKey`) altera; grava hash BCrypt.
361
+
362
+ **`POST /v3/package/password/verify`** — `verifyPassword`
363
+ Body `{ pack, password }`. Retorna `{authorized:true}` se o BCrypt bater. **Endpoint isolado** — não é chamado por `find`/`install`.
364
+
365
+ **`GET /v3/package/component/type/{type}`** — `findOptionsByType`
366
+ Retorna `[{_id, label}]` de uma coleção (action, point_category, challenge, widget_v3, level, leaderboard, trigger, mystery_box, lottery, technique_field, ou genérico). Alimenta o seletor de componentes do Studio.
367
+
368
+ **`GET /v3/package/marketplace/{id}`** — `findMarketplace`
369
+ Lê pacote global + enriquece `provider`. Retorna o `Pack` completo (componentes + hash de senha). Sem gate de visibility/status/password.
370
+
371
+ **`POST /v3/package/marketplace/aggregate`** — `findAllAggregate`
372
+
373
+ | Aspecto | Detalhe |
374
+ |---|---|
375
+ | Finalidade | Lista/filtra pacotes do marketplace |
376
+ | Body | Pipeline de agregação MongoDB **cru** (array) |
377
+ | Escopo | **Nenhum filtro server-side** — opera sobre toda a coleção `package` |
378
+ | Paginação | Header `Range` (default `0-100`) |
379
+
380
+ **`GET /v3/package/installed/{id}`** / **`GET /v3/package/installed`** / **`GET /v3/package/installedIds`**
381
+ Leem da coleção `installed_package` (escopo da conexão da gamificação).
382
+
383
+ **`GET /v3/package/install/{id}?session={sid}`** — `install`
384
+ Instala os componentes na gamificação corrente. Com `session`, dispara o callback de pagamento Stripe que grava a `subscription` no install log.
385
+
386
+ **`DELETE /v3/package/uninstall/{id}`** — `uninstall`
387
+ Remove componentes, tenta cancelar assinatura, remove de `installed_package`, grava log `uninstall`.
388
+
389
+ **`GET /v3/package/checkout?pack={id}&price={priceId}`** — `createCheckoutSession`
390
+ Cria sessão Stripe ou PayPal para compra do pacote.
391
+
392
+ **`GET /v3/package/{id}/plan/billing`** — faturas Stripe da assinatura do pacote instalado.
393
+ **`DELETE /v3/package/{id}/plan/subscription`** — cancela a assinatura.
394
+ **`GET /v3/package/{id}/plan/usage`** — reporta uso medido (jogadores/requests) ao Stripe.
395
+ **`GET /v3/package/install/{id}/log`** — retorna o `PackageInstallationLog` da instalação.
396
+
397
+ #### Exemplo — publicar pacote (snapshot de componentes)
398
+
399
+ ```http
400
+ POST /v3/package
401
+ Authorization: Bearer <token>
402
+ Content-Type: application/json
403
+ ```
404
+ ```json
405
+ {
406
+ "title": "Pacote de Onboarding",
407
+ "description": "Challenges + widget de boas-vindas",
408
+ "visibility": "globally",
409
+ "status": "approved",
410
+ "price_type": "free",
411
+ "components": [
412
+ { "_id": "welcome_challenge", "type": "challenge" },
413
+ { "_id": "*", "type": "widget_v3" }
414
+ ]
415
+ }
416
+ ```
417
+ Resposta (201) — note `apikey`/`creation`/`updated` preenchidos e `components[].content` materializado pelo servidor.
418
+
419
+ #### Exemplo — listar marketplace (pipeline do cliente)
420
+
421
+ ```http
422
+ POST /v3/package/marketplace/aggregate
423
+ Range: items=0-49
424
+ ```
425
+ ```json
426
+ [
427
+ { "$match": { "visibility": "globally", "status": "approved" } },
428
+ { "$sort": { "priority": -1, "updated": -1 } },
429
+ { "$project": { "title": 1, "image": 1, "description": 1, "price_type": 1, "techniques": 1 } }
430
+ ]
431
+ ```
432
+
433
+ ### Controller `/v3/partner/package` (escopo: parceiro)
434
+
435
+ Exige `checkSystemPermission("api", "/partner/package", op)`. Resolve o `Partner` pelo claim `account` do token.
436
+
437
+ - `GET /{id}` — `find`: só se o `accountId` do `game` do pacote == `accountId` do parceiro.
438
+ - `POST /` — `insert`: valida o mesmo match de conta; usa `system.PackageManager.insert` → **não materializa componentes nem força apikey** (o cliente fornece `content` e `apikey`).
439
+ - `DELETE /{id}` — `delete`: valida match de conta.
440
+ - `POST /aggregate` — `findAllAggregate` **com escopo**: prepende `{$match:{_id:{$in:[ids da conta]}}}` antes do pipeline do cliente.
441
+ - `PUT /password` — altera senha (sem checagem de dono).
442
+ - `GET /stripe/plan`, `GET /stripe/product` — lista planos/produtos Stripe do vendedor.
443
+
444
+ ### Controller `/v3/system/package` (escopo: admin)
445
+
446
+ Exige `checkSystemPermission("api", "/system/package", op)`.
447
+
448
+ - `GET /{id}` — find global.
449
+ - `POST /` — insert global (sem materialização/forçar apikey).
450
+ - `POST /install` `{apikey, pack}` — instala em uma gamificação arbitrária.
451
+ - `POST /uninstall` `{apikey, pack}` — desinstala de uma gamificação arbitrária.
452
+ - `DELETE /{id}` — delete global.
453
+ - `POST /aggregate` — `findAllAggregate` **sem escopo**.
454
+ - `POST /install/log/aggregate` — agregação crua sobre `package_install_log`.
455
+ - `PUT /password` — altera senha.
456
+
457
+ ---
458
+
459
+ ## 5. Regras de Negócio
460
+
461
+ Regras que existem no código mas não no schema:
462
+
463
+ - **Snapshot na publicação (`/v3/package`)**: o pacote captura o JSON dos componentes no momento da publicação. Mudanças posteriores nos componentes originais **não** refletem no pacote até nova publicação.
464
+ - **`apikey` forçado (`/v3/package`)**: qualquer `apikey` no body é sobrescrito pela gamificação autenticada. Os endpoints partner/system **não** forçam isso.
465
+ - **Não pode instalar o próprio pacote**: `install` lança UNAUTHORIZED se `pack.apikey == installer.apikey`. Pacotes só instalam em gamificações diferentes da criadora.
466
+ - **Instalação reidrata por `_id` preservado**: `insertContentByComponent` faz `save` do doc com o `_id` original → **sobrescreve** qualquer documento local de mesmo `_id` na coleção de destino (upsert).
467
+ - **Desinstalação remove por `_id`**: `deleteContentByComponent` apaga o doc por `_id` sem checar proveniência → pode apagar configuração local que coincida no `_id`.
468
+ - **Update limpa a versão anterior**: ao reinstalar (operation=update) com `pre.apikey != installer`, todos os componentes da versão anterior são removidos antes de instalar os novos.
469
+ - **Componentes `trigger` re-registram código**: instalar um pacote com componente `trigger` executa `triggerManager.add`, registrando o trigger (e seu script) na gamificação.
470
+ - **Componentes `lottery` têm caminho próprio**: instalação grava raw como qualquer outro, mas remoção usa `lotteryManager.delete`.
471
+ - **Cópia em massa (`"*"`)**: limitada a **1000 documentos** por coleção (`.find().limit(1000)`).
472
+ - **Comissão da plataforma**: 15% (`FUNIFIER_COMISSION`) aplicada como `application_fee_amount` (one_time) ou `application_fee_percent` (subscription).
473
+ - **`managed` propaga atualizações**: republicar um pacote `managed` reinstala em todas as instalações ativas (síncrono, exceções engolidas).
474
+ - **Multi-tenant fraco no marketplace**: leitura por id e o `aggregate` do marketplace são globais — qualquer gamificação autenticada vê qualquer pacote, independentemente de `visibility`/`status`.
475
+
476
+ ---
477
+
478
+ ## 6. Comportamentos Automáticos
479
+
480
+ | Comportamento | Trigger | Impacto | Persistência |
481
+ |---|---|---|---|
482
+ | Geração de `_id` curto | `insert` com `id` vazio | Define `pack.id` | `package` |
483
+ | `creation` / `updated` automáticos | `insert` | Timestamps | `package` |
484
+ | `apikey` forçado | `insert` (`/v3/package`) | Sobrescreve apikey do body | `package` |
485
+ | Materialização de componentes (snapshot) | `insert` (`/v3/package`) | `components[].content` preenchido; lista substituída | `package` |
486
+ | Cópia em massa `"*"` | componente com `_id == "*"` | Até 1000 docs viram componentes | `package` |
487
+ | Propagação `managed` | `insert` com `managed=true` | Reinstala em instalações ativas | `installed_package`, coleções de destino |
488
+ | Re-registro de trigger | `install` de componente `trigger` | `triggerManager.add` (registra código) | coleção `trigger` + runtime |
489
+ | Limpeza de versão antiga | `install` quando já instalado (update) | Remove componentes anteriores | coleções de destino |
490
+ | Cancelamento de assinatura | `uninstall` | Tenta cancelar subscription Stripe | gateway externo |
491
+ | Gravação de install log | `install` / `uninstall` | Registro de auditoria | `package_install_log` |
492
+ | Callback de pagamento | `install?session=` | Grava `subscription` no log | `package_install_log` |
493
+ | Enriquecimento `provider` | `findMarketplace` | Adiciona `account.extra` à resposta | nenhuma (runtime) |
494
+
495
+ ```mermaid
496
+ flowchart LR
497
+ A["install component"] --> B{"type?"}
498
+ B -->|trigger| C["save raw +<br/>triggerManager.add()"]
499
+ B -->|lottery| D["save raw<br/>(remoção usa lotteryManager)"]
500
+ B -->|outros| E["save raw (upsert por _id)"]
501
+ ```
502
+
503
+ ---
504
+
505
+ ## 7. Suportado vs NÃO Suportado
506
+
507
+ ### ✅ Suportado
508
+
509
+ - Publicação de pacotes com snapshot automático de componentes (`/v3/package`).
510
+ - Cópia de coleção inteira via `"*"` (até 1000 docs).
511
+ - Instalação/desinstalação cross-gamificação com log de auditoria.
512
+ - Atualização automática de instalações para pacotes `managed`.
513
+ - Pacotes pagos (one-time e subscription) via Stripe e PayPal, com comissão de 15%, faturas, cancelamento e uso medido.
514
+ - Marketplace por agregação crua dirigida pelo cliente (listagem/filtro/projeção).
515
+ - Pacotes para IA (`PackAI`) consumidos pelo módulo de IA (widgets).
516
+ - Senha de pacote (definição/verificação via endpoints dedicados).
517
+ - Publicação/gestão por parceiros (escopo de conta) e por admin (global).
518
+
519
+ ### ❌ NÃO Suportado / comportamento ausente
520
+
521
+ - **Aprovação/visibilidade enforçada**: `status` (`waiting/approved/denied`) e `visibility` (`private/globally`) são **declarados mas nunca lidos** pelo servidor. Não há fluxo de moderação server-side; o filtro é 100% do cliente.
522
+ - **Proteção por senha real**: `password` **não** é verificado em `find`/`findMarketplace`/`install`. O hash inclusive é retornado na resposta. A verificação só existe no endpoint isolado `/password/verify`, que nada bloqueia.
523
+ - **Isolamento multi-tenant na leitura**: `GET /v3/package/{id}` e `/v3/package/marketplace/*` não filtram por `apikey` — leitura global de qualquer pacote.
524
+ - **Webhooks de instalação**: nenhum webhook é disparado em publish/install/uninstall (confirmado — sem referências a `WebhookManager` no módulo).
525
+ - **Eventos de gamificação (`before_win`/`after_win`/`fireAction`)**: não são disparados pelo pacote.
526
+ - **Scheduler/job**: nenhum job processa as coleções `package`/`installed_package`/`package_install_log`. A propagação `managed` é síncrona no `insert`, não agendada.
527
+ - **Materialização de componentes nos endpoints partner/system**: o `system.PackageManager.insert` **não** snapshota componentes nem força `apikey` — o cliente deve fornecer `content`.
528
+ - **Ordenação por `priority`**: o campo existe mas não há ordenação server-side; depende do pipeline do cliente.
529
+ - **Versionamento de pacote**: não há histórico de versões; `install` de uma versão atualizada sobrescreve a anterior.
530
+ - **Código legado / morto**:
531
+ - `engine.packages.PackageManager.findContentByEntity` (linhas 366-414): só tem chamadores comentados → **código morto**.
532
+ - Blocos comentados grandes em `insertContentByComponent`/`deleteContentByComponent` (tratamento tipado por entidade) — substituídos por gravação/remoção raw genérica.
533
+ - Loop comentado no início do `insert` (linhas 95-99).
534
+
535
+ ---
536
+
537
+ ## 8. Segurança e Permissões
538
+
539
+ - **Autenticação**: Bearer token (`AuthBean`) em todos os endpoints. `/partner/*` e `/system/*` exigem permissão de sistema adicional (`checkSystemPermission`); `/v3/package` (gamificação) **não** exige permissão extra além do token.
540
+ - **Isolamento por tenant — fraco no marketplace**:
541
+ - `GET /v3/package/{id}` lê a coleção `package` global sem filtro de `apikey`.
542
+ - `POST /v3/package/marketplace/aggregate` roda pipeline cru sobre **todos** os pacotes, sem `$match` de escopo. Qualquer gamificação autenticada lê qualquer pacote (inclusive pagos/privados) e todo o conteúdo snapshotado dos componentes.
543
+ - **Injeção de agregação (NoSQL)**: `findAllAggregate` (marketplace, partner e system) e `findAllInstallLogAggregate` recebem o pipeline diretamente do cliente e o executam via Jongo. A variante partner mitiga prependendo `$match _id $in [ids da conta]`; a do marketplace e a do system **não** mitigam. Estágios como `$lookup`, `$out`/`$merge` ou expressões `$where` ficam a depender apenas do que o servidor MongoDB permite.
544
+ - **Execução de código via componente `trigger`**: instalar um pacote de terceiros que contenha um componente `trigger` re-registra o trigger (`triggerManager.add`, linha 427), introduzindo script server-side (Groovy/Java) na gamificação que instala. Risco de supply-chain: trate pacotes de origem desconhecida como código não confiável.
545
+ - **Sobrescrita/remoção por `_id`**: instalação faz upsert por `_id` (pode clobберar config local de mesmo `_id`); desinstalação remove por `_id` sem checar proveniência (pode apagar config local homônima).
546
+ - **Vazamento de hash de senha**: `findMarketplace`/`find` retornam `Pack` via `toJsonRemoveNullFields`, que só remove nulos — o campo `password` (hash BCrypt) é exposto na resposta. Não há `@JsonIgnore`.
547
+ - **Pagamentos**: chaves Stripe da Funifier (`StripeUtil.getFunifierSecretKey`) são usadas server-side; a conta conectada do vendedor (`stripe.stripe_user_id`) recebe o pagamento menos 15% de comissão. URLs de retorno são fixas em `my.funifier.com`.
548
+
549
+ ---
550
+
551
+ ## 9. Observabilidade e Troubleshooting
552
+
553
+ Diagnóstico rápido:
554
+
555
+ ```http
556
+ # O pacote existe na coleção global?
557
+ GET /v3/package/marketplace/{id}
558
+
559
+ # Está instalado nesta gamificação?
560
+ GET /v3/package/installed/{id}
561
+ GET /v3/package/installedIds
562
+
563
+ # Histórico de instalação (admin)
564
+ POST /v3/system/package/install/log/aggregate
565
+ Range: items=0-99
566
+ [ { "$match": { "pack": "<packId>" } }, { "$sort": { "time": -1 } } ]
567
+ ```
568
+
569
+ Queries MongoDB úteis:
570
+
571
+ ```js
572
+ // Pacotes publicados por uma gamificação
573
+ db.package.find({ apikey: "<APIKEY>" })
574
+
575
+ // Instalações ativas de um pacote (último install/update por gamificação)
576
+ db.package_install_log.aggregate([
577
+ { $match: { pack: "<packId>" } },
578
+ { $sort: { time: 1 } },
579
+ { $group: { _id: { pack: "$pack", apikey: "$apikey" }, operation: { $last: "$operation" }, time: { $max: "$time" } } },
580
+ { $match: { operation: { $in: ["install", "update"] } } }
581
+ ])
582
+
583
+ // Pacotes de IA por módulo
584
+ db.package.aggregate([ { $match: { "ai.module": "widget" } } ])
585
+ ```
586
+
587
+ Erros comuns e causas:
588
+
589
+ | Sintoma | Causa provável |
590
+ |---|---|
591
+ | `This package was created in this gamification...` (UNAUTHORIZED) | Tentou instalar pacote da própria gamificação. Só instala de outra |
592
+ | Componente "some" do pacote não foi instalado | Item sem `_id`/`type`, ou exceção engolida na materialização (`System.out.println`); confira logs do servidor |
593
+ | Atualização de pacote `managed` não chegou em uma instalação | Gamificação inexistente (`findByApiKey == null`) ou exceção engolida na propagação síncrona |
594
+ | Config local sumiu após instalar pacote | Componente do pacote tinha o mesmo `_id` e sobrescreveu/removeu o doc local |
595
+ | Pacote "privado" aparece para terceiros | `visibility`/`status` não são enforçados; só o pipeline do cliente filtra |
596
+ | Cobrança não registrou assinatura | `install` chamado sem `?session=`, ou falha no `applyStripePackageCheckoutCallback` (Stripe) |
597
+
598
+ O que verificar quando algo não funciona:
599
+ - Existência do pacote em `package` (global) vs `installed_package` (local).
600
+ - `pack.apikey` (criador) vs apikey que está instalando.
601
+ - Tipos de componente apontam para coleções válidas (`Component.type` == nome da coleção).
602
+ - Para pagos: `price_type == "checkout"`, `checkout._id` válido e provedor `stripe`/`paypal`.
603
+
604
+ ---
605
+
606
+ ## 10. Exemplos Práticos
607
+
608
+ ### Mínimo funcional — pacote gratuito com 1 challenge
609
+
610
+ ```http
611
+ POST /v3/package
612
+ Authorization: Bearer <token>
613
+ ```
614
+ ```json
615
+ {
616
+ "title": "Desafio Diário",
617
+ "description": "Um challenge de login diário",
618
+ "price_type": "free",
619
+ "components": [ { "_id": "daily_login", "type": "challenge" } ]
620
+ }
621
+ ```
622
+
623
+ ### Avançado — pacote `managed` pago, com mídia, técnicas e cópia em massa
624
+
625
+ ```http
626
+ POST /v3/package
627
+ Authorization: Bearer <token>
628
+ ```
629
+ ```json
630
+ {
631
+ "title": "Suite de Engajamento Pro",
632
+ "description": "Challenges, widgets e triggers de engajamento",
633
+ "image": "https://cdn.exemplo.com/icon.png",
634
+ "screenshot": "https://cdn.exemplo.com/shot.png",
635
+ "medias": [ { "title": "Demo", "type": "video", "url": "https://youtu.be/xyz" } ],
636
+ "tags": ["engajamento", "onboarding"],
637
+ "techniques": ["GT01", "GT08"],
638
+ "visibility": "globally",
639
+ "status": "approved",
640
+ "managed": true,
641
+ "priority": 10,
642
+ "price_type": "checkout",
643
+ "checkout": {
644
+ "_id": "<checkoutConfigId>",
645
+ "pricing": [
646
+ { "id": "<stripePlanId>", "model": "subscription", "unit": "player", "currency": "usd", "price": 0.5, "interval": "month" }
647
+ ]
648
+ },
649
+ "components": [
650
+ { "_id": "*", "type": "challenge" },
651
+ { "_id": "welcome_widget", "type": "widget_v3" },
652
+ { "_id": "engagement_trigger", "type": "trigger" }
653
+ ]
654
+ }
655
+ ```
656
+
657
+ Compra + instalação:
658
+
659
+ ```http
660
+ GET /v3/package/checkout?pack=<packId>&price=<stripePlanId> # cria sessão Stripe
661
+ # ... pagamento ... redireciona com session_id
662
+ GET /v3/package/install/<packId>?session=<stripeSessionId> # instala + registra subscription
663
+ ```
664
+
665
+ ### Anti-pattern — o que NÃO fazer
666
+
667
+ ```json
668
+ // ❌ NÃO confie em `password`/`visibility`/`status` como segurança
669
+ {
670
+ "title": "Pacote Confidencial",
671
+ "visibility": "private",
672
+ "status": "waiting",
673
+ "password": "ignorado-pelo-servidor",
674
+ "components": [ { "_id": "config_secreta", "type": "trigger" } ]
675
+ }
676
+ ```
677
+ Por quê: o servidor **não** lê `visibility`/`status`/`password` na leitura. Qualquer gamificação autenticada lê este pacote via `/v3/package/marketplace/{id}` ou pelo `aggregate`, incluindo o `content` completo do componente `trigger` (que pode conter código/segredos) e o próprio hash de senha. Não publique conteúdo sensível em pacotes.
678
+
679
+ Outros anti-patterns:
680
+ - Empacotar componentes cujo `_id` colide com config crítica de quem instala → sobrescrita silenciosa na instalação e remoção na desinstalação.
681
+ - Esperar moderação automática antes de o pacote ficar visível → não existe; a moderação é do frontend.
682
+ - Publicar pacote pesado com vários `"*"` → cada `"*"` copia até 1000 docs e infla `components` (e o documento `package`).
683
+
684
+ ---
685
+
686
+ ## Checklist de Configuração
24
687
 
25
- - [ ] Componente é importado com sucesso
26
- - [ ] Configurações importadas estão corretas
27
- - [ ] Componente funciona na gamificação atual
688
+ - [ ] `title` e `description` preenchidos (não validados server-side, mas exigidos pela UI).
689
+ - [ ] Cada `component` tem `_id` e `type` válidos; `type` == nome real da coleção MongoDB.
690
+ - [ ] Os documentos referenciados pelos componentes **existem** no momento da publicação (senão são descartados silenciosamente).
691
+ - [ ] Para publicar via `/v3/package`: não confie no `apikey` enviado — ele será forçado para a gamificação autenticada.
692
+ - [ ] Para pacote pago: `price_type == "checkout"`, `checkout._id` aponta para um `Checkout` válido, e há ao menos um `CheckoutPricing` com `model`/`currency`/`price`.
693
+ - [ ] Para pacote `managed`: ciente de que republicar reinstala em todas as instalações ativas (síncrono).
694
+ - [ ] Para componentes `trigger`: ciente de que a instalação registra/executa o trigger na gamificação de destino.
695
+ - [ ] **Armadilha**: `_id` de componente coincidente sobrescreve/remove config local de quem instala.
696
+ - [ ] **Armadilha**: `visibility`/`status`/`password` NÃO protegem o pacote — não coloque conteúdo sensível.
697
+ - [ ] **Armadilha**: instalar exige que o pacote seja de **outra** gamificação (não a criadora).
698
+ - [ ] Após instalar pacote pago, chame `install?session=<id>` para registrar a assinatura no log.