PayFacto API - v2.0 - Solution : Secure Hosted Checkout (SHC)

PayFacto API - v2.0 - Solution : Secure Hosted Checkout (SHC)

PayFacto API - v2.0 - Solution : Secure Hosted Checkout (SHC)

Secure Hosted Checkout (SHC) is a JavaScript library you add to your own page. It draws a form inside a secure iframe โ€” the cardholder name, card number, expiry, and CVV go directly from the cardholder's browser to PayFacto. Your page never touches raw card data, which keeps you out of scope for the strictest PCI requirements.

Version: v2.0   |  Last updated: 2026-09-16

How It Works

๐Ÿ”‘

Your server requests a session token

POST to the SHC session endpoint with your API key. The response returns a one-time token scoped to this checkout.

๐Ÿ“„

Your server returns the token to the browser

The token is passed back to the browser as JSON. Your API key never leaves the server.

๐Ÿซœ

The browser calls shc() โ€” the iframe appears

The browser passes the token to the global shc() function. The library renders a secure iframe containing the card form inside your #shc-fields div.

๐Ÿ’ณ

The cardholder fills in the form and clicks Pay

Card data is submitted directly from the iframe to PayFacto. Your page never sees the raw card data.

โœ…

PayFacto fires the callback with the result

Your callback function receives a response object. Check response.success first, then verify the HMAC server-side before fulfilling the order.

1

  Prerequisites

๐Ÿ”‘

API Key

PayFacto provides you with an API key โ€” a username and password pair. Base64-encode username:password and put it in the Authorization: Basic ... header on every server-side session request. Never put it in client-side JavaScript or a public repository.

๐ŸŒ

HTTPS server

Your server must support HTTPS. All PayFacto endpoints require a secure connection.

๐Ÿ’ง

UAT environment

Use UAT during development: https://uat.ca.shc.payfacto.cloud. Your PayFacto contact will provide the production URL once you are ready to go live.

๐Ÿ“„

A page you control

SHC works on any HTML page. You add one <script> tag and one <div> โ€” SHC handles the rest.

2

  Step 1 โ€” Request a Session Token (Server-Side)

โš ๏ธ

Call the session endpoint from your server, not the browser. The Authorization header contains your API secret. If you call it from the browser, anyone can steal your key.

โ„น๏ธ

The session token is a one-time token that expires in 5 minutes by default (configurable via expires). Request it fresh for each checkout โ€” never cache or reuse tokens.

Required session fields

Field Type Description
transactionType string ยท required What to do with the card. VERIFY โ€” validate the card, no money moves. PURCHASE โ€” charge immediately. PREAUTHORIZATION โ€” reserve funds, capture later. PURCHASE and PREAUTHORIZATION also require purchaseAmount.
language string ยท required Language for the form. en = English, fr = French.

Optional session fields

Field Type / Default Description
allowList array of strings Restrict the payment methods offered by the session. One or more of AMEX, DINERS, DISCOVER, INTERAC_DIRECT, JCB, MASTERCARD, VISA, APPLE_PAY, GOOGLE_PAY, KONEK, PAYMENT_FORM. When omitted, every payment method enabled on the merchant account is offered. See Restricting Payment Methods โ€” allowList below.
purchaseAmount integer Amount in cents. $10.25 = 1025. Required for PURCHASE and PREAUTHORIZATION. The amount set here is the amount charged โ€” a different value typed in the form is ignored.
amount string How to display the amount field. disabled, editable, or hidden. If purchaseAmount is set and amount is not passed, the field is hidden.
avs string Show address verification fields. postal (postal code only) or both (street + postal). If full AVS is enabled on your account, ignore this option.
disableSubmitIndicator boolean ยท default false Set true to stop the Pay button from changing to a spinner on submit.
email boolean ยท default false Show the email address field. Ignored if 3DS is active โ€” when 3DS is on, the email field is shown automatically and is required.
expires string (ISO-8601) When the session token expires. Default: 5 minutes from creation. Format: YYYY-MM-DDTHH:MM:SS.000Z.
ignoreInitAuthTimeout boolean ยท default false Set true to ignore a 3DS device-collection timeout and continue the 3DS flow. Warning: this may shift fraud liability to the merchant.
invoice string How to display the invoice field. disabled, editable, or hidden.
invoiceNumber string ยท max 25 chars Pre-fill the invoice number. If invoice is not set, the field displays as read-only.
transactionDescription string Text shown in the description field of the generated receipt.
useRecaptcha boolean ยท default true Set false to disable Google reCAPTCHA. Only disable if reCAPTCHA causes issues on your site โ€” disabling it increases exposure to automated attacks.
webhookUrl string (URL) An extra HTTPS URL that PayFacto will POST the transaction result to in addition to the browser callback.
JSON โ€” Session request with all options
{
"transactionType": "PURCHASE",
"purchaseAmount": 1025,
"language": "en",
"amount": "hidden",
"avs": "both",
"email": true,
"invoice": "editable",
"invoiceNumber": "INV-001",
"transactionDescription": "Order #2024-001",
"expires": "2026-06-30T23:59:59.0Z",
"useRecaptcha": true,
"webhookUrl": "https://your-site.com/webhook",
"disableSubmitIndicator": false,
"ignoreInitAuthTimeout": false
}
JavaScript โ€” Server-side session request
// Server-side โ€” Node.js example
const response = await fetch(
'https://uat.ca.shc.payfacto.cloud/secure-hosted-checkout/v1/sessions',
{
method: 'POST',
headers: {
'Authorization': `Basic ${Buffer.from('username:password').toString('base64')}`,
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
transactionType: 'PURCHASE',
purchaseAmount: 1025, // $10.25 in cents โ€” mandatory for PURCHASE
language: 'en'
})
}
);

const { token } = await response.json();
// Return this token to the browser โ€” never expose your API key
โ˜…

  Restricting Payment Methods โ€” allowList

Pass allowList in the session request to restrict which payment methods a session offers. It takes an array containing one or more of the following values:

Value What it controls
AMEX, DINERS, DISCOVER, JCB, MASTERCARD, VISA Card brands. Only the listed brand images are displayed, and a card number of any other brand is flagged as invalid in the form.
INTERAC_DIRECT Konek (pay-by-bank) payment method.
APPLE_PAY Offers Apple Pay. Only offered when this value is present, and the card networks Apple Pay accepts are limited to the card brands also listed in allowList.
GOOGLE_PAY Offers Google Pay. Only offered when this value is present, and the card networks Google Pay accepts are limited to the card brands also listed in allowList.
KONEK Offers the Konek wallet option. Only offered when this value is present.
PAYMENT_FORM Shows the card entry fields. Required to accept card payments at all when allowList is set โ€” see the warning below.
โ„น๏ธ

When allowList is omitted entirely, every payment method enabled on the merchant account is offered.

โš ๏ธ

PAYMENT_FORM Must Be Listed to Accept Cards

The card payment fields are only displayed when PAYMENT_FORM is listed. An allowList without PAYMENT_FORM offers the listed wallets only, and the server rejects any card submission with CARD_DECLINED / PAYMENT_FORM_NOT_ALLOWED.

This restriction is enforced on both sides โ€” hiding fields in the form is a convenience, not the security boundary. The server independently rejects a card whose brand (detected from the card number) is not listed, with CARD_DECLINED / CARD_BRAND_NOT_ALLOWED, at /browser/process and again during 3DS authentication.

JSON โ€” Restrict a session to Visa, Mastercard, and Apple Pay
{
"transactionType": "PURCHASE",
"purchaseAmount": 1025,
"language": "en",
"allowList": [
"VISA",
"MASTERCARD",
"APPLE_PAY",
"PAYMENT_FORM"
]
}

The allowList value you send is echoed back in the session response.

โ˜…

  Webhook Best Practices

Webhooks are a window into your system. Care must be taken that third parties are unable to send false or misleading information. The following tips can greatly reduce the possibility of bad actors introducing false information into your system.

๐ŸŽฒ

Randomize the Endpoints

When sending an endpoint to SHC, add a random element to it (i.e. /webhook/{randomId}). Map the random element (randomId) to the secureId in the token. When a message is received, compare the randomId and the secureId received to the stored pair to ensure they match, otherwise reject the message.

๐Ÿ”’

Always Verify the HMAC

When the message is received, verify the HMAC as shown in the Verifying the HMAC section to ensure the message has not been tampered with.

๐Ÿ””

Treat the Message as a Notification

The message sent to your webhook is a notification, not a source of truth. Reconcile your transactions with data provided securely by PayFacto.

โšก

Acknowledge Quickly

Send an acknowledgement as soon as you receive a notification โ€” this will greatly reduce the probability of getting multiple notifications for the same transaction. Ensure that a 2XX response, such as a 200, is sent to avoid confusion.

3

  Step 2 โ€” Add the Script and Placeholder to Your Page

โ„น๏ธ

Add the SHC library script tag to your page <head> and an empty <div> wherever you want the card form to appear. The default container id is shc-fields โ€” you can use a different id but must tell SHC about it via the sourceElement option.

HTML โ€” Script tag and placeholder div
<!-- 1. Add the SHC script to your page <head> -->
<script src="https://uat.ca.shc.payfacto.cloud/secure-hosted-checkout/v1/browser/shc.js"></script>

<!-- 2. Add a placeholder div where the iframe will appear -->
<div id="shc-fields"></div>
4

  Step 3 โ€” Pass the Token to shc() in the Browser

โ„น๏ธ

Your server returns the session token to the browser as JSON. The browser then calls the global shc() function with the token as the first argument. The second argument is a callback. The callback fires when the cardholder clicks Pay. The third argument is optional and is an object containing browser-side options.

JavaScript โ€” Fetch token from server and call shc()
// Fetch the session token from your server, then call shc()
function getShcToken(done) {
fetch('/your-server/get-shc-token')
.then(r => r.json())
.then(data => done(data.token));
}

getShcToken(shcToken => {
shc(shcToken, response => handleResponse(response));
});

// shc() draws the iframe inside #shc-fields automatically

Third argument โ€” browser-side options

โ„น๏ธ

These options are different from session options โ€” they control how the iframe behaves once it is created. Pass them as the third argument to shc(). Each entry in fields must have both visibility and value or SHC throws an error.

Option Description
sourceElement Override the default shc-fields container. Pass the id of any div on your page.
cardholderName Pre-fill the cardholder name and hide the name field.
fields Object of field overrides. Each key is a field name; each value must have visibility (hidden, disabled, or enabled) and value.
JavaScript โ€” Third argument (browser options)
const options = {
sourceElement: 'my-checkout-div', // use a different container id
cardholderName: 'Jane Doe', // pre-fill and hide the name field
purchaseAmount: 1025, // hidden field, required for 3DS
fields: {
email: { visibility: 'enabled', value: 'jane@example.com' },
postal: { visibility: 'disabled', value: 'M5V 1A1' },
address: { visibility: 'hidden', value: '123 Main St' }
}
};

shc(shcToken, response => handle(response), options);
5

  Step 4 โ€” Handle the Callback Response

โš ๏ธ

Never fulfil an order based on the callback alone. Always verify the HMAC on your server (Step 5) before releasing goods or services.

Successful response (Pay by card)

JSON โ€” Success callback
{
"success": true,
"response": {
"authCode": "754062",
"avs": "U",
"brand": "VISA",
"card": "************4242",
"cvv2Cvc2Status": "M",
"expiry": "1226",
"invoiceNumber": "41343428",
"isoResponseCode": "00",
"issuerMessage": "AP",
"lastFour": "4242",
"name": "Test Card",
"paymentMethod": "VISA CARD",
"purchaseAmount": 500,
"purchaseCurrency": "CAD",
"purchaseDate": "2026-06-23 17:49:16 EDT",
"secureId": "33f19cdc-5b74-456a-8e95-51527c5cc910",
"status": "SUCCEEDED",
"store": {
"address": {
"line1": "123 Some Street",
"city": "Toronto",
"state": "ON",
"postalCode": "A1A 1A1",
"country": "CA"
},
"email": "contact@merchant-store.com",
"name": "Merchant Store",
"phone": "(123) 456-7890",
"website": "www.merchant.com"
},
"token": "c2FLxMZn51DZAMjAM3CkcppWf",
"transactionDescription": "Transaction Description",
"transactionId": "af569819-0125-4979-9dff-a81cda21ebd0",
"transactionType": "PURCHASE",
"type": "CREDIT"
},
"metadata": {
"algorithm": "sha256",
"HMAC": "3928cbccac77e3b66404c37bbfa2ec44dc8f839ed478fcc4637e29f600ec70e4",
"receipt": "",
"timestamp": "2026-06-23 17:49:16 EDT"
}
}

Successful response (Pay by Konek)

JSON โ€” Success callback
{
"success": true,
"response": {
"accountType": "BANK_ACCOUNT_CHEQUING",
"expiry": "1230",
"invoiceNumber": "61341840",
"lastFour": "6457",
"paymentConsentId": "37c3449c-e876-4190-9284-3a744048e8b5",
"paymentMethod": "INTERAC_DIRECT",
"purchaseAmount": 500,
"purchaseCurrency": "CAD",
"purchaseDate": "2026-06-23 17:34:12 EDT",
"secureId": "d2f01954-a644-45b9-b1f3-03930f436af5",
"status": "SUCCEEDED",
"store": {
"address": {
"line1": "123 Some Street",
"city": "Toronto",
"state": "ON",
"postalCode": "A1A 1A1",
"country": "CA"
},
"email": "contact@merchant-store.com",
"name": "Merchant Store",
"phone": "(123) 456-7890",
"website": "www.merchant.com"
},
"transactionDescription": "Transaction Description",
"transactionId": "3ab9cd17-babb-4c53-aa02-23ed4bb0e962",
"transactionType": "PURCHASE",
"type": "INTERAC_DIRECT"
},
"metadata": {
"algorithm": "sha256",
"HMAC": "ee0600b30d0f0c955d40854334fd97245eb3c30d4bebc60668310c83fb221c4c",
"receipt": "",
"timestamp": "2026-06-23 17:34:12 EDT"
}
}
Field Description
successtrue if the transaction succeeded.
response.authCodeAuthorization code from the card issuer. Display on the receipt.
response.statusSUCCEEDED, DECLINED, etc.
response.brandCard brand โ€” Visa, Mastercard, Amex, etc.
response.cardMasked card number, safe to show on a receipt.
response.lastFourLast 4 digits of the card.
response.tokenReusable card token. Store securely for future charges.
response.transactionIdUnique PayFacto transaction identifier.
response.receiptHTML receipt you can display on screen or send by email.
response.purchaseAmountAmount charged, in cents.
response.purchaseCurrencyCurrency of the transaction.
metadata.HMACSHA-256 fingerprint of the response. Verify this on your server before fulfilling the order.
metadata.algorithmHashing algorithm used for the HMAC. Currently always sha256.
metadata.timestampISO-8601 timestamp of the response.

Additional response fields

Field Description
accountTypePay-by-bank bank account type.
avsThe AVS response code. Omitted for Konek.
cvv2Cvc2StatusThe CVV. Valid values are 'M' for PASS and 'N' for FAIL. Omitted for Konek.
emailThe cardholder's e-mail address.
expiryExpiry date of the card.
invoiceNumberPassed invoice number. If no invoice number is passed then one is generated on the fly.
isoResponseCodeISO response code from issuer. Omitted for pay-by-bank transactions.
issuerMessageMessage from the issuer. Omitted for pay-by-bank transactions.
nameCardholder name. Omitted for Konek.
paymentConsentIdVerification ID for pay-by-bank.
paymentMethodCard brand and type. If the transaction is pay-by-bank then the type is INTERAC_DIRECT.
processorAvsResultCodeThe AVS response code returned from the processor. Value varies by card brand and processor. Omitted for Konek.
purchaseDateThe date and time stamp when the transaction was processed.
secureIdThe session ID.
store.address.line1Store address line1.
store.address.cityStore city.
store.address.stateStore state, province, or territory.
store.address.postalCodeStore postal or ZIP code.
store.address.countryStore ISO 3166-1 alpha-2 country code. [CONFIRM: source list named this field "store.address.store" โ€” reproduced here as "country" to match the field name shown in the JSON example]
store.emailStore support contact e-mail address.
store.nameName of the store the merchant account is associated with.
store.phoneStore support contact phone number.
store.websiteStore website.
transactionDescriptionThe value that shows up on the description field of a receipt.
transactionTypeThe type of requested transaction. Valid values are 'PREAUTHORIZATION', PURCHASE, and 'VERIFY'.
typeCard type. Valid values are 'CREDIT' and 'DEBIT'.
algorithmThe algorithm used to generate an HMAC.
HMACThe contents of the response converted to a string and then encrypted using the sha256 algorithm and the merchant account's private API password.
timestampThe time the transaction was generated.
receiptAn HTML receipt that can be displayed on the screen or embedded in an e-mail.

Error response

JSON โ€” Error callback
{
"success": false,
"response": {
"type": "CARD_DECLINED",
"code": "INVALID_NUMBER",
"message": "card number is incorrect"
}
}
Error type Error codes
CARD_DECLINED INVALID_CARD_NUMBER, AVS_DECLINED, CARD_BRAND_NOT_SUPPORTED, CARD_BRAND_NOT_ALLOWED, CVV_DECLINED, RISK_DECLINED, ISSUER_DECLINED, PAYMENT_FORM_NOT_ALLOWED
AMOUNT_DECLINED AMOUNT_TOO_LARGE, AMOUNT_TOO_SMALL
UNAUTHORIZED โ€”
BATCH_CLOSING BATCH_CLOSING
RECAPTCHA_VERIFICATION_FAILED โ€”
TOO_MANY_REQUESTS โ€”
INTERNAL_ERROR โ€” or ISSUER_TIMEOUT
TIMEOUT TIMEOUT

3D Secure error codes

Type Code Meaning
CARD_ERRORNOT_VERIFIEDCould not verify the card number.
CARD_ERRORNOT_AUTHENTICATEDThe issuer rejected the card during 3DS.
DOWNSTREAM_ERRORNOT_VERIFIEDThe 3DS provider is having technical issues.
6

  Step 5 โ€” Verify the HMAC (Server-Side)

โš ๏ธ

Always verify HMACs on your server, never in the browser. The check only proves anything if your API secret stays secret.

โ„น๏ธ

Run JSON.stringify(response.response) โ€” the inner response object, not the full envelope โ€” through HMAC-SHA256 keyed with your API secret. Compare the result against metadata.HMAC. If they differ, discard the response and do not fulfil the order.

JavaScript โ€” HMAC verification
const crypto = require('crypto');

function verifyHmac(shcResponse, apiSecret) {
const computed = crypto
.createHmac(shcResponse.metadata.algorithm, apiSecret)
.update(JSON.stringify(shcResponse.response))
.digest('hex');

return computed === shcResponse.metadata.HMAC;
}

if (!verifyHmac(response, process.env.API_SECRET)) {
throw new Error('HMAC mismatch โ€” do not process this response');
}
// Safe to fulfil the order
7

  Custom Forms

โ„น๏ธ

If the default styling options are not enough, you can build your own form. Wrap it in a <div> with an id, and pass that id to shc() as sourceElement. Give any input the matching SHC field id to activate validation, formatting, and secure submission for that field.

โš ๏ธ

Before SHC copies your custom form into the secure iframe, it strips <script>, <iframe>, <object>, <a> tags, any <input type=submit> or <button type=submit>, the action and method attributes from <form> tags, and all inline JavaScript event handlers. This prevents code injection.

HTML + JavaScript โ€” Minimal custom form
<div id="custom-form">
<form style="display: flex; flex-direction: column; gap: 12px;">
<!-- Hidden name field โ€” value pre-filled -->
<input type="hidden" name="name" value="JOHN DOE" />

<!-- SHC handles validation for fields with matching ids -->
<input id="shc-card-input" name="card" type="text" placeholder="1234 1234 1234 1234" required autocomplete="cc-number" />
<input id="shc-expiry-input" name="expiry" type="text" placeholder="MM / YY" required />
<input id="shc-cvv-input" name="cvv" type="text" placeholder="123" required autocomplete="cc-csc" />

<!-- 3DS hidden fields (required if 3DS is enabled on your account) -->
<input id="purchaseAmount" name="purchaseAmount" type="hidden" value="1025" />
<input id="purchaseCurrency" name="purchaseCurrency" type="hidden" value="CAD" />
<input id="purchaseDate" name="purchaseDate" type="hidden" value="2026-06-01T14:30:00.000Z" />

<button id="shc-pay-button" type="button">Pay</button>
</form>
</div>

<script>
shc(shcToken, response => handle(response), { sourceElement: 'custom-form' });
</script>

3DS hidden fields โ€” required if your account uses 3D Secure

Hidden field id Value format Description
purchaseAmountInteger (minor units)Amount in cents. $9.20 = 920.
purchaseCurrencyStringMust match your merchant account currency. Usually CAD.
purchaseDateISO-8601 extendedDate/time in YYYY-MM-DDTHH:MM:SS.000Z format.

Sending messages from custom buttons

Add a data-message attribute to any button inside your custom form. When clicked, SHC sends the message to the parent page. Listen with a message event listener:

JavaScript โ€” Custom button messages
window.addEventListener('message', event => {
console.log(event.data); // { message: 'whatever you put in data-message' }
});
8

  Styling the Form

โ„น๏ธ

SHC reads a <style> tag with the id shc-style and injects it into the iframe. This is the only way to style the iframe โ€” normal CSS on your page does not reach inside it.

HTML โ€” Style block location
<style id="shc-style">
/* Your custom styles here โ€” applied inside the SHC iframe */
</style>

Common style recipes

CSS โ€” Hiding icons, removing borders, showing card brands
/* Hide all field icons */
.confidante-field-img svg { visibility: collapse; }

/* Hide just the card icon */
#shc-card-img { visibility: collapse; }

/* Move icons to the right side */
.confidante-field-img svg { left: auto; right: 5px; }
.confidante-field-validate svg { display: none; }

/* Show supported card brands (hidden by default) */
#shc-card-brands { display: flex; justify-content: center; }

/* Show the session timeout countdown (hidden by default) */
#shc-countdown { display: block; }

/* Remove the outer border */
#target { border: 0; padding: 0; padding-top: 20px; }
CSS โ€” Full re-skin (underline style)
/* Full re-skin โ€” underline style with a custom button color */
#shc label {
margin-bottom: 15px;
position: relative;
border: 0;
border-bottom: 1px solid #ddd;
box-shadow: none;
}
#shc input:not(.shc-hidden-fields) {
width: 100%;
padding: 20px 0 10px 0;
margin-top: 20px;
border: none;
outline: none;
}
#shc span.confidante-field-span {
position: absolute; top: 0; left: 0;
transform: translateY(40px);
font-size: 0.825em;
transition-duration: 400ms;
}
#shc button {
padding: 15px 0;
margin-top: 20px;
background: #BC955C;
color: #fff;
border: 1px solid #BC955C;
cursor: pointer;
border-radius: 3px;
}
#shc label:focus-within > span.confidante-field-span,
#shc input:not(:placeholder-shown) + span.confidante-field-span {
color: #BC955C;
transform: translate(32px, 12px);
}

Available CSS ids and classes

Selector What it targets
.confidante-field-labelEach field label wrapper
.confidante-field-imgIcon area at the start of each field
.confidante-field-inputThe input element inside each field
.confidante-field-spanThe label text (animates on focus)
.confidante-field-validateValidation icon area at the end of each field
.confidante-field-maskCard number masking overlay
#shc-{name}-inputTarget a specific field, e.g. #shc-card-input
#shc-pay-buttonThe Pay button
#shc-card-brandsCard brand logos row (hidden by default)
#shc-countdownSession timeout countdown (hidden by default)
#targetThe outer iframe container / border
โ˜…

  Listening for Events and Sending Commands โ€” shcMessenger

โ„น๏ธ

shc() returns an shcMessenger object. Use .on() to listen for events from the form, and the send methods to control the form programmatically.

JavaScript โ€” shcMessenger events and commands
let shcMessenger;

getShcToken(shcToken => {
shcMessenger = shc(shcToken, response => handle(response));
});

// โ”€โ”€ Listen for form events โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
shcMessenger.on('shc-card-brand', data => console.log('Brand:', data.brand));
shcMessenger.on('shc-ready-to-submit', data => console.log('Ready:', data.isReady));
shcMessenger.on('shc-submit', () => console.log('Form submitted'));
shcMessenger.on('shc-3ds', () => console.log('3DS started'));
shcMessenger.on('shc-3ds-challenge', () => console.log('3DS challenge shown'));
shcMessenger.on('shc-response', data => console.log('Result:', data));
shcMessenger.on('shc-reset', () => console.log('Form reset'));

// โ”€โ”€ Send commands to the form โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
shcMessenger.sendBlockSubmit(); // disable Pay button programmatically
shcMessenger.sendUnblockSubmit(); // re-enable Pay button
shcMessenger.sendSubmit(); // submit the form (ignored if fields invalid)
shcMessenger.sendCancel(); // destroy the form entirely
Event When it fires Payload
shc-card-brandAs soon as the card brand is detected from the number entered.{ brand: "Visa" }
shc-ready-to-submitAll fields are valid and the Pay button is enabled โ€” or re-fires with isReady: false if a field becomes invalid again.{ isReady: true }
shc-submitThe cardholder pressed Pay and the form was submitted.โ€”
shc-3dsThe 3DS process has started.โ€”
shc-3ds-challengeThe 3DS challenge step is being shown to the cardholder.โ€”
shc-responseThe same data as the main callback. Useful if you need to react in more than one place.response object
shc-resetA recoverable error occurred (often a digital wallet issue) and the form has reset itself.โ€”
โ˜…

  Helper Endpoints

โ„น๏ธ

If you need to retrieve the result of a session after the callback fires โ€” for example, to reconcile server-side โ€” use these GET endpoints. Plug the secureId from the response into the URL.

Endpoints โ€” Session result lookup
// All base URLs: https://uat.ca.shc.payfacto.cloud/secure-hosted-checkout/v1

GET /sessions/{secureId}/response // Full response from a session
GET /sessions/{secureId}/transaction // Underlying transaction data
GET /sessions/{secureId}/receipt // HTML receipt (if generated)
Endpoint What it returns
GET /sessions/{secureId}/responseThe full response object from the session.
GET /sessions/{secureId}/transactionThe underlying transaction data.
GET /sessions/{secureId}/receiptThe HTML receipt, if one was generated.
โ˜…

  3D Secure Test Cards (UAT)

โ„น๏ธ

Use these card numbers in UAT to test every 3DS outcome. For all scenarios: Cardholder Name = Test Card, CVV = 123 (or 1234 for Amex).

Scenario Visa Mastercard Amex Expiry
Frictionless โ€” no 3DS4100000000000100510000000000010734000000000010808/25
Frictionless โ€” with 3DS4100000000600008510000000060000534000000060000608/26
3DS challenge (password: 123456)4100000000005000510000000000500734000000000500808/26
Failed 3DS (password: 111111)4100000000300005510000000030000234000000030000308/26
Authentication unavailable4100000000400003510000000040000034000000040000108/26
Authentication rejected4100000000500000510000000050000734000000050000808/26
โš ๏ธ

ignoreInitAuthTimeout

Before 3DS starts, SHC collects device information. This can occasionally time out, failing the transaction. Setting ignoreInitAuthTimeout: true in the session request tells SHC to continue anyway. Use only if your failure rate from this timeout is high โ€” bypassing it may shift fraud liability to you.