Skip to main content

amwal_ecr 0.4.0 copy "amwal_ecr: ^0.4.0" to clipboard
amwal_ecr: ^0.4.0 copied to clipboard

Drive an Amwal POS terminal from Flutter. One Dart API over native Android and iOS ECR SDKs (Wi‑Fi, USB, Web Service, and Android app-to-app), plus a pure-Dart Windows host (LAN TCP + Web Service): sa [...]

Changelog #

All notable changes to amwal_ecr. This project follows semantic versioning, with the addition described in the release policy: any change to the platform-channel contract is breaking, and any change to what an outcome means is breaking, however small the diff.

0.4.0 #

Port of iOS AmwalECR 0.3.0 and Android ecr-sdk 1.0.8 Web Service contract changes (CocoaPods / SPM working tree + Kotlin package).

Changed (breaking) #

  • Web Service inquiry transport failure — when an inquiry (inquire / inquireByReference) gets no usable answer (timeout, socket error, empty or invalid body, HTTP 5xx), the result is now EcrInquiryFailed with EcrConnectionLost instead of EcrInquiryNotFound. "Not found" now means the terminal answered and has no such transaction; a transport failure means the outcome is still unknown. Reconcile, do not retry.
  • Sale / void / refund unknown outcomes are EcrFailed, not EcrDeclined. A success: false envelope with nextStep: INQUIRE_BY_MERCHANT_REFERENCE (typically NO_RESPONSE / timeout wording) is EcrFailed with EcrTimeout or EcrConnectionLost. An ordinary decline without that next step is unchanged. Money may have moved — inquire by reference; do not treat it as "no money taken" and retry the sale. Applies on the Dart Windows host and matches native AmwalECR / ecr-sdk readers.

Added #

  • iOS AmwalECR 0.3.0 — plugin pins (ecrSdkVersion=0.3.0; podspec ~> 0.3.0, SwiftPM .upToNextMinor(from: "0.3.0")). Android ecr-sdk is pinned to 1.0.8; the Gradle fallback version is raised from 1.0.5 to 1.0.8.
  • Web Service default read timeout is 130s (EcrConfig.webServiceResponseTimeout). EcrTerminal / EcrSessions.open apply it when the config still has the LAN default of 120s; an explicit override is kept. Passed to native hosts via responseTimeoutMs.
  • Dart Web Service parity (Windows host) with iOS 0.3.0 / Android 1.0.8:
    • requestDateTime always equals transactionDateTime in request bodies.
    • Void / refund send originalMerchantReference + originalTerminalId; inquiry-by-reference sends originalMerchantReference with empty stan.
    • Inquiry path is /Ecr/Inquiry.
    • Synthesized failures (timeout, socket error, empty / invalid body, HTTP 5xx) carry nextStep: INQUIRE_BY_MERCHANT_REFERENCE; 4xx synthesized errors do not.
    • Inquiry answers may be flat or nested: the transaction is read from data only when it carries transaction fields, otherwise from the response root; a bare correlation echo is EcrInquiryNotFound. The reference resolves from the transaction payload, then the envelope, then the request.
    • Environment endpoints updated for the UAT environment (:25453).

0.3.4 #

Fixed #

  • Web Service inquireByReference — no longer refused at the Dart API because Web Service has no reachability probe. Matches Kotlin EcrWebServiceTerminal.inquireByReference.
  • Web Service wire bodies — inquiry / void / refund now carry originalMerchantReference, originalTerminalId, and requestDateTime like ecr-sdk EcrWebServiceMessage (inquiry by reference sets originalMerchantReference; STAN inquiry leaves it empty).

0.3.3 #

Added #

  • App-to-app printReceipt — Android only. Sends com.amwalpay.pos.ecr.PRINT with a required receipt image (width 384 px, size ≤ 15 MB). Returns EcrPrintOutcome with status and message as separate fields. Wi‑Fi, USB cable and Web Service refuse before the host is called; iOS / Windows answer the same way. Channel method printReceipt; plugin <queries> includes PRINT.
  • Example till shows Print receipt on approved app-to-app results only (example/lib/print/ builds the bilingual 384 px PNG).
  • Pins Android host to com.amwal-pay:ecr-sdk 1.0.7 (app-to-app PRINT contract documented with the SDK).

Still platform-limited #

  • USB cable and app to app remain Android-only. Print is app to app only.

0.3.2 #

Added / fixed #

  • iOS signOn — the bridge calls native AmwalECR sign-on over Wi‑Fi and USB cable (same transports as Android). Published 0.3.1 on pub.dev still answers every iOS signOn with a typed unsupported stub; point a path dependency at this checkout (as example/ and AmwalECR-flutter-example do) to get the real answer.
  • iOS closeReceipt — same as Android: Wi‑Fi / USB cable ask the terminal to dismiss its on-screen receipt (idle / declined / failed). Web Service and app to app still answer typed unsupported before anything is sent.
  • Example apps show terminal readiness from sign-on (permitted operations, reported transport / terminal name).

Still platform-limited #

  • USB cable and app to app remain Android-only.

0.3.1 #

CI only: the publish check no longer fails on the tracked example lockfile. No code or API changes since 0.3.0 (which was never published to pub.dev).

0.3.0 #

Adds a fourth transport and a Windows host in the same release.

Breaking, in the way an enum is breaking. EcrTransport gains appToApp, so an exhaustive switch over it in your code stops compiling until it has a branch. The Windows host does not change the Dart API for Wi‑Fi or Web Service: the same EcrSessions.open / EcrTerminal path works on Android, iOS, and Windows. USB cable and app to app remain Android-only.

Added #

  • EcrTransport.appToApp (ecrMode 5, channel name app_to_app) and EcrTerminal.appToApp, which takes the payment app's application id instead of an address. An address passed there is refused at construction.
  • EcrPaymentApp.defaultPackage — the application id of the terminal build, overridable for a test build.
  • EcrOpenedSession.usesPaymentApp, and EcrTransport.hasReachabilityProbe / .supportsReceipt, which replace two open-coded predicate pairs that were one answer for three transports and are not for the fourth.
  • The Android host is now ActivityAware, and the plugin's manifest declares the payment app in <queries>.
  • Windows support via a pure-Dart AmwalEcrPlatform (DartIoAmwalEcrPlatform): LAN TCP (Wi‑Fi) and Web Service Hub REST, registered with dartPluginClass: AmwalEcrWindows. USB cable and app to app return a typed unsupported failure on Windows.
  • Protocol engine under lib/src/dart_io/ — framing, LAN / Web Service HMAC signing, message build, response parse, auto-inquire-on-failure, and cancel by closing the socket / aborting HTTP.
  • Example app Windows runner + Codemagic example-windows workflow; optional --dart-define live config seeds (ECR_*) documented in example/README.md.
  • Windows docs: Install / troubleshooting in the package README, full platform matrix in doc/compatibility-matrix.md, and Visual Studio ATL notes in both example READMEs (for flutter_secure_storage).

Worth knowing before you ship it #

  • App to app is Android only. On iOS, Windows, and the web an operation is refused before anything is sent, with a message about the platform rather than about a port.
  • An e-receipt can be fetched over app to app, as on the other local transports, at the cost of another handover the operator watches.
  • An interrupted app-to-app round trip — the payment app destroyed before it answered — is an unknown outcome pointing at inquireByMerchantReference, never a decline. Send a merchantReference on every request: it is the only handle that survives.
  • autoInquireOnFailure is forced off for app to app, because the inquiry is itself another trip through the payment app.

0.2.1 #

Brings the package level with com.amwal-pay:ecr-sdk 1.0.5 and AmwalECR 0.2.1 — the ECR modes release: Wi‑Fi as before, USB cable on Android, and the Web Service (REST) transport on both platforms, all opened through one native session path.

Added #

  • EcrOpenedSession / EcrSessions.open (Dart) — names the same session-open intent as native ecr_sdk / AmwalECR; wraps a transport-aware EcrTerminal.
  • Android / iOS hosts open sessions via native EcrSessions.open so LAN, USB cable, and Web Service share one dispatch path.
  • Example apps store signing secrets with flutter_secure_storage (Keychain / Keystore), with one-shot migration from plaintext SharedPreferences.

Changed #

  • EcrSecureHashKeys removed. The plugin/SDK only accepts EcrConfig.secureHashKey. Apps persist secrets and assign the value for the selected terminal mode.
  • Unit-test placeholders renamed to match ecr_sdk: SECURE_HASH_KEY_ECR_WIFI, SECURE_HASH_KEY_ECR_WIFI_OTHER, SECURE_HASH_KEY_WEBSERVICE on EcrTestConfigs (lan / lanOther / webService).
  • Example iOS local SDK wiring matches Android: ios/ecr_sdk.properties + example/ios/ecr_sdk.properties (project | cocoapods | spm), applied by ./tool/sync_ios_ecr_sdk.sh / ./tool/prepare_ios_example.sh to both the podspec and Package.swift faces.
  • EcrTerminal.probeReachability() / EcrReachability. Matches ecr_sdk and AmwalECR: the same probe as isReachable(), plus host, port, endpoint and the underlying error when the link fails. Channel method probeReachability; isReachable remains as a convenience over it.
  • Android USB accessory channel discards stale framed answers whose nonce does not match the request just sent.

Changed #

  • Channel transport still uses primary name webService; accepts web_service as an alias without breaking existing callers.

Breaking #

  • ecrMode 1 is USB cable. Matches ecr_sdk: Dart EcrTransport.usbCable, channel name "usb_cable" (wire value 1). isIpTransport is true only for Wi‑Fi. USB cable is driven on Android via AOA; iOS returns typed unsupported. There is no Ethernet transport or channel name.

0.2.0 #

Brings the package level with com.amwal-pay:ecr-sdk 1.0.4 and AmwalECR 0.2.0 — the same three features on both platforms, over the same channel contract.

Breaking. The protocol renamed the field that names a transaction, so requestId is merchantReference everywhere: on EcrResult, EcrInquiry, EcrReceipt and on the platform channel. Rename it at the call sites; the type is unchanged and it still arrives filled in whether or not you supply one. The channel contract changed too, so the Dart side and the native hosts must be upgraded together — an old host does not lose a field, it stops being understood.

Added #

  • A reference the till chooses. merchantReference on sale, voidTransaction, refund, run, inquire and receipt — an order number, a basket id, whatever already names the sale in your system. Pass it and the same string identifies the transaction in your books, in the terminal's records and in any later lookup; leave it out and the host generates one. Either way it comes back on EcrResult.merchantReference, and it is the only handle you hold if the answer never arrives.
  • EcrTerminal.inquireByReference. The answer to an outcome you never received: looks a transaction up by the reference it was sent with, rather than by a receipt number that only arrives in the answer.
  • EcrFailed.recovered, .recoveredTransaction and .settled. The host now follows a lost money-moving request with one inquiry and attaches what it found. A settled failure is a delivery that failed and an outcome that is known, so outcomeIsUnknown is false and the till books the transaction instead of reconciling it. Turn the follow-up off with EcrConfig.autoInquireOnFailure. Nothing is ever re-sent.
  • EcrConfig.secureHashKey. The secret this till shares with the terminal. Set it and the native SDK signs every request and checks every answer. Refused at construction if it is not an even-length hex string of at least 16 characters — on the shop floor a bad key is a decline the cashier cannot explain. Required in practice: a terminal refuses what it cannot verify. The key is never printed by toString.
  • EcrUnauthenticated, for an answer that cannot be shown to have come from the terminal. Leaves the outcome unknown, like a timeout — never a decline, and never to be retried.
  • EcrNextStep, on every EcrResult. What the terminal says to do about an outcome, rather than something a till has to infer from a response code. A decline that asks for an inquiry now reports outcomeIsUnknown.

Changed #

  • Config, arguments and results carry the new fields across the channel: secureHashKey, autoInquireOnFailure, merchantReference, originalMerchantReference, nextStep, recovered, and the unauthenticated failure kind. The frozen contract test lists them all.
  • A reference the wire format cannot carry — over 32 characters, or containing a space, & or = — throws EcrArgumentError before anything is sent, on both platforms and in Dart, with the same message.
  • The Android host now requires com.amwal-pay:ecr-sdk:1.0.4, and the iOS host AmwalECR ~> 0.2.0.

0.1.0 #

First release.

Added #

  • One Dart API over both platforms. EcrTerminal with isReachable, sale, voidTransaction, refund, inquire and receipt, plus a run form for a till driving the operation from its own menu.
  • Typed outcomes. Sealed EcrResult (EcrApproved / EcrDeclined / EcrFailed), EcrInquiry and EcrReceipt, each with its own branches, so an inquiry that failed cannot be mistaken for a sale that was refused.
  • outcomeIsUnknown on every result. Set for timeouts, lost connections, unreadable answers, cancellations, and a decline carrying response code 91. Nothing in the package retries a money-moving request, and this is the flag that tells a caller not to either.
  • Exact amounts. EcrAmount holds a decimal, never a double, and converts to the wire's minor units once — in the native host, with half-up rounding, so the two platforms cannot disagree about a rounding boundary.
  • Cancellable operations. The start… methods return an EcrOperation with a cancel(), which answers the caller at once with EcrCancelled. Cancelling does not stop the terminal, and the outcome is unknown — documented as such everywhere it appears.
  • Transport awareness. EcrTransport mirrors the TMS profile's ecrMode. bluetooth and webService answer with a typed EcrUnsupported failure without opening a socket, identically on both platforms.
  • Android host bridging to com.amwal-pay:ecr-sdk 1.0.3, pinned.
  • iOS host bridging to AmwalECR ~> 0.1.0, from CocoaPods trunk or Swift Package Manager. Both hosts are bridges: no wire protocol lives in this package, and a native module in the same app can drive the same terminal through the same SDK without Flutter.
  • Both CocoaPods and Swift Package Manager on iOS, from the same sources — ios/amwal_ecr.podspec and ios/amwal_ecr/Package.swift, naming the same dependency range.
  • Contract tests on all three sides. Every method name, argument key, result key, outcome, failure kind and error code is asserted against frozen literals in Dart, Kotlin and Swift — the only thing keeping three hand-written copies of the same document in step.
  • An example till demonstrating every operation, and in particular an outcome card that offers Ask the terminal and never Retry when the outcome is unknown.

Compatibility #

Dart ^3.5.0
Flutter >=3.22.0
Android API 21+, com.amwal-pay:ecr-sdk:1.0.3
iOS 12.0+, Swift 5.5, AmwalECR ~> 0.1.0
ECR protocol version 1
0
likes
130
points
521
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

Drive an Amwal POS terminal from Flutter. One Dart API over native Android and iOS ECR SDKs (Wi‑Fi, USB, Web Service, and Android app-to-app), plus a pure-Dart Windows host (LAN TCP + Web Service): sale, void, refund, inquiry, sign-on, e-receipt, close-receipt, and app-to-app printReceipt, with typed outcomes that keep an unknown result apart from a refusal.

Repository (GitHub)
View/report issues
Contributing

Topics

#payments #pos #ecr #terminal

License

unknown (license)

Dependencies

crypto, flutter, plugin_platform_interface

More

Packages that depend on amwal_ecr

Packages that implement amwal_ecr