Loan Calculator Widget
Comprehensive Developer Guide
v0.3.0Table of Contents
Overview
The loan calculator is delivered as a single script, loan-calculator-combined.js.
It provides createLoanCalculatorWidget for the two-column UI (slider, deposit, trade-in, and results),
exports LoanCalculator for a compact form-based flow, injects its own styles,
and includes lightweight polyfills for broader mobile browser support (e.g. Samsung Internet).
For most sites, you initialise the widget with createLoanCalculatorWidget(container, config).
The factory returns a LoanCalculatorWidget instance, or null if creation fails (a small fallback message may be rendered in the container).
Single file
No separate CSS bundle — styles are injected when the widget loads.
Configurable API
Point apiEndpoint at your Kyiper loan calculate URL; authenticate with apiToken.
Responsive layout
Desktop two-card layout with a mobile apply flow; debounced recalculation on input changes.
Apply URL
Optional applyURL; after a successful quote, users can open apply with pAmount derived from credit amount.
Commercial GBP / VAT
When isCommercial and currency are GBP, initial asset cost handling includes VAT logic for the first calculation.
Callbacks
Optional onCalculate runs after a successful widget quote. When using LoanCalculator directly, you can also supply onError for failures handled by that class.
Installation
1. Host the script
Serve loan-calculator-combined.js from your site (or CDN). In Kyiper Dashboard it lives under static resources and is available at the site root, for example:
<script th:src="@{/loan-calculator-combined.js}" src="/loan-calculator-combined.js"></script>
Plain HTML sites can use a relative or absolute src URL.
2. Add a container
<div id="my-calculator"></div>
3. Initialise after DOM ready
document.addEventListener('DOMContentLoaded', function () {
const widget = createLoanCalculatorWidget('#my-calculator', {
apiEndpoint: 'https://your-host/api/loan/calculate',
apiToken: 'your-api-token',
environment: 'TEST',
currency: 'EUR',
assetCost: 25000,
termMonths: 60,
deposit: 0
});
});
Basic Usage
Factory function
Use createLoanCalculatorWidget(container, config) where container is a CSS selector string or a DOM element.
const widget = createLoanCalculatorWidget('#my-calculator', {
apiEndpoint: '/api/loan/calculate',
apiToken: 'your-token',
environment: 'TEST',
currency: 'GBP',
assetCost: 46366,
termMonths: 60,
deposit: 0,
isCommercial: false,
lenderLogo: 'https://example.com/logo.svg',
applyURL: 'https://apply.example.com/init/motor'
});
Minimal HTML page
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Loan Calculator Demo</title>
<script src="loan-calculator-combined.js"></script>
</head>
<body>
<div id="my-calculator"></div>
<script>
document.addEventListener('DOMContentLoaded', function () {
createLoanCalculatorWidget('#my-calculator', {
apiEndpoint: 'https://your-api.example.com/api/loan/calculate',
apiToken: 'your-token',
environment: 'TEST',
currency: 'EUR',
assetCost: 25000,
termMonths: 48,
deposit: 0
});
});
</script>
</body>
</html>
Declarative Setup
If you prefer markup-driven setup, add data-loan-calculator-widget to a container.
On DOMContentLoaded, the script looks for matching elements and constructs a widget from data-* attributes.
<div data-loan-calculator-widget
data-environment="TEST"
data-api-endpoint="https://your-host/api/loan/calculate"
data-api-token="your-token"
data-currency="EUR"
data-lender-logo="https://example.com/logo.svg"
data-apply-url="https://apply.example.com/"
data-asset-cost="25000"
data-term-months="60"
data-deposit="0">
</div>
<script src="loan-calculator-combined.js"></script>
Optional hooks: data-on-calculate and data-on-error must be the names of functions on window (e.g. data-on-calculate="myHandler" resolves to window.myHandler).
Optional data-interest-rate sends a fixed rate when set.
loan-calculator-combined.js only reads the attributes above.
It does not currently wire isCommercial, theme, or other options from data-* attributes — use createLoanCalculatorWidget in JavaScript for those.
Standalone LoanCalculator (same bundle)
window.LoanCalculator renders a compact form (asset cost, term, optional monthly payment and deposit) inside a container you provide.
createLoanCalculatorWidget uses LoanCalculator with isWidget: true for shared calculation behaviour while keeping the two-column layout as the visible UI.
Constructor & validation
Typical non-widget construction:
const calc = new LoanCalculator({
container: '#my-form-or-element',
apiEndpoint: 'https://example.com/api/loan/calculate',
apiToken: 'your-token',
environment: 'TEST', // non-widget: must be TEST or LIVE
currency: 'EUR',
minTermMonths: 12,
maxTermMonths: 360,
onCalculate: function (result) { /* ... */ },
onError: function (err) { /* err has code, message */ }
});
Default config also includes locale, defaultProductType (e.g. 'HP'), and theme object — see DEFAULT_CONFIG in the source.
For non-widget mode, apiToken, apiEndpoint, and environment are required; environment must be TEST or LIVE.
Request body (form submit)
On submit, the LoanCalculator form posts JSON with assetCost, termMonths, optional monthlyPayment, and optional depositAmount. The widget request uses the same endpoint shape with tradeIn as its own field.
Events & methods
- Events (EventEmitter):
calculating,calculate(payload includesresultandwarnings),error. reset()— clears form and hides results.setValue(field, value)— sets an input bynameand triggers input handling.getValue(field)— reads last calculated state value.destroy()— clears container and removes listeners.
Configuration Options
These options apply to the embedded widget (createLoanCalculatorWidget / LoanCalculatorWidget). Properties marked required must be set for API calls to run.
| Property | Type | Default | Description |
|---|---|---|---|
apiEndpoint |
string | null |
Required. Full or relative URL of the loan calculate endpoint. |
apiToken |
string | null |
Required. Sent as X-API-Token on each request. |
environment |
string | null |
Required for widget initialisation checks (e.g. TEST, LIVE). The current script build does not send X-Environment on fetch (commented in source); confirm with your API if the header is needed server-side. |
currency |
string | 'EUR' |
ISO-style code: EUR, GBP, or USD. Display uses mapped symbols (£, €, $). |
assetCost |
number | null |
Initial vehicle/asset price; used for first paint and initial calculation. |
termMonths |
number | 60 |
Initial term; the UI slider typically ranges 12–60 in steps of 6 (see script). |
deposit |
number | 0 |
Initial deposit shown in the form; sent as depositAmount in the JSON body. |
isCommercial |
boolean | false |
When true and currency is GBP, initial asset cost is adjusted for VAT before the first API call (see script). |
lenderLogo |
string | null |
Optional image URL (or site-relative path) shown in the widget chrome. |
applyURL |
string | null |
Optional apply journey URL. After a quote, the widget can open this URL with pAmount appended from totalCreditAmount. |
interestRate |
number | null |
If set, included in request JSON as interestRate. |
theme |
object | see script |
Default visual tokens live on the module-level WIDGET_CONFIG.theme in the source. The injected stylesheet (WIDGET_STYLES) is built from that object when the script loads, not from your instance config.
Passing theme or calling setTheme updates this.config.theme only; it does not change the already-injected CSS (and injectStyles returns immediately if the style tag already exists). Use host-page CSS under .loan-calc-widget to rebrand the live widget.
|
onCalculate |
function | null |
After a successful widget API response, called with the result object (see handleCalculation in the script). |
onError |
function | null |
Used with new LoanCalculator({ ... }) when handleError invokes your callback. For createLoanCalculatorWidget, network failures are surfaced in the console and the UI returns to an idle state; use onCalculate for successful responses. |
Theme object (defaults in source)
The widget’s look is defined by WIDGET_CONFIG.theme inside loan-calculator-combined.js and baked into WIDGET_STYLES at parse time.
To change colours or radii for a given deployment, edit those defaults in the bundle (or override with your own CSS selectors). Do not rely on constructor theme or setTheme to restyle the embedded widget in the current implementation.
// Reference only — these are the bundled defaults (see WIDGET_CONFIG.theme)
{
primaryColor: '#0056b3',
secondaryColor: '#ffb84d',
backgroundColor: 'transparent',
textColor: '#222',
borderRadius: '18px',
fontFamily: 'system-ui, -apple-system, sans-serif'
}
API Integration
Widget request (embedded UI)
On each calculation, the widget reads the form and POSTs JSON to apiEndpoint:
POST {apiEndpoint}
Content-Type: application/json
X-API-Token: {apiToken}
{
"assetCost": 50000,
"depositAmount": 3000,
"tradeIn": 1000,
"termMonths": 60,
"interestRate": 8.5
}
depositAmount comes from the deposit field; tradeIn is sent as its own field. interestRate is only sent when configured.
The X-Environment header is present in the design but currently commented out in loan-calculator-combined.js; align with your backend expectations.
Standalone LoanCalculator form (same file)
The LoanCalculator form submit payload may include optional monthlyPayment alongside assetCost and termMonths.
Response shape
The UI maps numeric fields onto elements with data-field attributes, including:
standardMonthlyRepayment— monthly paymenttotalAmountPayable,interestPayable,totalCreditAmountinterestRate— shown in the “APR” row (formatted with%)depositAmount,termMonths— may drive follow-up UI updates
{
"termMonths": 60,
"initialMonthlyRepayment": 950.00,
"standardMonthlyRepayment": 850.00,
"finalMonthlyRepayment": 850.00,
"assetCost": 50000,
"depositAmount": 4000,
"totalCreditAmount": 45000,
"interestPayable": 6000,
"totalAmountPayable": 51000,
"interestRate": 8.5,
"warnings": {}
}
warnings.depositAmount together with an updated depositAmount, the widget may sync the deposit input to match the server.
Apply URL
When applyURL is configured, the widget’s primary Apply For Finance control (desktop form submit and the mobile button path) calls redirectToApplyURL() instead of running calculate() again.
The opened URL is your applyURL plus pAmount when a totalCreditAmount is present in the results panel (the numeric value is parsed from the formatted display text).
Ensure users have a successful quote in the panel before relying on pAmount. If applyURL is omitted, the same control runs a normal calculation.
Features
Widget UI
Two-column layout
Input card (vehicle price, deposit, trade-in, term slider) and results card (key figures and warnings).
Debounced calculate
Input changes trigger recalculation after a debounce (currently about 800 ms in the script) to limit API traffic.
Primary CTA
“Apply For Finance” runs a calculation or navigates to applyURL when appropriate.
Mobile layout
Secondary mobile button path in the same bundle; responsive spacing and logo placement.
Kyiper branding
Footer “Powered by” artwork is embedded in the widget template.
Browser polyfills
Lightweight shims where needed (e.g. fetch / Promise / Intl) — see console compatibility warnings.
Standalone calculator
Use new LoanCalculator(config) for a form-driven flow in your own container.
Non-widget mode requires environment values LIVE or TEST — refer to the source for exact validation rules.
Instance Methods
Assign the return value of createLoanCalculatorWidget to a variable (when not null).
Useful instance APIs on LoanCalculatorWidget include:
-
setTheme(partialTheme)
widget.setTheme({ primaryColor: '#003087', secondaryColor: '#ffcc00' })Shallow-merges into
config.theme. The injected stylesheet is not regenerated from these values;injectStylesis a no-op once#loan-calc-widget-stylesexists. -
setFormValues(values)
widget.setFormValues({ assetCost: 30000, deposit: 2000, tradeIn: 500, termMonths: 48 })Updates visible inputs (and related labels where applicable) programmatically.
-
calculate()
widget.calculate()Runs a calculation using current DOM field values (subject to validation guards in the script).
-
debounce(fn, wait)
widget.debounce(myFn, 300)Utility exposed on the instance for advanced integrations.
The file also exports window.LoanCalculator with methods such as calculate(data), destroy(), and event emitter style hooks — see loan-calculator-combined.js for the full standalone API.
Examples
Typical integration
document.addEventListener('DOMContentLoaded', function () {
createLoanCalculatorWidget('#my-calculator', {
environment: 'TEST',
apiEndpoint: 'https://your-api.example.com/api/loan/calculate',
apiToken: 'your-token',
currency: 'GBP',
assetCost: 46366,
termMonths: 60,
deposit: 0,
isCommercial: true,
lenderLogo: 'https://your-cdn.example.com/lender-logo.svg',
applyURL: 'https://apply.example.com/init/motor',
onCalculate: function (result) {
console.log('Quote:', result.standardMonthlyRepayment);
}
});
});
Use onError with new LoanCalculator({ ... }) (non-widget). It is not called for embedded widget network failures in this build.
Branding with host CSS
/* Example: override the results card after the widget mounts */
.loan-calc-widget .card-right {
background: #111827;
}
.loan-calc-widget .card-right .calculate-button {
background: #f59e0b;
color: #111827;
}
Styling & Theming
Injected stylesheet
The widget mounts under .loan-calc-widget. The bundle injects one <style id="loan-calc-widget-styles"> in document.head, whose rules are generated from the module’s WIDGET_CONFIG.theme when the script loads — not from per-instance theme options.
Changing the default look
Edit WIDGET_CONFIG.theme (and thus WIDGET_STYLES) in loan-calculator-combined.js for a forked build, or layer host CSS on .loan-calc-widget … as in the example above.
Host page overrides
Custom CSS is the practical way to rebrand without modifying the bundle. Watch specificity and internal class names, which may change between releases.
Layout notes
- Desktop: Side-by-side cards with lender logo offset.
- Mobile: Adjusted logo and bottom spacing reserved for Kyiper footer artwork.
Troubleshooting
Common issues
Nothing renders or you see the fallback panel
createLoanCalculatorWidget returned null or the container shows the generic error panel.Solution: Confirm the script loaded without 404s, the container selector matches a real element, and check the console for thrown errors (e.g. missing container).
CORS / network errors
fetch to apiEndpoint.Solution: Use a same-origin path, or configure the API for cross-origin POSTs from your site. Absolute URLs to another host require correct CORS and HTTPS/mixed-content rules.
401 / 403 from API
Solution: Ensure
apiToken matches an active Kyiper API token for the intended environment and company.
Environment header
X-Environment on requests. If your deployment requires it, either enable it in the script or accept server defaults — coordinate with backend owners.
Console diagnostics
The script logs VAT/debug and calculation traces during development. Open DevTools to inspect payloads and warning handling.
Performance
- Serve
loan-calculator-combined.jswith caching headers or via CDN. - Avoid mounting dozens of widgets on one page without testing debounce behaviour.