diff --git a/poscreators/middleware-doc/general/cash-register-integration/multi-markets-integration-guide.md b/poscreators/middleware-doc/general/cash-register-integration/multi-markets-integration-guide.md deleted file mode 100644 index 9b681be..0000000 --- a/poscreators/middleware-doc/general/cash-register-integration/multi-markets-integration-guide.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -slug: /poscreators/interface-doc/general/cash-register-integration/multi-markets-integration-guide -title: Multi-Markets Integration ---- - -# Multi-Markets Integration - -This guide provides a guideline of which business cases should be implemented in which market. -For business cases which are only differing by the Country Code we recommend to use a mapping table in your POS Software. - -## Typical sign flow -- generate generic receipt requests - -The typical sign flow happens when a cash register transfers the data of a receipt request to the fiskaltrust.Middleware using the ReceiptRequest data structure, which will be then processed by the fiskaltrust.SecurtityMechanism. -After being processed by the fiskaltrust.SecurtityMechanism, the data will be added to the fiskaltrust.ReceiptResponse, which will be sent to the database of the cash register enabling the receipt to be printed either on paper or digitally. -For more detailed information, please visit the [PosCreators documentation](../../general/cash-register-integration/cash-register-integration-regular-workflow.md) - -- depending on the market, set the chargeitem-, payitem- and receiptcases (not every operation may have an e.g. receiptcase in each market -> in that case, do nothing. example: order in AT and FR) -![](./images/12-multi-market-mapping.png) - -## Mapping Table -The table below shows a comparison of common business cases (e.g. ftReceiptcases, ftChargeItemcases and ftPayItemcases) for every market. -More details for ftReceiptcases, ftPayItemcases and ft ChargeItemcases can be found for each market on the appropriate country specific appendix. - -|**business cases** | **AT** | **DE** |**FR** |**ME**|**IT**| -|----------------------|-----------|-----------------------|--------------------------------------|-----------------------------|-----------------------------| -|**ftReceiptcase**||||||| -|Cash sales / POS-receipt / Ticket|`0x4154000000000001`|`0x4445000100000001`|`0x4652000000000001`|`0x4D45000000000001`| `0x4954200000000001`| -|Zero receipt|`0x4154000000000002`|`0x4445000000000002`|`0x465200000000000F`|`0x4D45000000000002`| `0x4954200000002000`| -|Initial operation/start receipt|`0x415400000000000`|`0x4445000000000003`|`0x4652000000000010`|`0x4D45000000000003`| `0x4954200000004001`| -|Out of operation/stop receipt|`0x4154000000000004`|`0x4445000000000004`|`0x4652000000000011`|`0x4D45000000000004`| `0x4954200000004002`| -|Monthly closing|`0x4154000000000005`|*optional* `0x4445000000000005`|`0x4652000000000006`|`0x4D45000000000005`|| -|Yearly closing|`0x4154000000000006`|*optional* `0x4445000000000006`|`0x4652000000000007`|`0x4D45000000000006`|| -|Daily closing|| `0x4445000000000007`|`0x4652000000000005`||`0x4954200000002011`| -|Opening balance||||`0x4D45000000000007`|| -|Cash withdrawal||||`0x4D45000000000008`|| -|Start-transaction Receipt||`0x4445000000000008`|||| -|Update-transaction Receipt||`0x4445000000000009)`||| -|Fail transaction Receipt||`0x444500000000000B` (single) `0x444500010000000B` (multiple) ||||| -|Initiate SCU switch||`0x4445000000000017`|||| -|Finish SCU switch||`0x4445000000000018`|||| -|Archives|||`0x4652000000000015`||| - -|**business cases** | **AT** | **DE** |**FR** |**ME**|**IT**| -|----------------------|-----------|-----------------------|--------------------------------------|-----------------------------|-----------------------------| -|**ftChargeItemcase**| | | | | | -|Unknown type of service/product normal|`0x4154000000000003`|`0x4445000000000001`|`0x465200000000003`|`0x4D45000000000001`|`0x4954200000000003`| -|Unknown type of service/product discounted-1|`0x4154000000000001`|`0x4445000000000002`|`0x465200000000001`|`0x4D45000000000002`|`0x4954200000000001`| -|Unknown type of service/product discounted-2|`0x4154000000000002`||`0x465200000000002`||`0x4954200000000002`| - -|**business cases** | **AT** | **DE** |**FR** |**ME**|**IT**| -|----------------------|-----------|-----------------------|--------------------------------------|-----------------------------|-----------------------------| -|**ftPayItemcase** |||||| -|Cash payment in national currency|`0x4154000000000001`|`0x4445000000000001`|`0x4652000000000001`|`0x4D45000000000001`|`0x4954200000000001`| -|Cash payment in foreign currency|`0x4154000000000002`|`0x4445000000000002`|`0x4652000000000002`|`0x4D45000000000002`|| -|Crossed cheque|`0x4154000000000003`|`0x4445000000000003`|`0x4652000000000003`|`0x4D45000000000003`|`0x4954200000000003`| -|Debit card payment|`0x4154000000000004`|`0x4445000000000004`|`0x4652000000000004`|`0x4D45000000000004`|`0x4954200000000004`| -|Credit card payment|`0x4154000000000005`|`0x4445000000000005`|`0x4652000000000005`|`0x4D45000000000005`|`0x4954200000000005`| -|Online payment|`0x4154000000000007`|`0x4445000000000006`|`0x4652000000000007`|`0x4D45000000000008`|`0x4954200000000007`| -|Customer card payment|`0x4154000000000008`|`0x4445000000000007`|`0x4652000000000008`|`0x4D45000000000009`|`0x4954200000000008`| -|Sepa transfer|`0x415400000000000C`|`0x4445000000000008`|`0x465200000000000C`|`0x4D4500000000000A`|`0x495420000000000A`| -|Internal material consumption|`0x4154000000000011`|`0x444500000000000A`|`0x4652000000000011`|`0x4D4500000000000C`|`0x495420000000000D`| diff --git a/poscreators/middleware-doc/possystem-api/android-intent.md b/poscreators/middleware-doc/possystem-api/android-intent.md new file mode 100644 index 0000000..2a1ddce --- /dev/null +++ b/poscreators/middleware-doc/possystem-api/android-intent.md @@ -0,0 +1,527 @@ +--- +slug: /poscreators/possystem-api/android-intent +title: Android Intent Integration +--- + +# Android Intent Integration + +## Overview + +This document describes the Android intent call interface for unlocking offline capability of the fiskaltrust.Middleware. The POS System API provides a standardized REST API for Point of Sale (POS) systems/Electronic Cash Register (ECR) systems to interact with fiscal middleware services across multiple European markets (Austria, Germany, France, Italy, Greece, Spain, Portugal, Belgium). + +By using Android intents, mobile applications can communicate with the fiskaltrust.Middleware running side-by-side on Android devices to perform fiscal operations even without an active internet connection. + +### Key Features + +- **Offline fiscalization**: Perform fiscal operations without internet connectivity +- **Multi-market compliance**: Support for AT, DE, FR, IT, GR, ES, PT, BE fiscal requirements +- **Synchronous operation mode** +- **Idempotent operations**: Safe retry mechanism with operation IDs +- **State-based operation tracking**: Monitor operation progress + +### API Version + +This documentation is based on the [fiskaltrust PosSystemAPI v2.1](https://docs.fiskaltrust.cloud/apis/pos-system-api-v21) specification. + +## Architecture Overview + +The PosSystemAPI v2 uses a state-based operation model where each operation follows a lifecycle: + +``` +Pending → Processing → Done/Failed +``` + +Operations are identified by a unique `x-operation-id` header, enabling idempotent retries and duplicate detection. + +### Operation Modes + +**Synchronous Mode**: Send request to `/v2/{endpoint}` and receive the response directly via `startActivityForResult()`. + +All operations in this documentation use the synchronous mode for simplicity and immediate responses. If there is a breakdown in communication, the POS system can retry the operation using the same `x-operation-id`. + +## Android Intent Integration + +### Intent Action Format + +Android intents to communicate with fiskaltrust.Middleware follow this pattern: + +``` +className: "eu.fiskaltrust.androidlauncher.PosSystemAPI" +package: "eu.fiskaltrust.androidlauncher" +``` + +**Important**: Always use **explicit intents** by specifying the package name for security: + +```kotlin +val intent = Intent() +intent.setClassName("eu.fiskaltrust.androidlauncher", "eu.fiskaltrust.androidlauncher.PosSystemAPI") +// Note: Using setClassName for explicit targeting of the PosSystemAPI activity +``` + +### Intent Extras Structure + +All API calls use the following intent extras: + +| Extra Key | Type | Required | Description | +|-----------|------|----------|-------------| +| `Method` | String | Yes | HTTP method ("POST", "GET", "PUT", "DELETE") | +| `Path` | String | Yes | API path (e.g., "/echo", "/sign", "/pay" for latest version; e.g., "/v2/echo", "/v2/sign", "/v2/pay" for v2 specific) | +| `HeaderJsonObjectBase64Url` | String | Yes | Base64URL-encoded JSON headers object (includes authentication, content-type, etc.) | +| `BodyBase64Url` | String | No* | Base64URL-encoded request body | + +* Empty strings in `BodyBase64Url` indicate no body content for methods like GET or DELETE. + +**Note on Base64URL encoding**: All data is Base64URL-encoded to avoid character encoding issues and handle binary data safely. Use standard Base64URL encoding (RFC 4648 §5) which replaces `+` with `-` and `/` with `_`, with no padding. + +### Complete Intent Call Example + +Here's a complete example showing how to make an intent call with proper Base64URL encoding: + +**Kotlin Example:** + +```kotlin +import android.content.Intent +import android.util.Base64 +import org.json.JSONObject + +fun callPosSystemAPI(method: String, path: String, headers: Map, body: String?) { + // Create headers JSON + val headersJson = JSONObject(headers).toString() + + // Base64URL encode (no padding, replace + with -, / with _) + val headerB64 = headersJson.toByteArray(Charsets.UTF_8) + .let { Base64.encodeToString(it, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP) } + + val bodyB64 = body?.toByteArray(Charsets.UTF_8) + ?.let { Base64.encodeToString(it, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP) } + + // Create intent + val intent = Intent() + intent.setClassName("eu.fiskaltrust.androidlauncher", "eu.fiskaltrust.androidlauncher.PosSystemAPI") + intent.putExtra("Method", method) + intent.putExtra("Path", path) + intent.putExtra("HeaderJsonObjectBase64Url", headerB64) + if (bodyB64 != null) { + intent.putExtra("BodyBase64Url", bodyB64) + } + + startActivityForResult(intent, REQUEST_CODE) +} + +// Example: Echo call +fun echoExample() { + val headers = mapOf( + "Accept" to "application/json", + "x-cashbox-id" to "de12c75f-5587-48b8-8ac5-64b7c81a05ec", + "x-cashbox-accesstoken" to "your-access-token", + "x-operation-id" to java.util.UUID.randomUUID().toString(), + ) + val body = """{"Message": "Hello, World!"}""" + + callPosSystemAPI("POST", "/echo", headers, body) +} + +override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + + if (requestCode == REQUEST_CODE && data != null) { + val statusCode = data.getStringExtra("StatusCode") ?: "500" + val contentB64 = data.getStringExtra("ContentBase64Url") ?: "" + val contentTypeB64 = data.getStringExtra("ContentTypeBase64Url") ?: "" + + // Decode Base64URL + val content = Base64.decode(contentB64, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP) + .toString(Charsets.UTF_8) + val contentType = Base64.decode(contentTypeB64, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP) + .toString(Charsets.UTF_8) + + Log.i("PosSystemAPI", "Status: $statusCode, Type: $contentType") + Log.i("PosSystemAPI", "Response: $content") + } +} +``` + +**Java Example:** + +```java +import android.content.Intent; +import android.util.Base64; +import org.json.JSONObject; + +public void callPosSystemAPI(String method, String path, JSONObject headers, String body) { + try { + // Base64URL encode headers + String headersJson = headers.toString(); + byte[] headerBytes = headersJson.getBytes("UTF-8"); + String headerB64 = Base64.encodeToString(headerBytes, + Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP); + + // Base64URL encode body + String bodyB64 = null; + if (body != null) { + byte[] bodyBytes = body.getBytes("UTF-8"); + bodyB64 = Base64.encodeToString(bodyBytes, + Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP); + } + + // Create intent + Intent intent = new Intent(); + intent.setClassName("eu.fiskaltrust.androidlauncher", + "eu.fiskaltrust.androidlauncher.PosSystemAPI"); + intent.putExtra("Method", method); + intent.putExtra("Path", path); + intent.putExtra("HeaderJsonObjectBase64Url", headerB64); + if (bodyB64 != null) { + intent.putExtra("BodyBase64Url", bodyB64); + } + + startActivityForResult(intent, REQUEST_CODE); + } catch (Exception e) { + Log.e("PosSystemAPI", "Error creating intent", e); + } +} + +@Override +protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (requestCode == REQUEST_CODE && data != null) { + String statusCode = data.getStringExtra("StatusCode"); + String contentB64 = data.getStringExtra("ContentBase64Url"); + String contentTypeB64 = data.getStringExtra("ContentTypeBase64Url"); + + try { + // Decode Base64URL + byte[] contentBytes = Base64.decode(contentB64, + Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP); + String content = new String(contentBytes, "UTF-8"); + + byte[] typeBytes = Base64.decode(contentTypeB64, + Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP); + String contentType = new String(typeBytes, "UTF-8"); + + Log.i("PosSystemAPI", "Status: " + statusCode + ", Type: " + contentType); + Log.i("PosSystemAPI", "Response: " + content); + } catch (Exception e) { + Log.e("PosSystemAPI", "Error decoding response", e); + } + } +} +``` + +**C#/MAUI Example:** + +```csharp +#if ANDROID +using Android.App; +using Android.Content; +using Android.OS; +using Android.Runtime; +using System; +using System.Text; +using Newtonsoft.Json; + +public class PosSystemApiActivityCaller : Activity +{ + private const int RequestCode = 1001; + + public void CallPosSystemAPI(string method, string path, object headers, string body) + { + // Serialize headers to JSON + var headersJson = JsonConvert.SerializeObject(headers); + + // Base64URL encode + var headerB64 = ToBase64Url(headersJson); + var bodyB64 = body != null ? ToBase64Url(body) : null; + + // Create intent + var intent = new Intent(); + intent.SetClassName("eu.fiskaltrust.androidlauncher", + "eu.fiskaltrust.androidlauncher.PosSystemAPI"); + intent.PutExtra("Method", method); + intent.PutExtra("Path", path); + intent.PutExtra("HeaderJsonObjectBase64Url", headerB64); + if (bodyB64 != null) + { + intent.PutExtra("BodyBase64Url", bodyB64); + } + + StartActivityForResult(intent, RequestCode); + } + + private string ToBase64Url(string text) + { + var bytes = Encoding.UTF8.GetBytes(text); + return Convert.ToBase64String(bytes) + .TrimEnd('=') + .Replace('+', '-') + .Replace('/', '_'); + } + + private string FromBase64Url(string base64Url) + { + var base64 = base64Url + .Replace('-', '+') + .Replace('_', '/'); + + // Add padding if needed + switch (base64.Length % 4) + { + case 2: base64 += "=="; break; + case 3: base64 += "="; break; + } + + var bytes = Convert.FromBase64String(base64); + return Encoding.UTF8.GetString(bytes); + } + + // Example: Echo call + public void EchoExample() + { + var headers = new Dictionary + { + { "Accept", "application/json" }, + { "x-cashbox-id", "de12c75f-5587-48b8-8ac5-64b7c81a05ec" }, + { "x-cashbox-accesstoken", "your-access-token" }, + { "x-operation-id", Guid.NewGuid().ToString() } + }; + var body = JsonConvert.SerializeObject(new { Message = "Hello, World!" }); + + CallPosSystemAPI("POST", "/echo", headers, body); + } + + protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data) + { + base.OnActivityResult(requestCode, resultCode, data); + + if (requestCode == RequestCode && data != null) + { + var statusCode = data.GetStringExtra("StatusCode") ?? "500"; + var contentB64 = data.GetStringExtra("ContentBase64Url") ?? ""; + var contentTypeB64 = data.GetStringExtra("ContentTypeBase64Url") ?? ""; + + // Decode Base64URL + var content = FromBase64Url(contentB64); + var contentType = FromBase64Url(contentTypeB64); + + Android.Util.Log.Info("PosSystemAPI", $"Status: {statusCode}, Type: {contentType}"); + Android.Util.Log.Info("PosSystemAPI", $"Response: {content}"); + } + } +} +#endif +``` + +### Response Structure + +The middleware responds via a result intent with these extras: + +| Extra Key | Type | Description | +|-----------|------|-------------| +| `StatusCode` | String | HTTP status code as string (e.g., "200", "201", "400", "500") | +| `ContentBase64Url` | String | Base64URL-encoded response body | +| `ContentTypeBase64Url` | String | Base64URL-encoded content type (e.g., "application/json") | +| `HeaderJsonObjectBase64Url` | String | Base64URL-encoded JSON headers object (optional, for response headers) | + +## Available Endpoints + +### 1. Echo - Connectivity Testing + +**Purpose**: Test basic communication with the middleware and verify configuration. + +**Endpoint**: `/echo` + +**Scenario**: Used to verify that the POS system can communicate with the middleware before performing fiscal operations. + +### 2. Sign - Receipt Fiscalization + +**Purpose**: Fiscalize receipts by sending them to the fiscal middleware for signing and compliance. + +**Endpoint**: `/sign` + +**Scenario**: Used after completing a transaction to ensure fiscal compliance by signing the receipt data. + +### 3. Pay - Payment Processing + +**Purpose**: Process payments through integrated payment providers (PayPal, Viva, BlueCode, Hobex, etc.). + +**Endpoint**: `/pay` + +**Scenario**: Initiate payment processing with external payment terminals or providers. + +### 4. Cart - Shopping Cart Management + +**Purpose**: Manage shopping carts for customer journeys across multiple steps. + +**Endpoints**: +- `POST /v2/cart` - Create new cart +- `GET /v2/cart` - Get active cart +- `GET /v2/cart/{journeyId}` - Get specific cart +- `PUT /v2/cart/{journeyId}` - Update cart +- `DELETE /v2/cart/{journeyId}` - Delete cart +- `POST /v2/cart/{journeyId}/setdone` - Mark cart as done +- `PUT /v2/cart/{journeyId}/deliverymethod` - Set delivery method +- `POST /v2/cart/{journeyId}/authorize` - Authorize cart + +**Scenario**: Track customer purchases across multiple interactions, especially useful for table service or order-ahead scenarios. + +### 5. Order - Order Item Management + +**Purpose**: Add charge items to a cart/journey. + +**Endpoint**: `/order` + +**Scenario**: Add items to an existing cart during the customer's shopping journey. + +### 6. Issue - Receipt Issuance + +**Purpose**: Issue/print receipts and make them available for customer retrieval. + +**Endpoints**: +- `PUT /v2/issue/{queueId}/{queueItemId}` - Issue receipt +- `GET /v2/issue/{queueId}/{queueItemId}` - Get issued receipt +- `GET /v2/issue/{queueId}/{queueItemId}/delivered` - Mark as delivered +- `GET /v2/issue/{queueId}/{queueItemId}/link/qrcode` - Get QR code link + +**Scenario**: After fiscalizing a receipt, make it available to the customer via digital channels. + +### 7. Journal - Transaction Logging + +**Purpose**: Query the fiscal journal for auditing and reporting. + +**Endpoints**: +- `POST /v2/journal` - Query journal entries +- `GET /v2/journal/OperationItem/{operationId}` - Get specific operation +- `GET /v2/PeekJournalItem/{queueId}/OperationItem/{operationId}` - Peek at operation +- `GET /v2/PeekJournalItem/{queueId}/OperationItem` - List operations + +**Scenario**: Retrieve historical transaction data for reporting or compliance purposes. + +## Error Handling + +### HTTP Status Codes + +| Code | Meaning | Description | +|------|---------|-------------| +| 200 | OK | Existing operation retrieved successfully | +| 201 | Created | New operation created and processed successfully | +| 400 | Bad Request | Invalid request data or validation error | +| 401 | Unauthorized | Invalid or missing access token | +| 404 | Not Found | Resource not found | +| 409 | Conflict | Operation ID conflict (duplicate with different payload) | +| 500 | Internal Server Error | Processing error | +| 502 | Bad Gateway | Middleware communication error | + +### Error Response Structure + +```json +{ + "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1", + "title": "Validation Error", + "status": 400, + "detail": "cbReceiptMoment is required", + "errors": { + "cbReceiptMoment": ["The cbReceiptMoment field is required."] + } +} +``` + +## Integration Steps for Android Applications + +### 1. Add Permissions to AndroidManifest.xml + +```xml + + + + + + + + + + + + +``` + +### 2. Verify Middleware Installation + +```kotlin +fun isMiddlewareInstalled(context: Context): Boolean { + val intent = Intent("eu.fiskaltrust.androidlauncher.POSSYSTEMAPI") + intent.addCategory(Intent.CATEGORY_DEFAULT) + val resolveInfo = context.packageManager.resolveActivity( + intent, + PackageManager.MATCH_DEFAULT_ONLY + ) + return resolveInfo != null +} +``` + +## Limitations and Requirements + +### Requirements + +1. **fiskaltrust.Middleware**: The middleware application must be installed and running on the Android device +2. **Configuration**: Middleware must be properly configured with valid cashbox ID and access token +3. **Android Version**: Minimum Android 8.0 (API level 26) recommended +4. **Permissions**: App must have appropriate permissions to send intents +5. **Internet**: Initial setup and some operations may require internet connectivity + +### Limitations + +1. **Offline Mode**: Full offline capability depends on middleware configuration and market requirements +2. **Payment Providers**: Not all payment providers support offline operation +3. **Queue Capacity**: Limited storage capacity for offline transactions on the device +4. **Synchronization**: Offline transactions must be synchronized when connectivity is restored +5. **Market-Specific**: Some features are only available in specific markets (e.g., Viva payments in Greece) + +### Best Practices + +1. **Operation IDs**: Always use unique GUIDs for operation IDs to ensure idempotency +2. **Error Handling**: Implement robust error handling and retry logic +3. **State Monitoring**: For long operations, use async mode and poll state regularly +4. **Timeout Handling**: Set appropriate timeouts and handle timeout scenarios +5. **Network Detection**: Check network availability and switch between online/offline modes accordingly +6. **Data Validation**: Validate request data before sending to avoid unnecessary errors +7. **Secure Storage**: Store access tokens and sensitive data securely +8. **Testing**: Thoroughly test both online and offline scenarios + +## Testing and Development + +### Local Testing + +For development and testing purposes, you can use localhost mode: + +```kotlin +// Use the callPosSystemAPI() helper function shown in the Complete Intent Call Example section above""") +} +``` + +### Sandbox Environment + +Use sandbox credentials for testing: + +```kotlin +const val SANDBOX_CASHBOX_ID = "sandbox-cashbox-id" +const val SANDBOX_ACCESS_TOKEN = "sandbox-access-token" +``` + +### Mock Responses + +For unit testing without middleware: + +```kotlin +class MockMiddlewareClient { + fun signReceipt(request: ReceiptRequest): ReceiptResponse { + return ReceiptResponse( + ftCashBoxID = request.ftCashBoxID, + ftQueueID = UUID.randomUUID(), + ftQueueItemID = UUID.randomUUID(), + // ... mock response fields + ) + } +} +``` diff --git a/poscreators/middleware-doc/possystem-api/images/pos-system-api-request-flow.svg b/poscreators/middleware-doc/possystem-api/images/pos-system-api-request-flow.svg new file mode 100644 index 0000000..7b35bb2 --- /dev/null +++ b/poscreators/middleware-doc/possystem-api/images/pos-system-api-request-flow.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/poscreators/middleware-doc/possystem-api/introduction.md b/poscreators/middleware-doc/possystem-api/introduction.md new file mode 100644 index 0000000..8500378 --- /dev/null +++ b/poscreators/middleware-doc/possystem-api/introduction.md @@ -0,0 +1,123 @@ +--- +slug: /poscreators/possystem-api/introduction +title: Introduction +--- + +# Introduction + +The **fiskaltrust POS System API** is the central, process-driven HTTP/JSON interface between a POS system and the **fiskaltrust.Middleware**. Unlike a typical REST API, it does not model resources to be created, read, updated, or deleted — instead it models a fiscal transaction as a **server-side state machine**, where each request advances the transaction through a deterministic state transition. All country-specific fiscal logic (signing, hash chaining, audit-trail formatting) is handled by the Middleware behind the same set of endpoints, so POS systems integrate once and run across all supported markets. + +Through this API, POS systems interact with the Middleware to: + +- Register order and payment data. +- Fiscalize and cryptographically seal receipts. +- Issue digital or printable receipts. +- Export journal data for audits and closings. + +## Process-Driven and Idempotent Design + +The API uses a state-based design: each transaction advances through deterministic state transitions on the server side, like a finite state machine. + +Key characteristics: + +- Each request represents one step in a fiscal process. +- Calls are idempotent and safe to retry. +- The backend guarantees deterministic results for repeated calls. + +To enable safe retries, every request must include a unique operation identifier (`x-operation-id`). If a request is repeated with the same identifier, the Middleware either: + +- Returns the already completed result, +- Blocks until the original operation finishes, or +- Returns `409 Conflict` if the same `x-operation-id` is reused with a different request body. + +This approach ensures robustness against network interruptions and timeouts without risking duplicate fiscal actions. A retry must always send the exact same body as the original request; otherwise the call is rejected as a conflict. + +## Authentication + +The POS System API is always scoped to a CashBox. A request is authenticated by sending the CashBox's access token (`x-cashbox-accesstoken`) alongside the CashBox identifier (`x-cashbox-id`). Both values are provisioned by the **fiskaltrust Portal** when the CashBox is created and must be stored securely on the POS side. There is no separate login or token-exchange step — credentials are sent on every request. + +## Request Headers + +Each request carries four headers covering authentication, identification, and idempotency: + +- `x-cashbox-id` – identifies the target CashBox. +- `x-cashbox-accesstoken` – authenticates the caller (see [Authentication](#authentication)). +- `x-possystem-id` – identifies the registered POS system variant. +- `x-operation-id` – per-operation idempotency key (see [Process-Driven and Idempotent Design](#process-driven-and-idempotent-design)). + +The first three are issued via the fiskaltrust Portal — `x-cashbox-id` and `x-cashbox-accesstoken` come from the CashBox configuration, `x-possystem-id` from registering the POS system variant. The `x-operation-id` is generated by the POS system per logical operation — typically a fresh UUID — and reused unchanged on retries. + +## Core API Endpoints + +The API exposes a compact, consistent set of endpoints that cover the full fiscal lifecycle. The same endpoints are used across all supported markets — country-specific compliance is driven by the CashBox configuration, not by a separate API surface. + +| Endpoint | Purpose | Typical use case | +|------------|------------------------------------------------------------------------------------------------------|-----------------------------------------------------------| +| `/echo` | Connectivity and health check; can also be used to verify Middleware version and capabilities. | Test the connection on POS startup. | +| `/pay` | Execute and monitor payment processing, including timeouts and payment-method allocation. | Settle an order with one or more payment methods. | +| `/sign` | Finalize and fiscalize the receipt according to national rules (signing, hash chaining, etc.). | Produce an audit-proof, country-compliant receipt. | +| `/issue` | Generate and manage receipt output, and update its delivery state (digital or printable). | Hand the signed receipt over to the customer. | +| `/journal` | Retrieve audit-relevant journal data and ranges for closings, exports and inspections. | Daily/monthly closings, audit exports, archive snapshots. | + +Not every integration needs all five groups. The minimum is `/echo` (connectivity check on startup) plus `/sign` (every transaction, including the daily closing). `/pay` is used only when electronic payments are processed through the Middleware — cash transactions skip it. `/issue` is optional and used for digital receipt distribution. `/journal` is used for audit exports and closings. + +For the full request/response models, payload schemas and per-endpoint error codes, see the [POS System API reference (v2.1)](https://docs.fiskaltrust.cloud/apis/pos-system-api). + +## End-to-End Request Flow + +The diagram below illustrates a typical fiscal transaction lifecycle, showing how a POS system interacts with the fiskaltrust.Middleware through the POS System API and how the Middleware in turn communicates with country-specific signing components and the fiskaltrust.Cloud. + +![POS System API end-to-end request flow](./images/pos-system-api-request-flow.svg) + +Every request carries the same identification and authentication headers (`x-cashbox-id`, `x-cashbox-accesstoken`, `x-possystem-id`) plus a per-operation `x-operation-id`. The `x-operation-id` is what makes each step safe to retry without producing duplicate fiscal actions. + +When `/pay` is part of the flow, the `ftPayItems` array returned by `/pay` can be reused directly as `cbPayItems` in the subsequent `/sign` request — no transformation needed. This links the fiscal receipt to the electronic payment without the POS having to re-map terminal data. + +## Availability + +The POS System API is available in every supported fiskaltrust deployment scenario: as a **cloud-hosted endpoint** and as part of the **Local Middleware on Windows, Linux, and Android**. The same v2 request format works across all of them, so a single POS integration runs unchanged regardless of where the Middleware is deployed. + +## Versioning and Compatibility + +The POS System API uses semantic versioning: + +- Breaking changes are introduced only in major versions. +- Non-breaking changes may add fields without altering existing models. +- If no version is specified, the latest available version is used. + +The currently published version and any prior major versions are shown in the version selector of the [POS System API reference](https://docs.fiskaltrust.cloud/apis/pos-system-api). Pinning to a specific major version is recommended for production integrations. + +## FAQ + +**Q: What is the difference between the POS System API and the fiskaltrust.Middleware?** + +A: The fiskaltrust.Middleware is the component that performs the actual fiscalization, signing, and journaling logic. The POS System API is the HTTP/JSON interface exposed by the Middleware that POS systems use to drive these operations. POS systems do not call signing components (SCU, TSE, RT, …) directly — they always interact with the Middleware through the POS System API. + +**Q: Do I need a different integration per country?** + +A: No. The POS System API is unified across markets and abstracts country-specific fiscal rules behind the same set of endpoints (`/echo`, `/pay`, `/sign`, `/issue`, `/journal`). Country-specific behaviour is driven by the CashBox configuration and the data sent in the requests, not by a separate API surface. + +**Q: What is `x-operation-id` used for, and how should it be generated?** + +A: `x-operation-id` is a unique identifier per logical operation that makes requests idempotent. If the same `x-operation-id` is sent twice (for example after a network timeout), the Middleware returns the result of the original operation or blocks until it completes, instead of executing the operation a second time. It should be a unique value per logical operation (typically a GUID/UUID) and must remain identical across retries of the same call. + +**Q: Is the POS System API safe to retry on network errors?** + +A: Yes. Retrying a request with the same `x-operation-id` and the same body is the recommended way to recover from transient network issues, timeouts, or interrupted responses without risking duplicate fiscal actions. If the body differs from the original, the retry is rejected with `409 Conflict`. + +**Q: Where do I get `x-cashbox-id`, `x-cashbox-accesstoken`, and `x-possystem-id`?** + +A: These values are issued via the fiskaltrust Portal as part of configuring a CashBox and registering a POS system variant. They are tied to a specific CashBox configuration and must be stored securely on the POS side. + +**Q: Can `/pay` be used without `/sign`, or vice versa?** + +A: Each endpoint represents a step in the fiscal workflow and is intended to be used as part of the overall process. Which steps are required depends on the country-specific fiscal rules and the business case being executed. The combination of steps performed for a given transaction must result in a complete, traceable, and compliant chain. + +**Q: Can the same POS integration run against both the cloud endpoint and the Local Middleware?** + +A: Yes. All deployment scenarios — the cloud-hosted endpoint and the Local Middleware on Windows, Linux, and Android — accept the same v2 request format. A single POS integration works against any of them without code changes. + +**Q: How are breaking changes handled?** + +A: The API uses semantic versioning. Breaking changes are introduced only in major versions; non-breaking changes may add optional fields without altering existing models. If no version is specified, the latest available version is used, so pinning to a specific major version is recommended for production integrations. + diff --git a/poscreators/toc.js b/poscreators/toc.js index 079bc64..5bce370 100644 --- a/poscreators/toc.js +++ b/poscreators/toc.js @@ -5,7 +5,6 @@ module.exports = [ items: [ "poscreators/getting-started/get-started", "poscreators/getting-started/portal-registration", - { type: "category", collapsed: true, @@ -15,10 +14,18 @@ module.exports = [ "poscreators/getting-started/integration-checklist", ], }, - "poscreators/getting-started/onboarding-posdealers", ], - }, +}, + { + type: "category", + label: "POS System API", + collapsed: true, + items: [ + "poscreators/middleware-doc/possystem-api/introduction", + "poscreators/middleware-doc/possystem-api/android-intent", + ], +}, { type: "category", collapsed: true, @@ -32,7 +39,6 @@ module.exports = [ key: "general-cash-register-integration", items: [ "poscreators/middleware-doc/general/cash-register-integration/cash-register-integration-regular-workflow", - "poscreators/middleware-doc/general/cash-register-integration/multi-markets-integration-guide", "poscreators/middleware-doc/general/cash-register-integration/cash-register-integration-failure-scenarios", ], },