path-terminal-init 0.2.12 → 0.2.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,294 +1,298 @@
1
- ---
2
- description: >
3
- Rules for AI agents integrating the Path Terminal SDK into a Windows 11 EPOS
4
- application — the modern WinUI 3 / .NET 10 stack. Apply when the user asks to
5
- integrate payments, connect to a payment terminal, wire a sale or refund, or
6
- set up the Path emulator on a WinUI 3 app. For the legacy WPF / .NET Framework
7
- 4.8 stack use path-integration-windows10.mdc instead.
8
- globs:
9
- - "**/*.cs"
10
- - "**/*.csproj"
11
- - "**/*.xaml"
12
- - "**/Package.appxmanifest"
13
- alwaysApply: false
14
- ---
15
-
16
- # Path Terminal SDK — Windows 11 (WinUI 3 / .NET 10) Integration Rules
17
-
18
- You are integrating the Path Terminal SDK into an existing **WinUI 3 / .NET 10**
19
- Windows EPOS application. This app is a **developer EPOS system** that replicates a
20
- production environment.
21
-
22
- > **Which recipe am I in?** This is the **Windows 11** recipe — modern WinUI 3,
23
- > `net10.0-windows`. If the project is WPF / `net48` (a `<UseWPF>true` csproj
24
- > targeting .NET Framework 4.8), STOP and use `path-integration-windows10.mdc`
25
- > instead. The two stacks differ in app shell, UI threading, and settings storage.
26
-
27
- You have access to the Path MCP server at mcp.path2ai.tech:
28
- - Tools: `get_code_example` (use platform='windows'), `explain_error`. Always call
29
- `get_code_example` before writing SDK code — never guess at SDK patterns.
30
-
31
- > **Language override.** Never write "Pico W" in any output or code comment.
32
- > Always use "Path POS Emulator" when referring to the physical device.
33
-
34
- > **Confirm the correct project directory first.** Run `pwd` (or `Get-Location`)
35
- > before anything. If the path contains `.Trash`, `tmp`, or `worktree`, stop — ask
36
- > the developer to relaunch from the real project directory.
37
-
38
- ---
39
-
40
- ## The purpose of this integration
41
-
42
- Wire the SDK so the user can **prove the payment functions against the Path POS
43
- Emulator, then switch — themselves, in the app — to a real Verifone terminal when
44
- ready.** So Settings gets a **backend switcher**, not one hardcoded transport.
45
-
46
- > **You wire the capability; the user enters the connection details.** You build the
47
- > switcher, the IP field, and the credential storage, and ship sensible test
48
- > defaults. You do NOT ask the user for an IP or password during this run — you
49
- > describe how to use the screen in your end-of-run summary (Step 4).
50
-
51
- The Windows 11 **demo app** (`Path-epos-demo-sdk-Windows11`) already implements this
52
- exact pattern — its `SdkTerminalManager.BuildTerminal()` is the reference. Mirror it.
53
-
54
- ---
55
-
56
- ## Step 0 — Install the SDK (NuGet feed — no checkout)
57
-
58
- The SDK is published to the **Path NuGet feed**; you install it like any package — no clone of
59
- the SDK repo. Call `get_code_example` with operation='install' and platform='windows'.
60
-
61
- **1) Add the feed** — a `nuget.config` next to your solution:
62
-
63
- ```xml
64
- <?xml version="1.0" encoding="utf-8"?>
65
- <configuration>
66
- <packageSources>
67
- <add key="path" value="https://mcp.path2ai.tech/nuget/index.json" />
68
- </packageSources>
69
- </configuration>
70
- ```
71
-
72
- **2) Reference the packages** in the app's `.csproj` (versions in lock-step with Android/iOS):
73
-
74
- ```xml
75
- <PropertyGroup>
76
- <!-- The Verifone binding is x64 mixed-mode — the app must run x64. -->
77
- <PlatformTarget>x64</PlatformTarget>
78
- </PropertyGroup>
79
- <ItemGroup>
80
- <PackageReference Include="Path.Terminal.Sdk" Version="1.5.0" />
81
- <PackageReference Include="Path.Terminal.EmulatorAdapter" Version="1.5.0" />
82
- <PackageReference Include="Path.Terminal.MockAdapter" Version="1.5.0" />
83
- <PackageReference Include="Path.Terminal.Diagnostics" Version="1.5.0" />
84
- <!-- Verifone backend (Step 2c) — bundles the Verifone .NET binding, nothing extra to download. -->
85
- <PackageReference Include="Path.Terminal.PsdkAdapter" Version="1.5.0" />
86
- </ItemGroup>
87
- ```
88
-
89
- `Path.Terminal.CoreModels` comes in transitively via `Path.Terminal.Sdk`.
90
- (The demo tills inside `Path-terminal-sdk-windows11` build against the SDK by `ProjectReference`;
91
- you, as an integrator, use the feed above.)
92
-
93
- > **Bluetooth needs no manifest entry here.** The harness is an *unpackaged* WinUI 3
94
- > app (`WindowsPackageType=None`), so BLE comes through the WinRT projections the
95
- > EmulatorAdapter already uses — there is no `Package.appxmanifest` capability to add.
96
-
97
- ---
98
-
99
- ## Your job — four steps, in this order
100
-
101
- ---
102
-
103
- ### Step 1 — Disconnect the existing loopback wiring
104
-
105
- The harness ships with a **loopback** adapter, `OCPayTerminalAdapter`, composed at the
106
- app root (look in `App.xaml.cs` / `Services/AppServices.cs` for where the terminal
107
- manager is built). **Read the actual code** — a comment is not proof.
108
-
109
- Create a Path-backed terminal manager that builds `PathTerminal` from the selected
110
- backend (mirror the demo's `SdkTerminalManager.BuildTerminal()`), and swap the app
111
- composition to use it instead of the OCPay manager.
112
-
113
- > Keep `OCPayTerminalAdapter` in the codebase (it is the shipped loopback default) —
114
- > just stop composing it as the live adapter. Do not delete it.
115
-
116
- **Also disable any auto-connect on launch** — the Path backends connect manually from
117
- Settings, never automatically on start.
118
-
119
- ---
120
-
121
- ### Step 2 — Add a backend switcher to Settings
122
-
123
- The harness already has a Settings page (look for `SettingsPage` / `SettingsViewModel`).
124
- Add a **Path POS Adapter** section with a backend picker offering **three** backends.
125
- **Call `get_code_example` with operation='backend-switch' and platform='windows' first**
126
- and reproduce its `BuildTerminal` shape exactly:
127
-
128
- | Backend | Transport | Adapter | Needs |
129
- |---|---|---|---|
130
- | **Emulator (Wi-Fi)** — *default* | TCP/IP | `new TcpPathTerminalAdapter(host, …)` | emulator IP |
131
- | **Emulator (Bluetooth)** | BLE | `new BlePathTerminalAdapter(…)` | scan + connect |
132
- | **Verifone** | TCP/IP (PSDK) | `new VerifonePsdkAdapter(config, …)` | terminal IP + stored login |
133
-
134
- ```csharp
135
- public enum TerminalBackend { EmulatorWifi, EmulatorBle, Verifone } // EmulatorWifi = default
136
- ```
137
-
138
- **Persist the selection and connection details** in the harness's settings store (the
139
- demo uses a small `AppSettings` class writing JSON to
140
- `%LOCALAPPDATA%\<App>\app.json`). Store: `Backend`, `EmulatorHost`, `VerifoneHost`,
141
- `LoginUsername`, `LoginPassword`, `LoginShift`, `RefundPassword` — with test defaults
142
- (`VerifoneHost` "192.168.1.88", username "user", password "password123", shift
143
- "shift123") so a fresh clone connects without typing.
144
-
145
- #### Step 2a — Emulator (Wi-Fi, the default) and Verifone — IP entry
146
-
147
- For the two IP-addressed backends show a **host/IP text box** (label "Emulator IP" or
148
- "Verifone terminal IP" by selection) and an **Apply & Connect** button. On connect,
149
- build the matching adapter from the stored host, dispose the previous `PathTerminal`,
150
- and build a new one (only **one** client per terminal / per emulator Wi-Fi session).
151
- TCP and Verifone need no scan — `DiscoverDevicesAsync()` returns one synthetic device
152
- for the host, so Apply & Connect connects directly.
153
-
154
- #### Step 2b — Emulator (Bluetooth) — scan and connect
155
-
156
- For the BLE backend keep a scan/connect flow: a **Scan** button → `DiscoverDevicesAsync()`
157
- list devices **Connect** live connection state from the `Events` stream.
158
- Marshal UI updates onto the WinUI UI thread (`DispatcherQueue.TryEnqueue`).
159
-
160
- #### Step 2c — Verifone backend and stored credentials
161
-
162
- Call `get_code_example` with operation='verifone-init' and platform='windows'.
163
- Construct it from a `VerifoneTerminalConfig` built from the **stored** login — never
164
- hardcode credentials inline:
165
- ```csharp
166
- using Path.Terminal.PsdkAdapter;
167
- var adapter = new VerifonePsdkAdapter(
168
- new VerifoneTerminalConfig {
169
- Host = settings.VerifoneHost,
170
- Username = settings.LoginUsername,
171
- Password = settings.LoginPassword,
172
- Shift = settings.LoginShift,
173
- RefundPassword = settings.RefundPassword,
174
- },
175
- log: Log);
176
- ```
177
- Keep the Verifone login fields **separate** (their own stored keys + editable fields).
178
- All terminals are treated as **generic Verifone** (same AGPA app, same PSDK
179
- connect/login) no per-terminal config beyond IP + login.
180
-
181
- ---
182
-
183
- ### Step 3 — Wire sale, refund and void through the Path SDK
184
-
185
- Once Step 1 is done, the harness's existing payment flow calls the Path-backed manager.
186
- Confirm the transaction methods map correctly:
187
- - Call `get_code_example` operation='sale' (platform='windows') — handle `TransactionState.Approved`, `Declined`, `TimedOut`, `Failed`, default.
188
- - Call `get_code_example` operation='refund' — pass `originalTransactionId` from the sale result.
189
- - Call `get_code_example` operation='void' — full reversal; success state is `Reversed`.
190
- - Amounts are **minor units** (`420` = £4.20). Never pass decimals.
191
- - Catch `PathException` and check whether it is recoverable before retrying.
192
-
193
- ---
194
-
195
- ### Step 3b — Merchant logo on the customer display (idle branding)
196
-
197
- The app can push a merchant logo to the terminal's **customer-facing display** (idle/attract
198
- mode). It works on the **emulator (Wi-Fi and Bluetooth)** and on Verifone. Call
199
- `get_code_example` operation='idle-branding' (platform='windows') first.
200
-
201
- **The SDK API** (on `PathTerminal`, async):
202
- ```csharp
203
- Task SetIdleBrandingAsync(CustomerDisplayContent? content, CancellationToken ct = default);
204
- // ImageBytes = raw PNG/JPEG bytes (NOT a BitmapImage / not base64); Caption is optional.
205
- new CustomerDisplayContent(imageBytes, caption); // pass null content to CLEAR the logo
206
- ```
207
-
208
- **Add a "Customer Display" section to Settings** with:
209
- - A **"Show merchant logo"** toggle (persisted).
210
- - A **file picker** (`FileOpenPicker`, `.png`/`.jpg`) storing the image **pre-scaled to ≤ 480px
211
- PNG** so a fresh launch still has a logo; ship a default logo asset so it works out of the box.
212
- - An optional **caption** field.
213
- - An **"Apply to terminal now"** button, **enabled only when connected**.
214
-
215
- > **CRITICAL — re-apply after every connect.** A fresh connection starts with a **blank**
216
- > display. Call `SetIdleBrandingAsync(...)` **right after every successful connect**, not just
217
- > from the button otherwise the logo never appears. Pass `null` content to clear when the
218
- > toggle is off.
219
-
220
- > **Best-effort + backend-gated.** `SetIdleBrandingAsync` swallows backend errors — "no
221
- > exception" does not prove it rendered; confirm on the device. Keep the Verifone payload small
222
- > (a logo pre-scaled to ≤ 480px PNG, not a full-res photo, so the base64 stays under the cap).
223
-
224
- ---
225
-
226
- ### Step 3.5 — Build to catch API mismatches (check the environment first)
227
-
228
- Confirm your C# compiles against the SDK's **real** API. **Probe the toolchain first**, and
229
- treat a missing one as an environment limitation to advise on, **never as a failure:**
230
-
231
- 1. `dotnet --version` is the .NET 10 SDK available?
232
- 2. **Present** → run `dotnet build Path.Epos.TestHarness.sln -c Debug`, fix any real API
233
- mismatches, then continue to Step 4.
234
- 3. **Absent** → the code is complete; you just can't build *here*. Do **not** report a failure.
235
- If a package manager is present, **offer** to install the .NET 10 SDK (`winget install Microsoft.DotNet.SDK.10`);
236
- otherwise tell the developer: *"The code is done build in Visual Studio or with `dotnet build`
237
- and paste back any errors,"* and summarise the files you changed.
238
-
239
- Frame a missing build toolchain as **"here's how to verify"**, never as a failure of the work.
240
-
241
- ### Step 4 Verify it works end to end
242
-
243
- 1. Confirm the app root composes the Path-backed manager, not `OCPayTerminalAdapter`.
244
- 2. **Prove on the emulator first (the default — Wi-Fi/IP):**
245
- - Put the Path POS Emulator in Wi-Fi mode (Config → Connection); note the `IP:port`
246
- on its welcome screen.
247
- - Run the app → Settings → Path POS Adapter **Emulator (Wi-Fi)**type that IP
248
- **Apply & Connect**. Connection state should reach Connected.
249
- - (Optionally prove **Emulator (Bluetooth)**: select it, **Scan**, **Connect**.)
250
- 3. Run a sale (amountMinor `100` = £1.00), present a card / tap the emulator's NFC tag,
251
- verify `result.State == Approved`, `TransactionId` is non-null, and receipt data is
252
- populated (`get_code_example` operation='receipt'). Run a refund and a void.
253
- 4. **Then prove the switch to Verifone:** select **Verifone**, enter the terminal IP and
254
- confirm the stored login, **Apply & Connect** the SDK runs the PSDK init + login and
255
- the same flow runs against the real terminal, unchanged. One client per terminal
256
- disconnect any other POS first.
257
-
258
- ---
259
-
260
- ## SDK rules — always follow these
261
-
262
- **ALWAYS:**
263
- - Call `get_code_example` (platform='windows') before writing SDK code
264
- - Use `TransactionRequest.Sale(...)` / `.Refund(...)` / `.Void(...)` factory methods
265
- - Use `RequestEnvelope.Create(...)` never build envelopes by hand
266
- - For **refunds** the success state is the refunded/approved state, and for **voids** it is `Reversed` not the sale's `Approved`
267
- - Dispose the old `PathTerminal` before building a new one on a backend change
268
- - Marshal UI updates from `Events` onto the WinUI UI thread (`DispatcherQueue.TryEnqueue`)
269
-
270
- **NEVER:**
271
- - Use a raw `TransactionRequest` constructor use the factory methods
272
- - Store or log card data (masked PAN only)
273
- - Retry a financial operation without a fresh `RequestEnvelope` (new idempotency key)
274
- - Leave `OCPayTerminalAdapter` composed as the live adapter — Path must be the live one
275
- - Leave auto-connect-on-launch running
276
- - Set `OnCardRead` / `OnAck` on an adapter — `PathTerminal` owns those
277
- - Write "Pico W" anywhere
278
-
279
- ---
280
-
281
- ## After making changes
282
-
283
- Summarise each file changed and confirm Step 1 (the live adapter is now Path-backed).
284
-
285
- Then give **tailored connect instructions** — the user enters the connection details, so
286
- tell them exactly where you wired things in:
287
- - The Settings section you added the backend switcher to
288
- - How to connect to the emulator over **Wi-Fi/IP** (the default): which backend, where to
289
- type the emulator's IP, which button connects
290
- - How to connect over **Bluetooth** instead (Scan → Connect)
291
- - How to **switch to Verifone** when ready: pick Verifone, enter the terminal IP, confirm
292
- the stored login, connect
293
-
294
- Keep it brief and concrete the user should follow it without reading code.
1
+ ---
2
+ description: >
3
+ Rules for AI agents integrating the Path Terminal SDK into a Windows 11 EPOS
4
+ application — the modern WinUI 3 / .NET 10 stack. Apply when the user asks to
5
+ integrate payments, connect to a payment terminal, wire a sale or refund, or
6
+ set up the Path emulator on a WinUI 3 app. For the legacy WPF / .NET Framework
7
+ 4.8 stack use path-integration-windows10.mdc instead.
8
+ globs:
9
+ - "**/*.cs"
10
+ - "**/*.csproj"
11
+ - "**/*.xaml"
12
+ - "**/Package.appxmanifest"
13
+ alwaysApply: false
14
+ ---
15
+
16
+ # Path Terminal SDK — Windows 11 (WinUI 3 / .NET 10) Integration Rules
17
+
18
+ You are integrating the Path Terminal SDK into an existing **WinUI 3 / .NET 10**
19
+ Windows EPOS application. This app is a **developer EPOS system** that replicates a
20
+ production environment.
21
+
22
+ > **Which recipe am I in?** This is the **Windows 11** recipe — modern WinUI 3,
23
+ > `net10.0-windows`. If the project is WPF / `net48` (a `<UseWPF>true` csproj
24
+ > targeting .NET Framework 4.8), STOP and use `path-integration-windows10.mdc`
25
+ > instead. The two stacks differ in app shell, UI threading, and settings storage.
26
+
27
+ You have access to the Path MCP server at mcp.path2ai.tech:
28
+ - Tools: `get_code_example` (use platform='windows'), `explain_error`. Always call
29
+ `get_code_example` before writing SDK code — never guess at SDK patterns.
30
+
31
+ > **Language override.** Never write "Pico W" in any output or code comment.
32
+ > Always use "Path POS Emulator" when referring to the physical device.
33
+
34
+ > **Confirm the correct project directory first.** Run `pwd` (or `Get-Location`)
35
+ > before anything. If the path contains `.Trash`, `tmp`, or `worktree`, stop — ask
36
+ > the developer to relaunch from the real project directory.
37
+
38
+ ---
39
+
40
+ ## The purpose of this integration
41
+
42
+ Wire the SDK so the user can **prove the payment functions against the Path POS
43
+ Emulator, then switch — themselves, in the app — to a real Verifone terminal when
44
+ ready.** So Settings gets a **backend switcher**, not one hardcoded transport.
45
+
46
+ > **You wire the capability; the user enters the connection details.** You build the
47
+ > switcher, the IP field, and the credential storage, and ship sensible test
48
+ > defaults. You do NOT ask the user for an IP or password during this run — you
49
+ > describe how to use the screen in your end-of-run summary (Step 4).
50
+
51
+ The Windows 11 **demo app** (`Path-epos-demo-sdk-Windows11`) already implements this
52
+ exact pattern — its `SdkTerminalManager.BuildTerminal()` is the reference. Mirror it.
53
+
54
+ ---
55
+
56
+ ## Step 0 — Install the SDK (NuGet feed — no checkout)
57
+
58
+ The SDK is published to the **Path NuGet feed**; you install it like any package — no clone of
59
+ the SDK repo. Call `get_code_example` with operation='install' and platform='windows'.
60
+
61
+ **1) Add the feed** — a `nuget.config` next to your solution:
62
+
63
+ ```xml
64
+ <?xml version="1.0" encoding="utf-8"?>
65
+ <configuration>
66
+ <packageSources>
67
+ <add key="path" value="https://mcp.path2ai.tech/nuget/index.json" />
68
+ </packageSources>
69
+ </configuration>
70
+ ```
71
+
72
+ **2) Reference the packages** in the app's `.csproj` (versions in lock-step with Android/iOS):
73
+
74
+ ```xml
75
+ <PropertyGroup>
76
+ <!-- The Verifone binding is x64 mixed-mode — the app must run x64. -->
77
+ <PlatformTarget>x64</PlatformTarget>
78
+ </PropertyGroup>
79
+ <ItemGroup>
80
+ <PackageReference Include="Path.Terminal.Sdk" Version="1.5.0" />
81
+ <PackageReference Include="Path.Terminal.EmulatorAdapter" Version="1.5.0" />
82
+ <PackageReference Include="Path.Terminal.MockAdapter" Version="1.5.0" />
83
+ <PackageReference Include="Path.Terminal.Diagnostics" Version="1.5.0" />
84
+ <!-- Verifone backend (Step 2c) — bundles the Verifone .NET binding, nothing extra to download. -->
85
+ <PackageReference Include="Path.Terminal.PsdkAdapter" Version="1.5.0" />
86
+ </ItemGroup>
87
+ ```
88
+
89
+ `Path.Terminal.CoreModels` comes in transitively via `Path.Terminal.Sdk`.
90
+ (The demo tills inside `Path-terminal-sdk-windows11` build against the SDK by `ProjectReference`;
91
+ you, as an integrator, use the feed above.)
92
+
93
+ > **Bluetooth needs no manifest entry here.** The harness is an *unpackaged* WinUI 3
94
+ > app (`WindowsPackageType=None`), so BLE comes through the WinRT projections the
95
+ > EmulatorAdapter already uses — there is no `Package.appxmanifest` capability to add.
96
+
97
+ ---
98
+
99
+ ## Your job — four steps, in this order
100
+
101
+ ---
102
+
103
+ ### Step 1 — Disconnect the existing loopback wiring
104
+
105
+ The harness ships with a **loopback** adapter, `OCPayTerminalAdapter`, composed at the
106
+ app root (look in `App.xaml.cs` / `Services/AppServices.cs` for where the terminal
107
+ manager is built). **Read the actual code** — a comment is not proof.
108
+
109
+ Create a Path-backed terminal manager that builds `PathTerminal` from the selected
110
+ backend (mirror the demo's `SdkTerminalManager.BuildTerminal()`), and swap the app
111
+ composition to use it instead of the OCPay manager.
112
+
113
+ > Keep `OCPayTerminalAdapter` in the codebase (it is the shipped loopback default) —
114
+ > just stop composing it as the live adapter. Do not delete it.
115
+
116
+ **Also disable any auto-connect on launch** — the Path backends connect manually from
117
+ Settings, never automatically on start.
118
+
119
+ ---
120
+
121
+ ### Step 2 — Add a backend switcher to Settings
122
+
123
+ The harness already has a Settings page (look for `SettingsPage` / `SettingsViewModel`).
124
+ Add a **Path POS Adapter** section with a backend picker offering **three** backends.
125
+ **Call `get_code_example` with operation='backend-switch' and platform='windows' first**
126
+ and reproduce its `BuildTerminal` shape exactly:
127
+
128
+ | Backend | Transport | Adapter | Needs |
129
+ |---|---|---|---|
130
+ | **Emulator (Wi-Fi)** — *default* | TCP/IP | `new TcpPathTerminalAdapter(host, …)` | emulator IP |
131
+ | **Emulator (Bluetooth)** | BLE | `new BlePathTerminalAdapter(…)` | scan + connect |
132
+ | **Verifone** | TCP/IP (PSDK) | `new VerifonePsdkAdapter(config, …)` | terminal IP + stored login |
133
+
134
+ ```csharp
135
+ public enum TerminalBackend { EmulatorWifi, EmulatorBle, Verifone } // EmulatorWifi = default
136
+ ```
137
+
138
+ **Persist the selection and connection details** in the harness's settings store (the
139
+ demo uses a small `AppSettings` class writing JSON to
140
+ `%LOCALAPPDATA%\<App>\app.json`). Store: `Backend`, `EmulatorHost`, `VerifoneHost`,
141
+ `LoginUsername`, `LoginPassword`, `LoginShift`, `RefundPassword` — with test defaults
142
+ (`VerifoneHost` "192.168.1.88", username "user", password "password123", shift
143
+ "shift123", refund password EMPTY test estates accept empty, matching the SDK and
144
+ Android defaults) so a fresh clone connects without typing.
145
+
146
+ #### Step 2a — Emulator (Wi-Fi, the default) and Verifone — IP entry
147
+
148
+ For the two IP-addressed backends show a **host/IP text box** (label "Emulator IP" or
149
+ "Verifone terminal IP" by selection) and an **Apply & Connect** button. On connect,
150
+ build the matching adapter from the stored host, dispose the previous `PathTerminal`,
151
+ and build a new one (only **one** client per terminal / per emulator Wi-Fi session).
152
+ TCP and Verifone need no scan `DiscoverDevicesAsync()` returns one synthetic device
153
+ for the host, so Apply & Connect connects directly.
154
+
155
+ #### Step 2b — Emulator (Bluetooth) — scan and connect
156
+
157
+ For the BLE backend keep a scan/connect flow: a **Scan** button `DiscoverDevicesAsync()`
158
+ list devices **Connect** live connection state from the `Events` stream.
159
+ Marshal UI updates onto the WinUI UI thread (`DispatcherQueue.TryEnqueue`).
160
+
161
+ #### Step 2c — Verifone backend and stored credentials
162
+
163
+ Call `get_code_example` with operation='verifone-init' and platform='windows'.
164
+ Construct it from a `VerifoneTerminalConfig` built from the **stored** login — never
165
+ hardcode credentials inline:
166
+ ```csharp
167
+ using Path.Terminal.PsdkAdapter;
168
+ var adapter = new VerifonePsdkAdapter(
169
+ new VerifoneTerminalConfig {
170
+ Host = settings.VerifoneHost,
171
+ Username = settings.LoginUsername,
172
+ Password = settings.LoginPassword,
173
+ Shift = settings.LoginShift,
174
+ RefundPassword = settings.RefundPassword,
175
+ },
176
+ log: Log);
177
+ ```
178
+ Keep the Verifone login fields **separate** (their own stored keys + editable fields).
179
+ All terminals are treated as **generic Verifone** (same AGPA app, same PSDK
180
+ connect/login) — no per-terminal config beyond IP + login.
181
+
182
+ ---
183
+
184
+ ### Step 3 — Wire sale, refund and void through the Path SDK
185
+
186
+ Once Step 1 is done, the harness's existing payment flow calls the Path-backed manager.
187
+ Confirm the transaction methods map correctly:
188
+ - Call `get_code_example` operation='sale' (platform='windows') handle `TransactionState.Approved`, `Declined`, `TimedOut`, `Failed`, default.
189
+ - Call `get_code_example` operation='refund' — pass `originalTransactionId` from the sale result.
190
+ - Call `get_code_example` operation='void' full reversal; success state is `Reversed`.
191
+ - Amounts are **minor units** (`420` = £4.20). Never pass decimals.
192
+ - Catch `PathError` — the SDK's exception type (there is NO `PathException`) — and check
193
+ its `.Recoverable` before retrying; `.Code` is the canonical error code.
194
+
195
+ ---
196
+
197
+ ### Step 3b Merchant logo on the customer display (idle branding)
198
+
199
+ The app can push a merchant logo to the terminal's **customer-facing display** (idle/attract
200
+ mode). It works on the **emulator (Wi-Fi and Bluetooth)** and on Verifone. Call
201
+ `get_code_example` operation='idle-branding' (platform='windows') first.
202
+
203
+ **The SDK API** (on `PathTerminal`, async):
204
+ ```csharp
205
+ Task SetIdleBrandingAsync(CustomerDisplayContent? content, CancellationToken ct = default);
206
+ // ImageBytes = raw PNG/JPEG bytes (NOT a BitmapImage / not base64); Caption is optional.
207
+ new CustomerDisplayContent(imageBytes, caption); // pass null content to CLEAR the logo
208
+ ```
209
+
210
+ **Add a "Customer Display" section to Settings** with:
211
+ - A **"Show merchant logo"** toggle (persisted).
212
+ - A **file picker** (`FileOpenPicker`, `.png`/`.jpg`) storing the image **pre-scaled to ≤ 480px
213
+ PNG** so a fresh launch still has a logo; ship a default logo asset so it works out of the box.
214
+ - An optional **caption** field.
215
+ - An **"Apply to terminal now"** button, **enabled only when connected**.
216
+
217
+ > **CRITICALre-apply after every connect.** A fresh connection starts with a **blank**
218
+ > display. Call `SetIdleBrandingAsync(...)` **right after every successful connect**, not just
219
+ > from the button — otherwise the logo never appears. Pass `null` content to clear when the
220
+ > toggle is off.
221
+
222
+ > **Best-effort + backend-gated.** `SetIdleBrandingAsync` swallows backend errors "no
223
+ > exception" does not prove it rendered; confirm on the device. Keep the Verifone payload small
224
+ > (a logo pre-scaled to ≤ 480px PNG, not a full-res photo, so the base64 stays under the cap).
225
+
226
+ ---
227
+
228
+ ### Step 3.5 Build to catch API mismatches (check the environment first)
229
+
230
+ Confirm your C# compiles against the SDK's **real** API. **Probe the toolchain first**, and
231
+ treat a missing one as an environment limitation to advise on, **never as a failure:**
232
+
233
+ 1. `dotnet --version` is the .NET 10 SDK available?
234
+ 2. **Present** → run `dotnet build Path.Epos.TestHarness.sln -c Debug`, fix any real API
235
+ mismatches, then continue to Step 4.
236
+ 3. **Absent** the code is complete; you just can't build *here*. Do **not** report a failure.
237
+ If a package manager is present, **offer** to install the .NET 10 SDK (`winget install Microsoft.DotNet.SDK.10`);
238
+ otherwise tell the developer: *"The code is done — build in Visual Studio or with `dotnet build`
239
+ and paste back any errors,"* and summarise the files you changed.
240
+
241
+ Frame a missing build toolchain as **"here's how to verify"**, never as a failure of the work.
242
+
243
+ ### Step 4 Verify it works end to end
244
+
245
+ 1. Confirm the app root composes the Path-backed manager, not `OCPayTerminalAdapter`.
246
+ 2. **Prove on the emulator first (the default — Wi-Fi/IP):**
247
+ - Put the Path POS Emulator in Wi-Fi mode (Config Connection); note the `IP:port`
248
+ on its welcome screen.
249
+ - Run the app → Settings → Path POS Adapter → **Emulator (Wi-Fi)** type that IP →
250
+ **Apply & Connect**. Connection state should reach Connected.
251
+ - (Optionally prove **Emulator (Bluetooth)**: select it, **Scan**, **Connect**.)
252
+ 3. Run a sale (amountMinor `100` = £1.00), present a card / tap the emulator's NFC tag,
253
+ verify `result.State == Approved`, `TransactionId` is non-null, and receipt data is
254
+ populated (`get_code_example` operation='receipt'). Run a refund and a void.
255
+ 4. **Then prove the switch to Verifone:** select **Verifone**, enter the terminal IP and
256
+ confirm the stored login, **Apply & Connect** — the SDK runs the PSDK init + login and
257
+ the same flow runs against the real terminal, unchanged. One client per terminal —
258
+ disconnect any other POS first.
259
+
260
+ ---
261
+
262
+ ## SDK rules — always follow these
263
+
264
+ **ALWAYS:**
265
+ - Call `get_code_example` (platform='windows') before writing SDK code
266
+ - Use `TransactionRequest.Sale(...)` / `.Refund(...)` / `.Void(...)` factory methods
267
+ - Use `RequestEnvelope.Create(...)` never build envelopes by hand
268
+ - For **refunds** the success state is the refunded/approved state, and for **voids** it is `Reversed` — not the sale's `Approved`
269
+ - Dispose the old `PathTerminal` before building a new one on a backend change
270
+ - Consume `Events` with `await foreach` on a background task — it is an
271
+ `IAsyncEnumerable<PathTerminalEvent>` (nested event records), NOT a C# event; there is no `+=`
272
+ - Marshal UI updates from `Events` onto the WinUI UI thread (`DispatcherQueue.TryEnqueue`)
273
+
274
+ **NEVER:**
275
+ - Use a raw `TransactionRequest` constructor — use the factory methods
276
+ - Store or log card data (masked PAN only)
277
+ - Retry a financial operation without a fresh `RequestEnvelope` (new idempotency key)
278
+ - Leave `OCPayTerminalAdapter` composed as the live adapter — Path must be the live one
279
+ - Leave auto-connect-on-launch running
280
+ - Set `OnCardRead` / `OnAck` on an adapter — `PathTerminal` owns those
281
+ - Write "Pico W" anywhere
282
+
283
+ ---
284
+
285
+ ## After making changes
286
+
287
+ Summarise each file changed and confirm Step 1 (the live adapter is now Path-backed).
288
+
289
+ Then give **tailored connect instructions** — the user enters the connection details, so
290
+ tell them exactly where you wired things in:
291
+ - The Settings section you added the backend switcher to
292
+ - How to connect to the emulator over **Wi-Fi/IP** (the default): which backend, where to
293
+ type the emulator's IP, which button connects
294
+ - How to connect over **Bluetooth** instead (Scan Connect)
295
+ - How to **switch to Verifone** when ready: pick Verifone, enter the terminal IP, confirm
296
+ the stored login, connect
297
+
298
+ Keep it brief and concrete — the user should follow it without reading code.