HPP Integration Guide
Integrate the Cresora Hosted Payment Page for the lowest PCI scope (SAQ A).
The Hosted Payment Page (HPP) lets you accept payments without your server ever handling raw card data. Cresora renders and hosts the payment form; your server creates a session and either redirects the customer to it or embeds it in an iframe.
PCI scope: SAQ A — the lowest possible scope because no card data touches your systems.
Embedding modes
| Mode | How it works | Best for |
|---|---|---|
| Redirect | Customer navigates away to the Cresora-hosted page, then returns to your return_url | Simplest integration; fewest moving parts |
| Iframe | HPP embedded inside your page via <iframe>; bidirectional communication via postMessage | Seamless checkout experience without leaving your site |
Both modes use the same session-creation API. The mode is determined by how you use the redirect_url from the session response.
Step 1 — Create an HPP session
curl -X POST https://api.cresoracommerce.com/api/v1/hpp/sessions \
-H "Authorization: Bearer csk_test_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: idem_$(uuidgen)" \
-d '{
"merchant_id": "mrch_xxxxxxxxxx",
"amount": "50.00",
"currency": "USD",
"return_url": "https://yourapp.com/checkout/complete",
"cancel_url": "https://yourapp.com/checkout/cancel"
}'import requests, uuid
resp = requests.post(
"https://api.cresoracommerce.com/api/v1/hpp/sessions",
headers={
"Authorization": "Bearer csk_test_xxxxxxxxxxxx",
"Idempotency-Key": f"idem_{uuid.uuid4()}",
},
json={
"merchant_id": "mrch_xxxxxxxxxx",
"amount": "50.00",
"currency": "USD",
"return_url": "https://yourapp.com/checkout/complete",
"cancel_url": "https://yourapp.com/checkout/cancel",
},
)
session = resp.json()const resp = await fetch("https://api.cresoracommerce.com/api/v1/hpp/sessions", {
method: "POST",
headers: {
Authorization: "Bearer csk_test_xxxxxxxxxxxx",
"Content-Type": "application/json",
"Idempotency-Key": "idem_" + crypto.randomUUID(),
},
body: JSON.stringify({
merchant_id: "mrch_xxxxxxxxxx",
amount: "50.00",
currency: "USD",
return_url: "https://yourapp.com/checkout/complete",
cancel_url: "https://yourapp.com/checkout/cancel",
}),
});
const session = await resp.json();Session parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
merchant_id | string | ✅ | Merchant UUID |
amount | string | ✅ | Decimal string e.g. "50.00" |
currency | string | ✅ | ISO 4217 code — "USD" |
return_url | string | ✅ | Customer redirected here on completion or failure |
cancel_url | string | ✅ | Customer redirected here on cancellation |
parent_origin | string | For iframe | HTTPS origin of your parent page e.g. "https://yourapp.com". Required for iframe mode — enables the postMessage channel. Must match a domain in your allowed embedding origins (see Iframe mode). |
expiry_seconds | integer | No | Session TTL in seconds. Default: 600 (10 min). Range: 60–900. |
enable_field_events | boolean | No | Enables field_focused/field_blurred postMessage events. Default: false. |
Session response
{
"session_id": "hpp_xxxxxxxxxxxx",
"redirect_url": "https://pay.cresoracommerce.com/s/hpp_xxxxxxxxxxxx",
"expires_at": "2026-05-29T10:10:00Z"
}HPP sessions are single-use. A second payment submission against the same session returns an error. Create a new session for each checkout attempt.
Redirect mode
The simplest integration. Redirect the customer to redirect_url and handle the return.
Flow
1. Your server creates an HPP session
2. Redirect the customer to session.redirect_url
3. Customer enters card details on the Cresora-hosted page
4. Cresora redirects back to your return_url
5. Cresora fires a payment webhook
6. Your server verifies the webhook and fulfills the orderHandle the return
Cresora redirects to your return_url with query parameters:
https://yourapp.com/checkout/complete?session_id=hpp_xxxx&status=capturedstatus | Meaning |
|---|---|
captured | Payment successful |
failed | Payment declined |
cancelled | Customer clicked cancel |
Do not trust the status query parameter alone to fulfill orders. Always wait for the webhook and verify its HMAC-SHA256 signature. Query parameters can be spoofed.
Iframe mode
Embed the HPP inside your page for a seamless checkout experience without navigating away.
Prerequisites
Before iframe embedding works, your domain must be registered as an allowed embedding origin in the Cresora Partner Portal under Merchant Settings → HPP → Allowed Origins.
The HPP enforces a strict Content-Security-Policy: frame-ancestors directive. If your domain is not in the allowed origins list, the browser will block the iframe from rendering — even if the session URL is valid.
Flow
1. Register your domain in Partner Portal → Merchant Settings → HPP → Allowed Origins
2. Create an HPP session with parent_origin set to your page's exact HTTPS origin
3. Embed redirect_url in an <iframe> on your page
4. Load the Cresora HPP client library
5. Register event handlers for the postMessage lifecycle
6. Fulfill the order on payment_succeeded (backed by webhook verification)Embed the iframe
<iframe
id="payment-frame"
src="https://pay.cresoracommerce.com/s/hpp_xxxxxxxxxxxx"
style="width: 100%; border: none;"
></iframe>
<script src="https://pay.cresoracommerce.com/client/cresora-hpp-client.v1.js"></script>
<script>
// Listen for lifecycle events from the HPP
CреsoraHpp.on('ready', () => {
// Form is mounted and interactive — safe to show your checkout UI
});
CреsoraHpp.on('payment_succeeded', (evt) => {
console.log('Payment captured:', evt.data.transactionId);
console.log('Amount:', evt.data.approvedAmount, 'Last 4:', evt.data.last4);
closeCheckoutModal();
// Still verify the webhook before fulfilling
});
CреsoraHpp.on('payment_failed', (evt) => {
showError(evt.data.reason || 'Payment failed. Please try again.');
});
CреsoraHpp.on('height_changed', (evt) => {
// Auto-resize the iframe to fit the form
document.getElementById('payment-frame').style.height = evt.data.height + 'px';
});
CреsoraHpp.on('session_invalid', (evt) => {
showError('Session expired. Please refresh and try again.');
});
// Cancel button — sends a cancel command back into the iframe
document.getElementById('cancel-button').addEventListener('click', () => {
CреsoraHpp.send(
document.getElementById('payment-frame'),
'cancel',
null,
sessionId
);
});
// Fallback: if no event arrives within 5 seconds, treat as session_invalid
const timeout = setTimeout(() => {
showError('Unable to load payment form. Please try again.');
}, 5000);
CреsoraHpp.on('session_loaded', () => clearTimeout(timeout));
</script>postMessage event catalog
All events share a common envelope:
interface HppMessage {
source: 'cresora-hpp';
version: 1;
sessionId: string;
type: HppEventType;
occurredAtUtc: string; // ISO-8601
data?: object; // shape varies by type
}| Event | When it fires | Payload |
|---|---|---|
session_loaded | Session metadata known; before form paints | none |
ready | Form mounted and interactive — use this as your "show UI" trigger | none |
payment_started | User submitted the form; request in flight | none |
payment_succeeded | Terminal success | See below |
payment_failed | Terminal failure | { reason, code } |
payment_pending | 3DS or async — terminal pending | { transactionId? } |
height_changed | Form height changed | { height: number } (pixels) |
validation_failed | Client-side validation blocked submit | { fields: string[] } |
session_expired | Session TTL elapsed while iframe was loaded | none |
session_invalid | Session revoked, consumed, expired at load, or cancelled | { reason } |
navigated | Internal page navigation (e.g. result screen) | { to: string } |
cancelled | Session cancelled (via parent cancel command) | { initiatedBy: 'parent', occurredAtUtc } |
field_focused | Input received focus (requires enable_field_events: true) | { field: string } |
field_blurred | Input lost focus (requires enable_field_events: true) | { field: string } |
payment_succeeded payload
{
transactionId: string,
requestedAmount: number,
approvedAmount: number,
last4: string,
brand: string, // VISA, MASTERCARD, etc.
authCode: string,
responseMessage: string,
status: string
}No PAN, CVV, expiration, or PII is ever included in postMessage payloads.
Parent-initiated commands
Your page can send a small set of commands back into the iframe:
| Command | Effect | Notes |
|---|---|---|
cancel | Transitions session to Cancelled; fires cancelled event | Idempotent — safe to call multiple times |
request_height | Triggers an immediate height_changed event | Useful after parent resize |
set_locale and prefill commands are accepted and logged but not yet active. set_locale requires merchant locale allow-list configuration; prefill validates the payload but does not yet write values into form fields. Both are tracked as platform enhancements.
Security — what your page must validate
The HPP enforces security on its side. Your parent page is responsible for these three checks before trusting any postMessage event:
window.addEventListener('message', (event) => {
// 1. Validate origin — must be the Cresora HPP origin
if (event.origin !== 'https://pay.cresoracommerce.com') return;
const msg = event.data;
// 2. Validate source discriminator
if (!msg || msg.source !== 'cresora-hpp') return;
// 3. Validate session scope — prevents cross-session bleed
if (msg.sessionId !== mySessionId) return;
// Safe to handle msg.type
});The Cresora HPP client library performs all three checks automatically — using the client library is strongly recommended over writing raw addEventListener handlers.
Local development
Your domain must be on HTTPS for iframe embedding to work. Options for local development:
| Approach | Allowed origins entry |
|---|---|
| Hosts-file alias + local HTTPS cert | localdev.yourapp.com |
| Public dev tunnel (ngrok, VS Dev Tunnels) | The tunnel's HTTPS hostname |
.local mDNS | mymac.local |
localhost and raw IP addresses cannot be added to allowed embedding origins.
Verify the webhook regardless of mode
Whether using redirect or iframe mode, always verify the webhook before fulfilling the order. The postMessage payment_succeeded event and the redirect status query parameter are both convenience signals — the webhook is the authoritative source.
See Webhook signature verification →
White-label branding
Full HPP theming — colors, typography, custom CSS, logo, and 5 color-scheme presets — is configurable per merchant in the Partner Portal under Merchant Settings → HPP → Appearance. This feature requires the hpp_whitelabel feature flag. Contact your Cresora account manager to enable it.