Skip to content

Getting Started

This guide walks you through running a real payment form in the browser without preparing your own Stripe account or API keys. Every step is verified, and finishing it ends with an actual successful payment in Stripe test mode.

Before writing code, you can try the live demo in CodeSandbox (Stripe test mode, no API keys needed).

Edit in CodeSandbox

The demo covers three components:

  • <stripe-card-element> (card input with a Payment Intent confirm flow)
  • <stripe-address-element> (address form)
  • <stripe-express-checkout-element> (Apple Pay / Google Pay / Link, device-dependent)

There is no <stripe-payment-element> demo yet.

The steps in this guide use a demo API provided by the documentation site. It creates Payment Intents and Checkout Sessions on a Stripe test-mode account, so you can try the full payment flow without your own Stripe account.

Endpoint Method and body Returns
/api/config GET { publishableKey }
/api/create-payment-intent POST { "currency": "jpy" } { clientSecret }
/api/create-checkout-session POST { "currency": "jpy" } { clientSecret }

The base URL is https://stripe-pwa-elements-docs.wp-kyoto.workers.dev. Supported currencies are usd, jpy, eur, gbp, and aud, with the amount fixed server-side at 1,000. Browsers can only call it from CORS-allowed origins: localhost ports 3000, 4321, and 5173, plus previews such as *.csb.app and *.stackblitz.io.

The publishable key also comes from the demo API. A clientSecret is bound to the Stripe account that created it, so it must be paired with a publishable key from the same account.

Note that the demo API is for trying these steps only. In production, create the Payment Intent or Checkout Session on your own server and pass its clientSecret to the browser. See Payment flows for the full picture.

Choose the tab for your environment. Every path ends with the same test-card payment.

The most straightforward setup: a Vite build.

ターミナルウィンドウ
npm create vite@latest stripe-demo -- --template vanilla-ts
cd stripe-demo
npm install
ターミナルウィンドウ
npm i stripe-pwa-elements

Replace index.html with the following.

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>stripe-pwa-elements demo</title>
</head>
<body>
<h1>Card demo</h1>
<div id="checkout"></div>
<pre id="log"></pre>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

Import the component you use from dist/components. The import itself registers the custom element.

import 'stripe-pwa-elements/dist/components/stripe-card-element';
const API = 'https://stripe-pwa-elements-docs.wp-kyoto.workers.dev/api';
const log = (message: string, detail?: unknown) => {
const out = document.querySelector<HTMLPreElement>('#log')!;
out.textContent += detail === undefined ? `${message}\n` : `${message}\n${JSON.stringify(detail, null, 2)}\n`;
};
// Fetch the publishable key and a clientSecret from the demo API
const { publishableKey } = await (await fetch(`${API}/config`)).json();
const { clientSecret } = await (
await fetch(`${API}/create-payment-intent`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ currency: 'jpy' }),
})
).json();
// Create the element and set its attributes
const el = document.createElement('stripe-card-element');
el.setAttribute('publishable-key', publishableKey);
el.setAttribute('intent-client-secret', clientSecret);
el.setAttribute('button-label', 'Pay now');
// Subscribe to events
el.addEventListener('stripeLoaded', () => log('event: stripeLoaded'));
el.addEventListener('formSubmit', () => log('event: formSubmit'));
el.addEventListener('defaultFormSubmitResult', event => {
const detail = (event as CustomEvent).detail;
if (detail instanceof Error) {
log('payment failed', { message: detail.message });
} else {
log('payment result', detail);
}
});
document.querySelector<HTMLDivElement>('#checkout')!.appendChild(el);
ターミナルウィンドウ
npm run dev

Open http://localhost:5173 and the card form appears. Enter card number 4242 4242 4242 4242, any future expiry, and any CVC, then press Pay now. When the logged payment result shows status: succeeded, the payment is complete.

  • Origin is not allowed — check that your dev server runs on a port allowed by the demo API’s CORS policy (3000, 4321, or 5173).
  • Failed to fetch dynamically imported module — check whether you are using the lazy loader stripe-pwa-elements/loader. In bundler environments, switch to static imports from dist/components as described in Bundler integration.
  • The component shell renders but the card inputs never appear — check your version. In v4.0.0, per-component dist/components imports never mounted the inner Stripe element. This is fixed in stripe-pwa-elements@4.0.1 and later.
  • Wallet buttons don’t render — check device support. <stripe-express-checkout-element>’s Apple Pay, Google Pay, and Link buttons only render on capable browsers and devices.
  • Payment flows — the Payment Intent, Setup Intent, and Checkout Session flows, and how to think about your own production API
  • Bundler integration — import patterns for webpack, Rollup, Capacitor, and more
  • Components — Props, Events, and Methods for each component