> For the complete documentation index, see [llms.txt](https://docs.payments.thalescloud.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.payments.thalescloud.io/nfc-wallet-sdk-ios/additional-features/customize-ppse.md).

# Customize PPSE

## Overview

As defined in EMV Book B, the PPSE (Proximity Payment System Environment) is the entry point for contactless payments. Each digital card has its own PPSE, defined in the token profile. It contains the list of supported application identifiers (AIDs), with the corresponding application labels and priorities.

NFC Wallet SDK provides **PPSE Management API.** It allows your digital wallet application to:

* Get the **PPSE** (default PPSE defined in the digital card profile).
* Get the **auxiliary PPSE** (PPSE define in the auxiliary digital card profile for co-badged cards).
* Customize a new PPSE (**custom PPSE**).
* Get or reset the **custom PPSE**.

NFC Wallet SDK uses the **custom PPSE** when it is defined for a contactless transaction.

{% hint style="info" %}
Use **PPSE Management API** to apply your policy in a co-badged digital wallet program.

**NFC Wallet SDK** uses only the **PPSE** or the **custom PPSE**. It does not use the **auxiliary PPSE**.
{% endhint %}

## SDK integration

The **PPSE Management API** includes the `PPSEFCITemplate` class and these `DigitalCard` operations:

* `DigitalCard.ppse`: Gets the default PPSE defined in the token profile.
* `DigitalCard.auxiliaryPPSE`: Gets the PPSE defined in the auxiliary digital card profile (co-badged cards).
* `DigitalCard.customPPSE`: Gets the custom PPSE. Returns `nil` if it is not defined.
* `DigitalCard.setCustomPPSE`: Sets a custom PPSE. Pass `nil` to clear it.

{% hint style="warning" %}
The SDK automatically validates the PPSE template when you call `DigitalCard.setCustomPPSE`.

See [Validation rules](#validation-rules).
{% endhint %}

### Building custom PPSE

Build a custom PPSE with `PPSEFCITemplate` in one of these ways:

* Use an existing PPSE template.
* Use raw PPSE bytes.
* Build it from scratch.

#### Method 1: Build PPSE from an existing template

Use this approach when you want to start with the digital card PPSE and make targeted modifications.

**When to use:**

* You want to customize specific fields in the existing PPSE, such as priority or tags.
* You want to preserve the default PPSE structure while making selective changes.
* You need a base template for customization.

```swift
var fci = try await card.ppse

let visaAID = Data([0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10])
let visaKernel = Data([0x03])

guard let index = fci.proprietaryTemplate.issuerDiscretionaryData.directoryEntryList.firstIndex(where: { 
    $0.applicationIdentifier == visaAID && $0.kernelIdentifier == visaKernel 
}) else {    
    return
}

let customDiscretionaryData = Data([0xDF, 0x01, 0x02, 0x11, 0x22])

// Set additional tag under directory entry
try fci.proprietaryTemplate.issuerDiscretionaryData.directoryEntryList[index].setAdditionalTag(
    value: customDiscretionaryData, 
    forKey: Data([0x73])
)
    
let transitNetworkIdentifier = Data([0x00, 0x00, 0x1A, 0x9F])
fci.proprietaryTemplate.issuerDiscretionaryData.directoryEntryList[index].asrpd = transitNetworkIdentifier

try await card.setCustomPPSE(fci)
```

#### Method 2: Build PPSE from raw bytes

Use this approach when you have the complete PPSE response data already available in BER-TLV format.

**When to use:**

* You already have the full PPSE response data as raw bytes.

```swift
private let knownPPSEFCIHex =
    Data([
        0x6F, 0x6E, 0x84, 0x0E, 0x32, 0x50, 0x41, 0x59, 0x2E, 0x53, 0x59, 0x53, 0x2E, 0x44, 0x44, 0x46,
        0x30, 0x31, 0xA5, 0x5C, 0xBF, 0x0C, 0x59, 0x61, 0x2B, 0x4F, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x98,
        0x08, 0x40, 0x50, 0x0B, 0x56, 0x69, 0x73, 0x61, 0x20, 0x43, 0x72, 0x65, 0x64, 0x69, 0x74, 0x87,
        0x01, 0x01, 0xBF, 0x63, 0x04, 0xDF, 0x20, 0x01, 0x00, 0x9F, 0x0A, 0x04, 0x00, 0x01, 0x01, 0x02,
        0x9F, 0x2A, 0x01, 0x03, 0x61, 0x2A, 0x4F, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x50,
        0x0A, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0x87, 0x01, 0x02, 0xBF, 0x63,
        0x04, 0xDF, 0x20, 0x01, 0x00, 0x9F, 0x0A, 0x04, 0x00, 0x01, 0x01, 0x01, 0x9F, 0x2A, 0x01, 0x03
    ])

let template = try DigitalCard.PPSEFCITemplate(data: knownPPSEFCIHex)

try await card.setCustomPPSE(template)
```

#### Method 3: Build PPSE from scratch

Use this approach when you need to construct a PPSE structure programmatically by defining each component individually.

**When to use:**

* You are building PPSE data without existing byte data.
* You need to define directory entries, AIDs, labels, and other PPSE components programmatically.
* You want type-safe construction with compile-time validation.
* You prefer structured objects over raw bytes.

`DigitalCard` exposes the PPSE response as `PPSEFCITemplate`. You can also build your own `PPSEFCITemplate`. The following example shows how.

```swift
let dfName = Data([
    0x32, 0x50, 0x41, 0x59, 0x2E, 0x53, 0x59, 0x53, 0x2E, 0x44, 0x44, 0x46, 0x30, 0x31
])

let entry = try DigitalCard.DirectoryEntry(
    applicationIdentifier: Data([0xA0, 0x00, 0x00, 0x00, 0x98, 0x08, 0x40]),
    label: Data([0x56, 0x69, 0x73, 0x61, 0x20, 0x43, 0x72, 0x65, 0x64, 0x69, 0x74]),
    kernelIdentifier: Data([0x03]),
    asrpd: Data([0x00, 0x01, 0x01, 0x02]),
    priority: Data([0x01]),
    additionalTags: [Data([0xBF, 0x63]): Data([0xDF, 0x20, 0x01, 0x00])],
    lockStatus: .unlocked
)

let idd = try DigitalCard.FCIIssuerDiscretionaryData(
    directoryEntryList: entries,
    additionalTags: bf0cAdditionalTags
)

let prop = try DigitalCard.FCIProprietaryTemplate(issuerDiscretionaryData: idd)

let template = try DigitalCard.PPSEFCITemplate(dfName: dfName, proprietaryTemplate: prop)

try await card.setCustomPPSE(template)
```

### Clear a custom PPSE

Pass `nil` to remove the custom PPSE and restore the default PPSE response.

```swift
/// 1. Retrieve current custom PPSE (if any)
if let currentPPSE = try? await card.customPPSE {
    print("\(currentPPSE) PPSE is active.")
}

// 2. Remove the current PPSE completely (revert to factory set PPSE)
try await card.setCustomPPSE(nil)
```

### Validation rules

NFC Wallet SDK validates the custom PPSE when you call `DigitalCard.setCustomPPSE`. It ensures that the custom PPSE follows EMV requirements and BER-TLV encoding.

#### Size limits

* The serialized BER-TLV payload must be 256 bytes or less.
* Custom tag keys inside `BF0C` or `61` must not exceed 2 bytes.

#### Required tags

Your `PPSEFCITemplate` must include:

1. DF Name — Tag `84`
2. At least one Directory Entry — Tag `61` inside `BF0C`
3. An Application Identifier in each Directory Entry — Tag `4F`

If validation fails, the SDK throws `DigitialCard.Error.invaldPPSE(_:)` error

#### AID lock restrictions

Locking an AID with `LockStatus.locked` is supported only for mono-badged Visa cards.

If you set `LockStatus.locked` for an unsupported card, the SDK throws `DigitialCard.Error.clientError(_:)` error


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.payments.thalescloud.io/nfc-wallet-sdk-ios/additional-features/customize-ppse.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
