Prepaid Cards
Introduction
The Card Service API offers a robust interface for managing prepaid card operations and eligible program management. This service streamlines the entire lifecycle of prepaid cards, from determining program eligibility to secure tokenization, enabling seamless integration and secure handling of card-related functionalities.
Key Features:
- Retrieve a list of eligible prepaid card programs available to a customer.
- Create a new prepaid card for a customer.
- Activate a physical prepaid card.
- Update card details and information.
- Retrieve all prepaid cards associated with a customer.
- Access detailed information about a specific prepaid card.
Prerequisites
Before integrating the Card Service, ensure you have:
- A valid Paysafe account with appropriate permissions.
- Set up instructions for Paysafe SDK Android or iOS.
To get started, initialize the CardService
instance in your app as shown below:
- Kotlin
- Swift
val cardService = Wallet.getInstance().getCardService()
import PaysafeWallet
let cardService = Wallet.instance.cardService
Get customer prepaid cards
- Kotlin
- Swift
Use getAll
method to retrieve a list of cards by passing a CardParameters
object that is used for filtering the customer's cards.
Use CardIncludesParam to add additional information into the response.
The response is a CardList
object which contains a list of Card
objects and PagingResultMeta
.
val cardParameters = CardParameters(
cardType = CardType.ACTIVE,
limit = null,
offset = null,
status = listOf(CardStatus.ACTIVE, CardStatus.EXPIRED),
include = listof(CardIncludesParam.TOKENIZATIONS, CardIncludesParam.AVAILABLE_ACTIONS)
)
val cards = cardService.getAll(cardParameters)
Use getAll
method to retrieve a list of cards by passing a CardParameters
object that is used for filtering the customer's cards.
Use CardIncludesParam to add additional information into the response.
The response is a CardList
object which contains a list of Card
objects and PagingResultMeta
.
let parameters = Wallet.CardParameters(cardType: .virtual,
include: [.tokenizations, .availableActions],
limit: nil,
offset: nil,
status: [.active, .expired])
cardService.getAll(parameters: parameters, completion: { result in
switch result {
case .success(let cards):
// Display cards
case .failure(let error):
// Handle error
}
})
You can achieve pagination by combining the limit
and offset
filters. For instance, implementing
pagination with a page size of 2 results per page would involve configuring:
- Page 1: limit=2, offset=0
- Page 2: limit=2, offset=2
- Page 3: limit=2, offset=4
Retrieve single prepaid card
- Kotlin
- Swift
Use get
method to retrieve detailed information about a specific card by passing th cardId and a list of CardIncludesParam to add additional information into the response. The response is a single Card
object.
If the available actions were requested in the include, the availableActions
property of the response Card
object will contains a list of CardAvailableAction
with all the CardAction
's that can be performed on the card.
val cardId = "f16ba382-eb42-481a-b08f-c57bdc9aae24"
val include = listOf(CardIncludesParam.TOKENIZATIONS, CardIncludesParam.AVAILABLE_ACTIONS)
val card = cardService.get(cardId, include)
Use get
method to retrieve detailed information about a specific card by passing the cardId and a list of CardIncludesParam to add additional information into the response. The response is a single Card
object.
If the available actions were requested in the include, the availableActions
property of the response Card
object will contains a list of CardAvailableAction
with all the CardAction
's that can be performed on the card.
cardService.get(cardID: "f16ba382-eb42-481a-b08f-c57bdc9aae24", include: [.tokenizations, .availableActions], completion: { result in
switch result {
case .success(let card):
// Display card
case .failure(let error):
// Handle error
}
})
Retrieve customer eligible prepaid cards programs
- Kotlin
- Swift
Use getProgram
to retrieve eligible programs for a customer. The result is a list of Program objects.
If the customer is eligible for a Prepaid card, the response will contain a non-empty programs array.
The number of cards that can be issued of a given type can be seen in the allowableCards
field.
val eligiblePrograms = cardService.getPrograms()
Use getProgram
to retrieve eligible programs for a customer. The result is a list of Program objects.
If the customer is eligible for a Prepaid card, the response will contain a non-empty programs array.
The number of cards that can be issued of a given type can be seen in the allowableCards
field.
cardService.getPrograms(completion: { result in
switch result {
case .success(let programs):
// Display prepaid programs
case .failure(let error):
// Handle error
}
})
Create a prepaid card
Creating a new prepaid Virtual card.
- Kotlin
- Swift
Use create
method to create a new Virtual card by passing a CardRequest
object.
The programName
parameter for the CardRequest
must be selected from the list of available programs retrieved using the cardService.getPrograms
method.
val cardRequest = CardRequest(
programName = "SKRILL-VIRTUAL-MC-EEA",
currency = "BGN",
mobile = "+1 123456789"
)
val card = cardService.create(cardRequest)
Use create
method to create a new Virtual card by passing a CardRequest
object.
The programName
parameter for the CardRequest
must be selected from the list of available programs retrieved using the cardService.getPrograms
method.
let request = Wallet.CardRequest(programName: "SKRILL-VIRTUAL-MC-EEA",
currency: "EUR",
mobile: "+1 123456789")
cardService.create(request: request, completion: { result in
switch result {
case .success(let card):
// Display created card
case .failure(let error):
// Handle error
}
})
Creating a new prepaid Physical card.
- Kotlin
- Swift
Use create
method to create a new Physical card by passing a CardRequest
object.
The programName
parameter for the CardRequest
must be selected from the list of available programs retrieved using the cardService.getPrograms
method.
When applying for physical card, the deliveryAddress
is required.
val cardRequest = CardRequest(
programName = "SKRILL-PHYSICAL-MC-EEA",
currency = "BGN",
mobile = "+1 123456789",
cardPin = null,
externalId = null,
deliveryAddress = DeliveryAddress(
address1 = "Tsarigradsko Shose 73",
address2 = null,
address3 = null,
city = "Sofia",
countryCode = "BG",
state = null,
postalCode = "1000"
)
)
val card = cardService.create(cardRequest)
Use create
method to create a new Physical card by passing a CardRequest
object.
The programName
parameter for the CardRequest
must be selected from the list of available programs retrieved using the cardService.getPrograms
method.
When applying for physical card, the deliveryAddress
is required.
let request = Wallet.CreateCardRequest(programName: "SKRILL-PHYSICAL-MC-EEA",
currency: "EUR",
mobile: "+1 123456789",
cardPin: nil,
externalId: nil,
deliveryAddress: .init(address1: "Tsarigradsko Shose 73",
address2: nil,
address3: nil,
city: "Sofia",
countryCode: "BG",
state: nil,
postalCode: "1000"))
cardService.create(request: request, completion: { result in
switch result {
case .success(let card):
// Display created card
case .failure(let error):
// Handle error
}
})
Activate a prepaid physical card
Physical cards should be activated prior first use. Digital cards can be used as Virtual prior activation, but physical POS or ATM operations won't be possible.
Only cards in status ISSUED
or DIGITAL
can be activated and their status will transition to ACTIVE
, enabling POS or ATM operations.
- Kotlin
- Swift
Use activate
method to activate a physical card by passing the cardId and a CardActivationRequest
object.
val cardId = "f16ba382-eb42-481a-b08f-c57bdc9aae24"
val cardActivationRequest = CardActivationRequest(
lastFourDigits = "1234",
cvv = "567"
)
val card = cardService.activate(cardId, cardActivationRequest)
Use `activate` method to activate a physical card by passing the cardId and a [`CardActivationRequest`](/docs/embedded-wallets/sdks/paysafe-wallet-saas-ios/x.x.x/documentation/paysafewallet/wallet/cardactivationrequest) object.
let request = Wallet.CardActivationRequest(lastFourDigits: "1234",
cvv: "567")
cardService.activate(cardID: "f16ba382-eb42-481a-b08f-c57bdc9aae24", request: request, completion: { result in
switch result {
case .success(let card):
// Display card
case .failure(let error):
// Handle error
}
})
Update prepaid card
The customer can change/update:
- Lock, unlock or cancel their prepaid card.
- The card's pin.
In the table below, you can find information about the statuses that the user can change.
From \ To | ACTIVE | CANCELLED | LOCKED | DIGITAL |
---|---|---|---|---|
ACTIVE | - | ✅ | ✅ | ❌ |
CANCELLED | ❌ | - | ❌ | ❌ |
LOCKED | ✅ | ✅ | - | ❌ |
DIGITAL | ✅ (By PIN & CHIP) | ✅ | ✅ | - |
- Kotlin
- Swift
To update card use the update
method by passing a CardUpdateRequest
.
// Status update request
val cardUpdateRequest = CardUpdateRequest(
status = Status.ACTIVE,
statusReason = "User Activate Card from LOCKED status"
)
// Pin change request
val cardUpdateRequest = CardUpdateRequest(
pin = "1243"
)
val cardId = "f16ba382-eb42-481a-b08f-c57bdc9aae24"
val card = cardService.update(cardId, cardUpdateRequest)
To update card use the update
method by passing a CardUpdateRequest
.
// Status update request
let request = Wallet.CardUpdateRequest(status: .active,
statusReason: "User Activate Card from LOCKED status")
// Pin change request
let request = Wallet.CardUpdateRequest(pin: "1234")
let cardID = "f16ba382-eb42-481a-b08f-c57bdc9aae24"
cardService.update(cardID: cardID, request: request, completion: { result in
switch result {
case .success(let card):
// Display card
case .failure(let error):
// Handle error
}
})
Get prepaid card details
Card sensitive information can be viewed by the customers via specialized custom views. This can be either one view containing the whole card with the information on it, or separate views for each sensitive information: card PAN, CVV, expiry date and cardholder's name.
This operation might require Strong Customer Authentication (SCA). Please read Strong Customer Authentication for more information on the process.
Get card details views
Call getSecureDetailsViews
method of the CardService
interface with the card ID to obtain a
CardSecureDetailsViews
object containing specialized secure views for the card PAN, CVV, expiry date and cardholder's name.
For security reasons these details cannot be obtained in plain text.
When the views are placed over card image, please make sure that the card design aligns with the approved guidelines from the Card Scheme.
This operation requires the SDK to be authenticated with a customer token with pci-dss-1
scope. The scope can be
requested during the token exchange process as described in
SDK User Authentication.
- Kotlin
- Swift
The getSecureDetailsViews
method has an optional parameter viewsConfiguration
that can be used to
set a custom PAN separator and which view should have a copy button.
By default the PAN separator is an empty space ("1234 5678 9012 3456") and the copy button is disabled for all views.
The copy button copies the text from the respective view to the clipboard with the ClipDescription.EXTRA_IS_SENSITIVE extra set to true.
val viewsConfiguration = SecureDetailsViewsConfiguration(
shouldShowPanCopyButton = true,
shouldShowCvvCopyButton = true,
shouldShowExpiryDateCopyButton = true,
shouldShowCardholderNameCopyButton = true,
panSeparator = "-"
)
val request = CardSecureDetailsViewRequest(
cardId = "f16ba382-eb42-481a-b08f-c57bdc9aae24",
viewsConfiguration = viewsConfiguration
)
val cardSecureDetailsViews: CardSecureDetailsViews = try {
// call getSecureDetailsViews with the card ID and the viewsConfiguration
cardService.getSecureDetailsViews(request )
} catch (e: ScaRequiredException) {
// solve SCA challenge using e.authenticationEvent
// as described in https://docs.paysafe.com/docs/embedded-wallets/strong-customer-authentication
// repeat getSecurePinView with the SCA details
cardService.getSecureDetailsViews(
CardSecureDetailsViewRequest(
cardId = "f16ba382-eb42-481a-b08f-c57bdc9aae24",
viewsConfiguration = viewsConfiguration,
scaDetails = ScaAuthenticationEventRequest(
eventId = e.authenticationEvent.eventId,
walletOperationId = e.authenticationEvent.walletOperationId
)
)
)
}
// Classic Views: Add the cardSecureDetailsViews to your view hierarchy
container.addView(cardSecureDetailsViews.panView)
container.addView(cardSecureDetailsViews.cvvView)
container.addView(cardSecureDetailsViews.expiryDateView)
container.addView(cardSecureDetailsViews.cardHolderNameView)
// Jetpack Compose: Create a composable function to display the cardSecureDetailsViews
@Composable
fun SecureDetailsComposable(request: CardSecureDetailsViewRequest) {
val cardSecureDetailsViews = remember { mutableStateOf<CardSecureDetailsViews?>(null) }
LaunchedEffect(true) {
cardSecureDetailsViews.value =
Wallet.getInstance().getCardService().getSecureDetailsViews(request)
}
cardSecureDetailsViews.value?.let { detailViews ->
val panView = detailViews.panView
val cvvView = detailViews.cvvView
val expiryDateView = detailViews.expiryDateView
val cardHolderNameView = detailViews.cardHolderNameView
Column {
AndroidView(factory = { panView })
AndroidView(factory = { cvvView })
AndroidView(factory = { expiryDateView })
AndroidView(factory = { cardHolderNameView })
}
}
}
Customize card details views
The copy button icon color is based on the text color of the respective view.
A custom icon for the copy button can be used by setting the pswIconCopyButton
attribute as explained in
UI Theme Configuration.
By default the card detail views use font sans-serif
with text size 24sp for the PAN and 20sp for CVV, Expiry and Cardholder's name.
The text style for each view can be changed by setting the pswTextAppearanceCardPan
, pswTextAppearanceCardCvv
,
pswTextAppearanceCardExpiry
and pswTextAppearanceCardCardholderName
attributes as explained in
UI Theme Configuration.
The getSecureDetailsViews
method has an optional parameter viewsConfiguration
that can be used to configure the views.
If not set, a default configuration is applied - Title 1
text style and Text Primary
color, PAN(card number) separator is an empty space ("1234 5678 9012 3456") and the copy button is disabled for all views.
let request = Wallet.CardSecureDetailsViewsRequest(
cardSecureViewRequest: Wallet.CardSecureViewRequest(cardID: "f16ba382-eb42-481a-b08f-c57bdc9aae24")
)
Customize card details views
Using SecureDetailsViewsConfiguration
each view can be customized.
Customization options include pan separator (for PAN/card number), font, text color, whether to have a copy button or not and action for tapping on the copy button.
The copy button's color and size are based on the text's respective properties.
Its default icon is a system icon doc.on.doc
. It can be changed by configuring iconCopy
as explained in UI Appearance configuration.
When a copy button is tapped, the text is copied in UIPasteboard.general
.
To trigger an additional action, set didTapCopyButton
for the respective view.
When the views are placed over card image, please make sure that the card design aligns with the approved guidelines from the Card Scheme.
let viewsConfiguration = Wallet.SecureDetailsViewsConfiguration(
panViewConfiguration: .init(
font: UIFont.systemFont(ofSize: 30),
textColor: .white,
hasCopy: true,
didTapCopyButton: { view in
// Trigger some event
}
),
panSeparator: "-",
cvvViewConfiguration: .init(
font: UIFont.systemFont(ofSize: 25),
textColor: .white,
hasCopy: true,
didTapCopyButton: { view in
// Trigger some event
}
),
expiryViewConfiguration: .init(
font: UIFont.systemFont(ofSize: 20),
textColor: .white
),
cardHolderViewConfiguration: .init(
font: UIFont.systemFont(ofSize: 20),
textColor: .white
)
)
let request = Wallet.CardSecureDetailsViewsRequest(
cardSecureViewRequest: Wallet.CardSecureViewRequest(cardID: "f16ba382-eb42-481a-b08f-c57bdc9aae24"),
viewsConfiguration: viewsConfiguration
)
Wallet.instance.cardService.getSecureDetailsViews(
request,
completion: { result in
switch result {
case .success(let views):
// Display the views
break
case .failure(let error):
if case .scaRequired(let authenticationEvent, _) = error as? Wallet.WalletError {
// solve SCA challenge using e.authenticationEvent
// as described in https://docs.paysafe.com/docs/embedded-wallets/strong-customer-authentication
// repeat getSecureDetailsViews with the SCA details
Wallet.instance.cardService.getSecureDetailsViews(
.init(
cardSecureViewRequest: .init(
cardID: "f16ba382-eb42-481a-b08f-c57bdc9aae24",
scaDetails: .init(
eventID: authenticationEvent.eventID,
walletOperationID: authenticationEvent.walletOperationID
)
),
viewsConfiguration: viewsConfiguration
),
completion: { _ in
// Handle the result
}
)
} else {
// Handle other errors
}
}
}
)
// SwiftUI: Create a UIViewRepresentable to wrap the UIView
struct ViewWrapper: UIViewRepresentable {
let wrapped: UIView
func makeUIView(context: Context) -> UIView {
wrapped
}
func updateUIView(_ uiView: UIView, context: Context) {}
}
struct ViewWrapperPresenter: View {
let uiView: UIView
var body: some View {
let viewSize = uiView.systemLayoutSizeFitting(.zero)
ViewWrapper(wrapped: uiView)
.frame(width: viewSize.width,
height: viewSize.height)
}
}
...
@State var views: Wallet.CardSecureDetailsViews?
var body: some View {
if let views {
VStack {
ViewWrapperPresenter(uiView: views.panView)
ViewWrapperPresenter(uiView: views.cvvView)
ViewWrapperPresenter(uiView: views.expiryView)
ViewWrapperPresenter(uiView: views.cardHolderNameView)
}
}
}
...
Get card fragment (Legacy)
- Kotlin
- Swift
On Android sensitive card information is displayed in a custom Fragment
. Call getSecureDetailsFragment
method
of the CardService
interface with the card ID to obtain a new instance of the fragment and use
supportFragmentManager
to display it. The sensitive data loads automatically when the fragment is visualized.
class ExampleActivity : AppCompatActivity(R.layout.example_activity) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
val fragment = cardService.getSecureDetailsFragment(
CardSecureDetailsRequest(cardId = "f16ba382-eb42-481a-b08f-c57bdc9aae24")
)
supportFragmentManager.commit {
replace(R.id.fragment_container_view, fragment)
}
}
}
}
The fragment transaction is only created when savedInstanceState
is null
. This is to ensure that
the fragment is added only once, when the activity is first created. When a configuration change occurs and the
activity is recreated, savedInstanceState
is no longer null
, and the fragment does not need to be added a second
time, as it is automatically restored from the savedInstanceState
.
Handle Strong Customer Authentication
A ScaAuthenticationEventListener
should be attached to this fragment using the listenScaAuthenticationEvent
method.
In the case when a Strong Customer Authentication (SCA) is required, this listener will be triggered.
Follow the steps outlined in Submit the SCA Authentication
to confirm the event. After the SCA event is accepted, call authenticateScaEvent
with the scaDetails obtained from the listener.
// use the fragment created with cardService.getSecureDetailsFragment()
cardService.listenScaAuthenticationEvent(fragment) { scaDetails ->
// solve SCA challenge using scaDetails
// as described in https://docs.paysafe.com/docs/embedded-wallets/strong-customer-authentication
// call authenticateScaEvent to obtain the card details
cardService.authenticateScaEvent(fragment, ScaAuthenticationEventRequest(...))
}
Implement the CardSecureDetailsViewControllerDelegate
and assign it
to delegate
property to the CardSecureDetailsViewController
returned from getSecureDetailsViewController
.
The delegate's cardSecureDetails(_:didReceiveSCAEvent:completionHandler:)
function will be invoked when Strong Customer Authentication is required.
Follow the steps outlined in Submit the SCA Authentication
to confirm the event. After the SCA event is accepted, invoke the completionHandler
.
let cardDetailsViewController = cardService.getSecureDetailsViewController(.init(
cardID: "f16ba382-eb42-481a-b08f-c57bdc9aae24"
))
cardDetailsViewController.delegate = ExampleViewController(...)
viewController.present(cardDetailsViewController, animated: true)
func cardSecureDetails(_ viewController: any CardSecureDetailsViewController,
didReceiveSCAEvent scaResponse: Wallet.SCAAuthenticationEventResponse,
completionHandler: @escaping @MainActor @Sendable (Wallet.SCAAuthenticationEventRequest) -> Void) {
// Present SCA authentication to the user (e.g. OTP)
// and record the attempt
// https://docs.paysafe.com/docs/embedded-wallets/strong-customer-authentication#submit-the-sca-authentication
// Then call completionHandler
}
Get prepaid card PIN
A card PIN can be viewed by the customers via specialized secure view. This secure view is obtained asynchronously
with the getSecurePinView
method of the CardService
interface and then attached to the screen. For security reasons,
the PIN cannot be obtained in plain text.
This operation requires the SDK to be authenticated with a customer token with pci-dss-1
scope. The scope can be
requested during the token exchange process as described in
SDK User Authentication.
This operation might require Strong Customer Authentication (SCA). Please read Strong Customer Authentication for more information on the process.
Customize PIN font and color
By default the PIN is displayed on a transparent background using Title 1
text style and Text Primary
color.
These values can be customized as explained in UI Theme Configuration
for Android and UI Appearance configuration
for iOS.
- Kotlin
- Swift
val pinView: View = try {
// call getSecurePinView with the card ID
cardService.getSecurePinView(CardSecureViewRequest(cardId = "f16ba382-eb42-481a-b08f-c57bdc9aae24"))
} catch (e: ScaRequiredException) {
// solve SCA challenge using e.authenticationEvent
// as described in https://docs.paysafe.com/docs/embedded-wallets/strong-customer-authentication
// repeat getSecurePinView with the SCA details
cardService.getSecurePinView(
CardSecureViewRequest(
cardId = "f16ba382-eb42-481a-b08f-c57bdc9aae24",
scaDetails = ScaAuthenticationEventRequest(
eventId = e.authenticationEvent.eventId,
walletOperationId = e.authenticationEvent.walletOperationId
)
)
)
}
// Jetpack Compose: Create a composable function to display the pinView
@Composable
fun CardPin(cardId: String) {
val pinView = remember { mutableStateOf<View?>(null) }
LaunchedEffect(true) {
pinView.value = cardService.getSecurePinView(CardSecureViewRequest(cardId))
}
pinView.value?.let { view ->
AndroidView(factory = { view })
}
}
// Classic Views: Add the pinView to your view hierarchy
container.addView(pinView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
Wallet.instance.cardService.getSecurePINView(
.init(cardID: "f16ba382-eb42-481a-b08f-c57bdc9aae24"),
completion: { result in
switch result {
case .success(let pinView):
// Show PIN view
break
case .failure(let error):
if case .scaRequired(let authenticationEvent, _) = error as? Wallet.WalletError {
// solve SCA challenge using e.authenticationEvent
// as described in https://docs.paysafe.com/docs/embedded-wallets/strong-customer-authentication
// repeat getSecurePINView with the SCA details
Wallet.instance.cardService.getSecurePINView(.init(cardID: "f16ba382-eb42-481a-b08f-c57bdc9aae24",
scaDetails: .init(eventID: authenticationEvent.eventID,
walletOperationID: authenticationEvent.walletOperationID)),
completion: { _ in
// Handle the result
})
} else {
// Handle other errors
}
}
}
)
// SwiftUI: Create a UIViewRepresentable to wrap the UIView
struct ViewWrapper: UIViewRepresentable {
let wrapped: UIView
func makeUIView(context: Context) -> UIView {
wrapped
}
func updateUIView(_ uiView: UIView, context: Context) {}
}
struct ViewWrapperPresenter: View {
let uiView: UIView
var body: some View {
let viewSize = uiView.systemLayoutSizeFitting(.zero)
ViewWrapper(wrapped: uiView)
.frame(width: viewSize.width,
height: viewSize.height)
}
}
...
@State var pinView: UIView?
var body: some View {
if let pinView {
ViewWrapperPresenter(uiView: pinView)
}
}
...
Reset PIN/CVV Attempts
Resetting the PIN or CVV attempt counters on a prepaid card clears failed attempts and restores the card's ability to authorize transactions.
- Kotlin
- Swift
Call resetVerificationAttempts
on the CardService
, providing card ID and ResetCardVerificationAttemptsRequest.
val request = ResetCardVerificationAttemptsRequest(action = AttemptResetAction.PIN_CVV_RETRIES)
val cardId = "2bef467a-b29e-2398-8fe4-34520bfb48f1"
val cardAttempts = cardService.resetVerificationAttempts(
cardId = cardId,
request = request
)
Call resetVerificationAttempts
on the CardService
, providing card ID and ResetCardVerificationAttemptsRequest.
let request = Wallet.ResetCardVerificationAttemptsRequest(action: .pinCVVRetries)
Wallet.instance.cardService.resetVerificationAttempts(
cardID: "2bef467a-b29e-2398-8fe4-34520bfb48f1",
request: request,
completion: { result in
switch result {
case .success(let cardAttemptResetInfo):
// Handle result of successful reset of PIN/CVV
case .failure(let error):
// Handle error
}
}
)
This functionality is only available for card programs in Europe.