Publicando plugins
Guia completo para autores que querem publicar plugins no Arqel Marketplace.
Esta página descreve o pipeline de publicação de ponta a ponta: setup de conta, submissão, auto-checks, security scan, review manual, releases subsequentes e tracking de estatísticas.
Pré-requisitos
Antes de submeter, você precisa ter:
- Pacote PHP publicado em Packagist (
vendor/package) comtype: arqel-pluginnocomposer.json. - (Opcional) Pacote npm companion para o lado React, publicado em npm registry (
@vendor/package). - Repositório público no GitHub com
LICENSE(preferencialmente MIT — ver allow-list em Boas práticas de segurança). - Pelo menos 1 release tagueado (
v0.1.0ou superior, semver-compliant). - Convention compliant — rode
arqel:plugin:list --validatelocalmente para garantir.
Se algum desses estiver faltando, abra primeiro o Tutorial de desenvolvimento — ele cobre setup de zero.
Passo 1 — Conta de publisher
Crie sua conta em arqel.dev/marketplace/signup. O form pede:
- Email (verificação obrigatória — link expira em 24h).
- Display name público que aparecerá ao lado de cada plugin seu.
- GitHub username para vinculação automática de repos.
- Composer vendor namespace (ex:
acme) — você só pode submeter plugins sob esse namespace. - (Opcional) Stripe Connect onboarding se você pretende publicar plugins pagos. Pode ser feito depois.
A conta tem três estados: unverified → verified → publisher. Apenas publisher pode submeter — escalação automática após verificar email + provar ownership do GitHub via OAuth.
Passo 2 — Form de submissão
Endpoint REST: POST /api/marketplace/plugins/submit (auth Sanctum required, controller PluginSubmissionController).
Payload mínimo:
{
"composer_package": "acme/stripe-card",
"npm_package": "@acme/arqel-stripe-fields",
"github_url": "https://github.com/acme/arqel-stripe-card",
"type": "field-pack",
"name": "Stripe Card Field",
"description": "Renderiza o Stripe Elements Card como um Field Arqel pronto para PaymentMethod.",
"screenshots": [
"https://raw.githubusercontent.com/acme/arqel-stripe-card/main/docs/screen-1.png",
"https://raw.githubusercontent.com/acme/arqel-stripe-card/main/docs/screen-2.png"
]
}Validação aplicada por SubmitPluginRequest:
| Campo | Regra |
|---|---|
composer_package | regex vendor/package, único em arqel_plugins |
npm_package | string opcional |
github_url | URL válida, host github.com (warn se outro) |
type | enum field-pack/widget-pack/integration/theme/language-pack/tool |
name | 3-100 chars |
description | 20-2000 chars (warn se < 50) |
screenshots[] | array de URLs públicas (warn se 0) |
slug | derivado de name via Str::slug quando ausente; uniqueness check |
A resposta 201 traz {plugin: {...}, checks: {checks: [...], passed: bool}} — você verá imediatamente quais auto-checks passaram. Se passed: false, o plugin ainda entra com status=pending, mas a review queue é alertada e o tempo de aprovação aumenta.
Passo 3 — Auto-checks (sem rede)
O PluginAutoChecker roda 5 verificações defensivas:
composer_package_format— fail se regex inválida.github_url_format— fail se host não égithub.com.description_length— warn se < 50 chars.screenshots_count— warn se 0.name_uniqueness— warn se outro plugin published já tem nome similar.
Esses checks são instantâneos — não fazem HTTP requests. A intenção é falhar rápido em erros óbvios sem prender CI por minutos.
Passo 4 — Convention validation
O PluginConventionValidator (MKTPLC-003) é o segundo gatekeeper. Ele exige que o composer.json do seu pacote contenha:
{
"name": "acme/stripe-card",
"type": "arqel-plugin",
"description": "Stripe Card Field for Arqel",
"license": "MIT",
"keywords": ["arqel", "plugin", "field", "stripe", "payments"],
"extra": {
"arqel": {
"plugin-type": "field-pack",
"category": "integrations",
"compat": {
"arqel": "^1.0"
},
"installation-instructions": "https://github.com/acme/arqel-stripe-card#installation"
}
}
}Erros (fail):
typenão éarqel-plugin.extra.arqel.plugin-typeausente ou fora do enum.extra.arqel.compat.arqelnão é constraint semver válida.extra.arqel.categoryausente ou vazia.
Warnings (passa mas alerta):
extra.arqel.installation-instructionsausente.keywordsnão incluiarqel+plugin.
E o package.json do companion npm precisa de um dos dois:
{
"arqel": { "plugin-type": "field-pack" }
}ou
{
"peerDependencies": { "@arqel-dev/types": "^1.0" }
}Passo 5 — Security scan
Após validation, o SecurityScanner (MKTPLC-009) cria uma row arqel_plugin_security_scans em running e roda quatro etapas:
- Vulnerability lookup — consulta
VulnerabilityDatabase(defaultStaticVulnerabilityDatabaseretornando empty; host apps podem rebindar para GitHub Advisory Database real). Cada package composer + npm é consultado. - License check — confere
composer.json#licensecontra allow-list (MIT,Apache-2.0,BSD-2-Clause,BSD-3-Clause). Fora da lista vira warninglow. - Suspicious patterns — placeholder atual (TODO MKTPLC-009-static-analysis). No futuro, scan estático para
eval,exec,file_get_contentsem URLs de user input, etc. - Severity rollup — pega o máximo de todos findings.
Resultado:
| Severity máxima | Ação |
|---|---|
critical | status=failed + auto-delist (status=archived) + dispatch PluginAutoDelistedEvent |
high ou medium | status=flagged + alerta para review manual |
low ou nenhum | status=passed |
Se seu plugin é flagged, não se desespere — abra a página de detalhe do scan no admin dashboard, leia os findings e responda com remediation. O reviewer humano decide caso a caso.
Passo 6 — Review manual
Plugins com status=pending entram na fila de moderação (GET /admin/plugins?status=pending, Gate marketplace.review). O reviewer humano:
- Lê descrição + screenshots.
- Visita
github_urle dá uma olhada no código (especialmente service provider e qualquerHttp/Process/Storagecall). - Confere se o plugin não viola guidelines (sem cripto adversarial, sem coleta de telemetria opaca, sem dependência abandonware).
- Aprova ou rejeita via
POST /admin/plugins/{slug}/review.
Timeline esperada:
| Cenário | Tempo |
|---|---|
| Auto-checks passed + scan passed + reviewer disponível | 1-2 dias |
| Warnings em auto-checks ou scan flagged | 3-5 dias |
| Rejeitado e re-submetido após fix | 5-7 dias |
| Backlog grande (releases majores do framework) | até 14 dias |
Aprovado → status=published + dispatch PluginApproved event → plugin aparece em /api/marketplace/plugins. Rejeitado → status=archived + rejection_reason populado + dispatch PluginRejected. Você recebe email com motivo (integração de email é TBD; por enquanto você consulta via GET /publisher/plugins).
Passo 7 — Releases subsequentes
Cada nova versão do seu plugin gera uma row em arqel_plugin_versions:
POST /api/marketplace/plugins/{slug}/versions
{
"version": "1.2.0",
"changelog": "Adicionado suporte a Stripe Connect Express. Fix em currency=EUR.",
"released_at": "2026-05-15T14:00:00Z"
}Versões seguem semver estrito. O marketplace não re-roda security scan automaticamente em toda release (caro) — mas roda diariamente via arqel:marketplace:scan agendado. Você pode forçar scan via dashboard quando shippa fix de vulnerability.
Ao publicar release com breaking change, atualize extra.arqel.compat.arqel no composer.json do tag novo. Usuários com framework <compat.arqel continuarão recebendo a versão antiga via Composer resolver — sem ação extra do marketplace.
Passo 8 — Estatísticas
A dashboard do publisher (/marketplace/publisher/dashboard) consome quatro endpoints:
GET /api/marketplace/publisher/plugins
GET /api/marketplace/publisher/plugins/{slug}/installations?days=30
GET /api/marketplace/publisher/plugins/{slug}/reviews
GET /api/marketplace/publisher/payoutsCada um retorna métricas filtradas por publisher_user_id = auth()->id(). Detalhe completo de stats fica em MKTPLC-004 — analytics (entrega futura).
Para plugins pagos, você também enxerga purchases agregadas + payouts pendentes:
GET /api/marketplace/publisher/payouts?per_page=20Detalhes em Pagamentos & licenças.
Pipeline visual
flowchart LR
A[Submit form] --> B[Auto-checks 5x]
B -->|fail| Z[422 erro]
B -->|passed/warnings| C[Convention validator]
C -->|fail| Z
C -->|passed| D[Security scan]
D -->|critical| F[failed + auto-delist]
D -->|high/medium| E[flagged → manual review]
D -->|low/nenhum| G[passed → manual review]
E --> R{Reviewer decide}
G --> R
R -->|approve| P[published]
R -->|reject| X[archived + reason]Checklist do publisher
Antes de submeter, confira:
- [ ]
composer.json#type === "arqel-plugin" - [ ]
extra.arqel.plugin-typecorreto - [ ]
extra.arqel.compat.arqelé semver constraint válida - [ ]
extra.arqel.categorypopulada - [ ]
keywordsincluiarqel+plugin - [ ]
LICENSEno repositório (MIT preferível) - [ ] README com installation, usage example, screenshots
- [ ] Pelo menos 1 release tagueado em GitHub
- [ ] Pacote publicado em Packagist
- [ ] (Opcional) Pacote npm companion publicado
- [ ] Auto-checks locais via
arqel:plugin:list --validate
Próximos passos
- Quer construir um plugin do zero? Veja Tutorial de desenvolvimento.
- Quer ativar pagamento? Veja Pagamentos & licenças.
- Plugin foi rejeitado por security? Veja Boas práticas de segurança.