• Organism
  • CORE COMPONENT
  • Taxonomic name: O-WALLET
  • Extension: PAYMENTS
  • Added on: v3.4.0 (08/06/18)
  • Updated on: v5.11.0 (07/09/21)

Wallet

Wallet allows users to add and use payment methods within their account to make payments on Abeille Assurances websites.


Design and usage

At the moment, core Framework functionality supports only credit/debit cards. Other payment methods will be added in future releases, but the code is flexible enough to allow teams to add their own sections in the meantime if so desired.

Structure

  • When a user pays with an existing card they will make a selection from their stored cards
  • Stored cards use a radio button to allow the user to make a selection
  • When the user selects a stored card, the card will expand and surface the security code question
  • Once the user has entered the security they will be able to make a payment
  • The CTA in the wallet payment screen will generate the amount needed to pay and display in this form; "Pay £xxx.xx"
  • On mobile devices when the user taps inside of the security code entry field the numerical keyboard will be surfaced
  • The security code hint will be generated based on whichever type of card the user has, eg. Visa etc (3 digits) or Amex (4 digits)

Labels

  • The card type and final 4 digits will act as the label to the stored cards
  • Security code text field should be consistent with the existing pattern for form UI

Errors

  • If a user enters the wrong security code an error will be displayed
  • If the stored cards cannot be loaded for whatever reason the payment UI should be displayed alongside a notification card error

Hover and selection state

  • The text entry field should adopt the focus state as per existing pattern

Security

  • Auto-complete must be disabled for all customer data sensitive fields, eg. card number and CVV. This will not prevent browser auto-fill from working if the user requests the browser to use this feature, but will prevent the browser remembering any value input if on a shared machine

Non-JS requirements and considerations

  • When the browser is running without JS, the payment type question is removed and replaced with H2 headings for each payment type

Accessibility and screen readers

  • Aria-invalid must be used to indicate when an error has occurred with this element
  • Aria-describedby must be used to link any help, instructional and/or error text with the element/form fields

Placement

  • Stored cards can sit within a payment page and should only ever be placed on a white background

Use case and exception scenarios

  • To allow users to pay with a stored card

Examples

Without previously stored cards

Interactive example

Code example

<div class="o-wallet" data-module="o-wallet" data-options="...">
	<div class="o-wallet-header">
		<h2 class="a-heading a-heading--2 a-heading--light">...</h2>
		<h3 class="a-heading a-heading--3 a-heading--light">...</h3>
		<p>...</p>
	</div>	<div class="o-wallet-selection">
		<fieldset class="m-form-row m-form-row--full-width o-radio-card-select">
			<legend>...</legend>
			<div class="m-form-row__content">
				<div class="l-columns l-columns--2-medium l-columns--2-large">
					<label class="l-columns__column m-card m-card-select a-radio" for="...">
						<input class="a-radio__input m-card-select__input" id="..." name="..." type="radio" value="..." checked="checked" />
						<span class="a-radio__label m-card-select__label"><span class="a-radio__label-inner">...</span></span>
						<span class="m-card-image m-card-image--icon-below">
							<img src="..." decoding="async" width="..." height="..." alt="..." />
						</span>
						<span class="a-radio__ui m-card-select__ui"></span>
					</label>
					...
				</div>
			</div>
		</fieldset>
	</div>	<fieldset class="m-form-row m-form-row--full-width o-wallet-card" id="...">
		<legend class="a-label o-wallet__section-heading">...</legend>
		<div class="m-form-row__content">
			<h3 class="a-heading a-heading--3 a-heading--light">...</h3>
			<ul class="a-list-plain">
				<li class="o-wallet-card-item">
					<input class="o-wallet-card-item__input" type="radio" name="..." id="..." value="..." />
					<label class="a-radio o-wallet-card-item-upper" for="...">
						<span class="a-radio__label"></span>
						<h3 class="a-heading a-heading--3 o-wallet-card-item-upper__name [ Modifier ]">...</h3>
						<div class="o-wallet-card-item-upper__text">
							<p>...</p>
							<p>...</p>
						</div>
					</label>
					<div class="o-wallet-card-item-lower">
						<label class="a-label" for="...">...</label>
						<div class="m-card-security-code">
							<div class="m-card-security-code__input">
								<input type="text" id="..." class="a-textbox a-textbox--3-character" pattern="\d*" inputmode="numeric" maxlength="3" autocomplete="off" aria-describedby="..." />
							</div>
							<p class="m-card-security-code__text" id="...">...</p>
						</div>
					</div>
				</li>
				...
				<li class="o-wallet-card-item">
					<input class="o-wallet-card-item__input" type="radio" name="..." id="..." value="..." />
					<label class="a-radio o-wallet-card-item-upper" for="...">
						<span class="a-radio__label"></span>
						<h3 class="a-heading a-heading--3 o-wallet-card-item-upper__name">...</h3>
					</label>
					<div class="o-wallet-card-item-lower o-wallet-add-card">
						<fieldset>
							<legend>...</legend>
							<div class="m-form-row m-form-row--full-width">
								<label class="a-label" for="...">...</label>
								<div class="m-form-row__content">
									<input type="text" id="..." pattern="([0-9]+\s?)*" inputmode="numeric" max-length="21" class="a-textbox a-textbox--creditcard" autocomplete="off" data-module="m-credit-card" />
								</div>
							</div>							<fieldset class="m-form-row m-form-row--full-width">
								<legend class="a-label">...</legend>
								<div class="m-form-row__content">
									<ul class="m-form-row-group">
										<li class="m-form-row-group-item">
											<label class="u-hidden--visually" for="...">...</label>
											<span class="a-dropdown">
												<select class="a-dropdown__select" id="..." autocomplete="off">
													<option>MM</option>
													<option value="...">...</option>
													...
												</select>
												<span class="a-dropdown__ui"></span>
											</span>
										</li>
										<li class="m-form-row-group-item">
											<label class="u-hidden--visually" for="...">...</label>
											<span class="a-dropdown">
												<select class="a-dropdown__select" id="..." autocomplete="off">
													<option>YYYY</option>
													<option value="...">...</option>
													...
												</select>
												<span class="a-dropdown__ui"></span>
											</span>
										</li>
									</ul>
								</div>
							</fieldset>							<div class="m-form-row m-form-row--full-width">
								<label class="a-label" for="...">...</label>
								<div class="m-form-row__content">
									<p class="m-form-row__instruction" id="...">Please enter the name as it appears on the card</p>
									<input type="text" id="..." class="a-textbox" aria-describedby="..." />
								</div>
							</div>							<div class="m-form-row m-form-row--full-width">
								<label class="a-label" for="...">Security code</label>
								<div class="m-form-row__content">
									<div class="m-card-security-code">
										<div class="m-card-security-code__input">
											<input type="text" id="..." class="a-textbox a-textbox--3-character" pattern="\d*" inputmode="numeric" maxlength="3" autocomplete="off" aria-describedby="..." />
										</div>
										<p class="m-card-security-code__text" id="...">...</p>
									</div>
								</div>
							</div>							<div class="m-form-row m-form-row--full-width">
								<label class="a-checkbox a-checkbox--standalone" for="...">
									<div class="m-form-row__content">
										<input class="a-checkbox__input" type="checkbox" id="..." value="..." />
										<span class="a-checkbox__label"><span class="a-checkbox__label-inner">...</span></span>
										<span class="a-checkbox__ui"></span>
									</div>
								</label>
							</div>
						</fieldset>
					</div>
				</li>
			</ul>
		</div>		<div class="m-form-row m-form-row--full-width">
			<div class="m-form-row__content">
				<ul class="m-button-group u-margin--bottom-none">
					<li class="m-button-group__item"><button class="a-button a-button--primary" type="submit">...</button></li>
					<li class="m-button-group__item"><button class="a-button a-button--secondary">...</button></li>
				</ul>
			</div>
		</div>
	</fieldset>	<div id="...">
		<h2 class="a-heading a-heading--2 a-heading--light o-wallet__section-heading">...</h2>
		...
	</div>
	...
</div>
Example list item with security code error
<li class="o-wallet-card-item">
	<input class="o-wallet-card-item__input" type="radio" name="..." id="..." value="..." />
	<label class="a-radio o-wallet-card-item-upper" for="...">
		<span class="a-radio__label"></span>
		<h3 class="a-heading a-heading--3 o-wallet-card-item-upper__name [ Modifiers ]">... card ending in: ...</h3>
		<div class="o-wallet-card-item-upper__text">
			<p>Expiry date: .. / ..</p>
			<p>...</p>
		</div>
	</label>
	<div class="o-wallet-card-item-lower is-error">
		<label class="a-label" for="...">Security code</label>
		<p class="m-form-row__error-message" id="..."><span class="u-hidden--visually">Error: </span>Incorrect security code</p>
		<div class="m-card-security-code">
			<div class="m-card-security-code__input">
				<input type="text" id="..." class="a-textbox a-textbox--3-character" pattern="\d*" inputmode="numeric" maxlength="3" autocomplete="off" aria-invalid="true" aria-describedby="..." />
			</div>
			<p class="m-card-security-code__text" id="...">For ..., look for the ... digits on the ...</p>
		</div>
	</div>
</li>
Example disabled list item (expired card)
<li class="o-wallet-card-item is-disabled">
	<input class="o-wallet-card-item__input" type="radio" name="..." id="..." value="..." disabled />
	<label class="a-radio o-wallet-card-item-upper" for="...">
		<span class="a-radio__label"></span>
		<h3 class="a-heading a-heading--3 o-wallet-card-item-upper__name [ Modifiers ]">... card ending in: ... has expired</h3>
		<div class="o-wallet-card-item-upper__text">
			<p>Expiry date: .. / ..</p>
			<p>...</p>
		</div>
	</label>
	<p class="is-disabled-override">You need to pay with a different card. To delete this card, please visit your profile.</p>
</li>
Example credit card fieldset for no saved cards (add card only)

This markup can also be used as a fallback if no cards can be found - a notification card can be added as the first item within the m-form-row__content div containing the appropriate message.

<fieldset>
	<legend>Pay with a new card</legend>
	<div class="m-form-row m-form-row--full-width">
		<label class="a-label" for="...">Card number</label>
		<div class="m-form-row__content">
			<input type="text" id="..." name="..." pattern="([0-9]+\s?)*" inputmode="numeric" max-length="21" class="a-textbox a-textbox--creditcard" autocomplete="off" data-module="m-credit-card" />
		</div>
	</div>	<fieldset class="m-form-row m-form-row--full-width">
		<span class="a-label"><legend>Expiry date</legend></span>
		<div class="m-form-row__content">
			<ul class="m-form-row-group">
				<li class="m-form-row-group-item">
					<label class="u-hidden--visually" for="...">Month</label>
					<span class="a-dropdown">
						<select class="a-dropdown__select" name="..." id="..." autocomplete="off">
							<option>MM</option>
							<option value="...">...</option>
							...
						</select>
						<span class="a-dropdown__ui"></span>
					</span>
				</li>
				<li class="m-form-row-group-item">
					<label class="u-hidden--visually" for="...">Month</label>
					<span class="a-dropdown">
						<select class="a-dropdown__select" name="..." id="..." autocomplete="off">
							<option>YYYY</option>
							<option value="...">...</option>
							...
						</select>
						<span class="a-dropdown__ui"></span>
					</span>
				</li>
			</ul>
		</div>
	</fieldset>	<div class="m-form-row m-form-row--full-width">
		<label class="a-label" for="...">Name on card</label>
		<div class="m-form-row__content">
			<p class="m-form-row__instruction" id="...">Please enter the name as it appears on the card</p>
			<input type="text" id="..." name="..." class="a-textbox" aria-describedby="..." />
		</div>
	</div>	<div class="m-form-row m-form-row--full-width">
		<label class="a-label" for="...">Security code</label>
		<div class="m-form-row__content">
			<div class="m-card-security-code">
				<div class="m-card-security-code__input">
					<input type="text" id="..." name="..." class="a-textbox a-textbox--3-character" pattern="\d*" inputmode="numeric" maxlength="3" autocomplete="off" aria-describedby="..." />
				</div>
				<p class="m-card-security-code__text" id="...">For most cards, look for the three digits on the back</p>
			</div>
		</div>
	</div>	<div class="m-form-row m-form-row--full-width">
		<label class="a-checkbox a-checkbox--standalone" for="...">
			<div class="m-form-row__content">
				<input class="a-checkbox__input" type="checkbox" name="..." id="..." value="..." />
				<span class="a-checkbox__label"><span class="a-checkbox__label-inner">Save this card</span></span>
				<span class="a-checkbox__ui"></span>
			</div>
		</label>
	</div>
</fieldset>

With previously stored cards

Example with error states

Development and test

Notes for developers

Basic setup

The data-options field alongside the data-module="o-wallet" tag should be completed with a comma separated list of the IDs of the payment options available, for example data-options="credit-card,installments,other".

Error states for the in-line security code on saved cards require the is-error class to be placed on a specific div and slightly amended markup; please see the example below for reference. Error states for the Pay with a new card section use standard form row error markup and classes.

Input types

Care should be taken when choosing input field types for various uses, particularly with regard to number entry, as making the wrong selection could prevent users from inputting the required details into a form. For further information, see the form introduction page.

Aria usage for errors and help text

To aid screen reader users in completing forms without error and also with understanding and fixing any errors that have occured, some additional aria attributes are now recommended to be applied across all form fields/rows.

All form row help, explanatory text, form row instructions and form row errors must have a unique id assigned to them and be appropriately linked to either the individual form input or the form row using aria-describedby. This ensures that the help text is read out immediately on focus of the form input/fieldset. When there is more that one item that requires referencing within the aria-describedby attribute the id values must be supplied in the form of a space separated list. For example if there is an error with id of 'err1' and help text with id of 'help1' the value for the aria-describedby attribute would be 'err1 help1'.

All inline form error messages should start with the hidden text 'Error: ' so that when read by out by a screen reader it is immediately clear that the text being read is an error message.

All erroring form fields (or fieldset if a group or radios/checkboxes) must have aria-invalid="true" set on them, ensuring that when a user focus' on an errored form field, the field will be announced as invalid, so the user is then aware that there in an issue that requires fixing.

When linking help or errors, or even adding aria-invalid to the markup careful consideration must be taken as to where it is most appropriate to apply the value. The following rules should be used to decide where the attribute(s) should be applied.

  • Single form field - All attributes should be applied directly to that form field
  • Group of form fields but only one in error or help text applies to only one - Apply the attribute(s) to the only field in error or where the help text applies
  • Group of form field where all in error or the help text applies to all - Apply the attribute(s) to the groups surrounding fieldset (form row)

There may situations where more than one of these rules applies to the overall form row for example a date range where the instruction text applies to both fields so should be linked to the fieldset for the date range but one of the two fields is in error so the error should be linked to just that field not to the fieldset.

Important messages

To help those using assistive technologies, make sure that success/error messages which need to be announced to users have their aria attributes updated to role="alert" and/or aria-live="assertive".

Changing locale settings

Locale settings will need to be updated to include appropriate translations for the security check number messages.

Within the default message the %s is replaced with the card name once detected.

en: {
	mCreditCard : {
		default : 'For %s, look for the three digits on the back',
		noMatch : 'Look for the three digits on the back',
		amex : 'For American Express, look for the four digits on the front',
	},
},

For more information on this, see our page on how to change locale settings within JavaScript.

Card types

Card type
The issuing network
Length
Number of allowable digits
IIN ranges
The card number will always begin with…
Spacing pattern
How the digits and spaces are printed on the card
Security code length
American Express 15 34, 37 4-6-5 4
China UnionPay 16-19 62 4-4-4-4 3
Dankort 16 5019 4-4-4-4 3
Diners Club
Carte Blanche, International, United States and Canada
14, 16 300-305, 309, 36, 38-39 4-6-5 3
Discover 16 6011, 622126-622925, 644-65 4-4-4-4 3
JCB 16 3528-3589 4-4-4-4 3
Maestro 12-19 50, 56-58, 6 4-4-4-4-4 3
MasterCard 16 51-55, 2221-2720 4-4-4-4 3
Mir 16-19 22-2204 4-4-4-4 3
RuPay 16 60, 6521 4-4-4-4 3
Visa 13-19 4 4-4-4-4 3
Visa Electron 16 4026, 417500, 4405, 4508, 4844, 4913, 4917 4-4-4-4 3

Note: Ranges shown for the IIN are inclusive of the upper and lower limits.

The order these cards are tested in for a match is: Visa Electron, Visa, MasterCard, American Express, Diners Club, JCB, UnionPay, RuPay, Discover, Dankort, Maestro, Mir

Controling the credit card textbox programatically

Credit card number textbox can be controlled programatically to set and clear the value. The following methods are available for use:

Method Description
setValue( '4444 3333 2222 1111' ) Sets the value of the textbox to the value passed in. This value can be provided both unformatted or pre-formatted.
clear() Clears the value of the textbox.

For example:

document.addEventListener( 'frameworkready', function () {
	FRAMEWORK.require([ 'blocks/m-credit-card' ], function ( mCreditCardModule ) {
		var creditCardEl = document.querySelector( '#inputId' ),
			creditCardInstance = mCreditCardModule.initInstance( creditCardEl );		creditCardInstance.setValue( '4444 3333 2222 1111' );
	});
});

Extension component

This component forms part of the 'forms-extended-a' and 'payments' extension and so requires additional stylesheets to be loaded in order to be used; include the following code in the header of your page to attach the relevant additional stylesheets:

<link media="all" href="./assets/[ theme type ]/[ release version ]/[ organisation ]/[ theme name ]/css/forms-extended-a.css" rel="stylesheet" />
<link media="all" href="./assets/[ theme type ]/[ release version ]/[ organisation ]/[ theme name ]/css/payments.css" rel="stylesheet" />;

Available image options for select payment type

The following table gives you a quick overview of the payment option images available in the icon library, however feel free to use your own.

<img src="./v.2023.01/shared/assets/themes-icons/v2/[ organisation ]/[ theme name ]/functional/payment-types/[ card type ].svg" decoding="async" width="..." height="..." alt="..." />

An example of the full image path would be /assets/themes-icons/v2/aviva/default/functional/payment-types/visa.svg.

Payment types Image Filename
Credit or debit card credit-card-bw.svg
Apple Pay apple-pay-bw.svg
Direct Debit direct-debit-bw.svg
PayPal paypal-bw.svg
UOB uob-bw.svg

Notes for testers

  • Not all the input types are understood by all browsers so they fall back to being a standard text input. This means that they may allow the entry of invalid characters, any invalid entries should be highlighted when the form is submitted like any other errors.
  • Ensure that all form fields have a correctly associated label. This not only provides context to each field for assistive technology users but also a larger clickable area to select the field. Clicking/pressing on a label for a single input field should cause the input to gain focus.
  • Ensure that autofill is not available for sensitive fields such as password, card/account number etc.
  • Users should be able to copy and paste if necessary.
  • If the field has been restricted in length the maximum number of characters allowed must also be restricted. Note: many browsers will not do this for number field and older browser will not do this at all.

Classes overview

The following table gives you a quick overview of the CSS classes that can be applied.

Class Outcome Required Applied to Comments
.o-wallet Base style wrapping container of wallet organism Yes div Defines styling for standard form elements when used within wallet
.o-wallet-header Base style wrapping header of wallet organism Yes .o-wallet > div  
.o-wallet-selection Base style wrapping payment option segmented controls Yes .o-wallet > div  
.o-wallet-card Base style wrapping available payment card options Yes fieldset  
.o-wallet-card-item Styling for payment card list Yes ul > li  
.o-wallet-card-item__input Styling for payment card list input Yes .o-wallet-card-item > input  
.o-wallet-card-item-upper Styling for upper existing payment card area Yes .o-wallet-card-item > label Defines the show/hide animation
.o-wallet-card-item-upper__name Styling for existing card icon image Yes .o-wallet-card-item-upper > heading  
.o-wallet-card-item-upper__name--american-express Modifier for existing card icon image   .o-wallet-card-item-upper__name American Express
.o-wallet-card-item-upper__name--dankort Modifier for existing card icon image   .o-wallet-card-item-upper__name Dankort
.o-wallet-card-item-upper__name--diners-club Modifier for existing card icon image   .o-wallet-card-item-upper__name Diners Club
.o-wallet-card-item-upper__name--discover Modifier for existing card icon image   .o-wallet-card-item-upper__name Discover
.o-wallet-card-item-upper__name--jcb Modifier for existing card icon image   .o-wallet-card-item-upper__name JCB
.o-wallet-card-item-upper__name--maestro Modifier for existing card icon image   .o-wallet-card-item-upper__name Maestro
.o-wallet-card-item-upper__name--mastercard Modifier for existing card icon image   .o-wallet-card-item-upper__name Mastercard
.o-wallet-card-item-upper__name--mir Modifier for existing card icon image   .o-wallet-card-item-upper__name MIR
.o-wallet-card-item-upper__name--rupay Modifier for existing card icon image   .o-wallet-card-item-upper__name Rupay
.o-wallet-card-item-upper__name--unionpay Modifier for existing card icon image   .o-wallet-card-item-upper__name Unionpay
.o-wallet-card-item-upper__name--visa Modifier for existing card icon image   .o-wallet-card-item-upper__name VISA
.o-wallet-card-item-upper__name--visa-electron Modifier for existing card icon image   .o-wallet-card-item-upper__name VISA Electron
.o-wallet-card-item-upper__text Styling for existing card information details Yes .o-wallet-card-item-upper > div  
.o-wallet-card-item-lower Styling for lower existing payment card area Yes .o-wallet-card-item > div Defines the show/hide animation
.o-wallet-add-card Base style wrapping new card form elements Yes .o-wallet-card-item-lower Defines styling for standard form elements when adding a new card

Keyboard operations

TAB

Tabbing to an input field should make the input clearly visually different so that the focus point on the page is obvious to the user.

Component releases

  • Added on: v3.4.0 (08/06/18)
  • Updated on: v5.11.0 (07/09/21)

Latest update:

  • fixed: Card replay now correctly shows the expiry date with the '/' when the value auto formatted.
  • fixed: Very long names on very narrow tablet displays now wrap rather than overflowing the content area.
  • fixed: 'Add card' button has now been disabled until all inputs have values.
  • fixed: Tabbing at mobile is now contained within the visible carousel area, rather bypassing the 'Next' button and jumping between them.
  • fixed: Various minor issues with the print stylesheet have been corrected.

Full version history

A full history of changes and enhancements detailing all minor and major updates to the component.

View full version history

Want something new in Framework, or to chat about an issue you're having with it?

Contact the team