Skip to content

Commit ea5b52a

Browse files
feat: Tap-to-Pay support on Android and iOS (v0.14.0)
- Add TapToPayRunner (Android): full TTP SDK integration with typed event handling, double-result guard, suspend runCheckout, proper tearDown - Add checkTapToPayAvailability() and presentTapToPayActivation() (iOS) - Add PaymentMethod enum (cardReader, tapToPay) to SumupPaymentRequest - Add SumupProduct model and products field to SumupPluginCheckoutResponse - Add merchantCode, cardScheme, errors fields to checkout response - Fix iOS: checkout error message was discarded on failed transactions - Fix Android: blank foreignTransactionId now auto-generates a UUID - Android: upgrade utopia-sdk to 1.0.6, minSdk 30 for TTP - Update README, CHANGELOG Closes #63
1 parent 22db418 commit ea5b52a

22 files changed

Lines changed: 1002 additions & 235 deletions

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
## 0.14.0
2+
3+
* Add Tap-to-Pay (TTP) support on Android and iOS
4+
* Add `checkTapToPayAvailability()` and `presentTapToPayActivation()` (iOS) APIs
5+
* Add `PaymentMethod` enum (`cardReader`, `tapToPay`) to `SumupPaymentRequest`
6+
* Add `products` field (`List<SumupProduct>`) to `SumupPluginCheckoutResponse` (iOS + Android card reader)
7+
* Add `merchantCode`, `cardScheme`, and `errors` fields to `SumupPluginCheckoutResponse`
8+
* Fix iOS: checkout error message was discarded on failed transactions
9+
* Android: fix blank `foreignTransactionId` causing duplicate-transaction errors
10+
111
## 0.13.1
212

313
* Android: upgrade Kotlin to 2.1.0

README.md

Lines changed: 82 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@
22

33
[![pub package](https://img.shields.io/pub/v/sumup.svg)](https://pub.dev/packages/sumup) [![likes](https://img.shields.io/pub/likes/sumup?logo=dart)](https://pub.dev/packages/sumup/score) [![popularity](https://img.shields.io/pub/dm/sumup?logo=dart)](https://pub.dev/packages/sumup/score) [![pub points](https://img.shields.io/pub/points/sumup?logo=dart)](https://pub.dev/packages/sumup/score)
44

5-
A Flutter wrapper to use the SumUp SDK.
6-
7-
With this plugin, your app can easily connect to a SumUp terminal,
8-
login and accept card payments on Android and iOS.
5+
A Flutter plugin for the SumUp SDK. Supports card reader and Tap-to-Pay payments on Android and iOS.
96

107
## Partner Links
118

@@ -53,11 +50,11 @@ Support this project by purchasing SumUp terminals through our affiliate links f
5350

5451
## Prerequisites
5552

56-
1) Registered for a merchant account via SumUp's [country websites](https://sumup.it/purplesoft) (or received a test account).
57-
2) Received SumUp card terminal: Solo, Air, Air Lite, PIN+ terminal, Chip & Signature reader, or SumUp Air Register.
58-
3) Requested an Affiliate (Access) Key and registered your application ID via [SumUp Dashboard](https://me.sumup.com/developers) for Developers.
59-
4) Deployment Target iOS 16.0 or higher.
60-
5) Android minSdkVersion 26 or higher.
53+
1. Registered for a merchant account via SumUp's [country websites](https://sumup.it/purplesoft) (or received a test account).
54+
2. Received a SumUp card terminal: Solo, Air, Air Lite, PIN+ terminal, Chip & Signature reader, or SumUp Air Register.
55+
3. Requested an Affiliate (Access) Key and registered your application ID via the [SumUp Dashboard](https://me.sumup.com/developers).
56+
4. iOS deployment target 16.0+ (16.4+ for Tap-to-Pay).
57+
5. Android minSdkVersion 26+ (30+ for Tap-to-Pay).
6158

6259
## Installing
6360

@@ -76,52 +73,41 @@ import 'package:sumup/sumup.dart';
7673

7774
## Getting Started
7875

79-
Init SumUp SDK:
80-
81-
```dart
82-
Sumup.init(affiliateKey);
83-
```
84-
85-
Login:
86-
8776
```dart
88-
Sumup.login();
89-
```
90-
91-
Or login with token:
77+
// 1. Initialise the SDK with your Affiliate Key
78+
await Sumup.init(affiliateKey);
9279
93-
```dart
94-
Sumup.loginWithToken(token);
95-
```
80+
// 2. Log in — either interactively or with an OAuth token
81+
await Sumup.login();
82+
// or
83+
await Sumup.loginWithToken(token);
9684
97-
Choose your preferred terminal:
85+
// 3. (Optional) Ask the user to pick / configure a card reader
86+
await Sumup.openSettings();
9887
99-
```dart
100-
Sumup.openSettings();
101-
```
88+
// 4. Optionally warm up the reader before checkout
89+
await Sumup.prepareForCheckout();
10290
103-
Prepare terminal for checkout:
104-
105-
```dart
106-
Sumup.prepareForCheckout();
107-
```
108-
109-
Complete a transaction:
110-
111-
```dart
91+
// 5. Run a checkout
11292
var payment = SumupPayment(
113-
title: 'Test payment',
114-
total: 1.2,
93+
title: 'Coffee',
94+
total: 3.50,
11595
currency: 'EUR',
116-
foreignTransactionID: '',
117-
saleItemsCount: 0,
96+
foreignTransactionId: '', // leave empty to auto-generate a unique ID
97+
saleItemsCount: 1,
11898
skipSuccessScreen: false,
119-
tip: .0,
99+
tip: 0.0,
120100
);
121101
122-
var request = SumupPaymentRequest(payment);
123-
124-
Sumup.checkout(request);
102+
var checkout = await Sumup.checkout(SumupPaymentRequest(payment));
103+
// checkout.success — bool
104+
// checkout.transactionCode — String
105+
// checkout.amount — double (not available on Android Tap-to-Pay)
106+
// checkout.currency — String (not available on Android Tap-to-Pay)
107+
// checkout.products — List<SumupProduct>; iOS and Android card reader only
108+
// checkout.merchantCode — String; Android Tap-to-Pay only
109+
// checkout.cardScheme — String; Android Tap-to-Pay only
110+
// checkout.errors — String; non-null when success is false
125111
```
126112

127113
## Available APIs
@@ -130,25 +116,66 @@ Sumup.checkout(request);
130116
Sumup.init(affiliateKey);
131117
132118
Sumup.login();
119+
Sumup.loginWithToken(token);
133120
134121
Sumup.isLoggedIn;
135-
136122
Sumup.merchant;
137123
138124
Sumup.openSettings();
139-
140125
Sumup.prepareForCheckout();
141-
142126
Sumup.isTipOnCardReaderAvailable;
127+
Sumup.isCardTypeRequired; // iOS only
128+
Sumup.isCheckoutInProgress; // iOS only
143129
144-
// iOS only
145-
Sumup.isCardTypeRequired;
130+
// Card reader checkout
131+
Sumup.checkout(SumupPaymentRequest(payment));
146132
147-
Sumup.checkout(request);
133+
// Tap-to-Pay checkout
134+
Sumup.checkout(SumupPaymentRequest(payment, paymentMethod: PaymentMethod.tapToPay));
135+
Sumup.checkTapToPayAvailability(); // → TapToPayAvailabilityResult
136+
Sumup.presentTapToPayActivation(); // iOS only; no-op on Android
148137
149138
Sumup.logout();
150-
151-
// iOS only
152-
Sumup.isCheckoutInProgress;
153-
154139
```
140+
141+
## Tap-to-Pay (TTP)
142+
143+
Accept contactless payments directly on compatible smartphones, without any additional hardware.
144+
145+
### iOS
146+
147+
**Requirements:**
148+
- iPhone XS or later, iOS 16.4+
149+
- Entitlement `com.apple.developer.proximity-reader.payment.acceptance` added to your project (requires approval from Apple)
150+
- See [Apple's HIG for Tap to Pay on iPhone](https://developer.apple.com/design/human-interface-guidelines/tap-to-pay-on-iphone)
151+
152+
**Setup:**
153+
1. Log in with `Sumup.login()` or `Sumup.loginWithToken()`.
154+
2. Call `Sumup.checkTapToPayAvailability()`. If `isActivated` is `false`, call `Sumup.presentTapToPayActivation()` to run the one-time activation flow.
155+
3. Use `PaymentMethod.tapToPay` in your request:
156+
```dart
157+
var request = SumupPaymentRequest(payment, paymentMethod: PaymentMethod.tapToPay);
158+
var checkout = await Sumup.checkout(request);
159+
```
160+
161+
### Android
162+
163+
**Requirements:**
164+
- NFC-enabled physical device, Android 11 (API 30)+
165+
- The TTP SDK is distributed via a private SumUp Maven repository — contact `integration@sumup.com` to get credentials, then add them to your `gradle.properties`:
166+
```
167+
SUMUP_TTP_MAVEN_USERNAME=...
168+
SUMUP_TTP_MAVEN_PASSWORD=...
169+
```
170+
- Add the `utopia-sdk` dependency to your app's `build.gradle` (same version used by the plugin) and configure the Maven repository. See `example/android/` for a full working setup.
171+
- TTP **requires a release build**: the SDK performs device attestation and will refuse to run if USB Debugging or Developer Options are enabled.
172+
173+
**Usage:**
174+
1. You must use `Sumup.loginWithToken(accessToken)` — token login is required for the TTP SDK to authenticate in the background.
175+
2. Use `PaymentMethod.tapToPay` in your request:
176+
```dart
177+
var request = SumupPaymentRequest(payment, paymentMethod: PaymentMethod.tapToPay);
178+
var checkout = await Sumup.checkout(request);
179+
```
180+
181+
> **Note:** The Android TTP SDK does not return `amount` or `currency` in the transaction result. If you need them, query the [SumUp Transactions API](https://developer.sumup.com/api/transactions/get) using `checkout.transactionCode` or `checkout.foreignTransactionId`.

android/build.gradle

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,24 @@ rootProject.allprojects {
1818
repositories {
1919
google()
2020
mavenCentral()
21-
maven { url 'https://maven.sumup.com/releases' }
21+
maven {
22+
url 'https://maven.sumup.com/releases'
23+
content {
24+
includeGroupByRegex 'com\\.sumup.*'
25+
includeGroup 'net.sf.smc'
26+
}
27+
}
28+
maven {
29+
url = uri('https://tap-to-pay-sdk.fleet.live.sumup.net/')
30+
content {
31+
includeGroupByRegex 'com\\.sumup.*'
32+
includeGroup 'ca.amadis.agnos'
33+
}
34+
credentials {
35+
username = rootProject.findProperty('SUMUP_TTP_MAVEN_USERNAME')?.toString() ?: ''
36+
password = rootProject.findProperty('SUMUP_TTP_MAVEN_PASSWORD')?.toString() ?: ''
37+
}
38+
}
2239
}
2340
}
2441

@@ -40,7 +57,7 @@ android {
4057
}
4158

4259
defaultConfig {
43-
minSdkVersion 26
60+
minSdkVersion 30 // Tap-to-Pay SDK requires 30+; card reader still works
4461
targetSdkVersion 35
4562
consumerProguardFiles 'consumer-rules.pro'
4663
}
@@ -56,6 +73,8 @@ android {
5673

5774
dependencies {
5875
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
76+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
5977
implementation "com.google.android.gms:play-services-location:21.3.0"
6078
api 'com.sumup:merchant-sdk:6.0.0'
79+
implementation 'com.sumup.tap-to-pay:utopia-sdk:1.0.6'
6180
}

android/consumer-rules.pro

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
11
# Please add these rules to your existing keep rules in order to suppress warnings.
22
# This is generated automatically by the Android Gradle plugin.
3+
4+
# Keep Tap-to-Pay SDK (loaded via reflection; R8 would otherwise strip it in release)
5+
-keep class com.sumup.taptopay.** { *; }
6+
-keep class com.sumup.tap.topay.** { *; }
7+
-keep class ca.amadis.agnos.** { *; }
8+
# SDK internal (e.g. ut.f4.init)
9+
-keep class com.sumup.taptopay.ut.** { *; }
10+
-keepclassmembers class com.sumup.taptopay.ut.** { *; }
11+
12+
# SumUp/Flutter optional libs not in release classpath (Chucker, Play Core, analytics, OpenTelemetry)
13+
-dontwarn com.chuckerteam.chucker.**
14+
-dontwarn com.google.android.play.core.**
15+
-dontwarn com.sumup.analyticskit.events.**
16+
-dontwarn io.opentelemetry.**
17+
318
-dontwarn com.sumup.analyticskit.FirebaseAnalytics
419
-dontwarn com.sumup.analyticskit.FirebasePerformance
520
-dontwarn com.sumup.analyticskit.FirebaseRemoteConfig

0 commit comments

Comments
 (0)