Skip to main content
Cresora Commerce
Integration Guides

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

ModeHow it worksBest for
RedirectCustomer navigates away to the Cresora-hosted page, then returns to your return_urlSimplest integration; fewest moving parts
IframeHPP embedded inside your page via <iframe>; bidirectional communication via postMessageSeamless 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
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"
  }'
Python
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()
Node.js
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

ParameterTypeRequiredDescription
merchant_idstringMerchant UUID
amountstringDecimal string e.g. "50.00"
currencystringISO 4217 code — "USD"
return_urlstringCustomer redirected here on completion or failure
cancel_urlstringCustomer redirected here on cancellation
parent_originstringFor iframeHTTPS 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_secondsintegerNoSession TTL in seconds. Default: 600 (10 min). Range: 60–900.
enable_field_eventsbooleanNoEnables 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"
}
Warning

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 order

Handle the return

Cresora redirects to your return_url with query parameters:

https://yourapp.com/checkout/complete?session_id=hpp_xxxx&status=captured
statusMeaning
capturedPayment successful
failedPayment declined
cancelledCustomer clicked cancel
Warning

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.

🔒Security requirement

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
}
EventWhen it firesPayload
session_loadedSession metadata known; before form paintsnone
readyForm mounted and interactive — use this as your "show UI" triggernone
payment_startedUser submitted the form; request in flightnone
payment_succeededTerminal successSee below
payment_failedTerminal failure{ reason, code }
payment_pending3DS or async — terminal pending{ transactionId? }
height_changedForm height changed{ height: number } (pixels)
validation_failedClient-side validation blocked submit{ fields: string[] }
session_expiredSession TTL elapsed while iframe was loadednone
session_invalidSession revoked, consumed, expired at load, or cancelled{ reason }
navigatedInternal page navigation (e.g. result screen){ to: string }
cancelledSession cancelled (via parent cancel command){ initiatedBy: 'parent', occurredAtUtc }
field_focusedInput received focus (requires enable_field_events: true){ field: string }
field_blurredInput 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:

CommandEffectNotes
cancelTransitions session to Cancelled; fires cancelled eventIdempotent — safe to call multiple times
request_heightTriggers an immediate height_changed eventUseful after parent resize
🔬Scaffolded — not yet functional

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:

ApproachAllowed origins entry
Hosts-file alias + local HTTPS certlocaldev.yourapp.com
Public dev tunnel (ngrok, VS Dev Tunnels)The tunnel's HTTPS hostname
.local mDNSmymac.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

🔬Preview feature

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.