path-terminal-init 0.2.8 → 0.2.9
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "path-terminal-init",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "One-line setup for the Path Terminal SDK integration assistant — connects the user's AI agent to the Path MCP server and installs the per-OS integration recipe (iOS, Android, Windows). The single source of truth for the bootstrap CLI and the recipes.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"path-terminal-init": "dist/cli.js"
|
|
@@ -151,11 +151,28 @@ returns one synthetic device for the configured host, so "Apply & Connect" conne
|
|
|
151
151
|
|
|
152
152
|
#### Step 2b — Emulator over Bluetooth — scan and connect
|
|
153
153
|
|
|
154
|
+
> **⚠ Runtime permissions are MANDATORY — this is the #1 reason Bluetooth silently
|
|
155
|
+
> fails.** Declaring `BLUETOOTH_SCAN` / `BLUETOOTH_CONNECT` in the manifest is **not
|
|
156
|
+
> enough** on Android 12+ (API 31). You must **request them at runtime** before the
|
|
157
|
+
> first scan, or `discoverDevices()` finds nothing / throws and the user sees an empty
|
|
158
|
+
> list with no error. Call `get_code_example` with operation='backend-switch' and
|
|
159
|
+
> platform='android' and reproduce its `rememberBlePermission()` pattern:
|
|
160
|
+
> - Use `ActivityResultContracts.RequestMultiplePermissions()` to request
|
|
161
|
+
> `BLUETOOTH_SCAN` **and** `BLUETOOTH_CONNECT` (guarded by `Build.VERSION.SDK_INT >= S`).
|
|
162
|
+
> - Show a **"Grant Bluetooth permission"** button when not yet granted, and **gate the
|
|
163
|
+
> Scan button** on the granted state — don't let the user scan without permission.
|
|
164
|
+
> - Keep `android:usesPermissionFlags="neverForLocation"` on `BLUETOOTH_SCAN` (call
|
|
165
|
+
> `get_manifest_requirements`) so `ACCESS_FINE_LOCATION` is not also required.
|
|
166
|
+
> - Surface **"Bluetooth is turned off"** — `discoverDevices()` throws
|
|
167
|
+
> `PathError(CONNECTIVITY)` when the radio is disabled. The emulator must be in
|
|
168
|
+
> Bluetooth mode and advertise a name containing "Path".
|
|
169
|
+
|
|
154
170
|
For the BLE backend keep the scan/connect flow:
|
|
155
|
-
1. **
|
|
156
|
-
2.
|
|
157
|
-
3.
|
|
158
|
-
4.
|
|
171
|
+
1. **Grant Bluetooth permission** (runtime, per the callout above) — required before scanning
|
|
172
|
+
2. **Scan for Path Terminals** — triggers BLE discovery (only enabled once permission is granted)
|
|
173
|
+
3. Discovered devices list — each with a **Connect** button
|
|
174
|
+
4. Live connection state (Scanning / Connected / Disconnected)
|
|
175
|
+
5. **Disconnect** when connected
|
|
159
176
|
|
|
160
177
|
**Add to `PaymentTerminalAdapter` interface** (with no-op default implementations):
|
|
161
178
|
```kotlin
|
|
@@ -248,6 +265,44 @@ Call `validate_integration` with platform='android' on your code before finalisi
|
|
|
248
265
|
|
|
249
266
|
---
|
|
250
267
|
|
|
268
|
+
### Step 3b — Merchant logo on the customer display (idle branding)
|
|
269
|
+
|
|
270
|
+
The demo can push a merchant logo to the terminal's **customer-facing display** (idle/
|
|
271
|
+
attract mode). This is a real feature you MUST wire — it works on the **emulator (Wi-Fi
|
|
272
|
+
and Bluetooth)** and on Verifone. Call `get_code_example` with operation='idle-branding'
|
|
273
|
+
and platform='android' first, then reproduce the demo's proven pattern.
|
|
274
|
+
|
|
275
|
+
**The SDK API** (on `PathTerminal`, a `suspend` function):
|
|
276
|
+
```kotlin
|
|
277
|
+
suspend fun setIdleBranding(content: CustomerDisplayContent?)
|
|
278
|
+
// content: raw PNG/JPEG bytes wrapped in a class — NOT a Bitmap/base64/resource id
|
|
279
|
+
CustomerDisplayContent(imageBytes = logoBytes, caption = caption?.ifBlank { null })
|
|
280
|
+
// pass null to CLEAR the logo (when the "show logo" toggle is off)
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**Add a "Customer Display" section to Settings** with:
|
|
284
|
+
- A **"Show merchant logo"** toggle (persisted).
|
|
285
|
+
- A **photo picker** — `ActivityResultContracts.GetContent()` launched with `"image/*"` —
|
|
286
|
+
storing the chosen image **pre-scaled to ≤ 480px PNG** in private storage (so a fresh
|
|
287
|
+
launch still has a logo). Ship a default logo asset so it works out of the box.
|
|
288
|
+
- An optional **caption** field.
|
|
289
|
+
- An **"Apply to terminal now"** button, **enabled only when `connectionState` is Connected**.
|
|
290
|
+
|
|
291
|
+
> **CRITICAL — re-apply after every connect.** A fresh connection starts with a **blank**
|
|
292
|
+
> display. The SDK re-pushes after each sale/refund, but the *first* show is yours: call
|
|
293
|
+
> your `applyBranding()` (which calls `terminal.setIdleBranding(...)`) **right after every
|
|
294
|
+
> successful `terminal.connect()`**, not just from the button. An integration that only
|
|
295
|
+
> sets branding once, or never calls `setIdleBranding` at all, shows nothing.
|
|
296
|
+
|
|
297
|
+
> **Best-effort + backend-gated.** `setIdleBranding` returns `Unit` and swallows backend
|
|
298
|
+
> errors — **"no exception" does not prove the logo rendered**; confirm on the device.
|
|
299
|
+
> Keep the Verifone payload small (a logo, not a full-res photo) — pre-scaling to ≤ 480px
|
|
300
|
+
> PNG keeps the base64 under the ~32 000-char cap that would otherwise wedge the display.
|
|
301
|
+
|
|
302
|
+
Call `validate_integration` with platform='android' before finalising.
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
251
306
|
### Step 3.5 — Compile the changes (check the environment first)
|
|
252
307
|
|
|
253
308
|
Confirm your code compiles against the SDK's **real** API — this catches wrong method
|
|
@@ -278,18 +333,25 @@ Frame a missing build toolchain as **"here's how to verify"**, never as a failur
|
|
|
278
333
|
|
|
279
334
|
1. Confirm the Application/MainActivity injects `PathTerminalAdapter` (not the old adapter).
|
|
280
335
|
2. Confirm AndroidManifest.xml has BLUETOOTH_SCAN and BLUETOOTH_CONNECT permissions
|
|
281
|
-
(
|
|
336
|
+
(with `neverForLocation` on SCAN) **and** that the app requests them at **runtime**
|
|
337
|
+
before scanning — manifest-only is not enough on Android 12+ (Step 2b).
|
|
282
338
|
3. **Prove on the emulator first (the default path — Wi-Fi/IP):**
|
|
283
339
|
- Put the Path POS Emulator in Wi-Fi mode (Config → Connection on the device);
|
|
284
340
|
it shows its `IP:port` on the welcome screen.
|
|
285
341
|
- Run the app → Settings → Payment Terminal → backend **Emulator (Wi-Fi)** → type
|
|
286
342
|
that IP → **Apply & Connect**. connectionState should reach Connected.
|
|
287
|
-
- (Optionally also prove **Emulator (Bluetooth)**:
|
|
343
|
+
- (Optionally also prove **Emulator (Bluetooth)**: **Grant Bluetooth permission** when
|
|
344
|
+
prompted, then select it, **Scan**, **Connect**. If Scan finds nothing, the runtime
|
|
345
|
+
permission was almost certainly not granted/requested — recheck Step 2b.)
|
|
288
346
|
4. Add items to cart → Card payment → present a card / tap NFC tag on emulator when prompted.
|
|
289
347
|
5. Verify:
|
|
290
348
|
- result.state == TransactionState.APPROVED (or result.isApproved == true)
|
|
291
349
|
- result.transactionId is non-null
|
|
292
350
|
- Receipt data populated via terminal.getReceiptData(transactionId)
|
|
351
|
+
5b. **Prove the merchant logo (Step 3b):** in Settings → Customer Display, pick a logo and
|
|
352
|
+
tap **Apply to terminal now** while connected — confirm it appears on the emulator's
|
|
353
|
+
customer display. Then disconnect/reconnect and confirm it re-appears (proves you
|
|
354
|
+
re-apply after connect).
|
|
293
355
|
6. **Then prove the switch to Verifone:** select backend **Verifone**, enter the terminal's
|
|
294
356
|
IP and confirm the stored login, **Apply & Connect** — the SDK runs the PSDK init + login
|
|
295
357
|
and the same sale/refund/void flow runs against the real terminal, unchanged. (Only one
|
|
@@ -306,6 +368,10 @@ Frame a missing build toolchain as **"here's how to verify"**, never as a failur
|
|
|
306
368
|
- Handle TransactionState: APPROVED, DECLINED, TIMED_OUT, FAILED, CANCELLED
|
|
307
369
|
- For **refunds**, success state is `REFUNDED` — not `APPROVED`
|
|
308
370
|
- Catch `PathError` and check `.recoverable`
|
|
371
|
+
- Request BLE runtime permissions (`BLUETOOTH_SCAN` + `BLUETOOTH_CONNECT`) before scanning —
|
|
372
|
+
the manifest declaration alone does NOT work on Android 12+ (see Step 2b)
|
|
373
|
+
- Re-apply idle branding (`terminal.setIdleBranding(CustomerDisplayContent(imageBytes, caption))`)
|
|
374
|
+
after every successful `connect()`, not just from the button (see Step 3b)
|
|
309
375
|
- Call `validate_integration` with platform='android' before presenting code
|
|
310
376
|
- Call from coroutines — `terminal.sale()` and `terminal.refund()` are `suspend` functions
|
|
311
377
|
|
|
@@ -196,6 +196,37 @@ Confirm the transaction methods map correctly:
|
|
|
196
196
|
|
|
197
197
|
---
|
|
198
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
|
+
|
|
199
230
|
### Step 3.5 — Build to catch API mismatches (check the environment first)
|
|
200
231
|
|
|
201
232
|
Confirm your C# compiles against the SDK's **real** API. **Probe the toolchain first**, and
|
|
@@ -192,6 +192,37 @@ Confirm the transaction methods map correctly:
|
|
|
192
192
|
|
|
193
193
|
---
|
|
194
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
|
+
|
|
195
226
|
### Step 3.5 — Build to catch API mismatches (check the environment first)
|
|
196
227
|
|
|
197
228
|
Confirm your C# compiles against the SDK's **real** API. **Probe the toolchain first**, and
|