Set PIN Widget

Using this widget you can add set pin to your website. It requires a Synctera widget vault token to
initialize it with. The user will have 5 minutes to complete the pin submission.

The Set Pin Widget injects a configurable set of iframes into your app, allowing your user to
securely enter a PIN in a PCI compliant manner. This helps remove some (but not all) of the PCI
compliance requirements you would otherwise need to handle.

The iframes injected by Synctera allow you complete control over the styling over the widget using
only CSS.

Quick start

  1. Add <script src=""></script>
    to your page.
  2. Embed the Set Pin widgets when and as needed (both the Controller and Confirm Set Pin widgets are
    <!-- Controller widget: -->
    <set-pin id="synctera-set-pin" token="[insert the token here]"></set-pin>
    <!-- Confirm widget: -->
    <set-pin isConfirm></set-pin>
  3. Add your own submit button under the Set Pin widgets.
  4. Listen for the validity event on the Controller Set Pin widget (the one with the id):
    const setPinWidget = document.getElementById('synctera-set-pin');
    setPinWidget.onValidity = (e, isValid) => button.disabled = !isValid;
  5. Have the button call setPinWidget.submit() to submit the pin.
    It returns a promise, which
    resolves if the pin submission was successful, or rejects if it failed.

Basic Example:

    <script src=""></script>
    <set-pin id="synctera-set-pin" token="[insert the token here]"></set-pin>

    <div>Confirm PIN</div>
    <set-pin isConfirm></set-pin>
    <button id="synctera-submit-button" disabled>Submit</button>
    <scirpt type="application/javascript">
       const setPin = document.getElementById('synctera-set-pin');
       const button = document.getElementById('synctera-submit-button');
       setPin.addEventListener('validity', () => button.disabled = !setPin.isValid);

       button.addEventListener('click', () => {
           .then(() => alert('Pin set!'))
           .catch((e) => console.error('Pin failed to submit', e));

Styling: If you add a custom class name to the Set Pin widget, custom styling will be activated
(see Custom Styling).
By default, the widgets use the browser's inbuilt
styling for the Set Pin input text fields (note that without custom styling the widget iframe is
4px bigger than the input field inside to allow for any browser outline effects).

See Custom Styling Example for a complete working example.


Browser support

The widgets work on both mobile and desktop, and we ensure support for all modern browsers. It also
works in many older browsers, which we try to support where feasible.

BrowserMinimum tested version
Chrome29 (2013)
Firefox27 (2014)
Edge79 (2020), 15 (2017)
Safari12 (2018)
Opera20 (2014)
IE11 (2013)

Set Pin flow


Fetch widget token from backend → Render widgets with token → Listen for "validity" event → User
submits controller widget → Widget submits PIN to Synctera → And returns success or failure →
Widget auto-destroys

Note: It is recommended to also add 'load' and 'error' listeners for UI management
(see Widget API below).

The flow for using the widgets:

  1. On your backend, request a widget token from Synctera for the particular card you wish to use.
  2. Make sure the Synctera Set Pin widget Javascript has been added to your website.
  3. Render the two specialized Set Pin HTML tags as above, setting the token attribute, and your own
    button to submit the widget.
  4. You can style these fields however you like (as explained below in Custom Styling).
  5. Listen for the "validity" event from the controller widget.
  6. When valid, enable your button.
  7. When the button is clicked, call the submit() function on the controller widget.
  8. The submit function will submit the pin.
  9. It also returns a promise which resolves when successful, or rejects when there's a failure.
  10. Once submitted, the widgets will auto-destroy and you can now remove them.

Widget API

Element Attributes:

tokenwidget token ID-Required
Incompatible with: isConfirm
<set-pin token="[widget token from Synctera]" ...>
isConfirmtrue (Present)
false (Omitted)
Incompatible with: token
<set-pin token="[widget token from Synctera]" ...>
<set-pin isConfirm></set-pin>
classclass name (string)OmittedOptional
If present, it will activate custom styling
<set-pin ... class="customStyles" ...>


NameCalled when...Why?addEventListeneron
loadThe widgets have are fully loadedFor best user experience, show the widgets only when this event is triggeredsetPinEl.addEventListener('load', () => ...);setPinEl.onLoad = () => ...
validityThe widgets are now (in)validOnly allow submit() to be called when valid
To get the status, check the "isValid" property
setPinEl.addEventListener('validity', () => {
␠ buttonEl.disabled = !setPinEl.isValid;
setPinEl.onValidity = (e, isValid) => {
␠ buttonEl.disabled = !isValid;
errorThe widgets failed to loadNon-recoverable error states
To get the error, check the "error" property
setPinEl.addEventListener('error', () => {
␠ console.log(setPinEl.error);
setPinEl.onError = () => {
␠ console.log(setPinEl.error);
successThe widget submitted successfullyKnow when the widget is done and the pin was saved
Alternatively: The "submit()" method returns a promise resolving when done
setPinEl.addEventListener('success', () => ...);setPinEl.onSuccess = () => ...
failureThe widget submitted unsuccessfullyKnow if the pin was not saved and the user will need to try again
Alternatively: The "submit()" method returns a promise rejecting on failure
To get the failure, check the "error" property
setPinEl.addEventListener('failure', () => {
␠ console.log(setPinEl.error);
setPinEl.onFailure = (e, errorDetails) => ...


NameValue / ParametersDefault / ReturnsDetailsExample
falsetrue if both fields are valid and form is ready to submitsetPinEl.isValid
error: *
undefinedError details if an error or failure occurssetPinEl.error
submit()No parametersReturns: PromiseSubmits the PIN to Sycntera's servers
Alternatively: The widget fires "success" and "failure" events
␠ .then(// success)
␠ .catch(// error)

Custom styling


  • Add a class name to set-pin tags to activate Custom Styling: <set-pin class='customStyles' ...
  • Style as desired (i.e. border), but Font Styles are special
  • For pseudo-selectors, e.g. :hover, :focus, etc - use pseudo-attributes instead:
    Example: .customStyles[_hover] { border: 1px solid #616161; }
  • Font styles are auto-forwarded, but only certain values are allowed (see below)
  • Optional: You can use .customStyles[_required] to show the widgets once they have loaded

By default, without custom styling, the widgets use the browser's inbuilt styling for the Set Pin
input text fields (note that the widget iframe is 4px bigger than the input field inside to allow
for any browser outline effects).

You may want to style the widget to match your webapp design or to add things like placeholders.
This can be done entirely through css styling. When a class attribute is added to the widget,
the 4px spacing is removed, the input box styling is stripped and the background is made transparent
such that any elements placed behind the widget will be visible (i.e. a placeholder element). The
text input is set to always take up 100% of the height and width of the iframe, and likewise for the
iframe in the widget tag.

Input text field pseudo selectors such as :hover, :focus and :blank are indirectly supported
through css-like attributes instead of actual pseudo selectors. As the user hovers, focuses and
types in the field, attributes will be added/removed on the set-pin tag automatically. Supported
"pseudo attributes" are inspired from:

Where customStyles is the class name added to widget tags, e.g. <set-pin class='customStyles'...:

Supported selectorInput field equivalentDescription
.customStyles[_required]input:requiredTrue as soon as the widget has rendered
(Useful to know when the widget is ready to be shown)
.customStyles[_hover]input:hoverTrue when the field is hovered over by the mouse
.customStyles[_focus]input:focusTrue when the field is focused
.customStyles[_active]input:activeTrue when the mouse or touch is pressing down on the field
.customStyles[_blank]input:blankTrue when there is no text in the field
.customStyles[_invalid]input:invalidTrue when the text in the field is not yet valid
.customStyles[_valid]input:validTrue when the text in the field is now valid. For confirm, also only if matching controller
.customStyles[_user-invalid]input:user-invalidLike :invalid, but only after the user first interacted with the field
.customStyles[_user-valid]input:user-validLike :valid, but only after the user first interacted with the field
.customStyles[_out-of-range]input:out-of-rangeTrue when the text in the field is less than four numbers
.customStyles[_in-range]input:in-rangeTrue when the text in the field is now four numbers

Note that for the confirm widget, the invalid and valid are dependent upon the value matching
the controller widget's value. However, out-of-range and in-range are not.

See Font Styles for information on acceptable font style values.


Custom Styling Example:

         .ct {
            /* Setting the container to relative allows us to control the placement of absolutely positioned children */
            position: relative;
            margin: 5px;
         .customStyles {
            /* Set on the widget <set-pin> tag */
            box-sizing: border-box;
            border: 1px solid #b7b7b7;
            width: 193px;
            height: 41px;
            border-radius: 4px;
            padding: 0 8px;
         /* Rather than using pseudo selectors (e.g. .customStyles:hover), a "pseudo attribute" is
          * added to the set-pin tag, you can select using attribute selectors like this */
         .customStyles[_hover] {
            border: 1px solid #616161;
         .customStyles[_focus] {
            border: 2px solid #2564eb;
            padding: 0 7px;
         .customStyles[_disabled] {
            background: #d3d3d3;
         .customStyles[_user-invalid] {
            border-color: #e31b0c;
         .pin-placeholder {
            /* These fonts will automatically be sent to the iframe (after sanitization) */
            font-family: Arial, sans-serif;
            font-size: 1rem;
            line-height: 1.4375em;
            font-weight: 400;
            color: rgba(0, 0, 0, 0.87);
            letter-spacing: 0.15px;
         .pin-placeholder {
            /* You can absolutely position a placeholder behind the set-pin tag */
            position: absolute;
            display: none;
            top: 9px;
            left: 10px;
            color: rgba(0, 0, 0, 0.4);
            z-index: -1;
         .customStyles[_blank] + .pin-placeholder {
            /* This will show the placeholder if the field is blank */
            display: block;
         .customStyles[_focus] + .pin-placeholder {
            /* But hide the placeholder again when the set-pin tag indicates it has focus */
            display: none;
         /* Hide the container until the component is ready to show */
         .ct:has(.customStyles:not([_required])) {
            display: none;
      <!-- Remove "customStyles" to see default styling -->
      <div class="ct">
         <set-pin id="synctera-set-pin" class="customStyles" token="[insert the token here]"></set-pin>
         <div class="pin-placeholder">PIN</div>
      <div class="ct">
         <set-pin class="customStyles" isconfirm></set-pin>
         <div class="pin-placeholder">Confirm PIN</div>
      <div class="ct">
         <button id="synctera-submit-button" disabled="">Submit</button>
         var setPin = document.getElementById('synctera-set-pin');
         var button = document.getElementById('synctera-submit-button');
         setPin.addEventListener('load', () => console.log('LOADED'));
         setPin.addEventListener('validity', () => button.disabled = !setPin.isValid);
         button.addEventListener('click', () => {
               .then(() => alert('Pin set!'))
               .catch((e) => console.error('Pin failed to submit', e));

Font Styles

You can style the tag as you see fit (border, background, etc). Css font properties
are handled in a special way to forward them to the iframe for styling the input, in a secure and
sanitized manner. As such only certain css font properties are supported and only with certain
values (all other values may be ignored, if they work they are not guaranteed across all browsers):

Font css property nameAllowed values
colorrgb(<R>, <G>, <B>) or rgba(<R>, <G>, <B>, <Alpha>)
font-familyOne of:
courier: Courier New, Courier, Lucida Console, Lucida Sans Typewriter, monospace
arial: Arial, Helvetica, sans-serif
georgia: Georgia, Times New Roman, Times, serif
helvetica: Helvetica, Arial, sans-serif
lucida: Lucida Console, Lucida Sans Typewriter, Courier New, Courier, monospace
times: Times New Roman, Times, Georgia, serif
tahoma: Tahoma, Verdana, sans-serif
verdana: Verdana, Tahoma, sans-serif
font-weight<N>00 (a number, not 'bold' etc)

Note that with the font-family property, we look for a keyword in the first font in the comma
seperated list of fonts you supply. If there's a match, we use the font-family strings in the above
table and not the font-family specified (for security). The above list of fonts are designed to
ensure maximum coverage across different operating systems for similar style fonts. Custom fonts are
not supported at this time.

Typescript type files

The following type definitions are available for Set Pin: