@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.
- package/README.md +196 -363
- package/android/build.gradle +23 -34
- package/android/src/main/java/com/tapaya/acceptsdkreactnative/AcceptSdkReactNativeModule.kt +191 -673
- package/android/src/main/java/com/tapaya/acceptsdkreactnative/Errors.kt +81 -0
- package/android/src/main/java/com/tapaya/acceptsdkreactnative/Parsing.kt +166 -0
- package/android/src/main/java/com/tapaya/acceptsdkreactnative/Serialization.kt +216 -0
- package/app.json +2 -2
- package/app.plugin.js +1 -1
- package/build/Accept.d.ts +255 -0
- package/build/Accept.d.ts.map +1 -0
- package/build/Accept.js +387 -0
- package/build/Accept.js.map +1 -0
- package/build/Accept.types.d.ts +352 -0
- package/build/Accept.types.d.ts.map +1 -0
- package/build/Accept.types.js +9 -0
- package/build/Accept.types.js.map +1 -0
- package/build/AcceptError.d.ts +53 -0
- package/build/AcceptError.d.ts.map +1 -0
- package/build/AcceptError.js +107 -0
- package/build/AcceptError.js.map +1 -0
- package/build/AcceptSdkReactNativeModule.d.ts +67 -26
- package/build/AcceptSdkReactNativeModule.d.ts.map +1 -1
- package/build/AcceptSdkReactNativeModule.js +53 -3
- package/build/AcceptSdkReactNativeModule.js.map +1 -1
- package/build/index.d.ts +7 -237
- package/build/index.d.ts.map +1 -1
- package/build/index.js +7 -254
- package/build/index.js.map +1 -1
- package/build/utils.d.ts.map +1 -1
- package/build/utils.js +2 -2
- package/build/utils.js.map +1 -1
- package/expo-module.config.json +1 -6
- package/package.json +8 -12
- package/plugin/withAcceptSdk.js +45 -0
- package/build/AcceptSdkReactNative.types.d.ts +0 -259
- package/build/AcceptSdkReactNative.types.d.ts.map +0 -1
- package/build/AcceptSdkReactNative.types.js +0 -2
- package/build/AcceptSdkReactNative.types.js.map +0 -1
- package/ios/.xcode.env +0 -11
- package/ios/AcceptSdkReactNative.podspec +0 -33
- package/ios/AcceptSdkReactNativeAppDelegate.swift +0 -13
- package/ios/AcceptSdkReactNativeModule.swift +0 -749
- package/ios/Podfile +0 -63
- package/ios/Podfile.properties.json +0 -4
- package/ios/acceptsdkreactnative.xcodeproj/project.pbxproj +0 -540
- package/ios/acceptsdkreactnative.xcodeproj/xcshareddata/xcschemes/acceptsdkreactnative.xcscheme +0 -88
- package/plugin/withCustomRepoModule.js +0 -96
|
@@ -1,744 +1,262 @@
|
|
|
1
1
|
package com.tapaya.acceptsdkreactnative
|
|
2
2
|
|
|
3
3
|
import android.util.Base64
|
|
4
|
-
import com.google.gson.Gson
|
|
5
4
|
import com.tapaya.accept.Accept
|
|
6
|
-
import com.tapaya.accept.
|
|
7
|
-
import com.tapaya.accept.
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import com.tapaya.core.model.external.BusinessType
|
|
11
|
-
import com.tapaya.core.model.external.Currency
|
|
12
|
-
import com.tapaya.core.model.external.NfcIndicator
|
|
13
|
-
import com.tapaya.core.model.external.NfcPosition
|
|
14
|
-
import com.tapaya.core.model.external.NfcZone
|
|
15
|
-
import com.tapaya.core.model.external.PaymentMethod
|
|
16
|
-
import com.tapaya.core.model.external.RefundReason
|
|
17
|
-
import com.tapaya.core.model.external.exception.AcceptException
|
|
18
|
-
import com.tapaya.core.model.external.intent.CardChargeIntent
|
|
19
|
-
import com.tapaya.core.model.external.intent.CardRefundIntent
|
|
20
|
-
import com.tapaya.core.model.external.intent.CertisInstantPaymentIntent
|
|
21
|
-
import com.tapaya.core.model.external.intent.SepaInstantPaymentIntent
|
|
22
|
-
import com.tapaya.core.model.external.result.AbstractPaymentResult
|
|
23
|
-
import com.tapaya.core.model.external.result.CardPaymentResult
|
|
24
|
-
import com.tapaya.core.model.external.result.CertisInstantPaymentResult
|
|
25
|
-
import com.tapaya.core.model.external.result.SepaInstantPaymentResult
|
|
26
|
-
import com.tapaya.core.ui.theme.AcceptColorTheme
|
|
27
|
-
import com.tapaya.core.ui.theme.AcceptImageSource
|
|
28
|
-
import com.tapaya.core.ui.theme.AcceptImageTheme
|
|
29
|
-
import com.tapaya.core.ui.theme.AcceptThemeConfiguration
|
|
30
|
-
import com.tapaya.core.ui.theme.AcceptThemeSource
|
|
31
|
-
import com.tapaya.core.ui.theme.GradientColors
|
|
32
|
-
import expo.modules.interfaces.permissions.PermissionsStatus
|
|
5
|
+
import com.tapaya.accept.AcceptLogLevel
|
|
6
|
+
import com.tapaya.accept.AcceptLogger
|
|
7
|
+
import expo.modules.kotlin.exception.CodedException
|
|
8
|
+
import expo.modules.kotlin.exception.Exceptions
|
|
33
9
|
import expo.modules.kotlin.functions.Coroutine
|
|
34
10
|
import expo.modules.kotlin.modules.Module
|
|
35
11
|
import expo.modules.kotlin.modules.ModuleDefinition
|
|
36
|
-
import
|
|
37
|
-
import
|
|
38
|
-
import kotlinx.coroutines.
|
|
39
|
-
import
|
|
40
|
-
import
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
var settlementCurrency: String = ""
|
|
55
|
-
|
|
56
|
-
fun toCardInstantPaymentIntent(): CardChargeIntent {
|
|
57
|
-
val requested = Currency.valueOf(requestedCurrency)
|
|
58
|
-
return CardChargeIntent(
|
|
59
|
-
paymentIntentId = paymentIntentId,
|
|
60
|
-
amount = amount,
|
|
61
|
-
requestedCurrency = requested,
|
|
62
|
-
settlementCurrency = if (settlementCurrency.isEmpty()) requested else Currency.valueOf(settlementCurrency)
|
|
63
|
-
)
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
class CardPaymentResultReactNative(
|
|
68
|
-
@Field
|
|
69
|
-
var status: String = "",
|
|
70
|
-
@Field
|
|
71
|
-
var readerStatus: String = "",
|
|
72
|
-
@Field
|
|
73
|
-
var last4: String = "",
|
|
74
|
-
@Field
|
|
75
|
-
var companyName: String = "",
|
|
76
|
-
@Field
|
|
77
|
-
var companyAddress1: String = "",
|
|
78
|
-
@Field
|
|
79
|
-
var companyAddress2: String = "",
|
|
80
|
-
@Field
|
|
81
|
-
var companyCity: String = "",
|
|
82
|
-
@Field
|
|
83
|
-
var companyCountry: String = "",
|
|
84
|
-
@Field
|
|
85
|
-
var terminalId: String = "",
|
|
86
|
-
@Field
|
|
87
|
-
var authorizationCode: String = "",
|
|
88
|
-
@Field
|
|
89
|
-
var brand: String = "",
|
|
90
|
-
@Field
|
|
91
|
-
var reason: String = "",
|
|
92
|
-
@Field
|
|
93
|
-
var receiptUrl: String? = null,
|
|
94
|
-
@Field
|
|
95
|
-
var refundToken: String? = null,
|
|
96
|
-
@Field
|
|
97
|
-
var type: String = "",
|
|
98
|
-
) : Record
|
|
99
|
-
|
|
100
|
-
fun CardPaymentResult.toReactNative(): CardPaymentResultReactNative {
|
|
101
|
-
return CardPaymentResultReactNative(
|
|
102
|
-
status = status.name.lowercase(),
|
|
103
|
-
readerStatus = readerStatus?.name?.lowercase() ?: "",
|
|
104
|
-
last4 = last4 ?: "",
|
|
105
|
-
companyName = companyName ?: "",
|
|
106
|
-
companyAddress1 = companyAddress1 ?: "",
|
|
107
|
-
companyAddress2 = companyAddress2 ?: "",
|
|
108
|
-
companyCity = companyCity ?: "",
|
|
109
|
-
companyCountry = companyCountry.toCountryAlpha2(),
|
|
110
|
-
terminalId = terminalId ?: "",
|
|
111
|
-
authorizationCode = authorizationCode ?: "",
|
|
112
|
-
brand = brand?.name ?: "",
|
|
113
|
-
reason = reason?.name ?: "",
|
|
114
|
-
receiptUrl = receiptUrl,
|
|
115
|
-
refundToken = refundToken,
|
|
116
|
-
type = "card"
|
|
117
|
-
)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
class SepaPaymentIntentReactNative : Record {
|
|
121
|
-
@Field
|
|
122
|
-
var paymentIntentId: String = ""
|
|
123
|
-
|
|
124
|
-
@Field
|
|
125
|
-
var amount: Long = 0L
|
|
126
|
-
|
|
127
|
-
@Field
|
|
128
|
-
var requestedCurrency: String = ""
|
|
129
|
-
|
|
130
|
-
@Field
|
|
131
|
-
var settlementCurrency: String = ""
|
|
132
|
-
|
|
133
|
-
fun toSepaInstantPaymentIntent(): SepaInstantPaymentIntent {
|
|
134
|
-
val requested = Currency.valueOf(requestedCurrency)
|
|
135
|
-
return SepaInstantPaymentIntent(
|
|
136
|
-
paymentIntentId = paymentIntentId,
|
|
137
|
-
amount = amount,
|
|
138
|
-
requestedCurrency = requested,
|
|
139
|
-
settlementCurrency = if (settlementCurrency.isEmpty()) requested else Currency.valueOf(settlementCurrency)
|
|
140
|
-
)
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
class SepaPaymentInstantResultReactNative(
|
|
145
|
-
@Field
|
|
146
|
-
var status: String = "",
|
|
147
|
-
@Field
|
|
148
|
-
var postponed: Boolean = false,
|
|
149
|
-
@Field
|
|
150
|
-
var receivedAt: String = "",
|
|
151
|
-
@Field
|
|
152
|
-
var receivedFromIban: String = "",
|
|
153
|
-
@Field
|
|
154
|
-
var receiptUrl: String? = null,
|
|
155
|
-
@Field
|
|
156
|
-
var type: String = "",
|
|
157
|
-
) : Record
|
|
158
|
-
|
|
159
|
-
fun SepaInstantPaymentResult.toReactNative(): SepaPaymentInstantResultReactNative {
|
|
160
|
-
return SepaPaymentInstantResultReactNative(
|
|
161
|
-
status = status.name.lowercase(),
|
|
162
|
-
postponed = postponed,
|
|
163
|
-
receivedAt = receivedAt.toString(),
|
|
164
|
-
receivedFromIban = receivedFromIban ?: "",
|
|
165
|
-
receiptUrl = receiptUrl,
|
|
166
|
-
type = "sepa"
|
|
167
|
-
)
|
|
168
|
-
}
|
|
12
|
+
import java.util.concurrent.ConcurrentHashMap
|
|
13
|
+
import kotlin.time.Duration.Companion.milliseconds
|
|
14
|
+
import kotlinx.coroutines.CoroutineScope
|
|
15
|
+
import kotlinx.coroutines.Dispatchers
|
|
16
|
+
import kotlinx.coroutines.Job
|
|
17
|
+
import kotlinx.coroutines.SupervisorJob
|
|
18
|
+
import kotlinx.coroutines.cancel
|
|
19
|
+
import kotlinx.coroutines.launch
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Expo module wrapping the Accept SDK for Android.
|
|
23
|
+
*
|
|
24
|
+
* The bridge is deliberately flat: every SDK surface is exposed as a `<surface><Method>` function
|
|
25
|
+
* and the namespaced `Accept.auth.*` / `Accept.payments.*` shape is reassembled in TypeScript. The
|
|
26
|
+
* Kotlin SDK's two non-request/response surfaces — `Accept.state` and the `Flow<PaymentEvent>`
|
|
27
|
+
* returned by `pay()` — become the `onStateChange` and `onPaymentEvent` events.
|
|
28
|
+
*/
|
|
29
|
+
class AcceptSdkReactNativeModule : Module() {
|
|
169
30
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Scope for the SDK's flow collection. Main-immediate because the payment flow it collects
|
|
33
|
+
* launches activities, and cancelled wholesale when the module goes away so an in-flight
|
|
34
|
+
* payment's collector can't outlive the JavaScript context it reports to.
|
|
35
|
+
*/
|
|
36
|
+
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
|
|
173
37
|
|
|
174
|
-
|
|
175
|
-
|
|
38
|
+
/** Live `pay()` collectors, keyed by the request id the caller correlates its events with. */
|
|
39
|
+
private val paymentJobs = ConcurrentHashMap<String, Job>()
|
|
176
40
|
|
|
177
|
-
|
|
178
|
-
var requestedCurrency: String = ""
|
|
41
|
+
private var stateJob: Job? = null
|
|
179
42
|
|
|
180
|
-
|
|
181
|
-
|
|
43
|
+
override fun definition() = ModuleDefinition {
|
|
44
|
+
Name("AcceptSdkReactNative")
|
|
182
45
|
|
|
183
|
-
|
|
184
|
-
val requested = Currency.valueOf(requestedCurrency)
|
|
185
|
-
return CertisInstantPaymentIntent(
|
|
186
|
-
paymentIntentId = paymentIntentId,
|
|
187
|
-
amount = amount,
|
|
188
|
-
requestedCurrency = requested,
|
|
189
|
-
settlementCurrency = if (settlementCurrency.isEmpty()) requested else Currency.valueOf(settlementCurrency)
|
|
190
|
-
)
|
|
191
|
-
}
|
|
192
|
-
}
|
|
46
|
+
Events(EVENT_STATE_CHANGE, EVENT_PAYMENT, EVENT_LOG)
|
|
193
47
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
@Field
|
|
200
|
-
var receivedAt: String = "",
|
|
201
|
-
@Field
|
|
202
|
-
var receivedFromIban: String = "",
|
|
203
|
-
@Field
|
|
204
|
-
var receiptUrl: String? = null,
|
|
205
|
-
@Field
|
|
206
|
-
var type: String = "",
|
|
207
|
-
) : Record
|
|
208
|
-
|
|
209
|
-
fun CertisInstantPaymentResult.toReactNative(): CertisPaymentInstantResultReactNative {
|
|
210
|
-
return CertisPaymentInstantResultReactNative(
|
|
211
|
-
status = status.name.lowercase(),
|
|
212
|
-
postponed = postponed,
|
|
213
|
-
receivedAt = receivedAt.toString(),
|
|
214
|
-
receivedFromIban = receivedFromIban ?: "",
|
|
215
|
-
receiptUrl = receiptUrl,
|
|
216
|
-
type = "certis"
|
|
217
|
-
)
|
|
218
|
-
}
|
|
48
|
+
OnCreate {
|
|
49
|
+
stateJob = scope.launch {
|
|
50
|
+
Accept.state.collect { sendEvent(EVENT_STATE_CHANGE, mapOf("state" to it.serialize())) }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
219
53
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
54
|
+
OnDestroy {
|
|
55
|
+
stateJob = null
|
|
56
|
+
paymentJobs.clear()
|
|
57
|
+
scope.cancel()
|
|
58
|
+
}
|
|
223
59
|
|
|
224
|
-
|
|
225
|
-
var refundToken: String? = null
|
|
60
|
+
// ── Lifecycle ───────────────────────────────────────────────────────────
|
|
226
61
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
62
|
+
Function("initialize") { isProduction: Boolean ->
|
|
63
|
+
Accept.initialize(appContext.reactContext ?: throw Exceptions.ReactContextLost(), isProduction)
|
|
64
|
+
}
|
|
230
65
|
|
|
231
|
-
|
|
232
|
-
@Field
|
|
233
|
-
var refundToken: String? = null,
|
|
234
|
-
@Field
|
|
235
|
-
var status: String = "",
|
|
236
|
-
@Field
|
|
237
|
-
var refundReason: String = "",
|
|
238
|
-
) : Record
|
|
66
|
+
Function("getState") { Accept.state.value.serialize() }
|
|
239
67
|
|
|
68
|
+
AsyncFunction("clear") Coroutine { -> sdkCall { Accept.clear() } }
|
|
240
69
|
|
|
241
|
-
|
|
242
|
-
@Field var start: String = ""
|
|
243
|
-
@Field var end: String = ""
|
|
244
|
-
}
|
|
70
|
+
// ── Diagnostics ─────────────────────────────────────────────────────────
|
|
245
71
|
|
|
246
|
-
|
|
247
|
-
@Field var accent: String = ""
|
|
248
|
-
@Field var error: String = ""
|
|
249
|
-
@Field var warning: String = ""
|
|
250
|
-
@Field var success: String = ""
|
|
251
|
-
@Field var brandGradientColors: GradientColorsRecord? = null
|
|
252
|
-
@Field var brandSubtleGradientColors: GradientColorsRecord? = null
|
|
253
|
-
|
|
254
|
-
fun toAcceptColorTheme(): AcceptColorTheme = AcceptColorTheme(
|
|
255
|
-
accent = accent.toComposeColor(),
|
|
256
|
-
error = error.toComposeColor(),
|
|
257
|
-
warning = warning.toComposeColor(),
|
|
258
|
-
success = success.toComposeColor(),
|
|
259
|
-
brandGradientColors = brandGradientColors?.let { GradientColors(it.start.toComposeColor(), it.end.toComposeColor()) },
|
|
260
|
-
brandSubtleGradientColors = brandSubtleGradientColors?.let { GradientColors(it.start.toComposeColor(), it.end.toComposeColor()) },
|
|
261
|
-
)
|
|
262
|
-
}
|
|
72
|
+
Function("setDebugLoggingEnabled") { enabled: Boolean -> Accept.setDebugLoggingEnabled(enabled) }
|
|
263
73
|
|
|
264
|
-
|
|
265
|
-
@Field var brandLogo: String = ""
|
|
266
|
-
@Field var toolbarLogo: String = ""
|
|
267
|
-
@Field var solidLogo: String = ""
|
|
268
|
-
|
|
269
|
-
fun toAcceptImageTheme(): AcceptImageTheme = try {
|
|
270
|
-
AcceptImageTheme(
|
|
271
|
-
brandLogo = AcceptImageSource.Data(Base64.decode(brandLogo, Base64.DEFAULT)),
|
|
272
|
-
toolbarLogo = AcceptImageSource.Data(Base64.decode(toolbarLogo, Base64.DEFAULT)),
|
|
273
|
-
solidLogo = AcceptImageSource.Data(Base64.decode(solidLogo, Base64.DEFAULT)),
|
|
274
|
-
)
|
|
275
|
-
} catch (e: IllegalArgumentException) {
|
|
276
|
-
throw Exception("ThemeError: invalid base64 image data — ${e.message}")
|
|
277
|
-
}
|
|
278
|
-
}
|
|
74
|
+
Function("setLogLevel") { level: String -> Accept.setLogLevel(parseLogLevel(level)) }
|
|
279
75
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
fun toAcceptThemeConfiguration(): AcceptThemeConfiguration {
|
|
287
|
-
themeId?.let { id ->
|
|
288
|
-
return AcceptThemeConfiguration(
|
|
289
|
-
colors = AcceptThemeSource.ById(id),
|
|
290
|
-
images = AcceptThemeSource.ById(id),
|
|
291
|
-
)
|
|
76
|
+
/**
|
|
77
|
+
* Mirrors the SDK's logs onto [EVENT_LOG] so JavaScript can route them into its own
|
|
78
|
+
* logging stack. Disabling restores the SDK's default Logcat sink.
|
|
79
|
+
*/
|
|
80
|
+
Function("setLogForwardingEnabled") { enabled: Boolean ->
|
|
81
|
+
Accept.setLogger(if (enabled) EventLogger() else null)
|
|
292
82
|
}
|
|
293
|
-
return AcceptThemeConfiguration(
|
|
294
|
-
colors = colors?.let { AcceptThemeSource.Local(it.toAcceptColorTheme()) } ?: AcceptThemeSource.Default,
|
|
295
|
-
images = images?.let { AcceptThemeSource.Local(it.toAcceptImageTheme()) } ?: AcceptThemeSource.Default,
|
|
296
|
-
)
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
83
|
|
|
300
|
-
|
|
301
|
-
androidx.compose.ui.graphics.Color(android.graphics.Color.parseColor(this))
|
|
302
|
-
} catch (e: IllegalArgumentException) {
|
|
303
|
-
throw Exception("ThemeError: invalid color '$this'. Expected #RRGGBB or #AARRGGBB — ${e.message}")
|
|
304
|
-
}
|
|
84
|
+
// ── Theming ─────────────────────────────────────────────────────────────
|
|
305
85
|
|
|
306
|
-
|
|
307
|
-
8 -> "AL"
|
|
308
|
-
20 -> "AD"
|
|
309
|
-
40 -> "AT"
|
|
310
|
-
51 -> "AM"
|
|
311
|
-
56 -> "BE"
|
|
312
|
-
70 -> "BA"
|
|
313
|
-
100 -> "BG"
|
|
314
|
-
112 -> "BY"
|
|
315
|
-
191 -> "HR"
|
|
316
|
-
196 -> "CY"
|
|
317
|
-
203 -> "CZ"
|
|
318
|
-
208 -> "DK"
|
|
319
|
-
233 -> "EE"
|
|
320
|
-
234 -> "FO"
|
|
321
|
-
246 -> "FI"
|
|
322
|
-
250 -> "FR"
|
|
323
|
-
268 -> "GE"
|
|
324
|
-
276 -> "DE"
|
|
325
|
-
292 -> "GI"
|
|
326
|
-
300 -> "GR"
|
|
327
|
-
304 -> "GL"
|
|
328
|
-
336 -> "VA"
|
|
329
|
-
348 -> "HU"
|
|
330
|
-
352 -> "IS"
|
|
331
|
-
372 -> "IE"
|
|
332
|
-
380 -> "IT"
|
|
333
|
-
398 -> "KZ"
|
|
334
|
-
417 -> "KG"
|
|
335
|
-
428 -> "LV"
|
|
336
|
-
438 -> "LI"
|
|
337
|
-
440 -> "LT"
|
|
338
|
-
442 -> "LU"
|
|
339
|
-
470 -> "MT"
|
|
340
|
-
492 -> "MC"
|
|
341
|
-
498 -> "MD"
|
|
342
|
-
499 -> "ME"
|
|
343
|
-
528 -> "NL"
|
|
344
|
-
578 -> "NO"
|
|
345
|
-
616 -> "PL"
|
|
346
|
-
620 -> "PT"
|
|
347
|
-
642 -> "RO"
|
|
348
|
-
643 -> "RU"
|
|
349
|
-
674 -> "SM"
|
|
350
|
-
688 -> "RS"
|
|
351
|
-
703 -> "SK"
|
|
352
|
-
705 -> "SI"
|
|
353
|
-
724 -> "ES"
|
|
354
|
-
752 -> "SE"
|
|
355
|
-
756 -> "CH"
|
|
356
|
-
792 -> "TR"
|
|
357
|
-
804 -> "UA"
|
|
358
|
-
807 -> "MK"
|
|
359
|
-
826 -> "GB"
|
|
360
|
-
else -> ""
|
|
361
|
-
}
|
|
86
|
+
Function("setTheme") { theme: Map<String, Any?>? -> Accept.setTheme(theme?.let(::parseTheme)) }
|
|
362
87
|
|
|
363
|
-
|
|
364
|
-
"CARD" -> PaymentMethod.CARD
|
|
365
|
-
"SEPA" -> PaymentMethod.SEPA_INST
|
|
366
|
-
"CERTIS" -> PaymentMethod.CERTIS
|
|
367
|
-
"CRYPTO" -> PaymentMethod.CRYPTO
|
|
368
|
-
else -> throw IllegalArgumentException("Unknown payment method: $this")
|
|
369
|
-
}
|
|
88
|
+
Function("getTheme") { Accept.getTheme()?.serialize() }
|
|
370
89
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
PaymentMethod.CERTIS -> "CERTIS"
|
|
375
|
-
PaymentMethod.CRYPTO -> "CRYPTO"
|
|
376
|
-
}
|
|
90
|
+
Function("createLocalThemeImageUri") { base64: String, fileExtension: String ->
|
|
91
|
+
Accept.createLocalThemeImageUri(Base64.decode(base64, Base64.DEFAULT), fileExtension)
|
|
92
|
+
}
|
|
377
93
|
|
|
378
|
-
|
|
379
|
-
"customerRequest" -> RefundReason.CUSTOMER_REQUEST
|
|
380
|
-
"duplicate" -> RefundReason.DUPLICATE
|
|
381
|
-
"fraudulent" -> RefundReason.FRAUDULENT
|
|
382
|
-
"expiredCharge" -> RefundReason.EXPIRED_CHARGE
|
|
383
|
-
else -> throw IllegalArgumentException("Unknown refund reason: $this")
|
|
384
|
-
}
|
|
94
|
+
// ── Auth ────────────────────────────────────────────────────────────────
|
|
385
95
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
class NfcZoneRecord : Record {
|
|
392
|
-
@Field var indicator: String = "DEFAULT"
|
|
393
|
-
@Field var position: NfcPositionRecord? = null
|
|
96
|
+
AsyncFunction("authenticate") Coroutine { merchantToken: String ->
|
|
97
|
+
sdkCall { Accept.auth.authenticate(merchantToken) }
|
|
98
|
+
}
|
|
394
99
|
|
|
395
|
-
|
|
396
|
-
val pos = position?.let { NfcPosition.Custom(it.xBias, it.yBias) } ?: NfcPosition.Default
|
|
397
|
-
return NfcZone(indicator = indicator.toNfcIndicator(), position = pos)
|
|
398
|
-
}
|
|
399
|
-
}
|
|
100
|
+
// ── SDK ─────────────────────────────────────────────────────────────────
|
|
400
101
|
|
|
401
|
-
|
|
402
|
-
@Field var resultScreenDurationSeconds: Int = 10
|
|
403
|
-
@Field var showResultScreen: Boolean = true
|
|
404
|
-
@Field var nfcZone: NfcZoneRecord? = null
|
|
102
|
+
Function("getSdkVersion") { Accept.sdk.version }
|
|
405
103
|
|
|
406
|
-
|
|
407
|
-
resultScreenDurationSeconds = resultScreenDurationSeconds,
|
|
408
|
-
showResultScreen = showResultScreen,
|
|
409
|
-
nfcZone = nfcZone?.toNfcZone(),
|
|
410
|
-
)
|
|
411
|
-
}
|
|
104
|
+
Function("isProduction") { sdkCallSync { Accept.sdk.isProduction } }
|
|
412
105
|
|
|
413
|
-
|
|
414
|
-
"DEFAULT" -> NfcIndicator.DEFAULT
|
|
415
|
-
"ABOVE" -> NfcIndicator.ABOVE
|
|
416
|
-
"BELOW" -> NfcIndicator.BELOW
|
|
417
|
-
"FRONT" -> NfcIndicator.FRONT
|
|
418
|
-
"BEHIND" -> NfcIndicator.BEHIND
|
|
419
|
-
else -> throw IllegalArgumentException("Unknown NFC indicator: $this")
|
|
420
|
-
}
|
|
106
|
+
AsyncFunction("getDeviceId") Coroutine { -> sdkCall { Accept.sdk.deviceId() } }
|
|
421
107
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
@Field var registrationNumber: String? = null
|
|
426
|
-
@Field var vatNumber: String? = null
|
|
427
|
-
@Field var legalName: String? = null
|
|
428
|
-
@Field var addressLine1: String? = null
|
|
429
|
-
@Field var addressLine2: String? = null
|
|
430
|
-
@Field var city: String? = null
|
|
431
|
-
@Field var postalCode: String? = null
|
|
432
|
-
@Field var merchantCategoryCode: Int? = null
|
|
433
|
-
@Field var businessEmail: String? = null
|
|
434
|
-
@Field var bankAccountIban: String? = null
|
|
435
|
-
@Field var businessUrl: String? = null
|
|
436
|
-
@Field var productDescription: String? = null
|
|
437
|
-
@Field var supportPhone: String? = null
|
|
438
|
-
|
|
439
|
-
fun toIdentityData(): IdentityData = IdentityData(
|
|
440
|
-
businessType = businessType?.let { BusinessType.valueOf(it) },
|
|
441
|
-
countryCode = countryCode,
|
|
442
|
-
registrationNumber = registrationNumber,
|
|
443
|
-
vatNumber = vatNumber,
|
|
444
|
-
legalName = legalName,
|
|
445
|
-
addressLine1 = addressLine1,
|
|
446
|
-
addressLine2 = addressLine2,
|
|
447
|
-
city = city,
|
|
448
|
-
postalCode = postalCode,
|
|
449
|
-
merchantCategoryCode = merchantCategoryCode,
|
|
450
|
-
businessEmail = businessEmail,
|
|
451
|
-
bankAccountIban = bankAccountIban,
|
|
452
|
-
businessUrl = businessUrl,
|
|
453
|
-
productDescription = productDescription,
|
|
454
|
-
supportPhone = supportPhone,
|
|
455
|
-
)
|
|
456
|
-
}
|
|
108
|
+
AsyncFunction("getMinimumAmounts") Coroutine { ->
|
|
109
|
+
sdkCall { Accept.sdk.minimumAmounts().map { it.serialize() } }
|
|
110
|
+
}
|
|
457
111
|
|
|
458
|
-
//
|
|
112
|
+
// ── Merchant ────────────────────────────────────────────────────────────
|
|
459
113
|
|
|
460
|
-
|
|
461
|
-
override fun definition() = ModuleDefinition {
|
|
462
|
-
Name("AcceptSdkReactNative")
|
|
114
|
+
AsyncFunction("getMerchantInfo") Coroutine { -> sdkCall { Accept.merchant.info()?.serialize() } }
|
|
463
115
|
|
|
464
|
-
|
|
465
|
-
"onCardPaymentStatusChange",
|
|
466
|
-
"onSepaPaymentStatusChange",
|
|
467
|
-
"onCertisPaymentStatusChange",
|
|
468
|
-
"onCardPaymentException",
|
|
469
|
-
"onSepaPaymentException",
|
|
470
|
-
"onCertisPaymentException",
|
|
471
|
-
)
|
|
472
|
-
|
|
473
|
-
AsyncFunction("initialize") Coroutine { demo: Boolean, theme: AcceptThemeConfigurationRecord? ->
|
|
474
|
-
val context = appContext.reactContext ?: throw Exception("Context not available")
|
|
475
|
-
val environment = if (demo) AcceptEnvironment.SANDBOX else AcceptEnvironment.PRODUCTION
|
|
476
|
-
Accept.initialize(context, environment, theme?.toAcceptThemeConfiguration() ?: AcceptThemeConfiguration.Default)
|
|
477
|
-
}
|
|
116
|
+
AsyncFunction("getMerchantConfig") Coroutine { -> sdkCall { Accept.merchant.config().serialize() } }
|
|
478
117
|
|
|
479
|
-
AsyncFunction("
|
|
480
|
-
Accept.
|
|
481
|
-
return@Coroutine
|
|
118
|
+
AsyncFunction("getOnboardingStatus") Coroutine { ->
|
|
119
|
+
sdkCall { Accept.merchant.onboardingStatus().serialize() }
|
|
482
120
|
}
|
|
483
121
|
|
|
484
|
-
AsyncFunction("
|
|
485
|
-
|
|
122
|
+
AsyncFunction("getAvailableCurrencies") Coroutine { -> sdkCall { Accept.merchant.availableCurrencies() } }
|
|
123
|
+
|
|
124
|
+
// ── Payments ────────────────────────────────────────────────────────────
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Sets the session-wide payment defaults. `setOptions` keeps every option the caller
|
|
128
|
+
* doesn't name, so each key present in [options] is applied by its own call — that is what
|
|
129
|
+
* lets a caller set one default without disturbing the others, and it keeps an absent key
|
|
130
|
+
* distinct from an explicit `null` timeout.
|
|
131
|
+
*/
|
|
132
|
+
Function("setPaymentOptions") { options: Map<String, Any?> ->
|
|
133
|
+
options.receiptConfig()?.let { Accept.payments.setOptions(receiptConfig = it) }
|
|
134
|
+
options.nfcPosition()?.let { Accept.payments.setOptions(nfcPosition = it) }
|
|
135
|
+
options.display()?.let { Accept.payments.setOptions(display = it) }
|
|
136
|
+
if (options.containsKey("transactionTimeoutMs")) {
|
|
137
|
+
Accept.payments.setOptions(transactionTimeout = options.transactionTimeout())
|
|
138
|
+
}
|
|
486
139
|
}
|
|
487
140
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
141
|
+
/**
|
|
142
|
+
* Starts a payment and streams its `Flow<PaymentEvent>` onto [EVENT_PAYMENT], tagged with
|
|
143
|
+
* [requestId]. Returns as soon as the collector is running; the caller learns the outcome
|
|
144
|
+
* from the terminal `result` or `creationFailed` event.
|
|
145
|
+
*/
|
|
146
|
+
Function("startPayment") { requestId: String, request: Map<String, Any?> ->
|
|
147
|
+
val job = scope.launch {
|
|
148
|
+
Accept.payments.pay(
|
|
149
|
+
amount = request.amount(),
|
|
150
|
+
currency = request.currency(),
|
|
151
|
+
paymentToken = request["paymentToken"] as? String,
|
|
152
|
+
receiptConfig = request.receiptConfig(),
|
|
153
|
+
nfcPosition = request.nfcPosition(),
|
|
154
|
+
metadata = parseMetadata(request.metadata()),
|
|
155
|
+
tip = request.tip(),
|
|
156
|
+
transactionTimeout = request.transactionTimeout(),
|
|
157
|
+
display = request.display(),
|
|
158
|
+
).collect { event ->
|
|
159
|
+
sendEvent(EVENT_PAYMENT, mapOf("requestId" to requestId, "event" to event.serialize()))
|
|
498
160
|
}
|
|
161
|
+
paymentJobs.remove(requestId)
|
|
499
162
|
}
|
|
163
|
+
paymentJobs[requestId] = job
|
|
500
164
|
}
|
|
501
165
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
166
|
+
/**
|
|
167
|
+
* Stops reporting events for [requestId]. This detaches the listener only — the plugin's
|
|
168
|
+
* payment UI is already on screen by then, and is dismissed by the user or by
|
|
169
|
+
* `cancelPayment(paymentToken)` against the backend.
|
|
170
|
+
*/
|
|
171
|
+
Function("stopPaymentEvents") { requestId: String ->
|
|
172
|
+
paymentJobs.remove(requestId)?.cancel()
|
|
508
173
|
}
|
|
509
174
|
|
|
510
|
-
AsyncFunction("getPaymentStatus") Coroutine {
|
|
511
|
-
|
|
512
|
-
try {
|
|
513
|
-
Accept.getPaymentStatus(
|
|
514
|
-
paymentIntentId,
|
|
515
|
-
{ result: AbstractPaymentResult ->
|
|
516
|
-
val mapped = when (result) {
|
|
517
|
-
is CardPaymentResult -> result.toReactNative()
|
|
518
|
-
is CertisInstantPaymentResult -> result.toReactNative()
|
|
519
|
-
is SepaInstantPaymentResult -> result.toReactNative()
|
|
520
|
-
else -> throw Exception("Unsupported payment result type")
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
continuation.resume(Gson().toJson(mapped))
|
|
524
|
-
},
|
|
525
|
-
{ error: AcceptException ->
|
|
526
|
-
(error as? Throwable)?.let { continuation.resumeWithException(it) }
|
|
527
|
-
}
|
|
528
|
-
)
|
|
529
|
-
} catch (e: Exception) {
|
|
530
|
-
continuation.resumeWithException(e)
|
|
531
|
-
}
|
|
532
|
-
}
|
|
175
|
+
AsyncFunction("getPaymentStatus") Coroutine { paymentToken: String ->
|
|
176
|
+
sdkCall { Accept.payments.status(paymentToken).serialize() }
|
|
533
177
|
}
|
|
534
178
|
|
|
535
|
-
AsyncFunction("
|
|
536
|
-
|
|
537
|
-
appContext.permissions?.askForPermissions(
|
|
538
|
-
{
|
|
539
|
-
val result = it.map { (_, value) ->
|
|
540
|
-
value.status == PermissionsStatus.GRANTED
|
|
541
|
-
}.reduce { acc, i -> acc.and(i) }
|
|
542
|
-
continuation.resume(result)
|
|
543
|
-
},
|
|
544
|
-
"android.permission.ACCESS_FINE_LOCATION",
|
|
545
|
-
"android.permission.ACCESS_COARSE_LOCATION"
|
|
546
|
-
)
|
|
547
|
-
}
|
|
179
|
+
AsyncFunction("cancelPayment") Coroutine { paymentToken: String ->
|
|
180
|
+
sdkCall { Accept.payments.cancel(paymentToken).name }
|
|
548
181
|
}
|
|
549
182
|
|
|
550
|
-
AsyncFunction("
|
|
551
|
-
|
|
552
|
-
try {
|
|
553
|
-
Accept.logOut {
|
|
554
|
-
continuation.resume(true)
|
|
555
|
-
}
|
|
556
|
-
} catch (e: Exception) {
|
|
557
|
-
continuation.resumeWithException(e)
|
|
558
|
-
}
|
|
559
|
-
}
|
|
183
|
+
AsyncFunction("refundPayment") Coroutine { paymentToken: String, reason: String ->
|
|
184
|
+
sdkCall { Accept.payments.refund(paymentToken, parseRefundReason(reason)).name }
|
|
560
185
|
}
|
|
561
186
|
|
|
562
|
-
|
|
563
|
-
try {
|
|
564
|
-
return@AsyncFunction Accept.isPaymentPending()
|
|
565
|
-
} catch (e: Exception) {
|
|
566
|
-
throw e
|
|
567
|
-
}
|
|
568
|
-
}
|
|
187
|
+
// ── Plugin ──────────────────────────────────────────────────────────────
|
|
569
188
|
|
|
570
|
-
|
|
571
|
-
Accept.identity.presentKyb(prefilling?.toIdentityData())
|
|
572
|
-
}
|
|
189
|
+
Function("isPluginInstalled") { sdkCallSync { Accept.plugin.isInstalled() } }
|
|
573
190
|
|
|
574
|
-
AsyncFunction("
|
|
575
|
-
val status = Accept.identity.getOnboardingStatus()
|
|
576
|
-
return@Coroutine mapOf(
|
|
577
|
-
"isOnboarded" to status.isOnboarded,
|
|
578
|
-
"paymentMethods" to status.paymentMethods.map { pm ->
|
|
579
|
-
mapOf(
|
|
580
|
-
"method" to pm.method.toJsString(),
|
|
581
|
-
"status" to pm.status.name,
|
|
582
|
-
)
|
|
583
|
-
},
|
|
584
|
-
)
|
|
585
|
-
}
|
|
191
|
+
AsyncFunction("installPlugin") Coroutine { -> sdkCall { Accept.plugin.install() } }
|
|
586
192
|
|
|
587
|
-
AsyncFunction("
|
|
588
|
-
val status = Accept.identity.getPaymentMethodStatus(method.toPaymentMethod())
|
|
589
|
-
return@Coroutine status.name
|
|
590
|
-
}
|
|
193
|
+
AsyncFunction("activateTerminal") Coroutine { -> sdkCall { Accept.plugin.activateTerminal().serialize() } }
|
|
591
194
|
|
|
592
|
-
AsyncFunction("
|
|
593
|
-
return@Coroutine suspendCancellableCoroutine<CardPaymentResultReactNative> { continuation ->
|
|
594
|
-
try {
|
|
595
|
-
Accept.cardTerminal.process(
|
|
596
|
-
paymentIntent.toCardInstantPaymentIntent(),
|
|
597
|
-
onResult = {
|
|
598
|
-
continuation.resume(it.toReactNative())
|
|
599
|
-
},
|
|
600
|
-
onStatus = {
|
|
601
|
-
sendEvent("onCardPaymentStatusChange", mapOf("data" to it.name.lowercase()))
|
|
602
|
-
},
|
|
603
|
-
onError = {
|
|
604
|
-
sendEvent("onCardPaymentException", it.toReactNative())
|
|
605
|
-
}
|
|
606
|
-
)
|
|
607
|
-
} catch (e: Exception) {
|
|
608
|
-
continuation.resumeWithException(e)
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
}
|
|
195
|
+
AsyncFunction("getPluginStatus") Coroutine { -> sdkCall { Accept.plugin.status().serialize() } }
|
|
612
196
|
|
|
613
|
-
AsyncFunction("
|
|
614
|
-
return@Coroutine suspendCancellableCoroutine<SepaPaymentInstantResultReactNative> { continuation ->
|
|
615
|
-
try {
|
|
616
|
-
Accept.sepaTerminal.process(
|
|
617
|
-
paymentIntent.toSepaInstantPaymentIntent(),
|
|
618
|
-
onResult = {
|
|
619
|
-
continuation.resume(it.toReactNative())
|
|
620
|
-
},
|
|
621
|
-
onStatus = {
|
|
622
|
-
sendEvent("onSepaPaymentStatusChange", mapOf("data" to it.name.lowercase()))
|
|
623
|
-
},
|
|
624
|
-
onError = {
|
|
625
|
-
sendEvent("onSepaPaymentException", it.toReactNative())
|
|
626
|
-
}
|
|
627
|
-
)
|
|
628
|
-
} catch (e: Exception) {
|
|
629
|
-
continuation.resumeWithException(e)
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
}
|
|
197
|
+
AsyncFunction("getPluginUpdateInfo") Coroutine { -> sdkCall { Accept.plugin.updateInfo().serialize() } }
|
|
633
198
|
|
|
634
|
-
AsyncFunction("
|
|
635
|
-
return@Coroutine suspendCancellableCoroutine<CertisPaymentInstantResultReactNative> { continuation ->
|
|
636
|
-
try {
|
|
637
|
-
Accept.certisTerminal.process(
|
|
638
|
-
paymentIntent.toCertisInstantPaymentIntent(),
|
|
639
|
-
onResult = {
|
|
640
|
-
continuation.resume(it.toReactNative())
|
|
641
|
-
},
|
|
642
|
-
onStatus = {
|
|
643
|
-
sendEvent("onCertisPaymentStatusChange", mapOf("data" to it.name.lowercase()))
|
|
644
|
-
},
|
|
645
|
-
onError = {
|
|
646
|
-
sendEvent("onCertisPaymentException", it.toReactNative())
|
|
647
|
-
}
|
|
648
|
-
)
|
|
649
|
-
} catch (e: Exception) {
|
|
650
|
-
continuation.resumeWithException(e)
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
}
|
|
199
|
+
AsyncFunction("logoutPlugin") Coroutine { -> sdkCall { Accept.plugin.logout() } }
|
|
654
200
|
|
|
655
|
-
|
|
656
|
-
return@Coroutine suspendCancellableCoroutine<RefundResultReactNative> { continuation ->
|
|
657
|
-
try {
|
|
658
|
-
val refundIntent = CardRefundIntent(
|
|
659
|
-
sourcePaymentToken = intent.paymentToken,
|
|
660
|
-
refundReason = intent.reason.toRefundReason(),
|
|
661
|
-
refundToken = intent.refundToken,
|
|
662
|
-
)
|
|
663
|
-
Accept.cardTerminal.process(
|
|
664
|
-
refundIntent,
|
|
665
|
-
onResult = { result ->
|
|
666
|
-
continuation.resume(
|
|
667
|
-
RefundResultReactNative(
|
|
668
|
-
refundToken = result.refundToken,
|
|
669
|
-
status = result.status.name.lowercase(),
|
|
670
|
-
refundReason = intent.reason,
|
|
671
|
-
)
|
|
672
|
-
)
|
|
673
|
-
},
|
|
674
|
-
onStatus = { /* refund flow emits no UI status */ },
|
|
675
|
-
onError = { error ->
|
|
676
|
-
continuation.resumeWithException(error as Throwable)
|
|
677
|
-
}
|
|
678
|
-
)
|
|
679
|
-
} catch (e: Exception) {
|
|
680
|
-
continuation.resumeWithException(e)
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
}
|
|
201
|
+
// ── Displays ────────────────────────────────────────────────────────────
|
|
684
202
|
|
|
685
|
-
|
|
686
|
-
if (!Accept.isPaymentPending()) {
|
|
687
|
-
throw Exception("NoActivePayment")
|
|
688
|
-
}
|
|
689
|
-
Accept.cardTerminal.reset()
|
|
690
|
-
Accept.sepaTerminal.reset()
|
|
691
|
-
Accept.certisTerminal.reset()
|
|
692
|
-
}
|
|
203
|
+
Function("getDisplays") { Accept.displays.all().map { it.serialize() } }
|
|
693
204
|
|
|
694
|
-
|
|
695
|
-
Accept.enableDemoMode()
|
|
696
|
-
}
|
|
205
|
+
Function("getCustomerFacingDisplay") { Accept.displays.customerFacing()?.serialize() }
|
|
697
206
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
}
|
|
207
|
+
Function("isDualScreen") { Accept.displays.isDualScreen }
|
|
208
|
+
}
|
|
701
209
|
|
|
702
|
-
|
|
703
|
-
|
|
210
|
+
/** Forwards the SDK's consumer-facing logs to JavaScript as [EVENT_LOG]. */
|
|
211
|
+
private inner class EventLogger : AcceptLogger {
|
|
212
|
+
override fun log(level: AcceptLogLevel, tag: String, message: String, throwable: Throwable?) {
|
|
213
|
+
sendEvent(
|
|
214
|
+
EVENT_LOG,
|
|
215
|
+
mapOf(
|
|
216
|
+
"level" to level.name,
|
|
217
|
+
"tag" to tag,
|
|
218
|
+
"message" to message,
|
|
219
|
+
"throwable" to throwable?.message,
|
|
220
|
+
),
|
|
221
|
+
)
|
|
704
222
|
}
|
|
223
|
+
}
|
|
705
224
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
225
|
+
private companion object {
|
|
226
|
+
const val EVENT_STATE_CHANGE = "onStateChange"
|
|
227
|
+
const val EVENT_PAYMENT = "onPaymentEvent"
|
|
228
|
+
const val EVENT_LOG = "onLog"
|
|
229
|
+
}
|
|
230
|
+
}
|
|
709
231
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
onError = { error -> continuation.resumeWithException(error as Throwable) }
|
|
716
|
-
)
|
|
717
|
-
} catch (e: Exception) {
|
|
718
|
-
continuation.resumeWithException(e)
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
}
|
|
232
|
+
// ── Request map accessors ───────────────────────────────────────────────────────
|
|
233
|
+
//
|
|
234
|
+
// `pay()` and `setOptions()` read the same option names out of two different maps (a per-payment
|
|
235
|
+
// request and the session defaults), so the accessors are shared. Each returns null when the key is
|
|
236
|
+
// absent, which is exactly what the SDK reads as "use the default".
|
|
722
237
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
try {
|
|
726
|
-
Accept.presentCompanionAppSheet(
|
|
727
|
-
onSuccess = { continuation.resume(Unit) },
|
|
728
|
-
onError = { error -> continuation.resumeWithException(error as Throwable) }
|
|
729
|
-
)
|
|
730
|
-
} catch (e: Exception) {
|
|
731
|
-
continuation.resumeWithException(e)
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
}
|
|
238
|
+
private fun Map<String, Any?>.amount(): Long = (this["amount"] as? Number)?.toLong()
|
|
239
|
+
?: throw CodedException("ERR_INVALID_ARGUMENT", "amount is required", null)
|
|
735
240
|
|
|
736
|
-
|
|
241
|
+
private fun Map<String, Any?>.currency(): String = this["currency"] as? String
|
|
242
|
+
?: throw CodedException("ERR_INVALID_ARGUMENT", "currency is required", null)
|
|
737
243
|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
244
|
+
private fun Map<String, Any?>.tip(): Long? = (this["tip"] as? Number)?.toLong()
|
|
245
|
+
|
|
246
|
+
private fun Map<String, Any?>.transactionTimeout() =
|
|
247
|
+
(this["transactionTimeoutMs"] as? Number)?.toLong()?.milliseconds
|
|
248
|
+
|
|
249
|
+
@Suppress("UNCHECKED_CAST")
|
|
250
|
+
private fun Map<String, Any?>.receiptConfig() =
|
|
251
|
+
(this["receiptConfig"] as? Map<String, Any?>)?.let(::parseReceiptConfig)
|
|
252
|
+
|
|
253
|
+
@Suppress("UNCHECKED_CAST")
|
|
254
|
+
private fun Map<String, Any?>.nfcPosition() =
|
|
255
|
+
(this["nfcPosition"] as? Map<String, Any?>)?.let(::parseNfcPosition)
|
|
256
|
+
|
|
257
|
+
@Suppress("UNCHECKED_CAST")
|
|
258
|
+
private fun Map<String, Any?>.display() =
|
|
259
|
+
(this["display"] as? Map<String, Any?>)?.let(::parsePaymentDisplay)
|
|
260
|
+
|
|
261
|
+
@Suppress("UNCHECKED_CAST")
|
|
262
|
+
private fun Map<String, Any?>.metadata() = this["metadata"] as? Map<String, Any?>
|