Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions apps/wallet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,26 @@ Studio, and `i` for the iOS simulator, which requires Xcode on macOS.

`@euvena/qr` has to be built before the app can resolve it, which the second command does.

## Publishing

Store builds are made with EAS from `eas.json`:

| Profile | Builds |
| --- | --- |
| `simulator` | An iOS simulator build and an Android APK, to try the native build (splash screen, localised camera prompt) without a store |
| `preview` | Internal distribution builds for test devices |
| `production` | Store builds: an Android app bundle for Play App Signing, build numbers kept by EAS and raised on each build |

`app.json` carries the store-facing settings: version 1.0.0, iPhone only, no non-exempt
encryption, a privacy manifest that declares no tracking and no collected data plus the
required-reason APIs the dependencies use, and the camera prompt in the seven wallet languages
(`locales/`). `expo-system-ui` lets the Android build follow the light or dark appearance. Android
targets API 36, the React Native 0.86 default. Listings, review notes, the privacy policy text and
the questionnaire answers are in [`store/`](store/).

Linking the project to an EAS account (`eas init`) and the store credentials are left to the
publisher and are not part of the repository.

## Checks

```sh
Expand All @@ -300,6 +320,9 @@ pnpm --filter @euvena/wallet build # bundles the JS, no native toolchain requi
| Path | Purpose |
| --- | --- |
| `App.tsx` | Root component, loads the payee settings, opens incoming links, holds the tab bar and switches between the screens |
| `eas.json` | EAS build profiles: simulator, preview and production |
| `locales/` | The camera prompt in each wallet language, for the iOS Info.plist |
| `store/` | Store listings, review notes, privacy policy text and questionnaire answers |
| `plugins/` | Config plugin that keeps a restored Android activity from reopening its launch link |
| `src/epc/` | Form state to an EPC069-12 payload or an EN 18184 URL (`poi.ts`), the link form of a request, plus the display formatting |
| `src/i18n/` | The typed dictionary shape, one file per language, language resolution and number and date formatting |
Expand Down
69 changes: 62 additions & 7 deletions apps/wallet/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,51 @@
"expo": {
"name": "Euvena",
"slug": "euvena",
"version": "0.0.0",
"version": "1.0.0",
"scheme": "euvena",
"orientation": "portrait",
"icon": "./assets/icon.png",
"userInterfaceStyle": "automatic",
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.ledgerinnovation.euvena"
"supportsTablet": false,
"bundleIdentifier": "com.ledgerinnovation.euvena",
"config": {
"usesNonExemptEncryption": false
},
"privacyManifests": {
"NSPrivacyTracking": false,
"NSPrivacyTrackingDomains": [],
"NSPrivacyCollectedDataTypes": [],
"NSPrivacyAccessedAPITypes": [
{
"NSPrivacyAccessedAPIType": "NSPrivacyAccessedAPICategoryUserDefaults",
"NSPrivacyAccessedAPITypeReasons": [
"CA92.1"
]
},
{
"NSPrivacyAccessedAPIType": "NSPrivacyAccessedAPICategoryFileTimestamp",
"NSPrivacyAccessedAPITypeReasons": [
"0A2A.1",
"3B52.1",
"C617.1"
]
},
{
"NSPrivacyAccessedAPIType": "NSPrivacyAccessedAPICategoryDiskSpace",
"NSPrivacyAccessedAPITypeReasons": [
"85F4.1",
"E174.1"
]
},
{
"NSPrivacyAccessedAPIType": "NSPrivacyAccessedAPICategorySystemBootTime",
"NSPrivacyAccessedAPITypeReasons": [
"35F9.1"
]
}
]
}
},
"android": {
"package": "com.ledgerinnovation.euvena",
Expand All @@ -28,7 +65,7 @@
[
"expo-camera",
{
"cameraPermission": "The camera is used to read payment QR codes.",
"cameraPermission": "Euvena uses the camera only to read payment QR codes on the Pay tab. Nothing is recorded or sent.",
"microphonePermission": false,
"recordAudioAndroid": false
}
Expand All @@ -51,9 +88,27 @@
[
"expo-localization",
{
"supportedLocales": ["en", "de", "fr", "es", "it", "nl", "pl"]
"supportedLocales": [
"en",
"de",
"fr",
"es",
"it",
"nl",
"pl"
]
}
]
]
],
"expo-system-ui"
],
"locales": {
"en": "./locales/en.json",
"de": "./locales/de.json",
"fr": "./locales/fr.json",
"es": "./locales/es.json",
"it": "./locales/it.json",
"nl": "./locales/nl.json",
"pl": "./locales/pl.json"
}
}
}
32 changes: 32 additions & 0 deletions apps/wallet/eas.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"cli": {
"version": ">= 16.0.0",
"appVersionSource": "remote"
},
"build": {
"simulator": {
"distribution": "internal",
"ios": {
"simulator": true
},
"android": {
"buildType": "apk"
}
},
"preview": {
"distribution": "internal",
"android": {
"buildType": "apk"
}
},
"production": {
"autoIncrement": true,
"android": {
"buildType": "app-bundle"
}
}
},
"submit": {
"production": {}
}
}
5 changes: 5 additions & 0 deletions apps/wallet/locales/de.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"ios": {
"NSCameraUsageDescription": "Euvena nutzt die Kamera nur, um im Tab „Zahlen“ Zahlungs-QR-Codes zu lesen. Es wird nichts aufgezeichnet oder gesendet."
}
}
5 changes: 5 additions & 0 deletions apps/wallet/locales/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"ios": {
"NSCameraUsageDescription": "Euvena uses the camera only to read payment QR codes on the Pay tab. Nothing is recorded or sent."
}
}
5 changes: 5 additions & 0 deletions apps/wallet/locales/es.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"ios": {
"NSCameraUsageDescription": "Euvena usa la cámara solo para leer códigos QR de pago en la pestaña «Pagar». No se graba ni se envía nada."
}
}
5 changes: 5 additions & 0 deletions apps/wallet/locales/fr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"ios": {
"NSCameraUsageDescription": "Euvena utilise l'appareil photo uniquement pour lire les codes QR de paiement dans l'onglet « Payer ». Rien n'est enregistré ni envoyé."
}
}
5 changes: 5 additions & 0 deletions apps/wallet/locales/it.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"ios": {
"NSCameraUsageDescription": "Euvena usa la fotocamera solo per leggere i codici QR di pagamento nella scheda «Paga». Non viene registrato né inviato nulla."
}
}
5 changes: 5 additions & 0 deletions apps/wallet/locales/nl.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"ios": {
"NSCameraUsageDescription": "Euvena gebruikt de camera alleen om betaal-QR-codes te lezen op het tabblad ‘Betalen’. Er wordt niets opgenomen of verstuurd."
}
}
5 changes: 5 additions & 0 deletions apps/wallet/locales/pl.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"ios": {
"NSCameraUsageDescription": "Euvena używa aparatu tylko do odczytywania kodów QR płatności na karcie „Zapłać”. Nic nie jest nagrywane ani wysyłane."
}
}
1 change: 1 addition & 0 deletions apps/wallet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"expo-sharing": "~57.0.22",
"expo-splash-screen": "~57.0.9",
"expo-status-bar": "~57.0.1",
"expo-system-ui": "~57.0.4",
"qrcode": "^1.5.4",
"react": "19.2.3",
"react-native": "0.86.2",
Expand Down
12 changes: 12 additions & 0 deletions apps/wallet/store/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Store material

What the App Store and Google Play ask for beyond the build, kept beside the code it describes.

| File | Use |
| --- | --- |
| `listing.en.md`, `listing.de.md` | Name, subtitle, short and full description, keywords, category |
| `review-notes.md` | Notes for App Review; `review-code.png` is the sample request as a QR code to attach |
| `privacy-policy.md` | The privacy policy text, to publish at a public address |
| `declarations.md` | Answers for App Privacy, Data safety, the financial features declaration, content rating, export compliance and trader status |

Screenshots are taken from a build on a device or simulator and are not kept here.
76 changes: 76 additions & 0 deletions apps/wallet/store/declarations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Store declarations

The answers for the questionnaires in App Store Connect and the Play Console, with the reason for
each. They follow from what the app does: it has no server, sends nothing by itself and keeps
everything on the device.

## App Store Connect

**App Privacy: Data Not Collected.** Apple counts data as collected when it is transmitted off the
device in a way that lets the developer or a partner access it. Nothing is: payees, kept requests
and settings are stored on the device (and in the user's own device backups, which the developer
cannot access), and sharing or exporting hands data to the system share sheet at the user's
request, to a destination the user picks. The privacy manifest in
`app.json` says the same: no tracking, no tracking domains, no collected data types.

**Required reason APIs.** The privacy manifest declares the reasons the dependencies give in their
own manifests, since App Store processing does not read those from static CocoaPods:

| API | Reasons | Declared by |
| --- | --- | --- |
| UserDefaults | CA92.1 | React Native, expo-constants, expo-localization, expo-system-ui |
| File timestamp | 0A2A.1, 3B52.1, C617.1 | React Native and its pods, AsyncStorage, expo-file-system |
| Disk space | 85F4.1, E174.1 | expo-file-system |
| System boot time | 35F9.1 | React Native |

When a dependency is added or updated, compare its `ios/PrivacyInfo.xcprivacy` with this list.

**Export compliance.** `usesNonExemptEncryption: false`. The app makes no network requests and
contains no encryption of its own, so the question does not come up at each upload.

**Age rating: 4+.** No objectionable content, no web browsing inside the app, no user-generated
content shared through the app, no purchases.

**Devices.** iPhone only (`supportsTablet: false`). Screenshots are needed for the 6.9-inch
iPhone size.

## Play Console

**Data safety: no data collected, no data shared.** Google counts data as collected when it is
transmitted off the device; data processed only on the device is not collected, and a transfer
the user starts through the share sheet or an intent to another app is not sharing by the
developer. The camera image is processed on the device and never stored.

**Financial features declaration.** A choice for the publisher. The app creates and reads
payment request QR codes and hands the payment to the user's banking app; it holds no funds,
moves no money and is not a payment service. Two answers fit:

- *Support services, Other*, described as "Creates and reads SEPA payment request QR codes. No
funds are held or moved; the payment is made in the user's own banking app." This matches the
Finance category the listing uses and explains the app to the reviewer.
- *My app doesn't provide any financial features*, since none of the listed features (banking,
loans, wallets, transfers, trading) is offered.

"Mobile payments and digital wallets" and "Money transfer and wire services" do not fit: the app
neither stores payment credentials nor transmits money.

**Content rating (IARC).** Category: utility, productivity, communication or other. No violence,
sexual content, profanity, controlled substances, gambling or horror. Users cannot interact or
exchange content with each other inside the app, no location is shared and nothing is sold. The
expected result is the lowest rating in every region (PEGI 3, USK 0, Everyone).

**Target API.** 36, the default of React Native 0.86. The production profile in `eas.json` builds
an app bundle for Play App Signing.

**Ads.** The app contains no ads.

## Both stores

**Privacy policy.** The text is in `privacy-policy.md`. Both stores need it at a public address,
filled in with the publisher's name, address and contact.

**EU trader status (Digital Services Act).** A choice for the publisher: an app published by a
company is normally published as a trader, and both stores then show the trader's address, phone
number and email on the listing.

**Support URL.** The repository's issue tracker.
53 changes: 53 additions & 0 deletions apps/wallet/store/listing.de.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Store-Eintrag, Deutsch

Grenzen der Stores: App-Store-Name und Untertitel 30 Zeichen, Schlüsselwörter 100, Werbetext 170,
Beschreibung 4000; Google Play Titel 30, Kurzbeschreibung 80, vollständige Beschreibung 4000.

## Name (beide Stores)

Euvena

## Untertitel (App Store)

Zahlungs-QR-Codes für SEPA

## Kurzbeschreibung (Google Play)

SEPA-Überweisungen per QR-Code anfordern und bezahlen. Ohne Konto, ohne Server.

## Werbetext (App Store)

Fordere eine SEPA-Überweisung per QR-Code an, den europäische Banking-Apps lesen, oder lies einen und gib ihn an deine Banking-App weiter. Ohne Konto, ohne Server.

## Schlüsselwörter (App Store)

sepa,epc,qr,qr-code,iban,überweisung,zahlungsanforderung,rechnung,bank,en 18184,echtzeit

## Beschreibung (beide Stores)

Euvena macht aus einer SEPA-Überweisung einen QR-Code und wieder zurück.

GELD ANFORDERN
Gib einen Betrag ein, auf Wunsch auch den Verwendungszweck, und Euvena zeigt einen EPC-QR-Code (GiroCode), das Format, mit dem viele europäische Banking-Apps eine Überweisung ausfüllen. Teile die Anforderung als Link oder behalte sie im Verlauf auf deinem Handy. Mehrere Empfänger lassen sich speichern, für einen Haushalt, einen Verein oder ein kleines Geschäft.

ANFORDERUNG BEZAHLEN
Scanne einen Zahlungscode oder füge eine geteilte Anforderung ein. Euvena zeigt genau, was der Code enthält: Name, IBAN, Betrag und Referenz. Dann übergibt Euvena die Angaben als payto-Link an eine App, die ihn annimmt, oder lässt dich sie Feld für Feld in deine Banking-App kopieren. Ein Code, der eine Prüfung nicht besteht, wird ganz abgelehnt und nie nur teilweise angezeigt.

EN-18184-CODES
Euvena liest auch EN-18184-Codes (EPC024-22) für den Point of Interaction, die die Empfängerdaten enthalten, und erstellt sie, sobald du die Angaben eingibst, die dir dein Zahlungsrahmenwerk zugewiesen hat.

KEIN KONTO, KEIN SERVER, KEINE DATEN
Euvena hat keine Konten und kein Backend. Die App hält und bewegt nie Geld und kontaktiert keine Bank: Jede Zahlung wird in deiner eigenen Banking-App ausgeführt und freigegeben. Empfänger und gespeicherte Anforderungen liegen auf deinem Handy, nie auf einem Server von uns, und eine Exportdatei überträgt sie auf ein anderes Handy, wenn du das möchtest. Es wird nichts erhoben, verfolgt oder weitergegeben.

SIEBEN SPRACHEN
Deutsch, Englisch, Französisch, Spanisch, Italienisch, Niederländisch und Polnisch, in heller oder dunkler Darstellung.

Euvena ist Open Source. Der Code und die Bibliothek für Zahlungscodes dahinter liegen auf GitHub.

## Kategorie

Finanzen (beide Stores)

## Support-URL

https://github.com/LedgerInnovation/euvena/issues
Loading
Loading