Skip to content

Add Dynamic Payment Templates authoring kit #trivial - #7

Open
carolkrroo wants to merge 9 commits into
masterfrom
feat/dynamic-payment-mocker
Open

Add Dynamic Payment Templates authoring kit #trivial#7
carolkrroo wants to merge 9 commits into
masterfrom
feat/dynamic-payment-mocker

Conversation

@carolkrroo

@carolkrroo carolkrroo commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Replace the legacy Pink inline template flow with a Dynamic Payment Templates authoring kit: partner contract, reference bundle, validation, and local preview.
  • Serve template bundles through a sandboxed iframe (allow-scripts only) inside the checkout shell mock, with { locale } / { height } postMessage, locale switching, and dynamic iframe height.
  • Run @vtex/payment-templates-validator on grunt startup and on saves to template/ or lib/ — validation replaces manual smoke checks in the dev loop.
  • Align the wrapped document CSP with production (img-src 'self' data: for <img src> data URLs).
  • Restore checkout shell polish: payment-tab icons (credit, boleto, PayPal, MercadoPago), full locale tags in the language switcher (fr-FR, es-ES), and trimmed checkout CSS scoped to the current mock.
  • Remove unused Pink assets (Angular partial, Less/Sass, shell i18n, legacy images) and simplify the Grunt dev server to connect + livereload with preview middleware.

Test plan

  • Run npm i and grunt — validation must pass (ok: true) before the server starts
  • Open http://localhost:8080/ and confirm the checkout shell loads
  • Confirm the payment template renders inside the sandboxed iframe
  • Switch locales with the flag buttons (pt-BR, en-US, fr-FR, es-ES) and verify iframe content updates; unavailable locales should fall back to defaultLocale
  • Confirm payment-tab icons appear to the right of Cartão, Boleto, PayPal, and MercadoPago labels
  • Edit files under template/reference/ and confirm livereload refreshes the preview; saving an invalid bundle should fail validation on watch
  • Run npm run validate:reference manually and expect ok: true

@vtex-pr-sentinel

vtex-pr-sentinel Bot commented Aug 14, 2026

Copy link
Copy Markdown

🛡️ SDD Check — action required

I couldn't detect an SDD in this PR. Please check one option below (requires write access to the repo):

  • SDD lives in another PR — paste the SDD PR URL here:
  • This PR doesn't need an SDD
  • SDD applies, but I'm not adopting it in this PR

Introduce the partner-facing contract, preview config, and a complete
reference example with HTML, CSS, i18n files, and raster assets.
Wire @vtex/payment-templates-validator with shared lib helpers and npm
scripts to validate the reference bundle before submission.
Wrap template fragments into sandboxed HTML with CSP and i18n runtime,
and serve them through connect middleware during local development.
Replace the inline Pink partial with a sandboxed iframe host, locale
switcher, and trimmed checkout styles scoped to the current mock.
Drop the Angular partial, Less/Sass styles, shell i18n files, and unused
checkout images that belonged to the old inline template flow.
Simplify Grunt to connect + livereload with preview middleware, refresh
the README for the new workflow, and trim obsolete gitignore entries.
@carolkrroo
carolkrroo force-pushed the feat/dynamic-payment-mocker branch from 966d814 to 22c25b7 Compare August 14, 2026 00:48
@carolkrroo carolkrroo self-assigned this Aug 14, 2026
@carolkrroo
carolkrroo requested a review from fdaciuk August 14, 2026 00:48
Run template validation on grunt startup and watch, allow data: images in
iframe CSP, restore payment-tab icons, and use full locale tags in the
language switcher.
@carolkrroo
carolkrroo requested a review from huandrey August 14, 2026 19:20
@carolkrroo carolkrroo changed the title Add Dynamic Payment Templates authoring kit Add Dynamic Payment Templates authoring kit #trivial Aug 14, 2026

@fdaciuk fdaciuk left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bloqueando por: CSP com script-src 'unsafe-inline' sobre HTML de parceiro sem gate de validação no preview, escape de symlink no middleware, medição de altura que corta bundles com mais de uma raiz, filtro de nomes que impede o validador local de reprovar, e lockfile sem integrity em 194 de 200 pacotes. Detalhes nos comentários.

Comment thread lib/wrap-template.js
"default-src 'none'",
"style-src 'self'",
"img-src 'self' data:",
"script-src 'unsafe-inline'",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

script-src 'unsafe-inline' libera exatamente o que o CONTRACT.md:60-61 proíbe (<script> e handlers inline), e o HTML do parceiro é colado cru na linha 60. Como o preview não passa pelo validador (lib/preview-middleware.js:59-60 chama loadBundle + wrapTemplate direto), um bundle reprovado na validação ainda executa script. Use nonce ou hash apenas para o runtime injetado.

Comment thread lib/wrap-template.js

return [
'<!doctype html>',
'<html lang="' + defaultLocale + '">',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defaultLocale entra no atributo sem escape, e lib/preview-config.js:12 só checa se está preenchido. Um valor contendo "> fecha o atributo e, com o CSP inline liberado, executa. Valide contra ^[a-z]{2}-[A-Z]{2}$ — o regex que o CONTRACT.md:28 já define. Importa mais porque o comentário das linhas 8-13 prevê reuso deste módulo no handler de upload, onde o valor vem do parceiro.

Comment thread lib/preview-middleware.js
return;
}

const relativePath = decodeURIComponent(req.url.slice(BUNDLE_PREFIX.length).split('?')[0]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

decodeURIComponent sem proteção: GET /template-bundle/% lança URIError e devolve 500 com stack trace, em vez do 404 limpo que a linha 79 faz questão de retornar. Envolva em try/catch e responda 400.

Comment thread lib/preview-middleware.js

const filePath = path.join(config.bundlePath, relativePath);
const normalizedRoot = path.resolve(config.bundlePath) + path.sep;
if (!path.resolve(filePath).startsWith(normalizedRoot)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A contenção compara só o caminho textual; existsSync, statSync e createReadStream seguem symlink. Um bundle de parceiro (entregue como pasta, CONTRACT.md:128) com asset-logo.png -> ../../../../.ssh/id_rsa passa pelo 403 e é servido. Resolva com fs.realpathSync antes de comparar.

Comment thread lib/load-bundle.js
const assets = [];

for (const entry of fs.readdirSync(bundleDir)) {
const i18nMatch = /^i18n-([a-z]{2}-[A-Z]{2})\.json$/.exec(entry);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O filtro por nome roda antes de entregar ao validador, então as regras de nomenclatura nunca falham localmente: i18n-es.json é descartado em silêncio e npm run validate:reference imprime ok: true, apesar de o CONTRACT.md:16 dizer que esse nome é rejeitado. Mesmo problema na linha 29 com assets fora do padrão asset-*. Entregue todos os arquivos ao validador e deixe ele reprovar.

{
"bundleDir": "reference",
"defaultLocale": "pt-BR",
"icon": "asset-logo.png",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

asset-logo.png tem 300×300, mas o CONTRACT.md:18 diz que o ícone cabe numa caixa de 160×160. Como o ícone não é validado, nada acusa — e esta pasta é apresentada como exemplo completo e válido (CONTRACT.md:5). Ajuste a imagem ou a regra.

Comment thread template/CONTRACT.md

The payment step renders your bundle in an iframe with `sandbox="allow-scripts"`, applies translations from the wrapped document, resizes on content changes, and accepts locale switches via `postMessage` — matching the checkout host contract.

Images inside the iframe may use bundle-local files or `data:` URLs on `<img src>` (including `data:image/svg+xml`). Inline SVG in CSS `url(data:…)` is not supported.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Contradiz a linha 63, que lista data:... entre os itens proibidos em todos os casos. Defina qual das duas vale — hoje o parceiro não tem como saber o que o validador aplica.

Comment thread template/CONTRACT.md
- `style.css` with bundle-local asset references
- `asset-logo.png` and `asset-badge.png`
- `i18n-pt-BR.json` and `i18n-en-US.json` with matching keys
- `defaultLocale = pt-BR` (set in the validation script and documented above)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defaultLocale não é definido no script de validação, e sim em template/preview.config.json.

"pay": {
"title": "Example Pay",
"lead": "Ao finalizar a compra, você será redirecionado(a) para concluir o pagamento.",
"benefitGuaranteedTitle": "Guarantido",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Guarantido" → "Garantido". Esta é a pasta que o CONTRACT.md:146 manda copiar, então o erro se propaga para os templates dos parceiros.

Comment thread .gitignore
@@ -1,5 +1,2 @@
.DS_Store
.sass-cache
.grunt

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.grunt saiu do ignore, mas o projeto continua rodando grunt. .sass-cache e build saíram com razão; este parece ter ido junto sem querer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants