nexi_payment
A Flutter plugin for Nexi's payment gateways: Nexi's checkout on Android and iOS, and an outcome the app can trust.
- Role
- Author and maintainer: the Dart API, the Android (Java) and iOS (Swift) code, tests, CI and releases
- Type
- Open-source Flutter plugin, MIT licence, on pub.dev
- Period
- 2020 – today (2.3.0, July 2026)
- Platforms
- Android and iOS
The product
nexi_payment lets a Flutter app take payments through Nexi. The app hands over the order, Nexi's own checkout collects the card (the app never sees it), and the app gets back what happened: paid, cancelled, or failed and why.
I wrote it in June 2020, while adding Nexi to the Criluma Viaggi app, and published it on pub.dev. In July 2026 I rewrote it as 2.0: Nexi's newer NPG gateway next to the classic XPay one, one error contract on both platforms, tests that drive the real SDKs, and fixes for the moments when Nexi's native SDKs fall silent or report the wrong thing.
- 7
- stable releases on pub.dev, from 1.0.0 in June 2020 to 2.3.0 in July 2026
- 53
- of the 58 commits are mine; the others came as contributors' pull requests
- 2
- Nexi gateways behind one Dart API: classic XPay and NPG
- 49
- automated tests: 35 unit tests and 14 integration tests that run on a device
Walkthrough
The app, in motion
Pick a recording, or jump straight to a chapter.

Recorded on an Android emulator from a release build of the example app, paying in Nexi's public sandbox. The checkout's fields are found through DevTools and touched like a finger would; the sandbox page needs two small fixes to work, applied the same way.
What it does
From a button in the app to an outcome it can trust
01
One call opens Nexi's checkout
The app passes the order (id, amount, currency, language) and the plugin opens Nexi's Hosted Payment Page through the native SDK: a card, or one of the other methods Nexi offers. The card is typed on Nexi's page, never in the app.
02
Summary and 3-D Secure
Nexi shows the summary with the masked card, then hands over to the cardholder's bank for 3-D Secure: in the sandbox, DemoBank, which can be told to pass or to fail.
03
An outcome the app can trust
The native SDKs call their "completed" callback for any payment that finished, declined ones included. The plugin reports success only for a final EXECUTED or AUTHORIZED operation; a failed authentication comes back as a failure with Nexi's code. Before 2.0, iOS even reported a denied payment as a plain string; both platforms now raise the same coded errors.
04
Every way out ends the call
The back button, Nexi's own cancel link, a sheet dragged away on iOS: each one resolves the payment as cancelled, and the next payment opens normally. Two of those exits used to leave the app waiting for an answer that never came.
Under the hood
How it was built
Filling the SDKs' silences
XPaySDK closes its checkout on Android's back button without calling back, and NPGSDK on iOS can let its sheet go without a word. The plugin watches the checkout's lifecycle and resolves with a cancel when it disappears having said nothing (a real outcome always wins), and a guard refuses a second payment while one is open instead of leaving two calls pending.
When the SDK cannot read its own answer
NPGSDK 1.1.0 declares an operation's additionalData as a map of strings, but the backend nests an object in it, so on Android the SDK can fail to parse the outcome of a payment that went through. The plugin then reads the order back from NPG's orders API and reports what really happened: it never invents a success, and says "outcome unknown" when it cannot tell.
Packaging, tests and releases
Nexi publishes no Maven artifacts and its iOS pod has no simulator slice: the Android SDKs ship inside the plugin, and the iOS frameworks are fetched from Nexi's release repositories at pod install and checked against pinned SHA-256 checksums. Unit tests pin the channel contract, Patrol tests drive the real SDKs against Nexi's sandbox, CI analyses, tests and builds on every push and pull request, and a version tag publishes to pub.dev.
Recording a payment plugin
A real checkout, in Nexi's sandbox.
A plugin has no screens of its own, so the recordings show its example app, a release build on an Android emulator, paying for real in Nexi's public sandbox.
- 1
The published code, built for release
The repository at the 2.3.0 tag, the example app built in release mode with the sandbox credentials Nexi publishes, installed on an emulator. No private keys: the classic XPay buttons need a merchant's own test terminal and are not shown.
- 2
Into Nexi's page through DevTools
The checkout is a web page inside the SDK's WebView. The recording script finds its fields through Chrome DevTools, which also tells where the WebView sits on the screen, and touches them on the emulator's touchscreen like a finger, typing Nexi's test card.
- 3
Two sandbox quirks
The sandbox page sometimes leaves its card button without a label, and never enables its pay button for a typed card. The script fixes both from DevTools, the second exactly as the plugin's own device-testing runbook does.
- 4
Three endings
A payment that goes through, one whose 3-D Secure fails, and two ways of walking away. Each take starts from a cleared app and a new order.
The payment pages, the sandbox merchant (WWW.CHARTA.IT) and DemoBank are Nexi's public sandbox. The card is Nexi's published test card; the cardholder is invented.

Nothing to try here: the plugin runs inside Android and iOS apps. It is on pub.dev and GitHub.
Every screen
The full set of screenshots
The example appBoth gateways, card storage and a recurring charge, one button each. Hosted Payment PageNexi's page, opened by the native SDK: card or another method. The cardNexi's published test card; the app never sees the number. SummaryMerchant, amount, order and the masked card. 3-D SecureThe sandbox's DemoBank asks the cardholder to confirm. ExecutedThe plugin reports success only for an executed or authorised payment. FailedTHREEDS_FAILED, not a success: the plugin checks the final operation. CanceledBack button: the plugin turns the SDK's silence into a cancel. Leaving the pageNexi's own cancel asks for confirmation.
Next case study
Referi →