@tapayadot/accept-react-native 1.3.2 → 1.14.1

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
@@ -1,749 +0,0 @@
1
- import AcceptSDK
2
- @_spi(AcceptDemo) import AcceptSDK
3
- import CoreLocation
4
- import ExpoModulesCore
5
- import UIKit
6
-
7
- // MARK: - Records
8
-
9
- final class CardPaymentIntentRecord: Record {
10
- @Field var paymentIntentId: String = ""
11
- @Field var amount: Int = 0
12
- @Field var requestedCurrency: String = ""
13
- @Field var settlementCurrency: String? = nil
14
- }
15
-
16
- final class SepaPaymentIntentRecord: Record {
17
- @Field var paymentIntentId: String = ""
18
- @Field var amount: Int = 0
19
- @Field var requestedCurrency: String = ""
20
- @Field var settlementCurrency: String? = nil
21
- }
22
-
23
- final class CertisPaymentIntentRecord: Record {
24
- @Field var paymentIntentId: String = ""
25
- @Field var amount: Int = 0
26
- @Field var requestedCurrency: String = ""
27
- @Field var settlementCurrency: String? = nil
28
- }
29
-
30
- final class CardPaymentResultRecord: Record {
31
- @Field var status: String = ""
32
- @Field var last4: String? = nil
33
- @Field var companyName: String = ""
34
- @Field var companyAddress1: String = ""
35
- @Field var companyAddress2: String? = nil
36
- @Field var companyCity: String = ""
37
- @Field var companyCountry: String = ""
38
- @Field var terminalId: String = ""
39
- @Field var authorizationCode: String? = nil
40
- @Field var brand: String? = nil
41
- @Field var reason: String? = nil
42
- @Field var receiptUrl: String? = nil
43
- @Field var refundToken: String? = nil
44
- }
45
-
46
- final class RefundIntentRecord: Record {
47
- @Field var paymentToken: String = ""
48
- @Field var refundToken: String? = nil
49
- @Field var reason: String = "customerRequest"
50
- }
51
-
52
- final class RefundResultRecord: Record {
53
- @Field var refundToken: String? = nil
54
- @Field var status: String = ""
55
- @Field var paymentMethod: String? = nil
56
- @Field var requestedAmount: Int? = nil
57
- @Field var requestedCurrency: String? = nil
58
- @Field var settlementAmount: Int? = nil
59
- @Field var settlementCurrency: String? = nil
60
- @Field var fxRate: Double? = nil
61
- @Field var refundReason: String = ""
62
- @Field var createdAt: String? = nil
63
- @Field var processedAt: String? = nil
64
- }
65
-
66
- final class BankTransferPaymentResultRecord: Record {
67
- @Field var status: String = ""
68
- @Field var postponed: Bool = false
69
- @Field var receivedAt: String? = nil
70
- @Field var receivedFromIban: String? = nil
71
- @Field var receiptUrl: String? = nil
72
- }
73
-
74
- final class GradientColorsRecord: Record {
75
- @Field var start: String = ""
76
- @Field var end: String = ""
77
- }
78
-
79
- final class AcceptColorThemeRecord: Record {
80
- @Field var accent: String = ""
81
- @Field var error: String = ""
82
- @Field var warning: String = ""
83
- @Field var success: String = ""
84
- @Field var brandGradientColors: GradientColorsRecord? = nil
85
- @Field var brandSubtleGradientColors: GradientColorsRecord? = nil
86
- }
87
-
88
- final class AcceptImageThemeRecord: Record {
89
- @Field var brandLogo: String = ""
90
- @Field var toolbarLogo: String = ""
91
- @Field var solidLogo: String = ""
92
- }
93
-
94
- final class AcceptThemeConfigurationRecord: Record {
95
- @Field var themeId: String? = nil
96
- @Field var colors: AcceptColorThemeRecord? = nil
97
- @Field var images: AcceptImageThemeRecord? = nil
98
- }
99
-
100
- final class KybPrefillDataRecord: Record {
101
- @Field var businessType: String? = nil
102
- @Field var countryCode: String? = nil
103
- @Field var registrationNumber: String? = nil
104
- @Field var vatNumber: String? = nil
105
- @Field var legalName: String? = nil
106
- @Field var addressLine1: String? = nil
107
- @Field var addressLine2: String? = nil
108
- @Field var city: String? = nil
109
- @Field var postalCode: String? = nil
110
- @Field var merchantCategoryCode: Int? = nil
111
- @Field var businessEmail: String? = nil
112
- @Field var bankAccountIban: String? = nil
113
- @Field var businessUrl: String? = nil
114
- @Field var productDescription: String? = nil
115
- @Field var supportPhone: String? = nil
116
-
117
- func toKybPrefillData() -> KybPrefillData {
118
- var data = KybPrefillData()
119
- data.countryCode = countryCode
120
- data.registrationNumber = registrationNumber
121
- data.vatNumber = vatNumber
122
- data.legalName = legalName
123
- data.addressLine1 = addressLine1
124
- data.addressLine2 = addressLine2
125
- data.city = city
126
- data.postalCode = postalCode
127
- data.merchantCategoryCode = merchantCategoryCode
128
- data.businessEmail = businessEmail
129
- data.bankAccountIban = bankAccountIban
130
- data.businessUrl = businessUrl.flatMap { URL(string: $0) }
131
- data.productDescription = productDescription
132
- data.supportPhone = supportPhone
133
- data.businessType = businessType.flatMap { value -> AcceptBusinessType? in
134
- switch value {
135
- case "individual": .individual
136
- case "company": .company
137
- case "nonProfit": .nonProfit
138
- case "governmentEntity": .governmentEntity
139
- default: nil
140
- }
141
- }
142
- return data
143
- }
144
- }
145
-
146
- // MARK: - Module
147
-
148
- public class AcceptSdkReactNativeModule: Module {
149
- public func definition() -> ModuleDefinition {
150
- Name("AcceptSdkReactNative")
151
-
152
- AsyncFunction("initialize") { (demo: Bool, theme: AcceptThemeConfigurationRecord?) async throws in
153
- do {
154
- let themeConfig = try theme?.toAcceptThemeConfiguration() ?? .default
155
- try await Accept.initialize(environment: demo ? .sandbox : .production, theme: themeConfig)
156
- } catch let error as AcceptSDKError {
157
- throw error.toException()
158
- }
159
- }
160
-
161
- AsyncFunction("setTheme") { (theme: AcceptThemeConfigurationRecord) async throws in
162
- do {
163
- try await Accept.setTheme(try theme.toAcceptThemeConfiguration())
164
- } catch let error as AcceptSDKError {
165
- throw error.toException()
166
- }
167
- }
168
-
169
- AsyncFunction("setUiBehavior") { (_: [String: Any]) in
170
- // No-op on iOS — UI behavior configuration is Android-only.
171
- }
172
-
173
- AsyncFunction("authenticate") { (token: String) async throws in
174
- do {
175
- try await Accept.authenticate(merchantToken: token)
176
- } catch let error as AcceptSDKError {
177
- throw error.toException()
178
- }
179
- return true
180
- }
181
-
182
- AsyncFunction("getStatus") { () async -> String in
183
- let status = await Accept.status
184
- switch status {
185
- case .idle: return "IDLE"
186
- case .initialized: return "INITIALIZED"
187
- case .authenticated: return "LOGGED_IN"
188
- @unknown default: return "IDLE"
189
- }
190
- }
191
-
192
- AsyncFunction("isPaymentPending") { () async -> Bool in
193
- await Accept.isPaymentInProgress()
194
- }
195
-
196
- AsyncFunction("logOut") { () async throws in
197
- do {
198
- try await Accept.logout()
199
- } catch let error as AcceptSDKError {
200
- throw error.toException()
201
- }
202
- return true
203
- }
204
-
205
- AsyncFunction("presentKyb") { (prefilling: KybPrefillDataRecord?) async throws in
206
- let prefillData = prefilling?.toKybPrefillData()
207
- try await Accept.presentKyb(prefilling: prefillData)
208
- }
209
-
210
- AsyncFunction("getOnboardingStatus") { () async throws -> [String: Any] in
211
- do {
212
- let status = try await Accept.getOnboardingStatus()
213
- return [
214
- "isOnboarded": status.isOnboarded,
215
- "paymentMethods": status.paymentMethods.map { pm in
216
- [
217
- "method": pm.method.toJsString(),
218
- "status": pm.status.toJsString(),
219
- ]
220
- },
221
- ]
222
- } catch let error as AcceptSDKError {
223
- throw error.toException()
224
- }
225
- }
226
-
227
- AsyncFunction("getPaymentMethodStatus") { (method: String) async throws -> String in
228
- do {
229
- let status = try await Accept.getPaymentMethodStatus(for: try AcceptPaymentMethod.fromJsString(method))
230
- return status.toJsString()
231
- } catch let error as AcceptSDKError {
232
- throw error.toException()
233
- }
234
- }
235
-
236
- AsyncFunction("requestLocationPermission") { () async throws -> Bool in
237
- do {
238
- try await Accept.requestLocationPermission()
239
- } catch let error as AcceptSDKError {
240
- throw error.toException()
241
- }
242
- return true
243
- }
244
-
245
- AsyncFunction("pairWithCompanionApp") { () async throws in
246
- do {
247
- try await Accept.pairWithCompanionApp()
248
- } catch let error as AcceptSDKError {
249
- throw error.toException()
250
- }
251
- }
252
-
253
- AsyncFunction("isCompanionAppInstalled") { () async -> Bool in
254
- await MainActor.run { Accept.isCompanionAppInstalled() }
255
- }
256
-
257
- AsyncFunction("presentCompanionAppSheet") { () async throws in
258
- do {
259
- try await Accept.presentCompanionAppSheet()
260
- } catch let error as AcceptSDKError {
261
- throw error.toException()
262
- }
263
- }
264
-
265
- AsyncFunction("getPaymentStatus") { (paymentIntentId: String) async throws -> String in
266
- do {
267
- let status = try await Accept.getPaymentStatus(for: paymentIntentId)
268
- return try InfoResultSerializer.serialize(status)
269
- } catch let error as AcceptSDKError {
270
- throw error.toException()
271
- }
272
- }
273
-
274
- AsyncFunction("startCardPayment") { (intentRecord: CardPaymentIntentRecord) async throws -> CardPaymentResultRecord in
275
- do {
276
- let outcome = try await Accept.pay(intentRecord.toPaymentIntent())
277
- return outcome.toCardResult()
278
- } catch let error as AcceptSDKError {
279
- throw error.toException()
280
- }
281
- }
282
-
283
- AsyncFunction("startSepaPayment") { (intentRecord: SepaPaymentIntentRecord) async throws -> BankTransferPaymentResultRecord in
284
- do {
285
- let outcome = try await Accept.pay(intentRecord.toPaymentIntent())
286
- return outcome.toBankTransferResult()
287
- } catch let error as AcceptSDKError {
288
- throw error.toException()
289
- }
290
- }
291
-
292
- AsyncFunction("startCertisPayment") { (intentRecord: CertisPaymentIntentRecord) async throws -> BankTransferPaymentResultRecord in
293
- do {
294
- let outcome = try await Accept.pay(intentRecord.toPaymentIntent())
295
- return outcome.toBankTransferResult()
296
- } catch let error as AcceptSDKError {
297
- throw error.toException()
298
- }
299
- }
300
-
301
- AsyncFunction("refund") { (intent: RefundIntentRecord) async throws -> RefundResultRecord in
302
- do {
303
- let refund = try await Accept.refund(
304
- paymentToken: intent.paymentToken,
305
- refundToken: intent.refundToken,
306
- reason: try AcceptRefundReason.fromJsString(intent.reason)
307
- )
308
- return refund.toRecord()
309
- } catch let error as AcceptSDKError {
310
- throw error.toException()
311
- }
312
- }
313
-
314
- AsyncFunction("cancelActivePayment") { () async throws in
315
- do {
316
- try await MainActor.run { try Accept.cancelActivePayment() }
317
- } catch let error as AcceptSDKError {
318
- throw error.toException()
319
- }
320
- }
321
-
322
- AsyncFunction("enableDemoMode") { () async in
323
- await Accept.enableDemoMode()
324
- }
325
-
326
- AsyncFunction("disableDemoMode") { () async in
327
- await Accept.disableDemoMode()
328
- }
329
-
330
- AsyncFunction("isDemoMode") { () async -> Bool in
331
- return await MainActor.run { Accept.isDemoMode }
332
- }
333
- }
334
- }
335
-
336
- // MARK: - Theme conversion
337
-
338
- private extension AcceptThemeConfigurationRecord {
339
- func toAcceptThemeConfiguration() throws -> AcceptThemeConfiguration {
340
- if let id = themeId {
341
- return .byId(id)
342
- }
343
- var config = AcceptThemeConfiguration()
344
- if let colorsRecord = colors {
345
- config.colors = .local(try colorsRecord.toAcceptColorTheme())
346
- }
347
- if let imagesRecord = images {
348
- config.images = .local(try imagesRecord.toAcceptImageTheme())
349
- }
350
- return config
351
- }
352
- }
353
-
354
- private extension AcceptColorThemeRecord {
355
- func toAcceptColorTheme() throws -> AcceptColorTheme {
356
- AcceptColorTheme(
357
- accent: try color(fromHex: accent),
358
- error: try color(fromHex: error),
359
- warning: try color(fromHex: warning),
360
- success: try color(fromHex: success),
361
- brandGradientColors: try brandGradientColors.map { GradientColors(start: try color(fromHex: $0.start), end: try color(fromHex: $0.end)) },
362
- brandSubtleGradientColors: try brandSubtleGradientColors.map { GradientColors(start: try color(fromHex: $0.start), end: try color(fromHex: $0.end)) }
363
- )
364
- }
365
- }
366
-
367
- private extension AcceptImageThemeRecord {
368
- func toAcceptImageTheme() throws -> AcceptImageTheme {
369
- guard
370
- let brandLogoData = Data(base64Encoded: brandLogo),
371
- let brandLogoImage = UIImage(data: brandLogoData),
372
- let toolbarLogoData = Data(base64Encoded: toolbarLogo),
373
- let toolbarLogoImage = UIImage(data: toolbarLogoData),
374
- let solidLogoData = Data(base64Encoded: solidLogo),
375
- let solidLogoImage = UIImage(data: solidLogoData)
376
- else {
377
- throw Exception(name: "ThemeError", description: "Invalid base64 image data in AcceptImageTheme")
378
- }
379
- return AcceptImageTheme(brandLogo: brandLogoImage, toolbarLogo: toolbarLogoImage, solidLogo: solidLogoImage)
380
- }
381
- }
382
-
383
- private func color(fromHex input: String) throws -> AcceptColor {
384
- let hex = input.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
385
- guard hex.count == 3 || hex.count == 6 || hex.count == 8 else {
386
- throw Exception(name: "ThemeError", description: "Invalid hex color '\(input)'. Expected #RRGGBB or #AARRGGBB.")
387
- }
388
- var int: UInt64 = 0
389
- guard Scanner(string: hex).scanHexInt64(&int) else {
390
- throw Exception(name: "ThemeError", description: "Invalid hex color '\(input)'.")
391
- }
392
- let a, r, g, b: UInt64
393
- switch hex.count {
394
- case 3: (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
395
- case 6: (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
396
- case 8: (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
397
- default: fatalError()
398
- }
399
- return AcceptColor(red: Double(r) / 255, green: Double(g) / 255, blue: Double(b) / 255, alpha: Double(a) / 255)
400
- }
401
-
402
- // MARK: - Error mapping
403
-
404
- private extension AcceptSDKError {
405
- func toException() -> Exception {
406
- switch self {
407
- case .notInitialized:
408
- return Exception(name: "NotInitialized", description: errorDescription ?? "")
409
- case .merchantNotAuthenticated:
410
- return Exception(name: "MerchantNotAuthenticated", description: errorDescription ?? "")
411
- case .locationPermissionsNotGranted:
412
- return Exception(name: "LocationPermissionsNotGranted", description: errorDescription ?? "")
413
- case .companionAppNotInstalled:
414
- return Exception(name: "CompanionAppNotInstalled", description: errorDescription ?? "")
415
- case .failedToAuthenticateMerchant:
416
- return Exception(name: "FailedToAuthenticateMerchant", description: errorDescription ?? "")
417
- case .failedToPresent:
418
- return Exception(name: "FailedToPresent", description: errorDescription ?? "")
419
- case .kybAlreadyInProgress:
420
- return Exception(name: "KybAlreadyInProgress", description: errorDescription ?? "")
421
- case .kybAlreadySubmitted:
422
- return Exception(name: "KybAlreadySubmitted", description: errorDescription ?? "")
423
- case .paymentAlreadyInProgress:
424
- return Exception(name: "PaymentAlreadyInProgress", description: errorDescription ?? "")
425
- case .unexpectedPaymentResult:
426
- return Exception(name: "UnexpectedPaymentResult", description: errorDescription ?? "")
427
- case .paymentNotFound:
428
- return Exception(name: "PaymentNotFound", description: errorDescription ?? "")
429
- case .refundConflict:
430
- return Exception(name: "RefundConflict", description: errorDescription ?? "")
431
- case .merchantOperationFailed:
432
- return Exception(name: "MerchantOperationFailed", description: errorDescription ?? "")
433
- case let .amountBelowMinimum(currency, minimum):
434
- return Exception(name: "AmountBelowMinimum", description: "currency=\(currency) minimum=\(minimum)")
435
- case let .amountAboveMaximum(currency, maximum):
436
- return Exception(name: "AmountAboveMaximum", description: "currency=\(currency) maximum=\(maximum)")
437
- case let .unknownCurrency(code):
438
- return Exception(name: "UnknownCurrency", description: "code=\(code)")
439
- case .noActivePayment:
440
- return Exception(name: "NoActivePayment", description: errorDescription ?? "")
441
- @unknown default:
442
- return Exception(name: "AcceptSDKError", description: errorDescription ?? "")
443
- }
444
- }
445
- }
446
-
447
- // MARK: - Info result serialization
448
-
449
- private struct CardInfoResult: Encodable {
450
- let status: String
451
- let readerStatus: String
452
- let last4: String
453
- let companyName: String
454
- let companyAddress1: String
455
- let companyAddress2: String
456
- let companyCity: String
457
- let companyCountry: String
458
- let terminalId: String
459
- let authorizationCode: String
460
- let brand: String
461
- let reason: String
462
- let receiptUrl: String?
463
- let refundToken: String?
464
- let type: String
465
- }
466
-
467
- private struct BankTransferInfoResult: Encodable {
468
- let status: String
469
- let postponed: Bool
470
- let receivedAt: String
471
- let receivedFromIban: String
472
- let receiptUrl: String?
473
- let type: String
474
- }
475
-
476
- private enum InfoResultSerializer {
477
- static func serialize(_ status: any PaymentStatus) throws -> String {
478
- let encoder = JSONEncoder()
479
- let data: Data
480
-
481
- switch status.paymentMethod {
482
- case .card:
483
- guard let cardStatus = status as? CardPaymentStatus else {
484
- throw Exception(name: "SerializationError", description: "Unexpected card status type")
485
- }
486
- data = try encoder.encode(CardInfoResult(
487
- status: cardStatus.status.rnString,
488
- readerStatus: "",
489
- last4: cardStatus.details?.last4 ?? "",
490
- companyName: cardStatus.merchantDetails.legalName,
491
- companyAddress1: cardStatus.merchantDetails.addressLine1,
492
- companyAddress2: cardStatus.merchantDetails.addressLine2 ?? "",
493
- companyCity: cardStatus.merchantDetails.city,
494
- companyCountry: cardStatus.merchantDetails.country,
495
- terminalId: cardStatus.terminalId,
496
- authorizationCode: cardStatus.details?.authorizationCode ?? "",
497
- brand: cardStatus.details?.brand.rawValue.uppercased() ?? "",
498
- reason: cardStatus.details?.issuerDeclineReason?.rawValue ?? "",
499
- receiptUrl: cardStatus.receiptUrl?.absoluteString,
500
- refundToken: cardStatus.refund?.refundToken,
501
- type: "card"
502
- ))
503
-
504
- case .sepaIct:
505
- guard let bankStatus = status as? BankTransferPaymentStatus else {
506
- throw Exception(name: "SerializationError", description: "Unexpected bank transfer status type")
507
- }
508
- data = try encoder.encode(BankTransferInfoResult(
509
- status: bankStatus.status.rnString,
510
- postponed: bankStatus.details?.postponed ?? false,
511
- receivedAt: bankStatus.processedAt.map { ISO8601DateFormatter().string(from: $0) } ?? "",
512
- receivedFromIban: bankStatus.details?.bankAccountNumber ?? "",
513
- receiptUrl: bankStatus.receiptUrl?.absoluteString,
514
- type: "sepa"
515
- ))
516
-
517
- case .certisCz:
518
- guard let bankStatus = status as? BankTransferPaymentStatus else {
519
- throw Exception(name: "SerializationError", description: "Unexpected bank transfer status type")
520
- }
521
- data = try encoder.encode(BankTransferInfoResult(
522
- status: bankStatus.status.rnString,
523
- postponed: bankStatus.details?.postponed ?? false,
524
- receivedAt: bankStatus.processedAt.map { ISO8601DateFormatter().string(from: $0) } ?? "",
525
- receivedFromIban: bankStatus.details?.bankAccountNumber ?? "",
526
- receiptUrl: bankStatus.receiptUrl?.absoluteString,
527
- type: "certis"
528
- ))
529
-
530
- default:
531
- throw Exception(name: "UnsupportedPaymentMethod", description: "Payment method not supported by info()")
532
- }
533
-
534
- return String(data: data, encoding: .utf8) ?? "{}"
535
- }
536
- }
537
-
538
- private extension AcceptTransactionStatus {
539
- var rnString: String {
540
- switch self {
541
- case .pending: "PENDING"
542
- case .success: "APPROVED"
543
- case .cancelled: "ABORTED"
544
- case .refunded: "REFUNDED"
545
- case .failed: "DECLINED"
546
- case .actionNeeded: "PENDING"
547
- @unknown default: "PENDING"
548
- }
549
- }
550
-
551
- var cardPaymentStatus: String {
552
- switch self {
553
- case .pending: "pending"
554
- case .success: "approved"
555
- case .cancelled: "aborted"
556
- case .refunded: "refunded"
557
- case .failed: "declined"
558
- case .actionNeeded: "pending"
559
- @unknown default: "pending"
560
- }
561
- }
562
-
563
- var bankTransferStatus: String {
564
- switch self {
565
- case .pending: "pending"
566
- case .success: "verified"
567
- case .cancelled: "aborted"
568
- case .refunded: "refunded"
569
- case .failed: "declined"
570
- case .actionNeeded: "pending"
571
- @unknown default: "pending"
572
- }
573
- }
574
- }
575
-
576
- // MARK: - Payment intent conversion
577
-
578
- private extension CardPaymentIntentRecord {
579
- func toPaymentIntent() -> AcceptCardPaymentIntent {
580
- AcceptCardPaymentIntent(
581
- id: paymentIntentId,
582
- amount: amount,
583
- requestedCurrency: requestedCurrency,
584
- settlementCurrency: settlementCurrency ?? requestedCurrency
585
- )
586
- }
587
- }
588
-
589
- private extension SepaPaymentIntentRecord {
590
- func toPaymentIntent() -> AcceptSepaPaymentIntent {
591
- AcceptSepaPaymentIntent(
592
- id: paymentIntentId,
593
- amount: amount,
594
- requestedCurrency: requestedCurrency,
595
- settlementCurrency: settlementCurrency ?? requestedCurrency
596
- )
597
- }
598
- }
599
-
600
- private extension CertisPaymentIntentRecord {
601
- func toPaymentIntent() -> AcceptCertisPaymentIntent {
602
- AcceptCertisPaymentIntent(
603
- id: paymentIntentId,
604
- amount: amount,
605
- requestedCurrency: requestedCurrency,
606
- settlementCurrency: settlementCurrency ?? requestedCurrency
607
- )
608
- }
609
- }
610
-
611
- // MARK: - Payment outcome conversion
612
-
613
- private extension AcceptPaymentOutcome where Result == CardPaymentStatus {
614
- func toCardResult() -> CardPaymentResultRecord {
615
- let record = CardPaymentResultRecord()
616
- switch self {
617
- case .canceled:
618
- record.status = "aborted"
619
- case .completed(let status):
620
- record.status = status.status.cardPaymentStatus
621
- record.last4 = status.details?.last4
622
- record.companyName = status.merchantDetails.legalName
623
- record.companyAddress1 = status.merchantDetails.addressLine1
624
- record.companyAddress2 = status.merchantDetails.addressLine2
625
- record.companyCity = status.merchantDetails.city
626
- record.companyCountry = status.merchantDetails.country
627
- record.terminalId = status.terminalId
628
- record.authorizationCode = status.details?.authorizationCode
629
- record.brand = status.details?.brand.rawValue.uppercased()
630
- record.reason = status.details?.issuerDeclineReason?.rawValue
631
- record.receiptUrl = status.receiptUrl?.absoluteString
632
- record.refundToken = status.refund?.refundToken
633
- }
634
- return record
635
- }
636
- }
637
-
638
- private extension AcceptPaymentOutcome where Result == BankTransferPaymentStatus {
639
- func toBankTransferResult() -> BankTransferPaymentResultRecord {
640
- let record = BankTransferPaymentResultRecord()
641
- switch self {
642
- case .canceled:
643
- record.status = "aborted"
644
- case .completed(let status):
645
- record.status = status.status.bankTransferStatus
646
- record.postponed = status.details?.postponed ?? false
647
- record.receivedAt = status.processedAt.map { ISO8601DateFormatter().string(from: $0) }
648
- record.receivedFromIban = status.details?.bankAccountNumber
649
- record.receiptUrl = status.receiptUrl?.absoluteString
650
- }
651
- return record
652
- }
653
- }
654
-
655
- // MARK: - Onboarding status conversion
656
-
657
- private extension AcceptPaymentMethod {
658
- static func fromJsString(_ value: String) throws -> AcceptPaymentMethod {
659
- switch value {
660
- case "CARD": return .card
661
- case "SEPA": return .sepaIct
662
- case "CERTIS": return .certisCz
663
- case "CRYPTO": return .crypto
664
- default:
665
- throw Exception(name: "InvalidPaymentMethod", description: "Unknown payment method: \(value)")
666
- }
667
- }
668
-
669
- func toJsString() -> String {
670
- switch self {
671
- case .card: return "CARD"
672
- case .sepaIct: return "SEPA"
673
- case .certisCz: return "CERTIS"
674
- case .crypto: return "CRYPTO"
675
- @unknown default: return "UNKNOWN"
676
- }
677
- }
678
- }
679
-
680
- private extension AcceptPaymentMethodStatus.Status {
681
- func toJsString() -> String {
682
- switch self {
683
- case .notStarted: return "NOT_STARTED"
684
- case .pending: return "PENDING"
685
- case .active: return "ACTIVE"
686
- case .attentionNeeded: return "ATTENTION_NEEDED"
687
- case .actionRequired: return "ACTION_REQUIRED"
688
- case .blocked: return "BLOCKED"
689
- @unknown default: return "UNKNOWN"
690
- }
691
- }
692
- }
693
-
694
- // MARK: - Refund conversion
695
-
696
- private extension AcceptRefundReason {
697
- static func fromJsString(_ value: String) throws -> AcceptRefundReason {
698
- switch value {
699
- case "customerRequest": return .customerRequest
700
- case "duplicate": return .duplicate
701
- case "fraudulent": return .fraudulent
702
- case "expiredCharge": return .expiredCharge
703
- default:
704
- throw Exception(name: "InvalidRefundReason", description: "Unknown refund reason: \(value)")
705
- }
706
- }
707
-
708
- func toJsString() -> String {
709
- switch self {
710
- case .customerRequest: return "customerRequest"
711
- case .duplicate: return "duplicate"
712
- case .fraudulent: return "fraudulent"
713
- case .expiredCharge: return "expiredCharge"
714
- @unknown default: return "customerRequest"
715
- }
716
- }
717
- }
718
-
719
- private extension AcceptTransactionStatus {
720
- func toJsString() -> String {
721
- switch self {
722
- case .pending: return "pending"
723
- case .success: return "success"
724
- case .cancelled: return "cancelled"
725
- case .refunded: return "refunded"
726
- case .failed: return "failed"
727
- case .actionNeeded: return "actionNeeded"
728
- @unknown default: return "pending"
729
- }
730
- }
731
- }
732
-
733
- private extension AcceptRefund {
734
- func toRecord() -> RefundResultRecord {
735
- let record = RefundResultRecord()
736
- record.refundToken = refundToken
737
- record.status = status.toJsString()
738
- record.paymentMethod = paymentMethod.toJsString()
739
- record.requestedAmount = requestedAmount
740
- record.requestedCurrency = requestedCurrency
741
- record.settlementAmount = settlementAmount
742
- record.settlementCurrency = settlementCurrency
743
- record.fxRate = fxRate
744
- record.refundReason = refundReason.toJsString()
745
- record.createdAt = ISO8601DateFormatter().string(from: createdAt)
746
- record.processedAt = processedAt.map { ISO8601DateFormatter().string(from: $0) }
747
- return record
748
- }
749
- }