Show / Hide Table of Contents

Hosted Payment

The hosted payment page lets a cardholder pay on a PayLolly page, so card data never touches your servers. The page is mobile friendly, themeable (light, dark or automatic), and can offer new card entry, saved cards, or both, with optional or mandatory consent to save the card for later use (an agreement).

sequenceDiagram
    Browser->>+Website: Request to initiate payment
    Website->>+Api: Create Hosted Payment
    Api->>+Website: Response with url
    Website->>+Browser: Iframe or redirect to url
    Browser->>+PayLolly: Enter Card details and proceed to payment
    PayLolly->>+PayLolly: Process transaction
    PayLolly->>+Browser: Redirect to Website
    Browser->>+Website: Back from payment
    Api->>+Website: Webhook (optional)
Warning

Webhooks are strongly suggested as the Redirect to website and back from payment may not always work (due to network anomalies, cardholder abandonment, etc)

Note

If webhooks are not possible, one could use a polling mechanism to poll status of transaction using GetPaymentStatus

Important

If using polling, DO NOT enable retry, or else a transaction marked as Decline could be Approved as customer may be retrying it

On this page

  • Request reference
  • Modes
  • What the cardholder sees (layouts, extra details, fixed details, Pay button, retries, saved cards, tabs, card list)
  • Saving a card: agreements and consent
  • Saved cards
  • Themes: light, dark and automatic
  • CSS Customisation
  • Errors
  • Use Cases

Request reference

Payment/CreateHostedPayment takes the fields below. Fields not listed (for example threeDSConfig, webHooks) behave as on the other payment calls.

Field Required Description
merchantId yes Your merchant id
accountId no Force a particular merchant account
currency, amount yes For example EUR, 25.00
merchantRef yes Your reference, must be unique - suggest a Guid
cardHolder no email, name (prefilled on the page), reference (your id for the customer, mandatory when agreement is sent, see below), phone, address
cardData no Prepopulate card details, only if you are PCI D
tokenData with PresetToken token of the card to offer
tokens with PresetTokens List of tokenData to offer for selection
authorisationType yes Auth (preauth) or AuthCapture
recurrence yes OneTime, or OneClick to save the card (see agreements)
initiation no Always CIT (the default) on hosted payments. MIT or MOTO is rejected with VAL13
agreement no Register the card as an agreement, see below
mode no See Modes, defaults to Auto
cssSettings no theme (Light, Dark, Auto) and css (Bootstrap variable overrides), see Themes and CSS Customisation
returnUrl yes Where the cardholder returns to at the end of the payment
validUntil yes Until when the link is valid, suggest 20 minutes (a payment link to send by email may be valid for days)
webHooks no Server to server notifications, strongly suggested
Note

A hosted payment with the agreement object offers at most 20 saved cards: the cards of your tokens list first, in your order, then the cards found by LookupTokensByReference, most recently used first. Requests without the agreement object are not limited by this cap.

LookupTokensByReference looks at the cardholder reference's approved payments and finds at most the 50 most recently used cards, for every request (an agreement object then narrows that to 20 as above).

The hosted page only accepts a saved card it offered (your tokenData, your tokens, or the cards found by LookupTokensByReference). A card id posted to the page that was not offered is refused with TOK07 and the payment ends in error; the card is not read.

A token must belong to your merchant. A tokenData or a tokens entry that belongs to another merchant is refused when you create the hosted payment with TOK07, exactly as an unknown token is, and nothing of that card (masked number, name, email) is read or shown.

Modes

Hosted Payment system contains the mode string, which can be a combination of the following values to trigger particular features

Mode Description
New Request new card - may be prepopulated in CardData section (if PCI D)
PresetToken Present one existing card (specified in Token)- user needs only populate CVV2
PresetTokens Present multiple cards (specified in Tokens)- user needs to select card and only populate CVV2
FixedCardHolderEmail Fix cardholder email - ie make it read-only to user, see Fixed cardholder details
FixedCardHolderName Fix cardholder name - ie make it read-only to user
Retry Allow retries in case of Decline / Error
FriendlyErrors Display user friendly errors instead of a simple Decline (only for Retry)
AskTokenize Ask the cardholder for permission to save the card. Without an agreement object: a "Save card details for next time" checkbox. With an agreement object: the consent checkbox becomes optional, see Saving a card
LookupTokensByEmail No longer supported - rejected with VAL14 (matching on email allows impersonation). Use LookupTokensByReference
LookupTokensByName No longer supported - rejected with VAL14 (matching on name allows impersonation). Use LookupTokensByReference
LookupTokensByReference Offer the cardholder's previous cards (matched on cardholder reference) - only cards with recorded consent (an active agreement with consent recorded; with an agreement object only that agreement). Cancelled or expired agreements are not shown
Vertical Use Vertical template instead of Horizontal template for the new card form
WithZip Enforce collection of ZIP code, see More cardholder details
WithCountry Enforce collection of Coutry code
DisablePay Disable payment button, see The Pay button
WithAddress Enforce collection of Address
WithPhone Enforce collection of phone number

Modes can be combined

Mode Desciption
New, PresetTokens allow cardholder to enter new card, and select an existing card
New, PresetToken allow cardholder to enter new card, or select existing card
New, FixedCardHolderEmail, FixedCardHolderName allow cardhodler to enter new card but email and name details are read-only
New, PresetToken, PresetTokens allow cardholder to enter new card, or select from a single card or multiple cards depending on cards provided
New, AskTokenize new card, and the cardholder chooses whether to save it
Tip

Auto is equivalent to New, PresetToken, PresetTokens

Warning

Lookups require to have Auto or PresetTokens, and to specify the cardholder reference with the CreateHostedPayment request. Only LookupTokensByReference is supported, and it shows only cards with recorded consent (see Agreement/RecordConsent for existing cards)

Note

A merchant level default mode (set up by PayLolly) is combined with the mode you send. If a mode added at merchant level contains LookupTokensByEmail or LookupTokensByName every hosted payment of that merchant is rejected with VAL14.

What the cardholder sees

Every picture below is shown in the light and in the dark theme (see Themes). The page is the same in both themes, only the colours differ.

New card form: horizontal and vertical

mode New shows the card entry form. The default horizontal template lays the card number, expiry and CVV2 on one row and the cardholder name and email on the next. Add Vertical to stack every field with its label above it, which suits narrow iframes.

Horizontal (light) Horizontal (dark)
New card, horizontal, light New card, horizontal, dark
Vertical (light) Vertical (dark)
New card, vertical, light New card, vertical, dark

When the page offers only the new card form it has no heading and no tabs, exactly as before.

Apple Pay: WithApplePay

Add WithApplePay to mode (together with New) to show an Apple Pay button above the fields of the new card form. The button is part of the new card form: it is not shown on a saved card tab, and a request without New is not offered Apple Pay. It is offered only for a one-off payment (recurrence OneTime, no agreement) of an amount above zero, to a merchant that has Apple Pay switched on, with an account that can take it. Otherwise the request is accepted and the page simply shows the card form.

The Apple Pay permission of the merchant switches on both this button and Apple Pay through the Wallet API; the hosted page additionally needs WithApplePay in mode. The button is shown only when the merchant has an account whose processor takes Apple Pay (today only simulated, test, accounts do: for a live account ask PayLolly). The hosted page always uses PayLolly's Apple Pay registration, also for a merchant that has its own for the Wallet API.

Light Dark
WithApplePay, light WithApplePay, dark

The button follows the theme: black on the light theme and white on the dark theme, as Apple asks. The pictures show a plain button because they were taken without Safari; Safari, and other browsers on Apple devices, draw Apple's own button. A browser without Apple Pay, such as Chrome on Windows, shows no Apple Pay button and the card form stays as it is.

The payer's billing details come from the Apple Pay sheet: the name and email are always asked for, and the address, zip, country and phone only when WithAddress, WithZip, WithCountry and WithPhoneNumber are in the mode. The sheet accepts Visa and Mastercard cards only. An Apple Pay payment does not save the card, whatever the save card tick says.

In an iframe. Apple Pay is tied to the website the payer is on. When the payment page is shown in an iframe, the website that holds the iframe must be registered with PayLolly for Apple Pay, and its iframe tag must carry allow="payment":

<iframe src="https://.../payment/hosted/..." allow="payment" width="420" height="720"></iframe>

Without allow="payment" the browser refuses Apple Pay inside the iframe. The page then shows the card form only, and PayLolly logs the website that embedded it, so support can tell you which page needs the attribute. Safari 17 or later is needed for Apple Pay inside an iframe.

Google Pay: WithGooglePay

Add WithGooglePay to mode (together with New) to show a Google Pay button above the fields of the new card form. The button is part of the new card form: it is not shown on a saved card tab, and a request without New is not offered Google Pay. It is offered only for a one-off payment (recurrence OneTime, no agreement) of an amount above zero, to a merchant that has Google Pay switched on, with an account that can take it. Otherwise the request is accepted and the page simply shows the card form. Google Pay can be offered next to Apple Pay: both buttons are shown above the card fields.

The Google Pay permission of the merchant switches on both this button and Google Pay through the Wallet API; the hosted page additionally needs WithGooglePay in mode. The button is shown only when the merchant has an account whose processor takes Google Pay (today only simulated, test, accounts do: for a live account ask PayLolly). The hosted page always uses PayLolly's Google Pay registration, also for a merchant that has its own for the Wallet API.

Light Dark
WithGooglePay, light WithGooglePay, dark

The button is Google's own and follows the theme: black on the light theme and white on the dark theme, as Google asks. It is shown only where Google says the payer can use Google Pay (Chrome and other browsers with a Google account and a card in Google Pay); elsewhere the card form stays as it is. The pictures show a plain stand-in for Google's button, because they were taken without Google's script; Google draws its own button in the same place.

The payer pays with a network token of the card in the Google Pay wallet, which PayLolly decrypts and sends to the acquirer together with its cryptogram, in place of a 3D Secure result. Cards that are only stored in the Google account as a card number (PAN_ONLY) are not offered in production; Google's test environment only has such cards, so there they are accepted, without authentication, to be able to test. The sheet accepts Visa and Mastercard cards only. A Google Pay payment does not save the card, whatever the save card tick says.

The payer's billing details come from the Google Pay sheet: the name and email are always asked for, and the address, zip, country and phone only when WithAddress, WithZip, WithCountry and WithPhoneNumber are in the mode. (Google only gives the name together with the billing address, so the sheet always shows the billing address; the parts the mode does not ask for are not used.)

In an iframe. When the payment page is shown in an iframe, the iframe tag must carry allow="payment":

<iframe src="https://.../payment/hosted/..." allow="payment" width="420" height="720"></iframe>

Without allow="payment" the browser refuses Google Pay inside the iframe. The page then shows the card form only, and PayLolly logs the website that embedded it, so support can tell you which page needs the attribute. In production the website that embeds the page must also be registered with Google for the PayLolly Google Pay merchant.

To show Google Pay on your own page and send the payment from your server, see Wallets. The same goes for Apple Pay. To use your own Apple and Google registrations on your own page, see Setting up your own Apple Pay and Google Pay.

More cardholder details: WithAddress, WithZip, WithCountry, WithPhone

These modes add fields to the new card form. Every field they add is required: the cardholder cannot pay until it is filled in. They can be combined freely.

Mode Adds
WithAddress Address, City and County
WithZip Post Code
WithCountry A country list (Choose Country)
WithPhone Phone Number
WithAddress (light) WithAddress (dark)
WithAddress, light WithAddress, dark
WithZip (light) WithZip (dark)
WithZip, light WithZip, dark
WithCountry (light) WithCountry (dark)
WithCountry, light WithCountry, dark
WithPhone (light) WithPhone (dark)
WithPhone, light WithPhone, dark

All four together, horizontal and vertical (New, WithAddress, WithZip, WithCountry, WithPhone and the same with Vertical):

Horizontal (light) Horizontal (dark)
All details, horizontal, light All details, horizontal, dark
Vertical (light) Vertical (dark)
All details, vertical, light All details, vertical, dark

Fixed cardholder details: FixedCardHolderEmail, FixedCardHolderName

Send the cardholder's email and/or name in cardHolder and add the mode: the field is shown read-only and the cardholder cannot change it. This is how you make sure a payment link sent by email is paid for the person it was meant for.

FixedCardHolderEmail (light) FixedCardHolderEmail (dark)
FixedCardHolderEmail, light FixedCardHolderEmail, dark
FixedCardHolderName (light) FixedCardHolderName (dark)
FixedCardHolderName, light FixedCardHolderName, dark
Both fixed (light) Both fixed (dark)
Both fixed, light Both fixed, dark

The Pay button: DisablePay

DisablePay shows the page with the Pay button disabled (greyed out). Use it for a preview of the page, or when your own page decides when payment may proceed.

Light Dark
DisablePay, light DisablePay, dark

Save the card without an agreement: AskTokenize

With AskTokenize and no agreement object the page shows a "Save card details for next time" checkbox. It is unticked for a OneTime request (and pre-ticked for a OneClick request). Ticking it makes the payment save the card and return the token. With an agreement object the checkbox is the consent checkbox instead, see Saving a card.

Light Dark
AskTokenize, light AskTokenize, dark

Declines: Retry and FriendlyErrors

With Retry a declined or failed attempt does not end the payment: the page shows the reason and the form again so the cardholder can try another card. Without it the payment ends and the cardholder returns to your site.

FriendlyErrors (only meaningful with Retry) replaces the bare "Declined" with a plain wording for the decline reason when PayLolly has one, for example "Please check funds available, or try another card".

Retry (light) Retry (dark)
Retry, light Retry, dark
Retry, FriendlyErrors (light) Retry, FriendlyErrors (dark)
Retry and FriendlyErrors, light Retry and FriendlyErrors, dark
Important

If you rely on polling instead of webhooks, do not enable Retry: a payment you saw as declined could be approved by a later attempt.

One saved card

mode PresetToken with tokenData shows the card read-only (masked number and expiry) with the cardholder name and email; the cardholder only enters the CVV2. The saved card pages have one layout: the Vertical mode does not change them.

Light Dark
One saved card, light One saved card, dark

Several saved cards

mode PresetTokens with tokens shows the cards to choose from. The page starts with "Select a card to continue": the CVV2, name and email fields and the Pay button appear once a card is chosen, so a payment cannot be made without choosing.

Nothing selected (light) Nothing selected (dark)
Card list, nothing selected, light Card list, nothing selected, dark
Card selected (light) Card selected (dark)
Card list, card selected, light Card list, card selected, dark

Saved card or new card: the tabs

When more than one method is offered (for example New, PresetToken or New, PresetTokens, which is also what Auto gives you) the page shows tabs: Saved card (or Saved cards) first and selected by default, then New card. Each method has its own heading. Clicking a tab shows that method; if a payment attempt is refused (for example the consent box was not ticked) the page stays on the same tab.

Note

The new card form is prefilled with the cardholder name and email of the saved card. Paying with a new card never uses the saved card, whatever the preset token was. Note: when the cardholder types the same card number and expiry as the saved card, the stored card is replaced by this payment's data, which carries no scheme reference (Trace ID) and no processor references. This is how every payment with a raw card number behaves for a card that is already stored; later merchant initiated payments on that token then go out without the original reference. Offer one method (PresetToken only) when your customers pay with a saved card and you rely on it.

One saved card + new card: saved card tab

Light Dark
Saved card tab, light Saved card tab, dark

One saved card + new card: new card tab, horizontal and vertical

Horizontal (light) Horizontal (dark)
New card tab, horizontal, light New card tab, horizontal, dark
Vertical (light) Vertical (dark)
New card tab, vertical, light New card tab, vertical, dark

Several saved cards + new card: saved cards tab

Light Dark
Saved cards tab, light Saved cards tab, dark

Several saved cards + new card: new card tab, horizontal and vertical

Horizontal (light) Horizontal (dark)
New card tab, horizontal, light New card tab, horizontal, dark
Vertical (light) Vertical (dark)
New card tab, vertical, light New card tab, vertical, dark

The three plain pages side by side (light):

New PresetToken New + PresetToken
New card One saved card New card and saved card, saved card tab

Zero amount

With amount 0 nothing is charged: the page is a card check and its button reads Approve instead of Pay. Everything else about the page follows the modes you already know; the examples below are the same pictures with a zero amount.

A zero amount is a normal request, with these rules:

  • It needs a processor verified for zero value payments (SupportsZeroAmount, off until the acquirer confirmed it and it was tested). When none of your accounts in the currency qualifies, creating the hosted payment is rejected with RTE03; the customer never sees a page that cannot work.
  • A negative amount is rejected (PAB02).
  • The payment cannot be captured, refunded or voided (PRO30): there is nothing to settle.
  • The payment appears in your reports and webhooks with amount 0, and the result is Approved or Declined like any other.
  • With the agreement object and recurrence OneClick the approved check also registers the agreement: the card is saved and consent is recorded without a charge. This is the way to save a card with no payment.

See Sync Payment for the rules shared with the other payment types.

A card check with a new card

Request: "amount": 0, "mode": "New", no agreement object. The card is checked, not saved.

Horizontal Vertical
New card, zero amount, horizontal, light New card, zero amount, vertical, light
New card, zero amount, horizontal, dark New card, zero amount, vertical, dark

A card check with a saved card

Request: "amount": 0, "mode": "PresetToken" (one card) or "PresetTokens" (a list), with the token or tokens. The page asks for the CVV2 only. In a list the details and the button appear once a card is chosen.

One saved card Card list, a card selected
One saved card, zero amount, light Card list, zero amount, light
One saved card, zero amount, dark Card list, zero amount, dark
Note

The layout of saved card pages is the same with or without Vertical: Vertical only changes the form for a new card.

Saving a card without a charge: consent forced

Request: "amount": 0, "mode": "New", "recurrence": "OneClick", and the agreement object. The consent box is shown unticked and must be ticked (the server checks it as well). After an approved check the response and the webhook carry the agreement block with status Created, and the card has a token.

Light Dark
Zero amount with forced consent, light Zero amount with forced consent, dark

Saving a card without a charge: consent optional

Request: "amount": 0, "mode": "New,AskTokenize", "recurrence": "OneTime", and the agreement object. The box looks the same. Ticked, the approved check registers the agreement and returns a token. Not ticked, the card is only checked: the payment is Approved, the agreement block says NotCreated with reason ConsentDeclined, and no token is returned.

Light Dark
Zero amount with optional consent, light Zero amount with optional consent, dark

What the page does when the check is declined

Declines behave as for any payment: with Retry the page shows the reason and the form again, otherwise the payment ends and the cardholder returns to your returnUrl. No agreement is created for a declined check (NotCreated, reason PaymentNotApproved).

Saving a card: agreements and consent

To save a card for later payments (merchant initiated payments, one-click payments, subscriptions) send the agreement object together with recurrence OneClick. The cardholder gives consent on the page, PayLolly records the evidence, and after an approved payment the card is registered as an agreement. The payment response returns an agreementId you quote on later payments.

The agreement object

Field Required Description
agreementRef no Your own reference, up to 36 characters (letters, digits, -, _, .). It tells parallel agreements for the same customer and card apart
description yes (hosted) What the card may be used for, up to 200 characters. It is part of the consent sentence the cardholder agrees to
maxAmount no Cap per payment in the payment currency, greater than zero, with no more decimals than the currency allows. Shown in the consent sentence
expiresAt no End of the agreement (UTC), must be in the future. Shown in the consent sentence
consent not allowed On hosted payments PayLolly records the consent itself (AGR05 if you send it)

Rules:

  • cardHolder.reference is mandatory (1 to 64 characters, AGR01). It is your id for the customer. Together with the card and the agreementRef it identifies the agreement: the same customer, card and agreementRef registers the same agreement again (same agreementId, status Updated), a different combination creates a different agreement.
  • recurrence must be OneClick (AGR02), or OneTime together with the AskTokenize mode (the cardholder's tick then turns it into OneClick).
  • The merchant must be enabled for agreements (AGR09).
  • initiation is always CIT. 3D Secure requests a mandated challenge (indicator 04) on the payment that registers a card, so the cardholder is asked to authenticate.

Consent is mandatory or optional - the AskTokenize mode decides

Mode Consent checkbox Cardholder does not tick
without AskTokenize (default) Mandatory. Always shown, never pre-ticked The payment is not made: the page is shown again with "Please tick the box to agree". This is checked on the server, not only in the browser
with AskTokenize Optional, never pre-ticked The payment proceeds as a one-time payment, nothing is registered, and the response says so (ConsentDeclined)
Warning

Without the agreement object nothing changes: the existing behaviour of OneClick and AskTokenize (a "Save card details for next time" checkbox) is kept.

The consent text is fixed by PayLolly and filled with your description and limits:

I agree that my card details are saved and may be used for: {description}[, up to {currency} {maxAmount} per payment][, until {yyyy-MM-dd}].

The description is shown on its own line. The limits you send follow it on the same line; the ones you leave out are not mentioned. Without limits (the pictures further down) the sentence ends after the description.

With "maxAmount": 100 (payment currency EUR) the sentence ends "...Wallet top-ups, up to EUR 100 per payment.":

Light Dark
Consent with a maximum amount, light Consent with a maximum amount, dark

With "expiresAt": "2027-12-31T00:00:00Z" it ends "...Wallet top-ups, until 2027-12-31.":

Light Dark
Consent with an end date, light Consent with an end date, dark

With both it ends "...Wallet top-ups, up to EUR 100 per payment, until 2027-12-31.":

Light Dark
Consent with both limits, light Consent with both limits, dark

On the new card form with a description only (horizontal and vertical):

Horizontal (light) Horizontal (dark)
Consent, new card, horizontal, light Consent, new card, horizontal, dark
Vertical (light) Vertical (dark)
Consent, new card, vertical, light Consent, new card, vertical, dark

When the mandatory consent is not ticked:

Light Dark
Consent not ticked, light Consent not ticked, dark

Consent for a saved card

The checkbox is also shown for a saved card, but only when the card has no recorded consent for this agreement, or when what you now send differs from what the agreement holds: a different description, maximum amount, end date or schedule is something new to agree to. A card whose agreement says exactly the same is not asked again (this holds for scheduled agreements too). This also covers cards you saved before agreements existed: the first customer-present payment records the consent. Each attempt starts from what you sent: a retry does not inherit the tick or the recurrence of the previous attempt.

Saved card, consent needed

Light Dark
Consent, saved card, light Consent, saved card, dark

Saved card, consent not ticked

Light Dark
Consent not ticked, saved card, light Consent not ticked, saved card, dark

In a list of saved cards the checkbox follows the selected card: it appears for a card that needs consent and disappears for a card that already has it.

Card needing consent (light) Card needing consent (dark)
List, card needing consent, light List, card needing consent, dark
Card with consent (light) Card with consent (dark)
List, card with consent, light List, card with consent, dark

What PayLolly records

For each consent given on the page PayLolly keeps: that consent was recorded on the hosted page, a version of the exact text shown (hosted-v1: followed by a short hash of the sentence the cardholder saw - computed by PayLolly, never taken from the browser), the time and the IP address. The registration also keeps the scheme reference and processor references of the payment, and the identifiers (not the cryptogram) of the 3D Secure authentication.

How long an agreement lives

An agreement lives as long as its stored card. A card that has not been used for two years is deleted by PayLolly together with its agreements (and the consent evidence in them); any approved payment made with the card renews it. Cancelling an agreement (Agreement/Cancel) only flags it, it does not delete it. See Token service.

The result: the agreement block

The payment status response and the webhooks carry an agreement block when you sent the agreement object:

"agreement": {
  "agreementId": "0198ab5c-6f37-7a02-9d7e-3a5e2c1f4b10",
  "status": "Created",
  "agreementRef": "TOPUP-1",
  "consentStatus": "Recorded"
}
Field Values
agreementId Quote it as agreementId on later payments made under this agreement
status Created (new), Updated (the same customer, card and agreementRef again) or NotCreated
reason Only when NotCreated: ConsentDeclined (optional consent not ticked), AlreadyRegistered (optional consent, and the saved card already has an agreement with these terms: the cardholder is not asked and the payment is made as you sent it), PaymentNotApproved, Disabled (agreements not enabled), Error (registration failed after approval and is retried automatically, 5 times over about 5 hours (the first retry about a minute after the payment is stored); the retries that run out are counted (Agreement.RetryExhausted) and the outcome stays Error; when a retry succeeds the payment is updated and your completed / approved webhooks are sent again with the final outcome)
consentStatus Recorded, or LegacyUnrecorded (the agreement exists but no consent evidence is held, for example a saved card paid without the consent box)
Note

A failure to register never fails the payment: the payment result stays as the processor returned it.

Agreement API: existing cards, and cancelling

Record consent for a card you already hold (a token created before agreements, or imported) without a payment, when the cardholder gave consent in your own journey. The consent is then attested by you. Customer reference, consent text version and consent time (may be in the past, never in the future) are mandatory.

curl -X 'POST' \
  'https://api.dev.paylolly.app/api/rest/v1.0/Agreement/RecordConsent' \
  -H 'accept: application/json' \
  -H 'MerchantKey: 35236fcf-12f4-4c94-b4ba-5d236457b68a' \
  -H 'Content-Type: application/json' \
  -d '{
  "token": "08524FF1F8BE79B7EA0CE19204E81BF2",
  "cardHolder": { "reference": "CUST-1" },
  "agreement": {
    "agreementRef": "TOPUP-1",
    "description": "Wallet top-ups",
    "consent": { "textVersion": "v3", "timestamp": "2026-09-01T09:00:00Z" }
  }
}'
{
  "agreementId": "0198ab5c-6f37-7a02-9d7e-3a5e2c1f4b10",
  "status": "Created",
  "agreementRef": "TOPUP-1",
  "consentStatus": "Recorded"
}

Calling it again with the same token, customer and agreementRef returns Updated with the same agreementId. The token must be yours (TOK07 otherwise).

Cancel an agreement. This only flags it: payments that quote it are counted but not blocked, and a cancelled agreement is no longer offered to the cardholder on the hosted page.

curl -X 'POST' \
  'https://api.dev.paylolly.app/api/rest/v1.0/Agreement/Cancel?agreementId=0198ab5c-6f37-7a02-9d7e-3a5e2c1f4b10' \
  -H 'MerchantKey: 35236fcf-12f4-4c94-b4ba-5d236457b68a'
{ "agreementId": "0198ab5c-6f37-7a02-9d7e-3a5e2c1f4b10", "status": "Cancelled" }

Later payments: on a sync or async payment with a token you may send agreementId. PayLolly then uses the trace id and processor references of that agreement instead of the token's. An unknown agreement, or one that belongs to another card or merchant, is rejected with AGR08.

Saved cards

Three ways to present saved cards:

Mode You send The cardholder sees
PresetToken tokenData One saved card, enters the CVV2
PresetTokens tokens (list) A list of saved cards, chooses one, enters the CVV2
LookupTokensByReference cardHolder.reference The cards PayLolly holds for that customer that have recorded consent

Only cards with recorded consent are looked up. A card is offered again only when there is an active, unexpired agreement for your merchant, that card and that customer reference whose consent evidence is held. With an agreement object in the request only that agreement (by agreementRef) counts. Cards saved without consent, cancelled agreements and expired agreements are never offered by the lookup. For cards you saved before agreements existed, record consent with Agreement/RecordConsent to make them appear again.

PresetToken and PresetTokens show the cards you provide: they are your responsibility. When you also send an agreement object the consent checkbox covers a card whose agreement has no consent yet.

Warning

LookupTokensByEmail and LookupTokensByName are no longer supported: matching on an email address or a name allows one person to be offered another person's cards. They are rejected with VAL14. Use LookupTokensByReference with your own customer reference.

Themes: light, dark and automatic

The payment pages come in three themes:

Theme Behaviour
Light The pages as they always were. This is the default: nothing changes until you choose another theme
Dark Dark colours
Auto Follows the cardholder's device (light or dark), including when the device changes setting while the page is open

Where to set it (the request wins over the merchant setting, and either can set Light to switch back):

  • Per merchant: the Page Theme setting of the hosted page configuration in the PayLolly portal (or ask PayLolly).
  • Per payment: cssSettings.theme in CreateHostedPayment.
{
  "mode": "New",
  "cssSettings": { "theme": "Dark" }
}

The theme is decided once per payment and used for the whole flow: the payment form, the "Processing" page, the pages that redirect to the 3D Secure method and back, and the redirect to your returnUrl all use the same theme, so there is no white flash in the middle of a dark payment. One limit: the 3D Secure challenge itself is the cardholder's bank page and is not ours to theme (it appears as a light panel inside the dark page).

Light (default) Dark
New card, light New card, dark

All the pictures on this page exist in both themes.

Important

If you override Bootstrap variables for your branding (see CSS Customisation) the overrides on :root apply to both themes. A light body colour with the dark theme gives unreadable text. Keep your light overrides on :root and put the dark variants under the selector [data-bs-theme=dark], listed after :root in your css object: both selectors have the same weight, so the later one wins when the page is dark.

CSS Customisation

It is possible to customise the visual components of the payment page via CSS variables

The Hosted Payment page is using standard BootStrap 5.3, and all standard CSS variabels can be overridden.

Overrides can be static (advise PayLolly and they will set them up for you) or else dynamic, by specifying them in the CreateHostedPayment request in cssSettings->css object

Tip

Bootstrap css variables can be seen on https://getbootstrap.com/docs/5.3/customize/css-variables/

example

{
...
"cssSettings":{
"css":{
":root":{
"--bs-body-bg":"red",
"--bs-body-bg-rgb":"255,0,0"
}
}
}
}

example with a dark variant (the second selector is used only when the page is dark, and must come after :root)

{
"cssSettings":{
"theme":"Auto",
"css":{
":root":{
"--bs-primary":"#0a7d5a",
"--bs-body-bg":"#ffffff"
},
"[data-bs-theme=dark]":{
"--bs-primary":"#34c38f",
"--bs-body-bg":"#101418"
}
}
}
}

Errors

Error When
VAL13 initiation is MIT or MOTO: hosted (and async) payments are always cardholder initiated
VAL14 LookupTokensByEmail or LookupTokensByName in the mode (including from a merchant level mode)
AGR01 agreement sent without cardHolder.reference, or longer than 64 characters
AGR02 agreement with a recurrence other than OneClick (or OneTime with AskTokenize)
AGR03 agreement.description missing on a hosted payment
AGR05 agreement.consent sent on a hosted payment
AGR06 maxAmount not positive (or too many decimals for the currency), or expiresAt not in the future
AGR07 Invalid agreement field (agreementRef format or length, description longer than 200)
AGR08 Unknown agreementId, or an agreement that belongs to another card or merchant
AGR09 Agreements are not enabled for the merchant
TOK07 Token missing, invalid or not yours
HPS01 The payment link has expired (validUntil)
HPS02 The payment was cancelled by the cardholder

Every code, with a sample response, is in Errors.

Use Cases

Hosted Payment - Single one-shot payment

Use Payment/CreateHostedPayment call

  • Populate merchantId
  • Optionally populate accountid if you want to force a particular account
  • Populate Currency ex EUR
  • Populate amount ex 1.00
  • Create a merchantRef - must be unique - suggest Guid
  • Enter cardholder details if known
  • If PCI D - and you know card details you can prepopulate them in cardData
  • Populate AuthorisationType depending on your use case
    • Preauth - Auth
    • Preauth and Capture - AuthCapture
  • Recurrence - OneTime
  • WebHooks - suggest population of completed and approved webhooks to receive server-to-server call when payment completed
  • Initiation - set to CIT
  • ReturnUrl - enter a valid url to redirect cardholder at the end of the payment
  • ThreeDSConfig - see ThreeDSConfig - default values apply if omitted
  • validUntil - enter a valid until date/time - suggest 20 minutes
  • mode - select New - see Modes

The above will create a single one-shot payment request

curl -X 'POST' \
  'https://api.dev.paylolly.app/api/rest/v1.0/Payment/CreateHostedPayment' \
  -H 'accept: application/json' \
  -H 'MerchantKey: 35236fcf-12f4-4c94-b4ba-5d236457b68a' \
  -H 'Content-Type: application/json' \
  -d '{
  "merchantId": "dbdab964-82ee-4b09-a26b-68403c31afa7",
  "currency": "EUR",
  "amount": 1,
  "merchantRef": "9e31818e-407b-48d8-8bbe-eb19fa565733",
  "cardHolder": {
    "email": "test@email.com",
    "name": "NAME SURNAME"
  },
  "authorisationType": "Auth",
  "recurrence": "OneTime",
  "initiation": "CIT",
  "returnUrl": "https://mywebsite.com/payment/return/abc123",
  "validUntil": "2022-11-04T09:36:54.749Z",
  "mode": "New"
}'

response:

{
  "redirectUrl": "https://api.dev.paylolly.app/payment/hosted/6d5f3349-40f2-4727-aac0-39eb0e21951f/Index",
  "paymentId": "6d5f3349-40f2-4727-aac0-39eb0e21951f",
  "merchantId": "dbdab964-82ee-4b09-a26b-68403c31afa7",
  "accountId": null,
  "currency": "EUR",
  "amount": 1,
  "cardData": null,
  "threeDSData": null,
  "result": null,
  "merchantRef": "9e31818e-407b-48d8-8bbe-eb19fa565733"
}

The page the cardholder sees:

Light Dark
New card, light New card, dark

Hosted Payment - one-shot payment with previous details (fixed) - repeated payment CIT

Use Payment/CreateHostedPayment call

  • Populate merchantId

  • Optionally populate accountid if you want to force a particular account

  • Populate Currency ex EUR

  • Populate amount ex 1.00

  • Create a merchantRef - must be unique - suggest Guid

  • Enter cardholder details if known

  • populate tokenData with previous payment Token

  • Populate AuthorisationType depending on your use case

    • Preauth - Auth
    • Preauth and Capture - AuthCapture
  • Recurrence - OneTime

  • WebHooks - suggest population of completed and approved webhooks to receive server-to-server call when payment completed

  • Initiation - set to CIT

  • ReturnUrl - enter a valid url to redirect cardholder at the end of the payment

  • ThreeDSConfig - see ThreeDSConfig - default values apply if omitted

  • validUntil - enter a valid until date/time - suggest 20 minutes

  • mode - select PresetToken - see Modes

  • The above will create a single one-shot payment request

The page the cardholder sees:

Light Dark
One saved card, light One saved card, dark

Hosted Payment - save the card with mandatory consent (subscription style)

Use Payment/CreateHostedPayment as for a one-shot payment, but:

  • recurrence: OneClick
  • cardHolder.reference: your id for the customer (mandatory)
  • agreement: description (mandatory), optionally agreementRef, maxAmount, expiresAt
  • mode: New (no AskTokenize: the consent is mandatory)
{
  "merchantId": "dbdab964-82ee-4b09-a26b-68403c31afa7",
  "currency": "EUR",
  "amount": 25,
  "merchantRef": "2f7c1b6a-5b57-4b29-8f57-3a0f1b0a9c11",
  "cardHolder": { "reference": "CUST-1", "email": "test@email.com", "name": "NAME SURNAME" },
  "authorisationType": "AuthCapture",
  "recurrence": "OneClick",
  "initiation": "CIT",
  "returnUrl": "https://mywebsite.com/payment/return/abc123",
  "validUntil": "2026-11-04T09:36:54.749Z",
  "mode": "New",
  "agreement": { "agreementRef": "TOPUP-1", "description": "Wallet top-ups", "maxAmount": 100 }
}

The cardholder must tick the consent box (see the pictures above). After approval GetPaymentStatus and the webhook carry the card token (store it) and the agreement:

{
  "result": { "status": "Approved" },
  "cardData": { "token": "08524FF1F8BE79B7EA0CE19204E81BF2", "maskedPAN": "400000*0002" },
  "agreement": { "agreementId": "0198ab5c-6f37-7a02-9d7e-3a5e2c1f4b10", "status": "Created", "agreementRef": "TOPUP-1", "consentStatus": "Recorded" }
}

Store the agreementId and the token for the customer; later merchant initiated payments quote them (see the sync and async documentation).

Hosted Payment - subscription or instalments (scheduled agreement)

As for mandatory consent, but send recurrence Scheduled and an agreement.schedule (amount, interval, firstPaymentDate, instalments, cancellationUrl; see Agreements). The consent checkbox is always mandatory (the AskTokenize mode is rejected with AGR15) and is asked again for a saved card when the schedule differs from the one its agreement holds (the same rule as for every agreement). The sentence next to the box is generated from your fields:

I agree that my card details are saved and EUR 10.00 will be charged every month, starting with this payment, for 12 payments. Gym membership I can cancel at https://shop.example/cancel

Light Dark
Scheduled consent, light Scheduled consent, dark
{
  "currency": "EUR",
  "amount": 10,
  "merchantRef": "2f7c1b6a-5b57-4b29-8f57-3a0f1b0a9c12",
  "cardHolder": { "reference": "CUST-1", "email": "test@email.com", "name": "NAME SURNAME" },
  "authorisationType": "AuthCapture",
  "recurrence": "Scheduled",
  "initiation": "CIT",
  "returnUrl": "https://mywebsite.com/payment/return/abc123",
  "mode": "New",
  "agreement": {
    "agreementRef": "GYM-1",
    "description": "Gym membership",
    "schedule": { "amount": 10, "interval": { "unit": "Month", "count": 1 }, "instalments": 12, "cancellationUrl": "https://shop.example/cancel" }
  }
}

Without firstPaymentDate the registering payment is the first payment, so amount must equal schedule.amount; an amount of 0 (card check, free trial) needs schedule.firstPaymentDate (AGR18). You collect the later payments yourself with merchant initiated Recurring payments that quote the agreementId.

Hosted Payment - save the card only if the cardholder agrees (optional consent)

As above, but send recurrence OneTime and mode New,AskTokenize. The consent box is shown unticked:

  • ticked: the payment registers the card (agreement.status Created, token returned)
  • not ticked: a normal one-time payment is made, no token is returned, and the response says "agreement": { "status": "NotCreated", "reason": "ConsentDeclined" }

Hosted Payment - returning customer: saved card, consent only the first time

  • mode: PresetToken (one card) or PresetTokens (several) or New,PresetToken (saved card or new card)
  • tokenData / tokens: the stored card(s)
  • cardHolder.reference and the same agreement as when the card was registered

If the agreement of that card already has recorded consent the customer is only asked for the CVV2. If not (for example the card was saved before agreements existed) the consent box is shown once; ticking it records the consent on that payment. Unticked: with a mandatory consent the page asks again, with AskTokenize the payment proceeds as a one-time payment.

Hosted Payment - offer the customer's earlier cards (lookup by reference)

  • mode: Auto or PresetTokens together with LookupTokensByReference
  • cardHolder.reference: your customer id

The page offers the customer's cards that have recorded consent, next to the new card form when New is in the mode. A customer with no consented card simply sees the new card form. To make cards you saved earlier appear, record their consent with Agreement/RecordConsent.

Hosted Payment - dark or automatic theme

Add "cssSettings": { "theme": "Dark" } (or "Auto") to the request, or have the theme set for your merchant in the portal, see Themes.

Create a payment Link to send by email

Follow Hosted Payment - Single one-shot payment above but

  • Modify validUntil to a suitable time example (1 week / 30 days)
  • Make sure you use a webhook and appropriate return url which cannot make use of any session data as session validity is typically minutes
  • Send link by email (link=return.redirectUrl)
curl -X 'POST' \
  'https://api.dev.paylolly.app/api/rest/v1.0/Payment/CreateHostedPayment' \
  -H 'accept: application/json' \
  -H 'MerchantKey: 35236fcf-12f4-4c94-b4ba-5d236457b68a' \
  -H 'Content-Type: application/json' \
  -d '{
  "merchantId": "dbdab964-82ee-4b09-a26b-68403c31afa7",
  "currency": "EUR",
  "amount": 1,
  "merchantRef": "9e31818e-407b-48d8-8bbe-eb19fa565733",
  "cardHolder": {
    "email": "test@email.com",
    "name": "NAME SURNAME"
  },
  "authorisationType": "Auth",
  "recurrence": "OneTime",
  "initiation": "CIT",
  "returnUrl": "https://mywebsite.com/payment/return/abc123",
  "validUntil": "2022-12-04T09:36:54.749Z",
  "mode": "New,FixedCardHolderEmail,FixedCardHolderName"
}'

response:

{
  "redirectUrl": "https://api.dev.paylolly.app/payment/hosted/6d5f3349-40f2-4727-aac0-39eb0e21951f/Index",
  "paymentId": "6d5f3349-40f2-4727-aac0-39eb0e21951f",
  "merchantId": "dbdab964-82ee-4b09-a26b-68403c31afa7",
  "accountId": null,
  "currency": "EUR",
  "amount": 1,
  "cardData": null,
  "threeDSData": null,
  "result": null,
  "merchantRef": "9e31818e-407b-48d8-8bbe-eb19fa565733"
}

Prepare and send email to cardholder with the link returned in redirectUrl https://api.dev.paylolly.app/payment/hosted/6d5f3349-40f2-4727-aac0-39eb0e21951f/Index

Tip

See Payment Actions for further actions possible on completed hosted payment

In this article
Back to top Generated by DocFX