@ianmenethil/zp-hcp
v0.1.10
Published
ZenPay Hosted Checkout Plugin — zero-dependency modal/redirect payment integration with optional jQuery and Bootstrap backward compatibility
Maintainers
Readme
ZenPay HCP Plugin
A hosted checkout (HCP) plugin for integrating ZenPay/Zenith Payments, B2BPay, TravelPay, RentalRewards, SchoolEasyPay, ChildCareEasyPay, and ThoroughbredPayments into merchant pages.
The plugin opens the hosted checkout page in a modal iframe (displayMode=0) or returns a generated redirect URL (displayMode=1).
Install
bun add @ianmenethil/zp-hcp// Full plugin (modal + all features)
import { zpPayment } from '@ianmenethil/zp-hcp';
// Client-safe utilities — tree-shakeable subpath (browser or server, no secrets, no DOM)
import { createZpTimestamp, createZpMupid, ZpPaymentStatus, isZpPaymentSuccessful } from '@ianmenethil/zp-hcp/client';
// Server-only utilities (require the merchant password — never ship to the browser)
import { createZpFingerprint, verifyZpValidationCode, createSha3_512 } from '@ianmenethil/zp-hcp/server';Full import/export map (main vs /client vs /server, types, session API, CDN): USAGE.md — Package entries.
Package contents
@ianmenethil/zp-hcp/
├── dist/
│ ├── npm/
│ │ ├── index.mjs ← ESM entry (browser, es2022)
│ │ ├── index.cjs ← CJS entry (Node 22+)
│ │ ├── index.d.ts ← public types
│ │ ├── server.mjs ← server-only utilities (ESM)
│ │ ├── server.cjs ← server-only utilities (CJS)
│ │ ├── server.d.ts ← server utility types
│ │ ├── client.mjs ← client-safe utilities (ESM)
│ │ ├── client.cjs ← client-safe utilities (CJS)
│ │ └── client.d.ts ← client utility types
│ └── cdn/
│ ├── v6/
│ │ ├── zp.hcp.js ← IIFE (unminified)
│ │ ├── zp.hcp.min.js ← IIFE (minified)
│ │ ├── zp.hcp.obf.js ← IIFE (minified + obfuscated)
│ │ ├── zp.hcp.esm.js ← ESM (for `import()`)
│ │ └── zp.hcp.manifest.json ← SRI integrity hashes
│ ├── v5/
│ │ ├── zenpay.payment.v5.js
│ │ ├── zenpay.payment.v5.min.js
│ │ ├── zenpay.payment.v5.obf.min.js
│ │ └── manifest.json
│ └── v4/
│ ├── zenpay.payment.v4.js
│ ├── zenpay.payment.v4.min.js
│ ├── zenpay.payment.v4.obf.min.js
│ └── manifest.json
├── README.md
└── LICENSECDN
CDN files are available from two sources — jsDelivr (automatic from each npm publish) and the self-hosted CDN at cdn.zenithpayments.support (deployed on every build).
| File | jsDelivr | Self-hosted |
|------|----------|-------------|
| v6 IIFE (minified) | @ianmenethil/zp-hcp/dist/cdn/v6/zp.hcp.min.js | hcp/v6/zp.hcp.min.js |
| v6 IIFE (unminified) | @ianmenethil/zp-hcp/dist/cdn/v6/zp.hcp.js | hcp/v6/zp.hcp.js |
| v6 IIFE (obfuscated) | @ianmenethil/zp-hcp/dist/cdn/v6/zp.hcp.obf.js | hcp/v6/zp.hcp.obf.js |
| v6 ESM | @ianmenethil/zp-hcp/dist/cdn/v6/zp.hcp.esm.js | hcp/v6/zp.hcp.esm.js |
| v6 Manifest | @ianmenethil/zp-hcp/dist/cdn/v6/zp.hcp.manifest.json | hcp/v6/zp.hcp.manifest.json |
| v5 IIFE (minified) | @ianmenethil/zp-hcp/dist/cdn/v5/zenpay.payment.v5.min.js | hcp/v5/zenpay.payment.v5.min.js |
| v5 IIFE (unminified) | @ianmenethil/zp-hcp/dist/cdn/v5/zenpay.payment.v5.js | hcp/v5/zenpay.payment.v5.js |
| v5 IIFE (obfuscated) | @ianmenethil/zp-hcp/dist/cdn/v5/zenpay.payment.v5.obf.min.js | hcp/v5/zenpay.payment.v5.obf.min.js |
| v4 IIFE (minified) | @ianmenethil/zp-hcp/dist/cdn/v4/zenpay.payment.v4.min.js | hcp/v4/zenpay.payment.v4.min.js |
| v4 IIFE (unminified) | @ianmenethil/zp-hcp/dist/cdn/v4/zenpay.payment.v4.js | hcp/v4/zenpay.payment.v4.js |
| v4 IIFE (obfuscated) | @ianmenethil/zp-hcp/dist/cdn/v4/zenpay.payment.v4.obf.min.js | hcp/v4/zenpay.payment.v4.obf.min.js |
- jsDelivr base:
https://cdn.jsdelivr.net/npm/— drop the version tag for latest, or pin with@5. - Self-hosted base:
https://cdn.zenithpayments.support/—latest/always points to the most recent build; versioned folders are created onbun run release.
CDN usage
<!-- jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/@ianmenethil/zp-hcp/dist/cdn/v6/zp.hcp.min.js"></script>
<!-- Self-hosted -->
<script src="https://cdn.zenithpayments.support/hcp/v6/zp.hcp.min.js"></script>
<script>
var payment = window.zpPayment({
url: 'https://payuat.travelpay.com.au/online/v5',
apiKey: 'YOUR_API_KEY',
fingerprint: '...',
theme: 'auto',
});
payment.open();
</script>ESM import (v6)
import { zpPayment, type ZpPaymentOptions, type ZpTheme } from '@ianmenethil/zp-hcp';
const payment = zpPayment({
url: 'https://payuat.travelpay.com.au/online/v5',
apiKey: 'YOUR_API_KEY',
fingerprint: '...',
theme: 'auto',
});
payment.open();Tree-shakeable utilities (v6)
// Client-safe (browser or server)
import { createZpTimestamp, createZpMupid, ZpPaymentStatus, isZpPaymentSuccessful } from '@ianmenethil/zp-hcp/client';
// Server-only (requires the merchant password)
import { createZpFingerprint } from '@ianmenethil/zp-hcp/server';
const timestamp = createZpTimestamp(); // "2026-05-27T14:30:00"
const mupid = createZpMupid(); // base64(UUID-Timestamp)
const fp = createZpFingerprint({
apiKey: "...", username: "...", password: "...",
mode: 0, paymentAmount: "1.00",
merchantUniquePaymentId: mupid, timestamp,
}).fingerprint;CJS require (v6)
const { zpPayment } = require('@ianmenethil/zp-hcp');The main plugin requires a browser DOM. Client utility functions (createZpTimestamp, createZpMupid, ZpPaymentStatus, isZpPaymentSuccessful) work in any environment with crypto.randomUUID() and btoa(). Fingerprint functions (createZpFingerprint, verifyZpValidationCode) are server-side — Node 19+, Bun, Deno.
Subresource Integrity (SRI)
A manifest with SHA-384 and SHA-256 hashes is published per version at dist/cdn/{v4,v5,v6}/manifest.json in the npm package. Use it to pin CDN scripts with integrity attributes:
<script src="https://cdn.jsdelivr.net/npm/@ianmenethil/zp-hcp/dist/cdn/v6/zp.hcp.min.js"
integrity="sha384-..."
crossorigin="anonymous"></script>SRI is optional — all existing CDN URLs continue to work without it.
Test Status
All tests passing on v6 (245 tests, 20 files).
| Suite | Files | Tests | Description |
|-------|-------|-------|-------------|
| test:unit (v3) | 14 | 118 | Node-environment unit tests for v3 source |
| test:dom (v3) | 1 | 16 | JSDOM browser integration tests for v3 |
| test:v6 | 20 | 245 | Full v6 test suite |
Known-bug tests are marked test.fails() — they assert the correct behavior and are expected to fail against the buggy source. They do not inflate the passing count.
bun run test runs unit + dom + v6 (test:vitest). v4 and v5 tests are run directly via vitest CLI (no npm script alias).
Coverage
v6 coverage is generated automatically with bun run test:v6 and output to coverage/v6/.
| Version | Statements | Branches | Functions | Lines | |---------|-----------|----------|-----------|-------| | v6 | 87.66% | 78.81% | 92.50% | 89.75% |
Plugin Versions
Version docs: v3→v4 · v4→v5 · v5→v6 — visual references (diagrams + tables).
This repository ships one plugin in four versions. All produce the same end-user payment experience when using the same options on a jQuery + Bootstrap page (v3/v4 parity path).
v3 — Production (jQuery + Bootstrap required)
Source: src/v3/zenpay.payment.bs5.js
The current live version, pulled by thousands of integrators from CDN today. Plain JavaScript. Hard dependencies on jQuery and Bootstrap 5. Contains known bugs — frozen, never edited.
<!-- Requires jQuery and Bootstrap 5 on the page -->
<script src="https://cdnuat.travelpay.com.au/js/zenpay.payment.bs5.js"></script>
<script>
var payment = $.zpPayment({ ... });
payment.init();
</script>v4 — TypeScript Port (jQuery + Bootstrap optional)
Source: src/v4/zenpay.payment.v4.ts
Same behavior as v3. jQuery and Bootstrap are no longer required — the plugin detects them at runtime and uses them if present. Existing integrators with jQuery and Bootstrap see zero difference. New integrators without them get a pure JS/CSS fallback. No bug fixes — parity with v3 only.
<!-- jQuery and Bootstrap are optional -->
<script src="dist/zenpay.payment.v4.min.js"></script>
<script>
var payment = zpPayment({ ... }); // window.zpPayment — no $ needed
payment.init();
</script>v5 — Current Release (zero dependencies, bug fixes)
Source: src/v5/zenpay.payment.v5.ts
v4 with targeted bug fixes and improvements. Same TypeScript codebase, same zero-dependency design, same backward compatibility. The fixes applied are limited to cases where the change cannot break any existing or new integration.
<script src="https://cdn.jsdelivr.net/npm/@ianmenethil/zp-hcp/dist/cdn/v5/zenpay.payment.v5.obf.min.js"></script>
<script>
var payment = window.zpPayment({ ... });
payment.open();
</script>v6 — Opt-In Features (theming, typed ESM, modern baseline)
Source: src/v6/zenpay.payment.v6.ts
v5 behavior plus opt-in improvements that are zero-impact when unused. Same factory API (zpPayment({ ... })), same side-effect global registration (window.zpPayment, $.zpPayment), same tests and same passing behavior. Existing integrators who upgrade and don't pass any new options see byte-identical output to v5.
What's new in v6:
themeoption —"light" | "dark" | "auto". When set, stampsdata-zp-themeon the modal/backdrop and switches palette via CSS variables. Brand palette: light ink#1C1C1C/ accent#D71F26; dark surface#161616/ accent#E83239."auto"follows OSprefers-color-scheme.- CSS custom properties — Integrators can override any
--zp-*variable in their own stylesheet. See the CSS Variables Reference table below for the full token list with light and dark defaults. - Modern baseline typography — system font stack, antialiased, tighter title (
font-weight: 600,letter-spacing: -0.01em), bodyline-height: 1.55. - Responsive width —
clamp(500px, 90vw, 900px)at ≥768px (up from fixed 600px/690px). - Softer visual chrome —
border-radius: 8px(was 3px), subtlebox-shadow, Material easingcubic-bezier(0.4, 0, 0.2, 1)on transitions. - Interactive close-button states — hover/active/focus-visible with 150ms transitions. Zero-specificity via
:where()so Bootstrap pages continue to use Bootstrap's rules. - Named ESM export +
.d.tstypes —import { zpPayment, type ZpPaymentOptions, type ZpTheme } from "@ianmenethil/zp-hcp"with full IDE autocomplete. - In-page error toast — plugin validation failures show a dismissible banner instead of v3
alert(). onPluginOpencallback — fires when the HCP iframe finishes loading (spinner hides). v6 only; not fired at modal shell show.@starting-styleentry animation — modal slides down + fades in on supported browsers.prefers-reduced-motionsupport — WCAG 2.2 AA compliance.createZpTimestamp()— produces a slice-19 ISO 8601 timestamp (YYYY-MM-DDTHH:MM:SS) for HCP fingerprint parameters.createZpMupid()— produces a base64-encoded MUPID (base64(UUID-Timestamp)) for idempotency.ZpPaymentStatus+isZpPaymentSuccessful()— typed result-status codes;isZpPaymentSuccessfulguards the3-is-success /1-is-error trap.createSha3_512(input)— generic SHA3-512 hash of any arbitrary string. No transformation applied — input is hashed as-is. Exported from/serveralongside the ZenPay-specific helpers.createZpFingerprint()— server-side SHA3-512 fingerprint generator with per-field verification. Works in Node 19+, Bun, and Deno. PasspaymentAmountin dollars (hash uses cents); mode 2 hashes"0". Details: USAGE.md.verifyZpValidationCode()— server-side callback verification. Hashes and compares (constant-time) theValidationCodequery parameter to authenticate HCP callbacks.- Tree-shakeable
/client+/serverimports —import { createZpTimestamp, createZpMupid, ZpPaymentStatus, isZpPaymentSuccessful } from '@ianmenethil/zp-hcp/client'andimport { createZpFingerprint, verifyZpValidationCode } from '@ianmenethil/zp-hcp/server'without pulling in the full plugin bundle. Client utilities are also exported from the main entry (@ianmenethil/zp-hcp/zp.hcp.esm.js) for CDN ESM consumers already loading the full bundle.
Usage — CDN / UMD (unchanged from v5):
<script src="https://cdn.jsdelivr.net/npm/@ianmenethil/zp-hcp/dist/cdn/v6/zp.hcp.obf.js"></script>
<script>
var payment = window.zpPayment({ theme: "dark", /* ... */ });
payment.open();
</script>Usage — ESM with full types:
import { zpPayment, type ZpPaymentOptions, type ZpTheme } from "@ianmenethil/zp-hcp";
const theme: ZpTheme = "auto";
const payment = zpPayment({ theme, url: "...", apiKey: "...", fingerprint: "..." });
payment.open();All three consumption paths (ESM import, CJS require, CDN script tag) produce the same function. Types are shipped at dist/npm/index.d.ts in the npm package.
CSS Variables Reference
The following CSS custom properties are defined in v6's baseline stylesheet. Integrators can override any of them in their own stylesheet to customize the modal appearance without opting into theme.
| Variable | Light default | Dark default (data-zp-theme="dark") | Applies to |
|----------|--------------|---------------------------------------|------------|
| --zp-modal-bg | #fff | #161616 | Modal content background |
| --zp-modal-fg | inherit | #f4f4f4 | Modal content text color |
| --zp-modal-border | rgba(0, 0, 0, 0.2) | #262626 | Modal content border, close button border |
| --zp-modal-header-border | #dee2e6 / #e5e5e5 | #262626 | Modal header bottom border |
| --zp-modal-muted | #6b7280 | #8e8e8e | Close button active background, hover border |
| --zp-accent | #0066cc (unthemed) · #D71F26 (theme: light) | #E83239 | Button hover / accent |
| --zp-backdrop-bg | rgba(0, 0, 0, 0.5) | rgba(0, 0, 0, 0.7) | Modal backdrop overlay |
| --zp-btn-fg | inherit | #f3f4f6 | Button text color |
| --zp-font-family | System font stack | System font stack | Modal typography |
| --zp-radius | 8px | 8px | Modal content and dialog border radius |
| --zp-shadow | 0 10px 30px -10px rgba(0, 0, 0, 0.25) | Same | Modal content box shadow |
| --zp-ease | cubic-bezier(0.4, 0, 0.2, 1) | Same | All modal/backdrop transitions |
| --zp-modal-width | clamp(500px, 90vw, 900px) | Same | Payment dialog width at ≥768px |
| --zp-modal-max-width | 900px | Same | Payment dialog max-width at ≥768px |
Three
@propertyregistrations (--zp-accent,--zp-modal-bg,--zp-radius) make these tokens smoothly interpolateable for CSS transitions (e.g.transition: --zp-accent 200ms). No-op on browsers without@propertysupport.
Quick Start
// In browser console or inline script — no build step required
await import("https://cdn.jsdelivr.net/npm/@ianmenethil/zp-hcp/dist/cdn/v6/zp.hcp.obf.js");
// createZpTimestamp() and createZpMupid() are safe for client-side use —
// they involve no secrets. createZpFingerprint() should only be called
// server-side since it requires your merchant password.
const id = createZpMupid();
const timestamp = createZpTimestamp();
window.zpPayment({
url: "https://payuat.travelpay.com.au/online/v5",
merchantCode: "YOUR_MERCHANT_CODE",
apiKey: "YOUR_API_KEY",
fingerprint: "FROM_YOUR_SERVER", // pre-computed by your backend (see USAGE.md#utilities)
redirectUrl: "https://your-site.com/payment/result",
merchantUniquePaymentId: id,
timestamp: timestamp,
displayMode: 0,
mode: 0,
paymentAmount: "1.00",
customerName: "Jane Smith",
customerEmail: "[email protected]",
customerReference: "ORDER-001",
}).open();Registration
| Context | Call |
|---------|------|
| v3 | $.zpPayment(options) — jQuery only |
| v4/v5/v6, no jQuery | window.zpPayment(options) or zpPayment(options) |
| v4/v5/v6, jQuery present | Both $.zpPayment(options) and window.zpPayment(options) work |
| v6, ESM import | import { zpPayment } from "@ianmenethil/zp-hcp" (also registers window globals) |
Options Reference
Canonical reference: USAGE.md — import/export map, full zpPayment payload, utility behaviour, fingerprint workflow.
Options merge with jQuery $.extend semantics (undefined does not overwrite defaults). Keys that are functions, theme, or lifecycle callbacks stay off the Authorise URL; other unknown keys are forwarded as query parameters (v3 parity).
Display Modes
displayMode=0 — Modal popup
Opens the hosted checkout page in a full-screen iframe modal. Call .open() on the returned instance.
displayMode=1 — Redirect URL
Does not open any UI. Returns { isSuccess: true, url, height, width }. Redirect the browser to url yourself. Useful for mobile apps or custom UI flows.
Instance API
zpPayment(options) returns an instance with .open() and .close() methods.
const payment = window.zpPayment(options);
payment.open(); // Opens the payment modal
payment.close(); // Closes and removes the modal| Method | Returns | Description |
|--------|---------|-------------|
| .open() | void | Opens the payment modal. Validates options, creates the iframe, injects baseline CSS, and shows the modal. Validation errors show an in-page toast (not alert()). For displayMode=1, returns void — use .init() (legacy) for redirect URL payload |
| .close() | void | Finds the .modal-payment element and hides/removes it. Fires onPluginClose callback, destroys Apple Pay plugin, and cleans up options |
Legacy .init() (v3 only)
.init() is the original v3 method. It is retained for backward compatibility only. New integrations should use .open() and .close().
| Method | Returns | Description |
|--------|---------|-------------|
| .init() | ZpUrlResult \| undefined | Legacy v3 entry point. Routes based on displayMode: 0 → opens modal, 1 → returns { isSuccess, url, height, width } without showing UI |
ZpUrlResult
| Field | Type | Description |
|-------|------|-------------|
| isSuccess | boolean | Whether URL generation succeeded |
| url | string? | Full HCP URL with query parameters (success) |
| height | string? | Recommended iframe min-height, e.g. "725px" (success) |
| width | string? | Recommended iframe width, e.g. "600px" (success) |
| message | string? | Human-readable error message (failure) |
Utility Functions
Canonical reference: USAGE.md — Package entries (what each path exports) and Utilities (behaviour, verification rules, fingerprint workflow).
Quick imports (also shown under Install):
import { createZpTimestamp, createZpMupid, ZpPaymentStatus, isZpPaymentSuccessful } from '@ianmenethil/zp-hcp/client';
import { createZpFingerprint, verifyZpValidationCode, createSha3_512 } from '@ianmenethil/zp-hcp/server';/client is safe in the browser; /server requires the merchant password and must never ship to client bundles.
Known Bugs (v3 and v4)
These bugs exist in v3 (frozen, never fixed) and are intentionally reproduced in v4 (parity rules). They are fixed in v5 and v6.
| # | Severity | Description | v3 | v4 | v5 | v6 |
|---|----------|-------------|----|----|----|-----|
| 001 | Medium | getPBoolValue strict equality — string "1" returns false instead of true | Bug | Bug (parity) | Fixed | Fixed |
| 002 | Low | Height calculation — mode="1" (string) gives 725px instead of 450px | Bug | Bug (parity) | Fixed | Fixed |
| 003 | Low | onPaymentPluginLoaded leaks as implicit global (window.onPaymentPluginLoaded) | Bug | Bug (parity) | Fixed | Fixed |
| 004 | Info | CSS class typo modal-dailog-payment (kept for backward compat) | Typo | Kept | Kept | Kept |
| 005 | Medium | closePayment() creates a new Bootstrap Modal instance instead of reusing the existing one | Bug | Bug (parity) | Fixed | Fixed |
All bugs are fixed in v5 and v6. Bug 004 is a deliberate backward-compat decision — both the original typo class and the corrected class are applied.
Development
Prerequisites
- Bun
1.3.9
bun installCommands
| Command | Description |
|---------|-------------|
| bun run test | All Vitest tests (unit + dom + v6) |
| bun run test:unit | v3 unit tests (Node environment) |
| bun run test:dom | v3 DOM tests (JSDOM) |
| bun run test:v6 | v6 test suite + coverage → coverage/v6/ |
| bun run test:e2e | Playwright live E2E (requires env vars) |
| bun run lint | ESLint flat config |
| bun run typecheck | TypeScript type check (tsc --noEmit) |
| bun run check | Full quality gate (lint + typecheck + jscpd + knip + test:v6) |
| bun run build | check + bundle all versions + declarations + manifest + validate:dist |
| bun run build:v6 | Build v6 only (IIFE + ESM + CJS + declarations) |
| bun run build:types:v6 | Emit v6 .d.ts declarations via tsc -p tsconfig.build.json |
| bun run release | build + npm publish + CDN deploy |
To run v4 or v5 tests directly (no npm script — use vitest CLI):
bunx vitest run tests/v4 --config vitest.config.ts
bunx vitest run tests/v5 --config vitest.config.tsThe build script accepts version flags for targeted builds:
bun run scripts/build.ts --v4 # v4 only
bun run scripts/build.ts --v5 # v5 only
bun run scripts/build.ts --v6 --esm # v6 ESM onlyBuild Outputs
dist/
npm/
index.mjs # v6 ESM entry (named export: zpPayment)
index.cjs # v6 CJS entry
index.d.ts # v6 public types entry (re-exports zenpay.payment.v6)
server.mjs # /server utilities (ESM) — createZpFingerprint, verifyZpValidationCode, createSha3_512
server.cjs # /server utilities (CJS)
server.d.ts # /server utility types (auto-emitted from src/v6/server.ts)
client.mjs # /client utilities (ESM) — createZpTimestamp, createZpMupid, payment-status
client.cjs # /client utilities (CJS)
client.d.ts # /client utility types (auto-emitted from src/v6/client.ts)
zenpay.payment.v6.d.ts # auto-emitted from src/v6/zenpay.payment.v6.ts
core/ # auto-emitted: types, constants, zp-payment, defaults, …
payment/ # auto-emitted: open-payment, close-payment, generate-url
modal/ # auto-emitted: modal, iframe-load-handler, import-script
compat/ # auto-emitted: jquery-shim
cdn/
v6/
zp.hcp.js # v6 plain IIFE
zp.hcp.min.js # v6 minified IIFE
zp.hcp.obf.js # v6 obfuscated + minified
manifest.json # SRI integrity hashes
v5/
zenpay.payment.v5.js # v5 plain IIFE
zenpay.payment.v5.min.js # v5 minified IIFE
zenpay.payment.v5.obf.min.js # v5 obfuscated + minified
manifest.json
v4/
zenpay.payment.v4.js # v4 plain IIFE
zenpay.payment.v4.min.js # v4 minified IIFE
zenpay.payment.v4.obf.min.js # v4 obfuscated + minified
manifest.jsonv4 and v5 are IIFE-only. Each file self-registers on window.zpPayment (and $.zpPayment if jQuery is detected). v6 ships IIFE (for CDN / <script> tags), ESM (dist/npm/index.mjs with named export), and CJS (dist/npm/index.cjs); all three side-effect-register the globals for backward compat. Types are auto-emitted by tsc -p tsconfig.build.json from src/v6/zenpay.payment.v6.ts (the single source of truth) into dist/npm/, with dist/npm/index.d.ts re-exporting the entry file's curated public API.
Test Suite Structure
tests/
unit/ # v3 Node-environment tests (14 files)
dom/ # v3 JSDOM tests (1 file)
v4/ # v4 tests (12 files)
v5/ # v5 tests (13 files)
v6/ # v6 tests (20 files, incl. create-mupid.v6.test.ts)
helpers/
plugin-loader.js # VM/eval loaders for v3 (V8 coverage compatible)
unit-browser-harness.js # JSDOM harness for v3 DOM tests
v4-dom-harness.ts # Shared harness for v4 tests
options.js # Canonical valid options baseline
fingerprint.js # Fingerprint generation utility
e2e/ # Playwright live tests (env-gated)
fixtures/ # HTML harness pages (4 variants)
support/ # Static server + visual masking CSStests/v5/v5-dom-harness.ts and tests/v6/v6-dom-harness.ts are co-located with their respective test suites (not in helpers/).
v3 test helpers
plugin-loader.js provides two loading strategies:
loadPluginInVm— compiles viaModule._compileso Vitest V8 coverage can instrument the sourceloadPluginInBrowserWindow— evaluates viawindow.evalfor JSDOM browser-like tests
v4/v5/v6 test pattern
Tests import the TypeScript source directly. There is no VM loader. The shared harness (setupV4Harness / setupV5Harness / setupV6Harness) resets modules between tests and configures window.jQuery and window.bootstrap mocks as needed.
E2E Tests
Live E2E tests require environment variables:
export ZENPAY_HCP_BASE_URL=https://payuat.travelpay.com.au
export ZENPAY_MERCHANT_CODE=1337
export ZENPAY_API_KEY=your-api-key
export ZENPAY_USER_NAME=your-username
export ZENPAY_PASSWORD=your-password
bun run test:e2eThree Playwright projects run against the live HCP:
| Project | Description |
|---------|-------------|
| hcp.live | Smoke flow — open modal, verify iframe loads |
| harness-matrix.live | Runs all four fixture harnesses (jQuery+BS, jQuery only, BS only, neither) |
| v3-v4.compare.live | Side-by-side v3 vs v4 behavioral comparison with screenshots |
Reports and screenshots go to reports/playwright/runs/{runId}/.
Repository Structure
v6 source layout
| Directory | File | Purpose |
|-----------|------|---------|
| v6/ | zenpay.payment.v6.ts | Main plugin entry — @ianmenethil/zp-hcp; also re-exports all /client utilities for CDN ESM consumers |
| | server.ts | Server-only entry — @ianmenethil/zp-hcp/server — createSha3_512 + createZpFingerprint + verifyZpValidationCode |
| | client.ts | npm /client entry (thin re-exports) |
| | client/ | types, timestamp-mupid, amount-to-cents, payment-status |
| | cdn.ts | IIFE entry for CDN <script> tags |
| v6/core/ | types.ts | All public types (ZpMode, ZpTheme, ZpPaymentOptions, …) |
| | constants.ts | Error messages, defaults, DOM selectors, URL params |
| | zp-payment.ts | ZpPayment class — constructor + open/init/close |
| | init-plugin.ts | init() routing — modal vs redirect |
| | defaults.ts | 16 default option values |
| | extend.ts | zpExtend — jQuery $.extend-compatible shallow merge |
| | option-normalizers.ts | getPValue / getPBoolValue / getKeyValue |
| v6/payment/ | open-payment.ts | open() flow — validation, modal DOM, Apple Pay, theming |
| | close-payment.ts | close() teardown — modal removal + cleanup |
| | generate-url.ts | URL construction + parameter encoding |
| v6/modal/ | modal.ts | Show/hide, backdrop, baseline CSS injection, brand fonts |
| | baseline.css | CSS custom properties + light/dark/auto theme blocks |
| | css.d.ts | Module declaration for .css imports |
| | iframe-load-handler.ts | onPaymentPluginLoaded — spinner hide + Apple Pay init |
| | import-script.ts | Dynamic <script> loader with dedup |
| v6/compat/ | jquery-shim.ts | Minimal $ shim for Apple Pay UMD plugin compatibility |
| scripts/ | build.ts | esbuild pipeline (IIFE, minify, obfuscate, ESM, CJS) |
| | deploy-local.ts | Local CDN copy (hostname-gated) |
| tests/ | unit/ dom/ v4/ v5/ v6/ e2e/ helpers/ | See Test Suite Structure |
| (repo root) | V3-TO-V4.md V4-TO-V5.md V5-TO-V6.md | Version migration pair references |
| docs/ | research/ | Research notes |
src/
v3/ zenpay.payment.bs5.js ← frozen production source (never edited)
v4/ zenpay.payment.v4.ts ← parity port
v5/ zenpay.payment.v5.ts ← stable (bug fixes, zero deps)
types.ts ← shared v4/v5 types
v6/ zenpay.payment.v6.ts ← active development
cdn.ts ← IIFE entry
server.ts ← /server subpath
client.ts ← /client subpath
core/ ← types, constants, zp-payment, defaults, …
payment/ ← open-payment, close-payment, generate-url
modal/ ← modal, baseline.css, iframe-load-handler, …
compat/ ← jquery-shimRoot-level config: tsconfig.json · tsconfig.build.json · eslint.config.ts · vitest.config.ts · knip.json · jscpd.json
License
MIT
