@tapayadot/accept-react-native 1.1.0 → 1.2.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.
@@ -1,7 +1,7 @@
1
1
  import AcceptSDK
2
+ @_spi(AcceptDemo) import AcceptSDK
2
3
  import CoreLocation
3
4
  import ExpoModulesCore
4
- import SwiftUI
5
5
  import UIKit
6
6
 
7
7
  // MARK: - Records
@@ -34,12 +34,33 @@ final class CardPaymentResultRecord: Record {
34
34
  @Field var companyAddress1: String = ""
35
35
  @Field var companyAddress2: String? = nil
36
36
  @Field var companyCity: String = ""
37
- @Field var companyCountry: Int = 0
37
+ @Field var companyCountry: String = ""
38
38
  @Field var terminalId: String = ""
39
39
  @Field var authorizationCode: String? = nil
40
40
  @Field var brand: String? = nil
41
41
  @Field var reason: String? = nil
42
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
43
64
  }
44
65
 
45
66
  final class BankTransferPaymentResultRecord: Record {
@@ -71,6 +92,7 @@ final class AcceptImageThemeRecord: Record {
71
92
  }
72
93
 
73
94
  final class AcceptThemeConfigurationRecord: Record {
95
+ @Field var themeId: String? = nil
74
96
  @Field var colors: AcceptColorThemeRecord? = nil
75
97
  @Field var images: AcceptImageThemeRecord? = nil
76
98
  }
@@ -132,7 +154,7 @@ public class AcceptSdkReactNativeModule: Module {
132
154
  let themeConfig = try theme?.toAcceptThemeConfiguration() ?? .default
133
155
  try await Accept.initialize(environment: demo ? .sandbox : .production, theme: themeConfig)
134
156
  } catch let error as AcceptSDKError {
135
- throw Exception(name: "AcceptSDKError", description: error.localizedDescription)
157
+ throw error.toException()
136
158
  }
137
159
  }
138
160
 
@@ -140,15 +162,19 @@ public class AcceptSdkReactNativeModule: Module {
140
162
  do {
141
163
  try await Accept.setTheme(try theme.toAcceptThemeConfiguration())
142
164
  } catch let error as AcceptSDKError {
143
- throw Exception(name: "AcceptSDKError", description: error.localizedDescription)
165
+ throw error.toException()
144
166
  }
145
167
  }
146
168
 
169
+ AsyncFunction("setUiBehavior") { (_: [String: Any]) in
170
+ // No-op on iOS — UI behavior configuration is Android-only.
171
+ }
172
+
147
173
  AsyncFunction("authenticate") { (token: String) async throws in
148
174
  do {
149
175
  try await Accept.authenticate(merchantToken: token)
150
176
  } catch let error as AcceptSDKError {
151
- throw Exception(name: "AcceptSDKError", description: error.localizedDescription)
177
+ throw error.toException()
152
178
  }
153
179
  return true
154
180
  }
@@ -171,7 +197,7 @@ public class AcceptSdkReactNativeModule: Module {
171
197
  do {
172
198
  try await Accept.logout()
173
199
  } catch let error as AcceptSDKError {
174
- throw Exception(name: "AcceptSDKError", description: error.localizedDescription)
200
+ throw error.toException()
175
201
  }
176
202
  return true
177
203
  }
@@ -181,11 +207,37 @@ public class AcceptSdkReactNativeModule: Module {
181
207
  try await Accept.presentKyb(prefilling: prefillData)
182
208
  }
183
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
+
184
236
  AsyncFunction("requestLocationPermission") { () async throws -> Bool in
185
237
  do {
186
238
  try await Accept.requestLocationPermission()
187
239
  } catch let error as AcceptSDKError {
188
- throw Exception(name: "AcceptSDKError", description: error.localizedDescription)
240
+ throw error.toException()
189
241
  }
190
242
  return true
191
243
  }
@@ -194,7 +246,7 @@ public class AcceptSdkReactNativeModule: Module {
194
246
  do {
195
247
  try await Accept.pairWithCompanionApp()
196
248
  } catch let error as AcceptSDKError {
197
- throw Exception(name: "AcceptSDKError", description: error.localizedDescription)
249
+ throw error.toException()
198
250
  }
199
251
  }
200
252
 
@@ -206,7 +258,7 @@ public class AcceptSdkReactNativeModule: Module {
206
258
  do {
207
259
  try await Accept.presentCompanionAppSheet()
208
260
  } catch let error as AcceptSDKError {
209
- throw Exception(name: "AcceptSDKError", description: error.localizedDescription)
261
+ throw error.toException()
210
262
  }
211
263
  }
212
264
 
@@ -215,7 +267,7 @@ public class AcceptSdkReactNativeModule: Module {
215
267
  let status = try await Accept.getPaymentStatus(for: paymentIntentId)
216
268
  return try InfoResultSerializer.serialize(status)
217
269
  } catch let error as AcceptSDKError {
218
- throw Exception(name: "AcceptSDKError", description: error.localizedDescription)
270
+ throw error.toException()
219
271
  }
220
272
  }
221
273
 
@@ -224,7 +276,7 @@ public class AcceptSdkReactNativeModule: Module {
224
276
  let outcome = try await Accept.pay(intentRecord.toPaymentIntent())
225
277
  return outcome.toCardResult()
226
278
  } catch let error as AcceptSDKError {
227
- throw Exception(name: "AcceptSDKError", description: error.localizedDescription)
279
+ throw error.toException()
228
280
  }
229
281
  }
230
282
 
@@ -233,7 +285,7 @@ public class AcceptSdkReactNativeModule: Module {
233
285
  let outcome = try await Accept.pay(intentRecord.toPaymentIntent())
234
286
  return outcome.toBankTransferResult()
235
287
  } catch let error as AcceptSDKError {
236
- throw Exception(name: "AcceptSDKError", description: error.localizedDescription)
288
+ throw error.toException()
237
289
  }
238
290
  }
239
291
 
@@ -242,9 +294,42 @@ public class AcceptSdkReactNativeModule: Module {
242
294
  let outcome = try await Accept.pay(intentRecord.toPaymentIntent())
243
295
  return outcome.toBankTransferResult()
244
296
  } catch let error as AcceptSDKError {
245
- throw Exception(name: "AcceptSDKError", description: error.localizedDescription)
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()
246
311
  }
247
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
+ }
248
333
  }
249
334
  }
250
335
 
@@ -252,6 +337,9 @@ public class AcceptSdkReactNativeModule: Module {
252
337
 
253
338
  private extension AcceptThemeConfigurationRecord {
254
339
  func toAcceptThemeConfiguration() throws -> AcceptThemeConfiguration {
340
+ if let id = themeId {
341
+ return .byId(id)
342
+ }
255
343
  var config = AcceptThemeConfiguration()
256
344
  if let colorsRecord = colors {
257
345
  config.colors = .local(try colorsRecord.toAcceptColorTheme())
@@ -292,7 +380,7 @@ private extension AcceptImageThemeRecord {
292
380
  }
293
381
  }
294
382
 
295
- private func color(fromHex input: String) throws -> Color {
383
+ private func color(fromHex input: String) throws -> AcceptColor {
296
384
  let hex = input.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
297
385
  guard hex.count == 3 || hex.count == 6 || hex.count == 8 else {
298
386
  throw Exception(name: "ThemeError", description: "Invalid hex color '\(input)'. Expected #RRGGBB or #AARRGGBB.")
@@ -308,7 +396,52 @@ private func color(fromHex input: String) throws -> Color {
308
396
  case 8: (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
309
397
  default: fatalError()
310
398
  }
311
- return Color(.sRGB, red: Double(r) / 255, green: Double(g) / 255, blue: Double(b) / 255, opacity: Double(a) / 255)
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
+ }
312
445
  }
313
446
 
314
447
  // MARK: - Info result serialization
@@ -321,12 +454,13 @@ private struct CardInfoResult: Encodable {
321
454
  let companyAddress1: String
322
455
  let companyAddress2: String
323
456
  let companyCity: String
324
- let companyCountry: Int
457
+ let companyCountry: String
325
458
  let terminalId: String
326
459
  let authorizationCode: String
327
460
  let brand: String
328
461
  let reason: String
329
462
  let receiptUrl: String?
463
+ let refundToken: String?
330
464
  let type: String
331
465
  }
332
466
 
@@ -357,12 +491,13 @@ private enum InfoResultSerializer {
357
491
  companyAddress1: cardStatus.merchantDetails.addressLine1,
358
492
  companyAddress2: cardStatus.merchantDetails.addressLine2 ?? "",
359
493
  companyCity: cardStatus.merchantDetails.city,
360
- companyCountry: cardStatus.merchantDetails.country.rawValue,
494
+ companyCountry: cardStatus.merchantDetails.country,
361
495
  terminalId: cardStatus.terminalId,
362
496
  authorizationCode: cardStatus.details?.authorizationCode ?? "",
363
497
  brand: cardStatus.details?.brand.rawValue.uppercased() ?? "",
364
498
  reason: cardStatus.details?.issuerDeclineReason?.rawValue ?? "",
365
499
  receiptUrl: cardStatus.receiptUrl?.absoluteString,
500
+ refundToken: cardStatus.refund?.refundToken,
366
501
  type: "card"
367
502
  ))
368
503
 
@@ -441,37 +576,35 @@ private extension AcceptTransactionStatus {
441
576
  // MARK: - Payment intent conversion
442
577
 
443
578
  private extension CardPaymentIntentRecord {
444
- func toPaymentIntent() throws -> AcceptCardPaymentIntent {
445
- let requested = try AcceptCurrency.from(requestedCurrency)
446
- let settlement = try settlementCurrency.map { try AcceptCurrency.from($0) } ?? requested
447
- return AcceptCardPaymentIntent(id: paymentIntentId, amount: amount, requestedCurrency: requested, settlementCurrency: settlement)
579
+ func toPaymentIntent() -> AcceptCardPaymentIntent {
580
+ AcceptCardPaymentIntent(
581
+ id: paymentIntentId,
582
+ amount: amount,
583
+ requestedCurrency: requestedCurrency,
584
+ settlementCurrency: settlementCurrency ?? requestedCurrency
585
+ )
448
586
  }
449
587
  }
450
588
 
451
589
  private extension SepaPaymentIntentRecord {
452
- func toPaymentIntent() throws -> AcceptSepaPaymentIntent {
453
- let requested = try AcceptCurrency.from(requestedCurrency)
454
- let settlement = try settlementCurrency.map { try AcceptCurrency.from($0) } ?? requested
455
- return AcceptSepaPaymentIntent(id: paymentIntentId, amount: amount, requestedCurrency: requested, settlementCurrency: settlement)
590
+ func toPaymentIntent() -> AcceptSepaPaymentIntent {
591
+ AcceptSepaPaymentIntent(
592
+ id: paymentIntentId,
593
+ amount: amount,
594
+ requestedCurrency: requestedCurrency,
595
+ settlementCurrency: settlementCurrency ?? requestedCurrency
596
+ )
456
597
  }
457
598
  }
458
599
 
459
600
  private extension CertisPaymentIntentRecord {
460
- func toPaymentIntent() throws -> AcceptCertisPaymentIntent {
461
- let requested = try AcceptCurrency.from(requestedCurrency)
462
- let settlement = try settlementCurrency.map { try AcceptCurrency.from($0) } ?? requested
463
- return AcceptCertisPaymentIntent(id: paymentIntentId, amount: amount, requestedCurrency: requested, settlementCurrency: settlement)
464
- }
465
- }
466
-
467
- private extension AcceptCurrency {
468
- static func from(_ code: String) throws -> AcceptCurrency {
469
- switch code.uppercased() {
470
- case "CZK": return .czk
471
- case "EUR": return .eur
472
- case "GBP": return .gbp
473
- default: throw Exception(name: "InvalidCurrency", description: "Unsupported currency: \(code)")
474
- }
601
+ func toPaymentIntent() -> AcceptCertisPaymentIntent {
602
+ AcceptCertisPaymentIntent(
603
+ id: paymentIntentId,
604
+ amount: amount,
605
+ requestedCurrency: requestedCurrency,
606
+ settlementCurrency: settlementCurrency ?? requestedCurrency
607
+ )
475
608
  }
476
609
  }
477
610
 
@@ -490,12 +623,13 @@ private extension AcceptPaymentOutcome where Result == CardPaymentStatus {
490
623
  record.companyAddress1 = status.merchantDetails.addressLine1
491
624
  record.companyAddress2 = status.merchantDetails.addressLine2
492
625
  record.companyCity = status.merchantDetails.city
493
- record.companyCountry = status.merchantDetails.country.rawValue
626
+ record.companyCountry = status.merchantDetails.country
494
627
  record.terminalId = status.terminalId
495
628
  record.authorizationCode = status.details?.authorizationCode
496
629
  record.brand = status.details?.brand.rawValue.uppercased()
497
630
  record.reason = status.details?.issuerDeclineReason?.rawValue
498
631
  record.receiptUrl = status.receiptUrl?.absoluteString
632
+ record.refundToken = status.refund?.refundToken
499
633
  }
500
634
  return record
501
635
  }
@@ -517,3 +651,99 @@ private extension AcceptPaymentOutcome where Result == BankTransferPaymentStatus
517
651
  return record
518
652
  }
519
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
+ }
package/package.json CHANGED
@@ -1,18 +1,15 @@
1
1
  {
2
2
  "name": "@tapayadot/accept-react-native",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "description": "Accept SDK for React Native/Expo",
5
5
  "keywords": [
6
6
  "react-native",
7
7
  "expo",
8
- "accept-react-native",
9
- "AcceptSdkReactNative",
10
- "expo-module",
11
- "native-module",
12
8
  "payments",
13
9
  "tapaya",
14
10
  "terminal",
15
- "softpos"
11
+ "softpos",
12
+ "tap-to-pay"
16
13
  ],
17
14
  "homepage": "https://github.com/tapayadot/accept-react-native#readme",
18
15
  "repository": {
@@ -100,4 +97,4 @@
100
97
  "workspaces": [
101
98
  "example"
102
99
  ]
103
- }
100
+ }
@@ -24,7 +24,7 @@ function withAcceptSdkIos(config) {
24
24
  repositoryURL: `"${ACCEPT_SDK_IOS_REPO}"`,
25
25
  requirement: {
26
26
  kind: "exactVersion",
27
- version: "1.1.2",
27
+ version: "1.2.2",
28
28
  },
29
29
  };
30
30