Five ways to auto-fill payment forms (Stripe Elements, Stripe Checkout, plain HTML, React) from a local JSON profile.
All methods share one core engine (core/autofill-engine.js) that uses proven patterns from production autofill tools (refined-github, Automa, Firefox iOS, Bitwarden, Stripe e2e suites).
git clone https://github.com/nopperabbo/auto-fill.git
cd auto-fill
./setup.sh # installs deps + generates profiles.json
./autofill https://your-site.test/checkout --submit # fill + submit, doneBatch mode (one identity per run, sequential):
./autofill <url> --profiles us_01,us_02,us_03 --submit # specific list
./autofill <url> --count 10 --submit # first 10 profiles
./autofill <url> --all --submit # every profile in profiles.jsonGenerate more identities (up to 100):
cd python && source .venv/bin/activate
python gen-profiles.py --count 50 --force --out ../profiles.jsonsetup.sh handles Python venv, Node deps, Playwright Chromium, and generates 10 US profiles with Stripe test cards. autofill wrapper auto-picks Node or Python backend.
Manual profile setup
cp profiles.example.json profiles.json # start from example
# or
cd python && source .venv/bin/activate
python gen-profiles.py --out ../profiles.json # generate 10 US entriesChrome extension / Tampermonkey / Bookmarklet
See TUTORIAL.md Step 3 for each method.
profiles.json is .gitignored — never commit real card data.
📘 Tutorial lengkap (Bahasa Indonesia, step-by-step): TUTORIAL.md 📗 English quick reference: USAGE.md
{
"defaultProfile": "personal",
"profiles": {
"personal": {
"label": "My Card",
"card": {
"number": "4242 4242 4242 4242",
"expMonth": "12",
"expYear": "34",
"cvc": "123",
"holder": "Your Name"
},
"billing": {
"email": "you@example.com",
"phone": "+1 555 0100",
"country": "ID",
"countryName": "Indonesia",
"line1": "Street",
"line2": "Unit",
"city": "Jakarta",
"state": "DKI Jakarta",
"stateCode": "JK",
"postalCode": "12190"
}
}
}
}Fields are all optional — engine only fills what matches. expYear accepts "34" or "2034".
Fills top-frame AND Stripe iframes by bypassing same-origin via CDP. Same behavior in both languages — pick whichever stack you prefer.
cd playwright
npm install
npx playwright install chromium
node autofill.js --url https://example.com/checkout --profile personal --submitcd python
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python -m playwright install chromium
python autofill.py --url https://example.com/checkout --profile personal --submitFlags (same for both):
--url <url>target page (required)--profile <key>profile key (default:defaultProfilein JSON)--submitclick the pay/submit button after filling--headfulshow the browser window, leaves it open--channel chromeuse installed Chrome instead of bundled Chromium--wait <ms>delay after page load before filling (default 3000)
Fills top-frame AND all iframes via all_frames: true content script. Four ways to trigger:
- Floating "Fill" button appears automatically when a payment form is detected on the page — one click fills, "+ Submit" variant also submits.
- Keyboard shortcut
Ctrl+Shift+F(Cmd on macOS) fills with default profile.Ctrl+Shift+Xfills and submits. - Right-click menu on any page:
Auto Fill → Fill — <profile>lists all saved profiles. - Popup (puzzle-piece icon or
Ctrl+Shift+Y) for explicit profile selection + inline JSON editor.
chrome://extensions → Developer mode → Load unpacked → select ./extension
Click the puzzle-piece → pin Auto Fill. Edit profiles in the popup: Edit profiles JSON → paste → Save. Shortcuts can be customized at chrome://extensions/shortcuts.
Auto-injects into top-frame AND Stripe iframes (via explicit @match https://js.stripe.com/*).
- Install Tampermonkey.
- Install
userscript/autofill.user.js— open the file, Tampermonkey prompts. - On any payment page: Tampermonkey icon →
Fill — <profile>orFill + Submit — <profile>. - Edit profiles: Tampermonkey icon →
Edit profiles (JSON).
Works on plain HTML and same-origin React forms. Cannot fill Stripe iframes (cross-origin sandbox).
node bookmarklet/build.js
open bookmarklet/dist/install.htmlDrag the buttons onto your bookmarks bar. Click on any payment page to fill.
| Plain HTML | React (same-origin) | Stripe Elements iframe | Stripe Checkout page | |
|---|---|---|---|---|
| Playwright CLI (Node) | ✅ | ✅ | ✅ | ✅ |
| Playwright CLI (Python) | ✅ | ✅ | ✅ | ✅ |
| Chrome extension | ✅ | ✅ | ✅ | ✅ |
| Tampermonkey userscript | ✅ | ✅ | ✅ | ✅ |
| Bookmarklet | ✅ | ✅ | ❌ (same-origin policy) | ✅ (top-frame) |
profiles.jsonstores card numbers in plaintext. Do not commit..gitignorealready blocks it along with*.pem,*.crx, and.env.*.- Extension profile data lives in
chrome.storage.local— cleared when extension is removed. - Userscript profile data lives in Tampermonkey's
GM_setValuestorage — per-browser, not synced by default. - This tool is for your own cards on your own devices. Don't use it to fill forms you don't own.
4242 4242 4242 4242and4111 1111 1111 1111in the example file are public Stripe test cards — use them for smoke tests.- Bookmarklets cannot reach Stripe Elements iframes due to cross-origin sandbox policy. This is a browser security boundary, not a bug. Use the extension, userscript, or Playwright CLI for Stripe Elements.
The core engine uses three techniques that together handle virtually every payment form:
- Native prototype setter —
Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set.call(el, val). React monkey-patches the setter for its_valueTracker; calling the original bypasses that. _valueTracker.setValue(prev)hack — forces React's next diff to detect a change even if the rendered value already matches. Credit: facebook/react#11488.input → change → blurevent sequence — triggers React onChange, React Hook Form / Formik validation, and touched-state flags in order.
Selectors are layered most-specific-first: data-elements-stable-field-name (Stripe's official stable API) → autocomplete="cc-*" (WHATWG spec) → name= attrs → aria-label / placeholder fuzzy match.
- Plain HTML form (test-fixtures/plain-form.html) — combined MM/YY
- Plain HTML form — split MM/YY via
?split=1 - React 18 controlled form with
<select>(test-fixtures/react-form.html) - Extension loaded unpacked in Chromium: Shadow DOM widget rendered, content script fills 10/10 fields on plain-form fixture
Run any of them:
# Node
cd playwright
node autofill.js --url "file://$PWD/../test-fixtures/plain-form.html" --profile test --submit
node autofill.js --url "file://$PWD/../test-fixtures/plain-form.html?split=1" --profile test
node autofill.js --url "file://$PWD/../test-fixtures/react-form.html" --profile test --submit --wait 2500
# Python
cd python && source .venv/bin/activate
python autofill.py --url "file://$PWD/../test-fixtures/plain-form.html" --profile test --submit.
├── profiles.example.json template (safe to commit)
├── profiles.json your real data (gitignored)
├── core/autofill-engine.js the shared engine
├── bookmarklet/ drag-to-bookmark install page generator
├── extension/ Chrome/Edge MV3 extension (floating button, shortcuts, context menu)
├── userscript/ Tampermonkey single-file script
├── playwright/ Node CLI + Playwright runtime
├── python/ Python CLI + Playwright runtime
└── test-fixtures/ HTML files for smoke tests
This engine wouldn't work without the pattern discoveries baked into these projects and threads:
- refined-github —
source/helpers/set-react-input-value.ts— canonical native-prototype-setter pattern for React inputs. - AutomaApp/automa —
src/utils/handleFormElement.js— full form-element handler including the_valueTracker.setValue()trick that forces React diff detection. - mozilla-mobile/firefox-ios —
LoginsHelper.js—input → change → blurevent sequence that triggers React Hook Form / Formik validation and touched state. - remix-project — e2e suite —
HTMLSelectElement.prototypesetter pattern (different from input). - freeCodeCamp, woocommerce-gateway-stripe, useautumn/autumn — e2e test suites demonstrating
data-elements-stable-field-nameselectors for Stripe iframes. - bitwarden/clients —
autofill-inline-menu-content.service.ts— Shadow DOM floating UI pattern withall: initialhost styles, randomized custom tag, MutationObserver throttle bailout, andcontextMenusrebuild-on-change pattern. - gildas-lormeau/SingleFile — element-based dedup pattern (
querySelector(TAGNAME)instead ofwindow.__flag) that survives SPA re-injection. - facebook/react#11488 — the React issue thread that documents the synthetic-event +
_valueTrackermechanism and why the native setter is required. - Stripe — for exposing
data-elements-stable-field-nameas a stable API on Elements inputs.
Unlicense — public domain. Do whatever you want with this.
Maintained by the Auto Fill Contributors.