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