@tapayadot/accept-react-native 1.3.3 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +196 -363
  2. package/android/build.gradle +23 -34
  3. package/android/src/main/java/com/tapaya/acceptsdkreactnative/AcceptSdkReactNativeModule.kt +191 -673
  4. package/android/src/main/java/com/tapaya/acceptsdkreactnative/Errors.kt +81 -0
  5. package/android/src/main/java/com/tapaya/acceptsdkreactnative/Parsing.kt +166 -0
  6. package/android/src/main/java/com/tapaya/acceptsdkreactnative/Serialization.kt +216 -0
  7. package/app.json +2 -2
  8. package/app.plugin.js +1 -1
  9. package/build/Accept.d.ts +255 -0
  10. package/build/Accept.d.ts.map +1 -0
  11. package/build/Accept.js +387 -0
  12. package/build/Accept.js.map +1 -0
  13. package/build/Accept.types.d.ts +352 -0
  14. package/build/Accept.types.d.ts.map +1 -0
  15. package/build/Accept.types.js +9 -0
  16. package/build/Accept.types.js.map +1 -0
  17. package/build/AcceptError.d.ts +53 -0
  18. package/build/AcceptError.d.ts.map +1 -0
  19. package/build/AcceptError.js +107 -0
  20. package/build/AcceptError.js.map +1 -0
  21. package/build/AcceptSdkReactNativeModule.d.ts +67 -26
  22. package/build/AcceptSdkReactNativeModule.d.ts.map +1 -1
  23. package/build/AcceptSdkReactNativeModule.js +53 -3
  24. package/build/AcceptSdkReactNativeModule.js.map +1 -1
  25. package/build/index.d.ts +7 -237
  26. package/build/index.d.ts.map +1 -1
  27. package/build/index.js +7 -254
  28. package/build/index.js.map +1 -1
  29. package/build/utils.d.ts.map +1 -1
  30. package/build/utils.js +2 -2
  31. package/build/utils.js.map +1 -1
  32. package/expo-module.config.json +1 -6
  33. package/package.json +8 -12
  34. package/plugin/withAcceptSdk.js +45 -0
  35. package/build/AcceptSdkReactNative.types.d.ts +0 -259
  36. package/build/AcceptSdkReactNative.types.d.ts.map +0 -1
  37. package/build/AcceptSdkReactNative.types.js +0 -2
  38. package/build/AcceptSdkReactNative.types.js.map +0 -1
  39. package/ios/.xcode.env +0 -11
  40. package/ios/AcceptSdkReactNative.podspec +0 -33
  41. package/ios/AcceptSdkReactNativeAppDelegate.swift +0 -13
  42. package/ios/AcceptSdkReactNativeModule.swift +0 -749
  43. package/ios/Podfile +0 -63
  44. package/ios/Podfile.properties.json +0 -4
  45. package/ios/acceptsdkreactnative.xcodeproj/project.pbxproj +0 -540
  46. package/ios/acceptsdkreactnative.xcodeproj/xcshareddata/xcschemes/acceptsdkreactnative.xcscheme +0 -88
  47. package/plugin/withCustomRepoModule.js +0 -96
package/README.md CHANGED
@@ -1,462 +1,295 @@
1
- ![AcceptSDK](banner-github.png)
1
+ ![Tapaya Accept](banner-github.png)
2
2
 
3
- ![Platform](https://img.shields.io/badge/platform-React%20Native-blue) ![iOS](https://img.shields.io/badge/iOS-26%2B-blue) ![Android](https://img.shields.io/badge/Android-11%2B-green) [![Docs](https://img.shields.io/badge/docs-tapaya.com-blue)](https://docs.tapaya.com) [![License](https://img.shields.io/badge/license-Apache%202.0-blue)](LICENSE) [![npm](https://img.shields.io/badge/npm-accept--sdk--react--native-red)](https://www.npmjs.com/package/@tapayadot/accept-react-native)
3
+ # Tapaya Accept SDK for React Native
4
4
 
5
- React Native / Expo SDK for integrating Tapaya Accept payment processing into your app.
5
+ Accept card payments on Android terminals from a React Native or Expo app.
6
6
 
7
- ## Installation
8
-
9
- ```sh
10
- npm install @tapayadot/accept-react-native
11
- ```
7
+ This package is a thin wrapper over the [Accept SDK for Android](https://docs.tapaya.com)
8
+ (`com.tapaya:accept`), which in turn drives the **Accept plugin app** — the companion
9
+ installed on the terminal that owns the card reader and the payment UI. Every surface here
10
+ mirrors the Android SDK 1:1, so its reference documentation applies unchanged.
12
11
 
13
- ### Expo plugin
12
+ Full documentation: [docs.tapaya.com](https://docs.tapaya.com)
14
13
 
15
- Add the plugin to your `app.json`. The plugin automatically configures the native Android Maven repository and iOS Swift Package Manager dependency:
14
+ ## Requirements
16
15
 
17
- ```json
18
- {
19
- "expo": {
20
- "plugins": ["@tapayadot/accept-react-native"]
21
- }
22
- }
23
- ```
16
+ - **Android only.** There is no iOS implementation. The package is safe to import on
17
+ iOS, but every call throws, so guard them with `Platform.OS === 'android'`.
18
+ - **`minSdkVersion` 30** (Android 11). The config plugin raises the host app to match.
19
+ - **Expo SDK 54+**, or a bare React Native app using `expo-modules-core`. Verified against
20
+ SDK 57 / React Native 0.86 on an Android 16 terminal.
21
+ - A **custom development build** — this package contains native code and does not run in
22
+ Expo Go.
24
23
 
25
- Then run:
24
+ ## Installation
26
25
 
27
26
  ```sh
28
- npx expo prebuild
27
+ npx expo install @tapayadot/accept-react-native
29
28
  ```
30
29
 
31
- #### Plugin options
32
-
33
- The plugin accepts options to toggle optional native features. Pass them as the second element of the plugin entry in `app.json`:
34
-
35
- | Option | Type | Default | Description |
36
- |---|---|---|---|
37
- | `enableStripe` | `boolean` | `false` | Android only. Bundle the optional `com.tapaya:accept-stripe` dependency. Enable this only if your merchant account uses Stripe-backed payment processing. |
30
+ Add the config plugin to `app.json`:
38
31
 
39
32
  ```json
40
33
  {
41
34
  "expo": {
42
- "plugins": [
43
- [
44
- "@tapayadot/accept-react-native",
45
- { "enableStripe": true }
46
- ]
47
- ]
35
+ "plugins": ["@tapayadot/accept-react-native"]
48
36
  }
49
37
  }
50
38
  ```
51
39
 
52
- When `enableStripe` is `true`, the plugin writes `com.tapaya.accept.enableStripe=true` into your app's `android/gradle.properties` during prebuild, which adds `com.tapaya:accept-stripe` to the Android build. iOS payment processing is bundled in the Swift Package and requires no flag.
53
-
54
- Re-run prebuild after changing plugin options so the native projects pick up the change:
40
+ Then rebuild the native project:
55
41
 
56
42
  ```sh
57
43
  npx expo prebuild --clean
44
+ npx expo run:android
58
45
  ```
59
46
 
60
- ### Android
61
-
62
- The Android SDK is distributed via Maven Central, which is a default repository in every Android project — no additional repository configuration is required.
63
-
64
- ### iOS
65
-
66
- The iOS SDK is resolved via Swift Package Manager during `prebuild` / `pod install`. No additional configuration is required.
67
-
68
- ## Requirements
69
-
70
- - React Native 0.74+ / Expo SDK 51+
71
- - iOS 26+
72
- - Android API 30+
73
-
74
- ## Usage
47
+ The plugin only raises `minSdkVersion` to 30. Everything else the SDK needs — the
48
+ `INTERNET`, `ACCESS_NETWORK_STATE`, and location permissions, the `<queries>` entries that
49
+ make the plugin app visible on API 30+, and the activities that run a payment on a
50
+ customer-facing display — ships in the Android SDK's own manifest and merges in
51
+ automatically.
75
52
 
76
- ### Setup
53
+ ## Quick start
77
54
 
78
55
  ```ts
79
56
  import Accept from '@tapayadot/accept-react-native';
80
57
 
81
- // Initialize once at app launch
82
- await Accept.initialize(/* demo: */ false);
58
+ // 1. Initialize once, at app startup.
59
+ Accept.initialize({ isProduction: false }); // false = sandbox
83
60
 
84
- // Authenticate with your merchant token
85
- await Accept.authenticate('your-merchant-token');
86
- ```
61
+ // 2. Authenticate the merchant.
62
+ await Accept.auth.authenticate(merchantToken);
87
63
 
88
- ### UI Theming
64
+ // 3. Make sure the plugin app is installed and its terminal is activated.
65
+ if (!Accept.plugin.isInstalled()) {
66
+ await Accept.plugin.install(); // opens the store listing
67
+ }
68
+ if ((await Accept.plugin.status()) !== 'readyForPayments') {
69
+ await Accept.plugin.activateTerminal();
70
+ }
89
71
 
90
- Customize colors and logos at initialization or any time after:
72
+ // 4. Take a payment. Amounts are in the currency's minor unit.
73
+ const result = await Accept.payments.payAsync({ amount: 1000, currency: 'USD' });
74
+ if (result.type === 'success') {
75
+ console.log(result.paymentToken, result.receiptUrl);
76
+ }
77
+ ```
91
78
 
92
- ```ts
93
- import Accept, { type AcceptThemeConfiguration } from '@tapayadot/accept-react-native';
94
- import { assetToBase64 } from '@tapayadot/accept-react-native/utils';
79
+ ### Location permission
95
80
 
96
- const theme: AcceptThemeConfiguration = {
97
- colors: {
98
- accent: '#FF8400',
99
- error: '#E6282B',
100
- warning: '#FF8000',
101
- success: '#228800',
102
- brandGradientColors: { start: '#FF8400', end: '#9A94E9' },
103
- },
104
- images: {
105
- brandLogo: await assetToBase64(require('./assets/brand-logo.png')),
106
- toolbarLogo: await assetToBase64(require('./assets/toolbar-logo.png')),
107
- solidLogo: await assetToBase64(require('./assets/solid-logo.png')),
108
- },
109
- };
81
+ A payment requires a device location fix, and the **host app** must request the permission —
82
+ the SDK does not. Request it before the first payment, or it fails with
83
+ `LocationPermissionRequired`:
110
84
 
111
- // Pass at initialization
112
- await Accept.initialize(false, theme);
85
+ ```ts
86
+ import { PermissionsAndroid } from 'react-native';
113
87
 
114
- // Or apply after initialization
115
- await Accept.setTheme(theme);
88
+ await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION);
116
89
  ```
117
90
 
118
- Omit `colors` or `images` to keep SDK defaults for that slot. Images must be PNG or JPEG — SVG is not supported.
91
+ ## API
119
92
 
120
- > `assetToBase64` requires `expo-asset` and `expo-file-system`. Both are included in managed Expo projects. Bare React Native users must install them manually.
93
+ Everything hangs off the default export.
121
94
 
122
- ### Request Location Permission
95
+ | Surface | Access | Purpose |
96
+ | -------- | ------------------ | ------------------------------------------------------------------------------ |
97
+ | State | `Accept.state` | `'idle' \| 'initialized' \| 'authenticated'`, plus `addStateListener()` |
98
+ | Auth | `Accept.auth` | `authenticate(merchantToken)` |
99
+ | SDK | `Accept.sdk` | `version`, `isProduction`, `deviceId()`, `minimumAmounts()` |
100
+ | Merchant | `Accept.merchant` | `info()`, `config()`, `onboardingStatus()`, `availableCurrencies()` |
101
+ | Payments | `Accept.payments` | `pay()`, `payAsync()`, `status()`, `cancel()`, `refund()`, `setOptions()` |
102
+ | Plugin | `Accept.plugin` | `isInstalled()`, `install()`, `activateTerminal()`, `status()`, `updateInfo()`, `logout()` |
103
+ | Displays | `Accept.displays` | `all()`, `customerFacing()`, `isDualScreen` |
123
104
 
124
- Required before processing card payments (Tap-to-Pay):
105
+ Top-level: `initialize()`, `clear()`, `setTheme()`, `getTheme()`,
106
+ `createLocalThemeImageUri()`, `setLogLevel()`, `setDebugLoggingEnabled()`, `setLogger()`.
125
107
 
126
- ```ts
127
- await Accept.requestLocationPermission();
128
- ```
108
+ ### Lifecycle
129
109
 
130
- ### Card Payment (Tap-to-Pay)
110
+ `Accept.state` moves `idle → initialized → authenticated`:
131
111
 
132
112
  ```ts
133
- import Accept, { CardPaymentIntent } from '@tapayadot/accept-react-native';
134
-
135
- const intent: CardPaymentIntent = {
136
- paymentIntentId: 'pi_xxx',
137
- amount: 1500,
138
- requestedCurrency: 'CZK', // ISO 4217 alpha-3
139
- settlementCurrency: 'EUR',
140
- };
141
-
142
- const result = await Accept.payments.startCardPayment(
143
- intent,
144
- (status) => console.log('Status update:', status),
145
- (message, exception) => console.error('Error:', message, exception),
146
- );
147
-
148
- switch (result.status) {
149
- case 'approved':
150
- console.log('Approved:', result.authorizationCode);
151
- break;
152
- case 'declined':
153
- console.log('Declined:', result.reason);
154
- break;
155
- }
113
+ const subscription = Accept.addStateListener((state) => {
114
+ if (state === 'authenticated') loadMerchant();
115
+ });
116
+
117
+ // later
118
+ subscription.remove();
156
119
  ```
157
120
 
158
- ### Refund a Card Payment
121
+ ### Payments as a stream
122
+
123
+ `payAsync()` is a convenience over `pay()`, which reports the whole lifecycle. Use it when
124
+ the UI reflects the intermediate states:
159
125
 
160
126
  ```ts
161
- const refund = await Accept.payments.refund({
162
- paymentToken: 'pi_xxx', // source payment intent id (or server payment UUID)
163
- reason: 'customerRequest', // 'customerRequest' | 'duplicate' | 'fraudulent' | 'expiredCharge'
127
+ const subscription = Accept.payments.pay({ amount: 1000, currency: 'USD' }, (event) => {
128
+ switch (event.type) {
129
+ case 'creating':
130
+ setStatus('Creating payment…');
131
+ break;
132
+ case 'created':
133
+ // The payment token and receipt URL exist before the plugin is launched.
134
+ setReceiptUrl(event.receiptUrl);
135
+ break;
136
+ case 'launched':
137
+ setStatus('Waiting for the card…');
138
+ break;
139
+ case 'result':
140
+ setResult(event.payResult); // success | declined | canceled | failed
141
+ break;
142
+ case 'creationFailed':
143
+ setError(event.error); // an AcceptError payload; the payment never started
144
+ break;
145
+ }
164
146
  });
165
-
166
- console.log(refund.status); // 'success' | 'pending' | 'cancelled' | 'refunded' | 'failed' | 'actionNeeded'
167
147
  ```
168
148
 
169
- ### Cancel an Active Payment
149
+ `pay()` never throws — a failure to create the payment arrives as `creationFailed`. The
150
+ stream ends after `result` or `creationFailed` and the subscription removes itself; call
151
+ `subscription.remove()` to stop listening early (the plugin's UI stays on screen).
170
152
 
171
- ```ts
172
- // Dismisses any payment UI and cancels the in-flight transaction.
173
- await Accept.payments.cancelActivePayment();
174
- ```
153
+ ### Payment options
175
154
 
176
- ### SEPA Instant Credit Transfer
155
+ Per payment, or session-wide via `Accept.payments.setOptions()`:
177
156
 
178
157
  ```ts
179
- const result = await Accept.payments.startSepaPayment(
180
- {
181
- paymentIntentId: 'pi_xxx',
182
- amount: 1000,
183
- requestedCurrency: 'EUR',
184
- settlementCurrency: 'EUR',
158
+ Accept.payments.setOptions({
159
+ receiptConfig: {
160
+ type: 'show',
161
+ showQrCodeReceipt: true,
162
+ dismissBehavior: { type: 'delayed', delayMs: 5000 },
163
+ printBehavior: 'onDemand',
185
164
  },
186
- (status) => console.log('Status update:', status),
187
- (message, exception) => console.error('Error:', message, exception),
188
- );
165
+ nfcPosition: { type: 'onDevice', surface: 'back', xFraction: 0.5, yFraction: 0.25 },
166
+ transactionTimeoutMs: 60_000, // 0…120000
167
+ });
189
168
 
190
- if (result.status === 'verified') {
191
- console.log('Received from:', result.receivedFromIban);
192
- }
169
+ await Accept.payments.payAsync({
170
+ amount: 2500,
171
+ currency: 'EUR',
172
+ tip: 250, // skips the plugin's own tip screen
173
+ metadata: { orderId: 'A-1024' }, // echoed back on the result
174
+ });
193
175
  ```
194
176
 
195
- ### Certis (Czech Instant Transfer)
177
+ Only the keys you pass are changed, so one default can be set without disturbing the others.
196
178
 
197
- ```ts
198
- const result = await Accept.payments.startCertisPayment(
199
- {
200
- paymentIntentId: 'pi_xxx',
201
- amount: 10000,
202
- requestedCurrency: 'CZK',
203
- settlementCurrency: 'CZK',
204
- },
205
- (status) => console.log('Status update:', status),
206
- (message, exception) => console.error('Error:', message, exception),
207
- );
208
- ```
179
+ ### Dual-screen terminals
209
180
 
210
- ### Onboarding Status
181
+ On dual-sided hardware, run the payment UI on the customer-facing panel while your app keeps
182
+ the merchant panel:
211
183
 
212
184
  ```ts
213
- // Aggregate status across all payment methods
214
- const onboarding = await Accept.identity.getOnboardingStatus();
215
- console.log(onboarding.isOnboarded);
216
- onboarding.paymentMethods.forEach((m) =>
217
- console.log(m.method, m.status), // e.g. 'CARD', 'ACTIVE'
218
- );
219
-
220
- // Single-method status
221
- const cardStatus = await Accept.identity.getPaymentMethodStatus('CARD');
222
- // 'NOT_STARTED' | 'PENDING' | 'ACTIVE' | 'ATTENTION_NEEDED' | 'ACTION_REQUIRED' | 'BLOCKED'
185
+ if (Accept.displays.isDualScreen) {
186
+ Accept.payments.setOptions({ display: { type: 'customerFacing' } });
187
+ }
223
188
  ```
224
189
 
225
- ### KYB Onboarding
190
+ Set this **before** `authenticate()` — the terminal warm-up it fires follows the same target,
191
+ and you don't want the warm-up flashing on the merchant's screen.
226
192
 
227
- ```ts
228
- // Check if onboarding is needed before launching the KYB flow
229
- await Accept.identity.presentKyb({
230
- businessType: 'company',
231
- legalName: 'Acme s.r.o.',
232
- businessEmail: 'billing@acme.com',
233
- countryCode: 'CZ',
234
- registrationNumber: '12345678',
235
- vatNumber: 'CZ12345678',
236
- addressLine1: 'Václavské náměstí 1',
237
- city: 'Praha',
238
- postalCode: '11000',
239
- bankAccountIban: 'CZ...',
240
- businessUrl: 'https://acme.com',
241
- productDescription: 'SaaS platform',
242
- supportPhone: '+420123456789',
243
- });
244
- ```
193
+ ### Errors
245
194
 
246
- ### Session Management
195
+ Every rejection is an `AcceptError` whose `code` names the failure, carrying that case's
196
+ fields:
247
197
 
248
198
  ```ts
249
- // Check current SDK status
250
- const status = await Accept.getStatus(); // 'IDLE' | 'INITIALIZED' | 'LOGGED_IN'
251
-
252
- // Query the result of a previous payment
253
- const result = await Accept.getPaymentStatus('pi_xxx');
254
-
255
- // Log out
256
- await Accept.logOut();
199
+ import Accept, { AcceptError } from '@tapayadot/accept-react-native';
200
+
201
+ try {
202
+ await Accept.payments.payAsync({ amount: 10, currency: 'USD' });
203
+ } catch (error) {
204
+ if (!AcceptError.is(error)) throw error;
205
+
206
+ switch (error.code) {
207
+ case 'AmountBelowMinimum':
208
+ alert(`Minimum is ${error.minimum} ${error.currency}`);
209
+ break;
210
+ case 'PluginNotReadyForPayments':
211
+ await Accept.plugin.activateTerminal();
212
+ break;
213
+ case 'LocationPermissionRequired':
214
+ await requestLocationPermission();
215
+ break;
216
+ case 'NoInternetConnection':
217
+ alert('No connection.');
218
+ break;
219
+ default:
220
+ throw error;
221
+ }
222
+ }
257
223
  ```
258
224
 
259
- ### Demo Mode
260
-
261
- ```ts
262
- await Accept.demo.enable(); // simulate payments without real terminals
263
- const isDemo = await Accept.demo.isEnabled();
264
- await Accept.demo.disable();
265
- ```
225
+ ### Theming
266
226
 
267
- ### UI Behavior (Android only)
227
+ Branding for the plugin's activation, status, and payment screens. Session-wide, and pushed
228
+ to the plugin the next time it is bound or launched:
268
229
 
269
230
  ```ts
270
- await Accept.setUiBehavior({
271
- resultScreenDurationSeconds: 5,
272
- showResultScreen: true,
273
- nfcZone: {
274
- indicator: 'BELOW', // 'DEFAULT' | 'ABOVE' | 'BELOW' | 'FRONT' | 'BEHIND'
275
- position: { xBias: 0.5, yBias: 0.7 }, // omit to use terminal default
231
+ Accept.setTheme({
232
+ light: {
233
+ colors: { primary: '#0B5FFF', onPrimary: '#FFFFFF', surface: '#FFFFFF' },
234
+ images: { logoSymbol: 'https://example.com/logo.png' },
235
+ },
236
+ dark: {
237
+ colors: { primary: '#6E9BFF', onPrimary: '#001B3D', surface: '#101418' },
276
238
  },
277
239
  });
278
240
  ```
279
241
 
280
- ### Companion App
281
-
282
- The Tapaya companion app is required for card payments. Supported on both iOS and Android:
242
+ For a logo bundled with the app rather than hosted, convert it and hand the plugin a
243
+ `content://` URL:
283
244
 
284
245
  ```ts
285
- const installed = await Accept.isCompanionAppInstalled();
286
-
287
- if (!installed) {
288
- // Prompt user to install via the store product sheet (App Store on iOS, Play Store on Android)
289
- await Accept.presentCompanionAppSheet();
290
- } else {
291
- // Pair with the companion app (rejects if it is not installed or not ready)
292
- await Accept.pairWithCompanionApp();
293
- }
294
- ```
295
-
296
- ## API Reference
297
-
298
- ### Top-level
299
-
300
- | Method | Description |
301
- |---|---|
302
- | `initialize(demo?, theme?)` | Initialize the SDK. Pass `true` for sandbox mode. Optionally provide a UI theme. |
303
- | `setTheme(theme)` | Apply a UI theme after initialization. |
304
- | `setUiBehavior(behavior)` | Android only. Configure result screen + NFC zone. |
305
- | `authenticate(token)` | Authenticate with a merchant JWT token. |
306
- | `getStatus()` | Returns `'IDLE' \| 'INITIALIZED' \| 'LOGGED_IN'`. |
307
- | `getPaymentStatus(paymentIntentId)` | Retrieve the result of a previous payment. |
308
- | `logOut()` | End the current merchant session. |
309
- | `requestLocationPermission()` | Request location permission (required for card payments). |
310
- | `isCompanionAppInstalled()` | Check if the Tapaya companion app is installed. |
311
- | `presentCompanionAppSheet()` | Show the store product sheet for the companion app (App Store on iOS, Play Store on Android). |
312
- | `pairWithCompanionApp()` | Pair with the installed companion app. |
313
-
314
- ### `Accept.payments`
315
-
316
- | Method | Description |
317
- |---|---|
318
- | `startCardPayment(intent, onStatus, onError)` | Start a card/Tap-to-Pay payment. |
319
- | `startSepaPayment(intent, onStatus, onError)` | Start a SEPA Instant payment. |
320
- | `startCertisPayment(intent, onStatus, onError)` | Start a Certis Instant payment. |
321
- | `refund(intent)` | Refund a previously completed card payment (headless, no UI). |
322
- | `cancelActivePayment()` | Dismiss in-flight payment UI and cancel the transaction. |
323
-
324
- ### `Accept.identity`
325
-
326
- | Method | Description |
327
- |---|---|
328
- | `presentKyb(prefilling?)` | Launch the KYB onboarding UI with optional prefill data. |
329
- | `getOnboardingStatus()` | Returns aggregate merchant onboarding status across payment methods. |
330
- | `getPaymentMethodStatus(method)` | Returns onboarding status for a single payment method. |
246
+ import { assetToBase64 } from '@tapayadot/accept-react-native/utils';
331
247
 
332
- ### `Accept.demo`
248
+ const base64 = await assetToBase64(require('./assets/logo.png'));
249
+ const uri = Accept.createLocalThemeImageUri(base64, 'png');
333
250
 
334
- | Method | Description |
335
- |---|---|
336
- | `enable()` | Enable demo mode — payments are simulated, no real terminal required. |
337
- | `disable()` | Return to real terminal flows. |
338
- | `isEnabled()` | Whether demo mode is currently enabled. |
251
+ Accept.setTheme({ light: { images: { logoSymbol: uri } } });
252
+ ```
339
253
 
340
- ## Types
254
+ `assetToBase64` needs `expo-asset` and `expo-file-system`, both present in managed Expo
255
+ projects. Bare React Native consumers must install them.
341
256
 
342
- Currency and country fields are plain ISO strings:
343
- - Currency — ISO 4217 alpha-3 (`'CZK'`, `'EUR'`, `'GBP'`, …)
344
- - Country — ISO 3166-1 alpha-2 (`'CZ'`, `'GB'`, …)
257
+ ### Logging
345
258
 
346
259
  ```ts
347
- type CardPaymentIntent = {
348
- paymentIntentId: string
349
- amount: number // smallest currency unit (e.g. hellers)
350
- requestedCurrency: string // ISO 4217 alpha-3
351
- settlementCurrency?: string // defaults to requestedCurrency
352
- }
353
-
354
- type CardPaymentResult = {
355
- status: 'pending' | 'approved' | 'declined' | 'refunded' | 'aborted'
356
- last4?: string
357
- brand?: string
358
- authorizationCode?: string
359
- terminalId?: string
360
- companyName?: string
361
- companyAddress1?: string
362
- companyAddress2?: string
363
- companyCity?: string
364
- companyCountry?: string // ISO 3166-1 alpha-2
365
- reason?: string
366
- receiptUrl?: string
367
- refundToken?: string // present when this card payment was refunded
368
- }
369
-
370
- type SepaPaymentIntent = { paymentIntentId: string; amount: number; requestedCurrency: string; settlementCurrency?: string }
371
- type SepaPaymentResult = { status: 'pending' | 'verified' | 'aborted' | 'refunded' | 'declined'; postponed: boolean; receivedAt: string; receivedFromIban: string; receiptUrl?: string }
372
-
373
- type CertisPaymentIntent = { paymentIntentId: string; amount: number; requestedCurrency: string; settlementCurrency?: string }
374
- type CertisPaymentResult = { status: 'pending' | 'verified' | 'aborted' | 'refunded' | 'declined'; postponed: boolean; receivedAt: string; receivedFromIban: string; receiptUrl?: string }
375
-
376
- type RefundReason = 'customerRequest' | 'duplicate' | 'fraudulent' | 'expiredCharge'
377
- type TransactionStatus = 'pending' | 'success' | 'cancelled' | 'refunded' | 'failed' | 'actionNeeded'
378
-
379
- type RefundIntent = {
380
- paymentToken: string // source payment UUID or original paymentIntentId
381
- refundToken?: string // idempotency token, pass same value on retry
382
- reason: RefundReason
383
- }
384
-
385
- type RefundResult = {
386
- refundToken?: string
387
- status: TransactionStatus
388
- refundReason: RefundReason
389
- paymentMethod?: PaymentMethod
390
- requestedAmount?: number
391
- requestedCurrency?: string
392
- settlementAmount?: number
393
- settlementCurrency?: string
394
- fxRate?: number
395
- createdAt?: string // ISO 8601 UTC
396
- processedAt?: string // ISO 8601 UTC
397
- }
398
-
399
- type PaymentMethod = 'CARD' | 'SEPA' | 'CERTIS' | 'CRYPTO'
400
- type PaymentMethodStatus = 'NOT_STARTED' | 'PENDING' | 'ACTIVE' | 'ATTENTION_NEEDED' | 'ACTION_REQUIRED' | 'BLOCKED'
401
-
402
- type AcceptPaymentMethodStatus = { method: PaymentMethod; status: PaymentMethodStatus }
403
- type AcceptOnboardingStatus = { isOnboarded: boolean; paymentMethods: AcceptPaymentMethodStatus[] }
260
+ Accept.setLogLevel('DEBUG'); // NONE | ERROR | WARN | INFO | DEBUG | VERBOSE
261
+ Accept.setLogger((entry) => Sentry.addBreadcrumb({ message: entry.message }));
262
+ ```
404
263
 
405
- type GradientColors = { start: string; end: string }
264
+ Logging defaults to `DEBUG` against sandbox and `NONE` against production. Pass `null` to
265
+ `setLogger` to restore the default Logcat sink.
406
266
 
407
- type AcceptColorTheme = {
408
- accent: string // #RRGGBB or #AARRGGBB
409
- error: string
410
- warning: string
411
- success: string
412
- brandGradientColors?: GradientColors
413
- brandSubtleGradientColors?: GradientColors
414
- }
267
+ ### Logout
415
268
 
416
- type AcceptImageTheme = {
417
- brandLogo: string // base64 PNG/JPEG
418
- toolbarLogo: string
419
- solidLogo: string
420
- }
269
+ ```ts
270
+ await Accept.plugin.logout(); // sign the merchant out of the plugin app
271
+ await Accept.clear(); // clear the local token, device id, cached config, and theme
272
+ ```
421
273
 
422
- type AcceptThemeConfiguration = {
423
- colors?: AcceptColorTheme
424
- images?: AcceptImageTheme
425
- }
274
+ ## Running the example app
426
275
 
427
- // Android-only
428
- type NfcIndicator = 'DEFAULT' | 'ABOVE' | 'BELOW' | 'FRONT' | 'BEHIND'
429
- type NfcPosition = { xBias: number; yBias: number } // 0..1 normalized; omit position for terminal default
430
- type NfcZone = { indicator: NfcIndicator; position?: NfcPosition }
431
- type AcceptUiBehavior = {
432
- resultScreenDurationSeconds?: number
433
- showResultScreen?: boolean
434
- nfcZone?: NfcZone
435
- }
276
+ `example/` is a demo harness mirroring the Accept SDK for Android's own `example` module: one
277
+ control per public API call, and a log pane showing what each one returned.
436
278
 
437
- type KybPrefillData = {
438
- businessType?: 'individual' | 'company' | 'nonProfit' | 'governmentEntity'
439
- countryCode?: string // ISO 3166-1 alpha-2
440
- registrationNumber?: string
441
- vatNumber?: string
442
- legalName?: string
443
- addressLine1?: string
444
- addressLine2?: string
445
- city?: string
446
- postalCode?: string
447
- merchantCategoryCode?: number
448
- businessEmail?: string
449
- bankAccountIban?: string
450
- businessUrl?: string
451
- productDescription?: string
452
- supportPhone?: string
453
- }
279
+ ```sh
280
+ npm install # installs the root package and the example workspace
281
+ cp example/.env.example example/.env.local # optional: prefill credentials
282
+ npm run android # prebuilds and runs on a connected device
454
283
  ```
455
284
 
456
- ## Documentation
285
+ `.env.local` is gitignored. Leave it out and type the API key and merchant token into the app's
286
+ Credentials fields instead — nothing is persisted either way. Never put a production key in it:
287
+ Expo inlines `EXPO_PUBLIC_*` variables into the bundle at build time.
457
288
 
458
- Full documentation is available at [docs.tapaya.com](https://docs.tapaya.com).
289
+ The example needs a real device with the Accept plugin app installed; an emulator has no card
290
+ reader. Tap **Login** to mint a merchant token and authenticate, then **activateTerminal()** once
291
+ before the first payment.
459
292
 
460
293
  ## License
461
294
 
462
- Licensed under the [Apache License, Version 2.0](LICENSE).
295
+ Apache-2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE).