@tapayadot/accept-react-native 1.1.0 → 1.2.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.
@@ -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 {
@@ -132,7 +153,7 @@ public class AcceptSdkReactNativeModule: Module {
132
153
  let themeConfig = try theme?.toAcceptThemeConfiguration() ?? .default
133
154
  try await Accept.initialize(environment: demo ? .sandbox : .production, theme: themeConfig)
134
155
  } catch let error as AcceptSDKError {
135
- throw Exception(name: "AcceptSDKError", description: error.localizedDescription)
156
+ throw error.toException()
136
157
  }
137
158
  }
138
159
 
@@ -140,15 +161,19 @@ public class AcceptSdkReactNativeModule: Module {
140
161
  do {
141
162
  try await Accept.setTheme(try theme.toAcceptThemeConfiguration())
142
163
  } catch let error as AcceptSDKError {
143
- throw Exception(name: "AcceptSDKError", description: error.localizedDescription)
164
+ throw error.toException()
144
165
  }
145
166
  }
146
167
 
168
+ AsyncFunction("setUiBehavior") { (_: [String: Any]) in
169
+ // No-op on iOS — UI behavior configuration is Android-only.
170
+ }
171
+
147
172
  AsyncFunction("authenticate") { (token: String) async throws in
148
173
  do {
149
174
  try await Accept.authenticate(merchantToken: token)
150
175
  } catch let error as AcceptSDKError {
151
- throw Exception(name: "AcceptSDKError", description: error.localizedDescription)
176
+ throw error.toException()
152
177
  }
153
178
  return true
154
179
  }
@@ -171,7 +196,7 @@ public class AcceptSdkReactNativeModule: Module {
171
196
  do {
172
197
  try await Accept.logout()
173
198
  } catch let error as AcceptSDKError {
174
- throw Exception(name: "AcceptSDKError", description: error.localizedDescription)
199
+ throw error.toException()
175
200
  }
176
201
  return true
177
202
  }
@@ -181,11 +206,37 @@ public class AcceptSdkReactNativeModule: Module {
181
206
  try await Accept.presentKyb(prefilling: prefillData)
182
207
  }
183
208
 
209
+ AsyncFunction("getOnboardingStatus") { () async throws -> [String: Any] in
210
+ do {
211
+ let status = try await Accept.getOnboardingStatus()
212
+ return [
213
+ "isOnboarded": status.isOnboarded,
214
+ "paymentMethods": status.paymentMethods.map { pm in
215
+ [
216
+ "method": pm.method.toJsString(),
217
+ "status": pm.status.toJsString(),
218
+ ]
219
+ },
220
+ ]
221
+ } catch let error as AcceptSDKError {
222
+ throw error.toException()
223
+ }
224
+ }
225
+
226
+ AsyncFunction("getPaymentMethodStatus") { (method: String) async throws -> String in
227
+ do {
228
+ let status = try await Accept.getPaymentMethodStatus(for: try AcceptPaymentMethod.fromJsString(method))
229
+ return status.toJsString()
230
+ } catch let error as AcceptSDKError {
231
+ throw error.toException()
232
+ }
233
+ }
234
+
184
235
  AsyncFunction("requestLocationPermission") { () async throws -> Bool in
185
236
  do {
186
237
  try await Accept.requestLocationPermission()
187
238
  } catch let error as AcceptSDKError {
188
- throw Exception(name: "AcceptSDKError", description: error.localizedDescription)
239
+ throw error.toException()
189
240
  }
190
241
  return true
191
242
  }
@@ -194,7 +245,7 @@ public class AcceptSdkReactNativeModule: Module {
194
245
  do {
195
246
  try await Accept.pairWithCompanionApp()
196
247
  } catch let error as AcceptSDKError {
197
- throw Exception(name: "AcceptSDKError", description: error.localizedDescription)
248
+ throw error.toException()
198
249
  }
199
250
  }
200
251
 
@@ -206,7 +257,7 @@ public class AcceptSdkReactNativeModule: Module {
206
257
  do {
207
258
  try await Accept.presentCompanionAppSheet()
208
259
  } catch let error as AcceptSDKError {
209
- throw Exception(name: "AcceptSDKError", description: error.localizedDescription)
260
+ throw error.toException()
210
261
  }
211
262
  }
212
263
 
@@ -215,7 +266,7 @@ public class AcceptSdkReactNativeModule: Module {
215
266
  let status = try await Accept.getPaymentStatus(for: paymentIntentId)
216
267
  return try InfoResultSerializer.serialize(status)
217
268
  } catch let error as AcceptSDKError {
218
- throw Exception(name: "AcceptSDKError", description: error.localizedDescription)
269
+ throw error.toException()
219
270
  }
220
271
  }
221
272
 
@@ -224,7 +275,7 @@ public class AcceptSdkReactNativeModule: Module {
224
275
  let outcome = try await Accept.pay(intentRecord.toPaymentIntent())
225
276
  return outcome.toCardResult()
226
277
  } catch let error as AcceptSDKError {
227
- throw Exception(name: "AcceptSDKError", description: error.localizedDescription)
278
+ throw error.toException()
228
279
  }
229
280
  }
230
281
 
@@ -233,7 +284,7 @@ public class AcceptSdkReactNativeModule: Module {
233
284
  let outcome = try await Accept.pay(intentRecord.toPaymentIntent())
234
285
  return outcome.toBankTransferResult()
235
286
  } catch let error as AcceptSDKError {
236
- throw Exception(name: "AcceptSDKError", description: error.localizedDescription)
287
+ throw error.toException()
237
288
  }
238
289
  }
239
290
 
@@ -242,9 +293,42 @@ public class AcceptSdkReactNativeModule: Module {
242
293
  let outcome = try await Accept.pay(intentRecord.toPaymentIntent())
243
294
  return outcome.toBankTransferResult()
244
295
  } catch let error as AcceptSDKError {
245
- throw Exception(name: "AcceptSDKError", description: error.localizedDescription)
296
+ throw error.toException()
246
297
  }
247
298
  }
299
+
300
+ AsyncFunction("refund") { (intent: RefundIntentRecord) async throws -> RefundResultRecord in
301
+ do {
302
+ let refund = try await Accept.refund(
303
+ paymentToken: intent.paymentToken,
304
+ refundToken: intent.refundToken,
305
+ reason: try AcceptRefundReason.fromJsString(intent.reason)
306
+ )
307
+ return refund.toRecord()
308
+ } catch let error as AcceptSDKError {
309
+ throw error.toException()
310
+ }
311
+ }
312
+
313
+ AsyncFunction("cancelActivePayment") { () async throws in
314
+ do {
315
+ try await MainActor.run { try Accept.cancelActivePayment() }
316
+ } catch let error as AcceptSDKError {
317
+ throw error.toException()
318
+ }
319
+ }
320
+
321
+ AsyncFunction("enableDemoMode") { () async in
322
+ await Accept.enableDemoMode()
323
+ }
324
+
325
+ AsyncFunction("disableDemoMode") { () async in
326
+ await Accept.disableDemoMode()
327
+ }
328
+
329
+ AsyncFunction("isDemoMode") { () async -> Bool in
330
+ return await MainActor.run { Accept.isDemoMode }
331
+ }
248
332
  }
249
333
  }
250
334
 
@@ -292,7 +376,7 @@ private extension AcceptImageThemeRecord {
292
376
  }
293
377
  }
294
378
 
295
- private func color(fromHex input: String) throws -> Color {
379
+ private func color(fromHex input: String) throws -> AcceptColor {
296
380
  let hex = input.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
297
381
  guard hex.count == 3 || hex.count == 6 || hex.count == 8 else {
298
382
  throw Exception(name: "ThemeError", description: "Invalid hex color '\(input)'. Expected #RRGGBB or #AARRGGBB.")
@@ -308,7 +392,52 @@ private func color(fromHex input: String) throws -> Color {
308
392
  case 8: (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
309
393
  default: fatalError()
310
394
  }
311
- return Color(.sRGB, red: Double(r) / 255, green: Double(g) / 255, blue: Double(b) / 255, opacity: Double(a) / 255)
395
+ return AcceptColor(red: Double(r) / 255, green: Double(g) / 255, blue: Double(b) / 255, alpha: Double(a) / 255)
396
+ }
397
+
398
+ // MARK: - Error mapping
399
+
400
+ private extension AcceptSDKError {
401
+ func toException() -> Exception {
402
+ switch self {
403
+ case .notInitialized:
404
+ return Exception(name: "NotInitialized", description: errorDescription ?? "")
405
+ case .merchantNotAuthenticated:
406
+ return Exception(name: "MerchantNotAuthenticated", description: errorDescription ?? "")
407
+ case .locationPermissionsNotGranted:
408
+ return Exception(name: "LocationPermissionsNotGranted", description: errorDescription ?? "")
409
+ case .companionAppNotInstalled:
410
+ return Exception(name: "CompanionAppNotInstalled", description: errorDescription ?? "")
411
+ case .failedToAuthenticateMerchant:
412
+ return Exception(name: "FailedToAuthenticateMerchant", description: errorDescription ?? "")
413
+ case .failedToPresent:
414
+ return Exception(name: "FailedToPresent", description: errorDescription ?? "")
415
+ case .kybAlreadyInProgress:
416
+ return Exception(name: "KybAlreadyInProgress", description: errorDescription ?? "")
417
+ case .kybAlreadySubmitted:
418
+ return Exception(name: "KybAlreadySubmitted", description: errorDescription ?? "")
419
+ case .paymentAlreadyInProgress:
420
+ return Exception(name: "PaymentAlreadyInProgress", description: errorDescription ?? "")
421
+ case .unexpectedPaymentResult:
422
+ return Exception(name: "UnexpectedPaymentResult", description: errorDescription ?? "")
423
+ case .paymentNotFound:
424
+ return Exception(name: "PaymentNotFound", description: errorDescription ?? "")
425
+ case .refundConflict:
426
+ return Exception(name: "RefundConflict", description: errorDescription ?? "")
427
+ case .merchantOperationFailed:
428
+ return Exception(name: "MerchantOperationFailed", description: errorDescription ?? "")
429
+ case let .amountBelowMinimum(currency, minimum):
430
+ return Exception(name: "AmountBelowMinimum", description: "currency=\(currency) minimum=\(minimum)")
431
+ case let .amountAboveMaximum(currency, maximum):
432
+ return Exception(name: "AmountAboveMaximum", description: "currency=\(currency) maximum=\(maximum)")
433
+ case let .unknownCurrency(code):
434
+ return Exception(name: "UnknownCurrency", description: "code=\(code)")
435
+ case .noActivePayment:
436
+ return Exception(name: "NoActivePayment", description: errorDescription ?? "")
437
+ @unknown default:
438
+ return Exception(name: "AcceptSDKError", description: errorDescription ?? "")
439
+ }
440
+ }
312
441
  }
313
442
 
314
443
  // MARK: - Info result serialization
@@ -321,12 +450,13 @@ private struct CardInfoResult: Encodable {
321
450
  let companyAddress1: String
322
451
  let companyAddress2: String
323
452
  let companyCity: String
324
- let companyCountry: Int
453
+ let companyCountry: String
325
454
  let terminalId: String
326
455
  let authorizationCode: String
327
456
  let brand: String
328
457
  let reason: String
329
458
  let receiptUrl: String?
459
+ let refundToken: String?
330
460
  let type: String
331
461
  }
332
462
 
@@ -357,12 +487,13 @@ private enum InfoResultSerializer {
357
487
  companyAddress1: cardStatus.merchantDetails.addressLine1,
358
488
  companyAddress2: cardStatus.merchantDetails.addressLine2 ?? "",
359
489
  companyCity: cardStatus.merchantDetails.city,
360
- companyCountry: cardStatus.merchantDetails.country.rawValue,
490
+ companyCountry: cardStatus.merchantDetails.country,
361
491
  terminalId: cardStatus.terminalId,
362
492
  authorizationCode: cardStatus.details?.authorizationCode ?? "",
363
493
  brand: cardStatus.details?.brand.rawValue.uppercased() ?? "",
364
494
  reason: cardStatus.details?.issuerDeclineReason?.rawValue ?? "",
365
495
  receiptUrl: cardStatus.receiptUrl?.absoluteString,
496
+ refundToken: cardStatus.refund?.refundToken,
366
497
  type: "card"
367
498
  ))
368
499
 
@@ -441,37 +572,35 @@ private extension AcceptTransactionStatus {
441
572
  // MARK: - Payment intent conversion
442
573
 
443
574
  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)
575
+ func toPaymentIntent() -> AcceptCardPaymentIntent {
576
+ AcceptCardPaymentIntent(
577
+ id: paymentIntentId,
578
+ amount: amount,
579
+ requestedCurrency: requestedCurrency,
580
+ settlementCurrency: settlementCurrency ?? requestedCurrency
581
+ )
448
582
  }
449
583
  }
450
584
 
451
585
  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)
586
+ func toPaymentIntent() -> AcceptSepaPaymentIntent {
587
+ AcceptSepaPaymentIntent(
588
+ id: paymentIntentId,
589
+ amount: amount,
590
+ requestedCurrency: requestedCurrency,
591
+ settlementCurrency: settlementCurrency ?? requestedCurrency
592
+ )
456
593
  }
457
594
  }
458
595
 
459
596
  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
- }
597
+ func toPaymentIntent() -> AcceptCertisPaymentIntent {
598
+ AcceptCertisPaymentIntent(
599
+ id: paymentIntentId,
600
+ amount: amount,
601
+ requestedCurrency: requestedCurrency,
602
+ settlementCurrency: settlementCurrency ?? requestedCurrency
603
+ )
475
604
  }
476
605
  }
477
606
 
@@ -490,12 +619,13 @@ private extension AcceptPaymentOutcome where Result == CardPaymentStatus {
490
619
  record.companyAddress1 = status.merchantDetails.addressLine1
491
620
  record.companyAddress2 = status.merchantDetails.addressLine2
492
621
  record.companyCity = status.merchantDetails.city
493
- record.companyCountry = status.merchantDetails.country.rawValue
622
+ record.companyCountry = status.merchantDetails.country
494
623
  record.terminalId = status.terminalId
495
624
  record.authorizationCode = status.details?.authorizationCode
496
625
  record.brand = status.details?.brand.rawValue.uppercased()
497
626
  record.reason = status.details?.issuerDeclineReason?.rawValue
498
627
  record.receiptUrl = status.receiptUrl?.absoluteString
628
+ record.refundToken = status.refund?.refundToken
499
629
  }
500
630
  return record
501
631
  }
@@ -517,3 +647,99 @@ private extension AcceptPaymentOutcome where Result == BankTransferPaymentStatus
517
647
  return record
518
648
  }
519
649
  }
650
+
651
+ // MARK: - Onboarding status conversion
652
+
653
+ private extension AcceptPaymentMethod {
654
+ static func fromJsString(_ value: String) throws -> AcceptPaymentMethod {
655
+ switch value {
656
+ case "CARD": return .card
657
+ case "SEPA": return .sepaIct
658
+ case "CERTIS": return .certisCz
659
+ case "CRYPTO": return .crypto
660
+ default:
661
+ throw Exception(name: "InvalidPaymentMethod", description: "Unknown payment method: \(value)")
662
+ }
663
+ }
664
+
665
+ func toJsString() -> String {
666
+ switch self {
667
+ case .card: return "CARD"
668
+ case .sepaIct: return "SEPA"
669
+ case .certisCz: return "CERTIS"
670
+ case .crypto: return "CRYPTO"
671
+ @unknown default: return "UNKNOWN"
672
+ }
673
+ }
674
+ }
675
+
676
+ private extension AcceptPaymentMethodStatus.Status {
677
+ func toJsString() -> String {
678
+ switch self {
679
+ case .notStarted: return "NOT_STARTED"
680
+ case .pending: return "PENDING"
681
+ case .active: return "ACTIVE"
682
+ case .attentionNeeded: return "ATTENTION_NEEDED"
683
+ case .actionRequired: return "ACTION_REQUIRED"
684
+ case .blocked: return "BLOCKED"
685
+ @unknown default: return "UNKNOWN"
686
+ }
687
+ }
688
+ }
689
+
690
+ // MARK: - Refund conversion
691
+
692
+ private extension AcceptRefundReason {
693
+ static func fromJsString(_ value: String) throws -> AcceptRefundReason {
694
+ switch value {
695
+ case "customerRequest": return .customerRequest
696
+ case "duplicate": return .duplicate
697
+ case "fraudulent": return .fraudulent
698
+ case "expiredCharge": return .expiredCharge
699
+ default:
700
+ throw Exception(name: "InvalidRefundReason", description: "Unknown refund reason: \(value)")
701
+ }
702
+ }
703
+
704
+ func toJsString() -> String {
705
+ switch self {
706
+ case .customerRequest: return "customerRequest"
707
+ case .duplicate: return "duplicate"
708
+ case .fraudulent: return "fraudulent"
709
+ case .expiredCharge: return "expiredCharge"
710
+ @unknown default: return "customerRequest"
711
+ }
712
+ }
713
+ }
714
+
715
+ private extension AcceptTransactionStatus {
716
+ func toJsString() -> String {
717
+ switch self {
718
+ case .pending: return "pending"
719
+ case .success: return "success"
720
+ case .cancelled: return "cancelled"
721
+ case .refunded: return "refunded"
722
+ case .failed: return "failed"
723
+ case .actionNeeded: return "actionNeeded"
724
+ @unknown default: return "pending"
725
+ }
726
+ }
727
+ }
728
+
729
+ private extension AcceptRefund {
730
+ func toRecord() -> RefundResultRecord {
731
+ let record = RefundResultRecord()
732
+ record.refundToken = refundToken
733
+ record.status = status.toJsString()
734
+ record.paymentMethod = paymentMethod.toJsString()
735
+ record.requestedAmount = requestedAmount
736
+ record.requestedCurrency = requestedCurrency
737
+ record.settlementAmount = settlementAmount
738
+ record.settlementCurrency = settlementCurrency
739
+ record.fxRate = fxRate
740
+ record.refundReason = refundReason.toJsString()
741
+ record.createdAt = ISO8601DateFormatter().string(from: createdAt)
742
+ record.processedAt = processedAt.map { ISO8601DateFormatter().string(from: $0) }
743
+ return record
744
+ }
745
+ }
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.0",
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": {
@@ -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