This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is a Val Town project — a Klarna payment integration prototype demonstrating iframe-based payment flows for the JPM (J.P. Morgan) partnership. It runs on the Val Town platform (Deno serverless runtime, not Node.js).
This project is deployed and run via Val Town. Locally, use the vt CLI:
vt dev # Start local dev server vt push # Deploy to Val Town vt pull # Pull latest from Val Town
The project has a .vt/state.json that tracks the Val Town project ID and branch.
The app uses a three-context iframe architecture for Klarna payment integration:
-
main.ts— HTTP request handler (Val Town entry point). Serves static files fromfrontend/andshared/using Val Town'sserveFile/readFileutilities. -
frontend/index.html— First-party merchant page. Orchestrates payment flow by:- Embedding
button-iframe.htmlfor the Klarna pay button - Dynamically creating a popup overlay with
popup-iframe.htmlwhen payment is initiated - Routing
postMessageevents between the two iframes
- Embedding
-
frontend/button-iframe.html— Third-party iframe containing the Klarna SDK payment button. On click, sendsshow-popupmessage to parent. -
frontend/popup-iframe.html— Third-party iframe for the Klarna payment flow. Receivesinitiate-paymentfrom parent, runsklarna.Payment.initiate(), and sends backpayment-completeorpayment-abort. -
shared/types.ts— TypeScript type definitions for allpostMessagepayloads (ShowPopupMessage,InitiatePaymentMessage,PaymentCompleteMessage,PaymentAbortMessage,ClosePopupMessage) plus type guard functions.
button-iframe → show-popup → index.html (parent)
index.html → initiate-payment → popup-iframe
popup-iframe → popup-ready → index.html
popup-iframe → payment-complete → index.html
popup-iframe → payment-abort → index.html
- Runtime is Deno, not Node.js — use
Deno.env.get()for env vars, notprocess.env - Use
https://esm.shfor npm imports (works in both server and browser) - Code in
shared/must work in both frontend and backend — cannot useDenonamespace there - Entry point exports a default async function:
export default async function(req: Request) - Use
Response.redirectworkaround:new Response(null, { status: 302, headers: { Location: url } }) - Do NOT use Deno KV,
alert(),prompt(), orconfirm() - Imports use URL-based specifiers (e.g.,
https://esm.town/v/std/utils@85-main/index.ts)
Both iframes initialize the Klarna Web SDK v2 from https://js.playground.klarna.com/web-sdk/v2/klarna.mjs using test credentials. The SDK is configured with products: ["PAYMENT"].
Uses TailwindCSS via twind CDN (https://cdn.twind.style). When using React, pin all dependencies to react@18.2.0.