@billdog.io/react-native 1.0.0-beta.2

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 (309) hide show
  1. package/BillDogReactNative.podspec +30 -0
  2. package/LICENSE +21 -0
  3. package/README.md +330 -0
  4. package/android/build.gradle +99 -0
  5. package/android/src/main/AndroidManifest.xml +1 -0
  6. package/android/src/main/kotlin/io/billdog/reactnative/BillDogABTestModule.kt +317 -0
  7. package/android/src/main/kotlin/io/billdog/reactnative/BillDogAnalyticsModule.kt +389 -0
  8. package/android/src/main/kotlin/io/billdog/reactnative/BillDogModule.kt +817 -0
  9. package/android/src/main/kotlin/io/billdog/reactnative/BillDogPaywallViewManager.kt +383 -0
  10. package/android/src/main/kotlin/io/billdog/reactnative/BillDogSurveyModule.kt +427 -0
  11. package/android/src/main/kotlin/io/billdog/reactnative/BillDogVirtualCurrencyModule.kt +407 -0
  12. package/android/src/main/kotlin/io/billdog/virtualcurrency/BillDogVirtualCurrencyPackage.kt +44 -0
  13. package/dist/AdvancedPaywallCache.d.ts +70 -0
  14. package/dist/AdvancedPaywallCache.js +344 -0
  15. package/dist/BillDog.d.ts +255 -0
  16. package/dist/BillDog.js +918 -0
  17. package/dist/BillDogConfiguration.d.ts +145 -0
  18. package/dist/BillDogConfiguration.js +229 -0
  19. package/dist/ComponentRenderer.d.ts +17 -0
  20. package/dist/ComponentRenderer.js +886 -0
  21. package/dist/LayoutEngine.d.ts +101 -0
  22. package/dist/LayoutEngine.js +394 -0
  23. package/dist/LocalizationResolver.d.ts +99 -0
  24. package/dist/LocalizationResolver.js +266 -0
  25. package/dist/NativePaywallView.d.ts +36 -0
  26. package/dist/NativePaywallView.js +59 -0
  27. package/dist/PaywallCache.d.ts +74 -0
  28. package/dist/PaywallCache.js +293 -0
  29. package/dist/PaywallStateManager.d.ts +62 -0
  30. package/dist/PaywallStateManager.js +147 -0
  31. package/dist/PaywallView.d.ts +42 -0
  32. package/dist/PaywallView.js +275 -0
  33. package/dist/ProductVariableProvider.d.ts +60 -0
  34. package/dist/ProductVariableProvider.js +239 -0
  35. package/dist/VariableResolver.d.ts +120 -0
  36. package/dist/VariableResolver.js +379 -0
  37. package/dist/WebViewPaywall.d.ts +75 -0
  38. package/dist/WebViewPaywall.js +360 -0
  39. package/dist/animations/presets.d.ts +49 -0
  40. package/dist/animations/presets.js +367 -0
  41. package/dist/components/AccoladeCpsComponent.d.ts +13 -0
  42. package/dist/components/AccoladeCpsComponent.js +248 -0
  43. package/dist/components/AccordionComponent.d.ts +10 -0
  44. package/dist/components/AccordionComponent.js +187 -0
  45. package/dist/components/AnimatedWrapper.d.ts +35 -0
  46. package/dist/components/AnimatedWrapper.js +277 -0
  47. package/dist/components/CarouselComponent.d.ts +23 -0
  48. package/dist/components/CarouselComponent.js +484 -0
  49. package/dist/components/ChartCurveComponent.d.ts +13 -0
  50. package/dist/components/ChartCurveComponent.js +333 -0
  51. package/dist/components/CheckboxComponent.d.ts +9 -0
  52. package/dist/components/CheckboxComponent.js +172 -0
  53. package/dist/components/ConfettiComponent.d.ts +9 -0
  54. package/dist/components/ConfettiComponent.js +172 -0
  55. package/dist/components/CountdownComponent.d.ts +16 -0
  56. package/dist/components/CountdownComponent.js +414 -0
  57. package/dist/components/DelayedXComponent.d.ts +12 -0
  58. package/dist/components/DelayedXComponent.js +240 -0
  59. package/dist/components/DrawerModalComponent.d.ts +20 -0
  60. package/dist/components/DrawerModalComponent.js +286 -0
  61. package/dist/components/GradientView.d.ts +38 -0
  62. package/dist/components/GradientView.js +126 -0
  63. package/dist/components/HapticFeedbackComponent.d.ts +16 -0
  64. package/dist/components/HapticFeedbackComponent.js +64 -0
  65. package/dist/components/HorizontalProductsComponent.d.ts +17 -0
  66. package/dist/components/HorizontalProductsComponent.js +284 -0
  67. package/dist/components/MultiChoiceGridComponent.d.ts +13 -0
  68. package/dist/components/MultiChoiceGridComponent.js +234 -0
  69. package/dist/components/NPSScaleComponent.d.ts +16 -0
  70. package/dist/components/NPSScaleComponent.js +260 -0
  71. package/dist/components/NavigationComponent.d.ts +26 -0
  72. package/dist/components/NavigationComponent.js +302 -0
  73. package/dist/components/OAuthButtonComponent.d.ts +20 -0
  74. package/dist/components/OAuthButtonComponent.js +237 -0
  75. package/dist/components/ProgressLoaderComponent.d.ts +13 -0
  76. package/dist/components/ProgressLoaderComponent.js +308 -0
  77. package/dist/components/ProgressStepperComponent.d.ts +18 -0
  78. package/dist/components/ProgressStepperComponent.js +228 -0
  79. package/dist/components/SegmentedPickerComponent.d.ts +10 -0
  80. package/dist/components/SegmentedPickerComponent.js +221 -0
  81. package/dist/components/ShareButtonsComponent.d.ts +21 -0
  82. package/dist/components/ShareButtonsComponent.js +265 -0
  83. package/dist/components/ShareCodeComponent.d.ts +16 -0
  84. package/dist/components/ShareCodeComponent.js +274 -0
  85. package/dist/components/SingleChoiceGridComponent.d.ts +13 -0
  86. package/dist/components/SingleChoiceGridComponent.js +210 -0
  87. package/dist/components/SliderComponent.d.ts +9 -0
  88. package/dist/components/SliderComponent.js +263 -0
  89. package/dist/components/StarRatingInputComponent.d.ts +20 -0
  90. package/dist/components/StarRatingInputComponent.js +198 -0
  91. package/dist/components/StickerCompositeComponent.d.ts +13 -0
  92. package/dist/components/StickerCompositeComponent.js +136 -0
  93. package/dist/components/TabsComponent.d.ts +19 -0
  94. package/dist/components/TabsComponent.js +431 -0
  95. package/dist/components/TextInputComponent.d.ts +18 -0
  96. package/dist/components/TextInputComponent.js +227 -0
  97. package/dist/components/TimelineComponent.d.ts +19 -0
  98. package/dist/components/TimelineComponent.js +104 -0
  99. package/dist/components/ToggleComponent.d.ts +14 -0
  100. package/dist/components/ToggleComponent.js +132 -0
  101. package/dist/components/VerticalProductsComponent.d.ts +17 -0
  102. package/dist/components/VerticalProductsComponent.js +311 -0
  103. package/dist/dynamicValues.d.ts +36 -0
  104. package/dist/dynamicValues.js +142 -0
  105. package/dist/filters/dateFilters.d.ts +50 -0
  106. package/dist/filters/dateFilters.js +390 -0
  107. package/dist/filters/index.d.ts +18 -0
  108. package/dist/filters/index.js +253 -0
  109. package/dist/hooks/useOfferings.d.ts +76 -0
  110. package/dist/hooks/useOfferings.js +99 -0
  111. package/dist/hooks/usePaywall.d.ts +27 -0
  112. package/dist/hooks/usePaywall.js +114 -0
  113. package/dist/hooks/usePlacement.d.ts +34 -0
  114. package/dist/hooks/usePlacement.js +89 -0
  115. package/dist/hooks/useSubscription.d.ts +40 -0
  116. package/dist/hooks/useSubscription.js +97 -0
  117. package/dist/index.d.ts +98 -0
  118. package/dist/index.js +198 -0
  119. package/dist/models/BillDogError.d.ts +40 -0
  120. package/dist/models/BillDogError.js +11 -0
  121. package/dist/models/CacheFetchPolicy.d.ts +8 -0
  122. package/dist/models/CacheFetchPolicy.js +4 -0
  123. package/dist/models/CustomProduct.d.ts +32 -0
  124. package/dist/models/CustomProduct.js +15 -0
  125. package/dist/models/CustomerInfo.d.ts +17 -0
  126. package/dist/models/CustomerInfo.js +11 -0
  127. package/dist/models/Delegate.d.ts +20 -0
  128. package/dist/models/Delegate.js +11 -0
  129. package/dist/models/EntitlementInfo.d.ts +19 -0
  130. package/dist/models/EntitlementInfo.js +2 -0
  131. package/dist/models/Placement.d.ts +54 -0
  132. package/dist/models/Placement.js +15 -0
  133. package/dist/models/PurchaseParams.d.ts +39 -0
  134. package/dist/models/PurchaseParams.js +60 -0
  135. package/dist/models/PurchaseResult.d.ts +30 -0
  136. package/dist/models/PurchaseResult.js +15 -0
  137. package/dist/models/RestoreResult.d.ts +30 -0
  138. package/dist/models/RestoreResult.js +22 -0
  139. package/dist/models/RestoredItem.d.ts +26 -0
  140. package/dist/models/RestoredItem.js +13 -0
  141. package/dist/modules/ABTest.d.ts +65 -0
  142. package/dist/modules/ABTest.js +113 -0
  143. package/dist/modules/Analytics.d.ts +91 -0
  144. package/dist/modules/Analytics.js +182 -0
  145. package/dist/modules/Survey.d.ts +65 -0
  146. package/dist/modules/Survey.js +82 -0
  147. package/dist/modules/VirtualCurrency.d.ts +63 -0
  148. package/dist/modules/VirtualCurrency.js +147 -0
  149. package/dist/murmur.d.ts +23 -0
  150. package/dist/murmur.js +104 -0
  151. package/dist/notifications/LiveActivitiesManager.d.ts +47 -0
  152. package/dist/notifications/LiveActivitiesManager.js +106 -0
  153. package/dist/notifications/NotificationScheduler.d.ts +55 -0
  154. package/dist/notifications/NotificationScheduler.js +199 -0
  155. package/dist/notifications/PurchaseNotificationHandler.d.ts +24 -0
  156. package/dist/notifications/PurchaseNotificationHandler.js +55 -0
  157. package/dist/notifications/useDeviceToken.d.ts +36 -0
  158. package/dist/notifications/useDeviceToken.js +97 -0
  159. package/dist/notifications/useLiveActivity.d.ts +39 -0
  160. package/dist/notifications/useLiveActivity.js +104 -0
  161. package/dist/review/ReviewAnalytics.d.ts +34 -0
  162. package/dist/review/ReviewAnalytics.js +73 -0
  163. package/dist/review/ReviewManager.d.ts +83 -0
  164. package/dist/review/ReviewManager.js +339 -0
  165. package/dist/review/ReviewTimingManager.d.ts +82 -0
  166. package/dist/review/ReviewTimingManager.js +224 -0
  167. package/dist/survey-core/types.d.ts +215 -0
  168. package/dist/survey-core/types.js +8 -0
  169. package/dist/types.d.ts +438 -0
  170. package/dist/types.js +158 -0
  171. package/dist/utils/ApiClient.d.ts +65 -0
  172. package/dist/utils/ApiClient.js +132 -0
  173. package/dist/utils/CSSConverter.d.ts +49 -0
  174. package/dist/utils/CSSConverter.js +470 -0
  175. package/dist/utils/animations.d.ts +41 -0
  176. package/dist/utils/animations.js +194 -0
  177. package/dist/utils/calendarIntegration.d.ts +36 -0
  178. package/dist/utils/calendarIntegration.js +89 -0
  179. package/dist/utils/configurationResolver.d.ts +23 -0
  180. package/dist/utils/configurationResolver.js +78 -0
  181. package/dist/utils/deviceDetection.d.ts +38 -0
  182. package/dist/utils/deviceDetection.js +59 -0
  183. package/dist/utils/hapticFeedback.d.ts +17 -0
  184. package/dist/utils/hapticFeedback.js +53 -0
  185. package/dist/utils/iconMapper.d.ts +26 -0
  186. package/dist/utils/iconMapper.js +113 -0
  187. package/dist/utils/notificationPermissions.d.ts +13 -0
  188. package/dist/utils/notificationPermissions.js +51 -0
  189. package/dist/utils/priceFormatting.d.ts +19 -0
  190. package/dist/utils/priceFormatting.js +99 -0
  191. package/dist/utils/shareSheet.d.ts +17 -0
  192. package/dist/utils/shareSheet.js +42 -0
  193. package/dist/utils/subscriptionUpgrade.d.ts +84 -0
  194. package/dist/utils/subscriptionUpgrade.js +100 -0
  195. package/dist/valueUtils.d.ts +48 -0
  196. package/dist/valueUtils.js +129 -0
  197. package/dist/xFlowTags.d.ts +18 -0
  198. package/dist/xFlowTags.js +385 -0
  199. package/ios/BillDogABTest.m +50 -0
  200. package/ios/BillDogABTest.swift +188 -0
  201. package/ios/BillDogAnalytics.m +105 -0
  202. package/ios/BillDogAnalytics.swift +324 -0
  203. package/ios/BillDogBridge.m +222 -0
  204. package/ios/BillDogBridge.swift +715 -0
  205. package/ios/BillDogLiveActivities.m +41 -0
  206. package/ios/BillDogLiveActivities.swift +104 -0
  207. package/ios/BillDogPaywallViewManager.m +39 -0
  208. package/ios/BillDogPaywallViewManager.swift +361 -0
  209. package/ios/BillDogSurvey.m +34 -0
  210. package/ios/BillDogSurvey.swift +257 -0
  211. package/ios/BillDogVirtualCurrencyBridge.m +63 -0
  212. package/ios/BillDogVirtualCurrencyBridge.swift +511 -0
  213. package/junit.xml +25 -0
  214. package/package.json +61 -0
  215. package/react-native.config.js +13 -0
  216. package/src/AdvancedPaywallCache.ts +433 -0
  217. package/src/BillDog.ts +983 -0
  218. package/src/BillDogConfiguration.ts +318 -0
  219. package/src/ComponentRenderer.tsx +1325 -0
  220. package/src/LayoutEngine.ts +459 -0
  221. package/src/LocalizationResolver.ts +311 -0
  222. package/src/NativePaywallView.tsx +132 -0
  223. package/src/PaywallCache.ts +358 -0
  224. package/src/PaywallStateManager.ts +154 -0
  225. package/src/PaywallView.tsx +328 -0
  226. package/src/ProductVariableProvider.ts +282 -0
  227. package/src/VariableResolver.ts +516 -0
  228. package/src/WebViewPaywall.tsx +454 -0
  229. package/src/animations/presets.ts +421 -0
  230. package/src/components/AccoladeCpsComponent.tsx +259 -0
  231. package/src/components/AccordionComponent.tsx +213 -0
  232. package/src/components/AnimatedWrapper.tsx +316 -0
  233. package/src/components/CarouselComponent.tsx +615 -0
  234. package/src/components/ChartCurveComponent.tsx +452 -0
  235. package/src/components/CheckboxComponent.tsx +185 -0
  236. package/src/components/ConfettiComponent.tsx +178 -0
  237. package/src/components/CountdownComponent.tsx +454 -0
  238. package/src/components/DelayedXComponent.tsx +266 -0
  239. package/src/components/DrawerModalComponent.tsx +324 -0
  240. package/src/components/GradientView.tsx +129 -0
  241. package/src/components/HapticFeedbackComponent.tsx +39 -0
  242. package/src/components/HorizontalProductsComponent.tsx +329 -0
  243. package/src/components/MultiChoiceGridComponent.tsx +265 -0
  244. package/src/components/NPSScaleComponent.tsx +294 -0
  245. package/src/components/NavigationComponent.tsx +329 -0
  246. package/src/components/OAuthButtonComponent.tsx +266 -0
  247. package/src/components/ProgressLoaderComponent.tsx +366 -0
  248. package/src/components/ProgressStepperComponent.tsx +250 -0
  249. package/src/components/SegmentedPickerComponent.tsx +245 -0
  250. package/src/components/ShareButtonsComponent.tsx +299 -0
  251. package/src/components/ShareCodeComponent.tsx +303 -0
  252. package/src/components/SingleChoiceGridComponent.tsx +229 -0
  253. package/src/components/SliderComponent.tsx +280 -0
  254. package/src/components/StarRatingInputComponent.tsx +223 -0
  255. package/src/components/StickerCompositeComponent.tsx +168 -0
  256. package/src/components/TabsComponent.tsx +496 -0
  257. package/src/components/TextInputComponent.tsx +258 -0
  258. package/src/components/TimelineComponent.tsx +129 -0
  259. package/src/components/ToggleComponent.tsx +142 -0
  260. package/src/components/VerticalProductsComponent.tsx +357 -0
  261. package/src/dynamicValues.ts +180 -0
  262. package/src/filters/dateFilters.ts +411 -0
  263. package/src/filters/index.ts +281 -0
  264. package/src/hooks/useOfferings.ts +192 -0
  265. package/src/hooks/usePaywall.ts +154 -0
  266. package/src/hooks/usePlacement.ts +143 -0
  267. package/src/hooks/useSubscription.ts +154 -0
  268. package/src/index.ts +245 -0
  269. package/src/models/BillDogError.ts +24 -0
  270. package/src/models/CacheFetchPolicy.ts +13 -0
  271. package/src/models/CustomProduct.ts +33 -0
  272. package/src/models/CustomerInfo.ts +26 -0
  273. package/src/models/Delegate.ts +22 -0
  274. package/src/models/EntitlementInfo.ts +27 -0
  275. package/src/models/Placement.ts +50 -0
  276. package/src/models/PurchaseParams.ts +81 -0
  277. package/src/models/PurchaseResult.ts +32 -0
  278. package/src/models/RestoreResult.ts +27 -0
  279. package/src/models/RestoredItem.ts +25 -0
  280. package/src/modules/ABTest.ts +131 -0
  281. package/src/modules/Analytics.ts +227 -0
  282. package/src/modules/Survey.ts +126 -0
  283. package/src/modules/VirtualCurrency.ts +223 -0
  284. package/src/murmur.ts +102 -0
  285. package/src/notifications/LiveActivitiesManager.ts +140 -0
  286. package/src/notifications/NotificationScheduler.ts +292 -0
  287. package/src/notifications/PurchaseNotificationHandler.ts +67 -0
  288. package/src/notifications/useDeviceToken.ts +116 -0
  289. package/src/notifications/useLiveActivity.ts +125 -0
  290. package/src/review/ReviewAnalytics.ts +89 -0
  291. package/src/review/ReviewManager.ts +355 -0
  292. package/src/review/ReviewTimingManager.ts +265 -0
  293. package/src/survey-core/types.ts +244 -0
  294. package/src/types/ambient.d.ts +9 -0
  295. package/src/types.ts +680 -0
  296. package/src/utils/ApiClient.ts +163 -0
  297. package/src/utils/CSSConverter.ts +552 -0
  298. package/src/utils/animations.ts +232 -0
  299. package/src/utils/calendarIntegration.ts +113 -0
  300. package/src/utils/configurationResolver.ts +106 -0
  301. package/src/utils/deviceDetection.ts +83 -0
  302. package/src/utils/hapticFeedback.ts +55 -0
  303. package/src/utils/iconMapper.ts +102 -0
  304. package/src/utils/notificationPermissions.ts +48 -0
  305. package/src/utils/priceFormatting.ts +119 -0
  306. package/src/utils/shareSheet.ts +55 -0
  307. package/src/utils/subscriptionUpgrade.ts +183 -0
  308. package/src/valueUtils.ts +108 -0
  309. package/src/xFlowTags.ts +463 -0
@@ -0,0 +1,511 @@
1
+ //
2
+ // BillDogVirtualCurrencyBridge.swift
3
+ // @billdog.io/react-native
4
+ //
5
+ // Swift implementation for React Native Virtual Currency bridge
6
+ //
7
+
8
+ import Foundation
9
+ import React
10
+
11
+ @objc(BillDogVirtualCurrency)
12
+ class BillDogVirtualCurrencyBridge: RCTEventEmitter {
13
+
14
+ private var hasListeners = false
15
+ private var baseUrl: String?
16
+ private var apiKey: String?
17
+ private var customerId: String?
18
+ private var enableLogging = false
19
+
20
+ override init() {
21
+ super.init()
22
+ }
23
+
24
+ override static func requiresMainQueueSetup() -> Bool {
25
+ return false
26
+ }
27
+
28
+ override func supportedEvents() -> [String]! {
29
+ return ["onBalanceChanged", "onTransaction"]
30
+ }
31
+
32
+ override func startObserving() {
33
+ hasListeners = true
34
+ }
35
+
36
+ override func stopObserving() {
37
+ hasListeners = false
38
+ }
39
+
40
+ // MARK: - Initialization
41
+
42
+ @objc
43
+ func initialize(_ config: NSDictionary,
44
+ resolver resolve: @escaping RCTPromiseResolveBlock,
45
+ rejecter reject: @escaping RCTPromiseRejectBlock) {
46
+
47
+ guard let baseUrl = config["baseUrl"] as? String,
48
+ let apiKey = config["apiKey"] as? String,
49
+ let customerId = config["customerId"] as? String else {
50
+ reject("INVALID_CONFIG", "Missing required configuration parameters", nil)
51
+ return
52
+ }
53
+
54
+ self.baseUrl = baseUrl
55
+ self.apiKey = apiKey
56
+ self.customerId = customerId
57
+ self.enableLogging = config["enableLogging"] as? Bool ?? false
58
+
59
+ if enableLogging {
60
+ print("[BillDog] Virtual Currency initialized for customer: \(customerId)")
61
+ }
62
+
63
+ resolve(nil)
64
+ }
65
+
66
+ // MARK: - Balance Operations
67
+
68
+ @objc
69
+ func getBalance(_ currencyId: String,
70
+ resolver resolve: @escaping RCTPromiseResolveBlock,
71
+ rejecter reject: @escaping RCTPromiseRejectBlock) {
72
+
73
+ guard let customerId = self.customerId else {
74
+ reject("NOT_INITIALIZED", "Virtual Currency module not initialized", nil)
75
+ return
76
+ }
77
+
78
+ fetchBalance(currencyId: currencyId, customerId: customerId) { result in
79
+ switch result {
80
+ case .success(let balance):
81
+ resolve(balance)
82
+ case .failure(let error):
83
+ reject("GET_BALANCE_ERROR", error.localizedDescription, error)
84
+ }
85
+ }
86
+ }
87
+
88
+ @objc
89
+ func addCurrency(_ currencyId: String,
90
+ amount: NSNumber,
91
+ reason: String?,
92
+ metadata: NSDictionary?,
93
+ resolver resolve: @escaping RCTPromiseResolveBlock,
94
+ rejecter reject: @escaping RCTPromiseRejectBlock) {
95
+
96
+ guard let customerId = self.customerId else {
97
+ reject("NOT_INITIALIZED", "Virtual Currency module not initialized", nil)
98
+ return
99
+ }
100
+
101
+ let amountValue = amount.doubleValue
102
+
103
+ performTransaction(
104
+ currencyId: currencyId,
105
+ customerId: customerId,
106
+ amount: amountValue,
107
+ type: "earn",
108
+ reason: reason,
109
+ metadata: metadata as? [String: Any]
110
+ ) { result in
111
+ switch result {
112
+ case .success(let newBalance):
113
+ if self.hasListeners {
114
+ self.sendEvent(withName: "onBalanceChanged", body: [
115
+ "currencyId": currencyId,
116
+ "newBalance": newBalance
117
+ ])
118
+ self.sendEvent(withName: "onTransaction", body: self.transactionEvent(
119
+ currencyId: currencyId, amount: amountValue, type: "earn",
120
+ reason: reason, newBalance: newBalance))
121
+ }
122
+ resolve(newBalance)
123
+ case .failure(let error):
124
+ reject("ADD_CURRENCY_ERROR", error.localizedDescription, error)
125
+ }
126
+ }
127
+ }
128
+
129
+ @objc
130
+ func deductCurrency(_ currencyId: String,
131
+ amount: NSNumber,
132
+ reason: String?,
133
+ metadata: NSDictionary?,
134
+ resolver resolve: @escaping RCTPromiseResolveBlock,
135
+ rejecter reject: @escaping RCTPromiseRejectBlock) {
136
+
137
+ guard let customerId = self.customerId else {
138
+ reject("NOT_INITIALIZED", "Virtual Currency module not initialized", nil)
139
+ return
140
+ }
141
+
142
+ let amountValue = amount.doubleValue
143
+
144
+ performTransaction(
145
+ currencyId: currencyId,
146
+ customerId: customerId,
147
+ amount: -amountValue,
148
+ type: "spend",
149
+ reason: reason,
150
+ metadata: metadata as? [String: Any]
151
+ ) { result in
152
+ switch result {
153
+ case .success(let newBalance):
154
+ if self.hasListeners {
155
+ self.sendEvent(withName: "onBalanceChanged", body: [
156
+ "currencyId": currencyId,
157
+ "newBalance": newBalance
158
+ ])
159
+ self.sendEvent(withName: "onTransaction", body: self.transactionEvent(
160
+ currencyId: currencyId, amount: -amountValue, type: "spend",
161
+ reason: reason, newBalance: newBalance))
162
+ }
163
+ resolve(newBalance)
164
+ case .failure(let error):
165
+ reject("DEDUCT_CURRENCY_ERROR", error.localizedDescription, error)
166
+ }
167
+ }
168
+ }
169
+
170
+ @objc
171
+ func grantCurrency(_ currencyId: String,
172
+ amount: NSNumber,
173
+ reason: String?,
174
+ resolver resolve: @escaping RCTPromiseResolveBlock,
175
+ rejecter reject: @escaping RCTPromiseRejectBlock) {
176
+
177
+ guard let customerId = self.customerId else {
178
+ reject("NOT_INITIALIZED", "Virtual Currency module not initialized", nil)
179
+ return
180
+ }
181
+
182
+ let amountValue = amount.doubleValue
183
+
184
+ performTransaction(
185
+ currencyId: currencyId,
186
+ customerId: customerId,
187
+ amount: amountValue,
188
+ type: "grant",
189
+ reason: reason,
190
+ metadata: nil
191
+ ) { result in
192
+ switch result {
193
+ case .success(let newBalance):
194
+ if self.hasListeners {
195
+ self.sendEvent(withName: "onBalanceChanged", body: [
196
+ "currencyId": currencyId,
197
+ "newBalance": newBalance
198
+ ])
199
+ self.sendEvent(withName: "onTransaction", body: self.transactionEvent(
200
+ currencyId: currencyId, amount: amountValue, type: "grant",
201
+ reason: reason, newBalance: newBalance))
202
+ }
203
+ resolve(newBalance)
204
+ case .failure(let error):
205
+ reject("GRANT_CURRENCY_ERROR", error.localizedDescription, error)
206
+ }
207
+ }
208
+ }
209
+
210
+ @objc
211
+ func getTransactionHistory(_ currencyId: String?,
212
+ limit: NSNumber,
213
+ resolver resolve: @escaping RCTPromiseResolveBlock,
214
+ rejecter reject: @escaping RCTPromiseRejectBlock) {
215
+
216
+ guard let customerId = self.customerId else {
217
+ reject("NOT_INITIALIZED", "Virtual Currency module not initialized", nil)
218
+ return
219
+ }
220
+
221
+ fetchTransactionHistory(currencyId: currencyId, customerId: customerId, limit: limit.intValue) { result in
222
+ switch result {
223
+ case .success(let transactions):
224
+ resolve(transactions)
225
+ case .failure(let error):
226
+ reject("GET_HISTORY_ERROR", error.localizedDescription, error)
227
+ }
228
+ }
229
+ }
230
+
231
+ @objc
232
+ func exchangeCurrency(_ fromCurrencyId: String,
233
+ toCurrencyId: String,
234
+ fromAmount: NSNumber,
235
+ resolver resolve: @escaping RCTPromiseResolveBlock,
236
+ rejecter reject: @escaping RCTPromiseRejectBlock) {
237
+
238
+ guard let customerId = self.customerId else {
239
+ reject("NOT_INITIALIZED", "Virtual Currency module not initialized", nil)
240
+ return
241
+ }
242
+
243
+ performExchange(
244
+ fromCurrencyId: fromCurrencyId,
245
+ toCurrencyId: toCurrencyId,
246
+ fromAmount: fromAmount.doubleValue,
247
+ customerId: customerId
248
+ ) { result in
249
+ switch result {
250
+ case .success(let exchangeResult):
251
+ if self.hasListeners {
252
+ self.sendEvent(withName: "onBalanceChanged", body: [
253
+ "currencyId": fromCurrencyId,
254
+ "newBalance": exchangeResult["newFromBalance"] as Any
255
+ ])
256
+ self.sendEvent(withName: "onBalanceChanged", body: [
257
+ "currencyId": toCurrencyId,
258
+ "newBalance": exchangeResult["newToBalance"] as Any
259
+ ])
260
+ }
261
+ resolve(exchangeResult)
262
+ case .failure(let error):
263
+ reject("EXCHANGE_ERROR", error.localizedDescription, error)
264
+ }
265
+ }
266
+ }
267
+
268
+ @objc
269
+ func getAllCurrencies(_ resolve: @escaping RCTPromiseResolveBlock,
270
+ rejecter reject: @escaping RCTPromiseRejectBlock) {
271
+
272
+ guard let customerId = self.customerId else {
273
+ reject("NOT_INITIALIZED", "Virtual Currency module not initialized", nil)
274
+ return
275
+ }
276
+
277
+ fetchAllCurrencies(customerId: customerId) { result in
278
+ switch result {
279
+ case .success(let currencies):
280
+ resolve(currencies)
281
+ case .failure(let error):
282
+ reject("GET_CURRENCIES_ERROR", error.localizedDescription, error)
283
+ }
284
+ }
285
+ }
286
+
287
+ // MARK: - Event Helpers
288
+
289
+ /// Build the `onTransaction` event body from the data available after a
290
+ /// transactions POST (which returns only `new_balance`).
291
+ private func transactionEvent(currencyId: String, amount: Double, type: String,
292
+ reason: String?, newBalance: Double) -> [String: Any] {
293
+ var body: [String: Any] = [
294
+ "currencyId": currencyId,
295
+ "amount": amount,
296
+ "type": type,
297
+ "newBalance": newBalance,
298
+ ]
299
+ if let reason = reason { body["reason"] = reason }
300
+ return body
301
+ }
302
+
303
+ // MARK: - Response Helper
304
+
305
+ /// Parse a response body and descend into the `data` envelope. BillDog edge
306
+ /// functions wrap payloads in `{ success, data, meta }`
307
+ /// (supabase/functions/_shared/response.ts), so callers must read from `data`.
308
+ private func payload(_ data: Data?) -> [String: Any]? {
309
+ guard let data = data,
310
+ let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
311
+ return nil
312
+ }
313
+ return (json["data"] as? [String: Any]) ?? json
314
+ }
315
+
316
+ // MARK: - API Helpers
317
+
318
+ private func fetchBalance(currencyId: String, customerId: String, completion: @escaping (Result<Double, Error>) -> Void) {
319
+ guard let baseUrl = self.baseUrl, let apiKey = self.apiKey else {
320
+ completion(.failure(NSError(domain: "BillDog", code: -1, userInfo: [NSLocalizedDescriptionKey: "Configuration missing"])))
321
+ return
322
+ }
323
+
324
+ let urlString = "\(baseUrl)/virtual-currency/balances?currency_id=\(currencyId)&customer_id=\(customerId)"
325
+ guard let url = URL(string: urlString) else {
326
+ completion(.failure(NSError(domain: "BillDog", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])))
327
+ return
328
+ }
329
+
330
+ var request = URLRequest(url: url)
331
+ request.httpMethod = "GET"
332
+ request.setValue(apiKey, forHTTPHeaderField: "X-BillDog-API-Key")
333
+ request.setValue("application/json", forHTTPHeaderField: "Content-Type")
334
+
335
+ URLSession.shared.dataTask(with: request) { data, response, error in
336
+ if let error = error {
337
+ completion(.failure(error))
338
+ return
339
+ }
340
+
341
+ guard let json = self.payload(data),
342
+ let balance = (json["balance"] as? NSNumber)?.doubleValue else {
343
+ completion(.failure(NSError(domain: "BillDog", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid response"])))
344
+ return
345
+ }
346
+
347
+ completion(.success(balance))
348
+ }.resume()
349
+ }
350
+
351
+ private func performTransaction(currencyId: String, customerId: String, amount: Double, type: String, reason: String?, metadata: [String: Any]?, completion: @escaping (Result<Double, Error>) -> Void) {
352
+ guard let baseUrl = self.baseUrl, let apiKey = self.apiKey else {
353
+ completion(.failure(NSError(domain: "BillDog", code: -1, userInfo: [NSLocalizedDescriptionKey: "Configuration missing"])))
354
+ return
355
+ }
356
+
357
+ let urlString = "\(baseUrl)/virtual-currency/transactions"
358
+ guard let url = URL(string: urlString) else {
359
+ completion(.failure(NSError(domain: "BillDog", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])))
360
+ return
361
+ }
362
+
363
+ var body: [String: Any] = [
364
+ "currency_id": currencyId,
365
+ "customer_id": customerId,
366
+ "amount": amount,
367
+ "type": type
368
+ ]
369
+
370
+ if let reason = reason {
371
+ body["reason"] = reason
372
+ }
373
+
374
+ if let metadata = metadata {
375
+ body["metadata"] = metadata
376
+ }
377
+
378
+ var request = URLRequest(url: url)
379
+ request.httpMethod = "POST"
380
+ request.setValue(apiKey, forHTTPHeaderField: "X-BillDog-API-Key")
381
+ request.setValue("application/json", forHTTPHeaderField: "Content-Type")
382
+ request.httpBody = try? JSONSerialization.data(withJSONObject: body)
383
+
384
+ URLSession.shared.dataTask(with: request) { data, response, error in
385
+ if let error = error {
386
+ completion(.failure(error))
387
+ return
388
+ }
389
+
390
+ guard let json = self.payload(data),
391
+ let newBalance = (json["new_balance"] as? NSNumber)?.doubleValue else {
392
+ completion(.failure(NSError(domain: "BillDog", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid response"])))
393
+ return
394
+ }
395
+
396
+ completion(.success(newBalance))
397
+ }.resume()
398
+ }
399
+
400
+ private func fetchTransactionHistory(currencyId: String?, customerId: String, limit: Int, completion: @escaping (Result<[[String: Any]], Error>) -> Void) {
401
+ guard let baseUrl = self.baseUrl, let apiKey = self.apiKey else {
402
+ completion(.failure(NSError(domain: "BillDog", code: -1, userInfo: [NSLocalizedDescriptionKey: "Configuration missing"])))
403
+ return
404
+ }
405
+
406
+ var urlString = "\(baseUrl)/virtual-currency/transactions/history?customer_id=\(customerId)&limit=\(limit)"
407
+ if let currencyId = currencyId {
408
+ urlString += "&currency_id=\(currencyId)"
409
+ }
410
+
411
+ guard let url = URL(string: urlString) else {
412
+ completion(.failure(NSError(domain: "BillDog", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])))
413
+ return
414
+ }
415
+
416
+ var request = URLRequest(url: url)
417
+ request.httpMethod = "GET"
418
+ request.setValue(apiKey, forHTTPHeaderField: "X-BillDog-API-Key")
419
+ request.setValue("application/json", forHTTPHeaderField: "Content-Type")
420
+
421
+ URLSession.shared.dataTask(with: request) { data, response, error in
422
+ if let error = error {
423
+ completion(.failure(error))
424
+ return
425
+ }
426
+
427
+ guard let json = self.payload(data),
428
+ let transactions = json["transactions"] as? [[String: Any]] else {
429
+ completion(.failure(NSError(domain: "BillDog", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid response"])))
430
+ return
431
+ }
432
+
433
+ completion(.success(transactions))
434
+ }.resume()
435
+ }
436
+
437
+ private func performExchange(fromCurrencyId: String, toCurrencyId: String, fromAmount: Double, customerId: String, completion: @escaping (Result<[String: Any], Error>) -> Void) {
438
+ guard let baseUrl = self.baseUrl, let apiKey = self.apiKey else {
439
+ completion(.failure(NSError(domain: "BillDog", code: -1, userInfo: [NSLocalizedDescriptionKey: "Configuration missing"])))
440
+ return
441
+ }
442
+
443
+ let urlString = "\(baseUrl)/virtual-currency/exchange"
444
+ guard let url = URL(string: urlString) else {
445
+ completion(.failure(NSError(domain: "BillDog", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])))
446
+ return
447
+ }
448
+
449
+ let body: [String: Any] = [
450
+ "from_currency_id": fromCurrencyId,
451
+ "to_currency_id": toCurrencyId,
452
+ "from_amount": fromAmount,
453
+ "customer_id": customerId
454
+ ]
455
+
456
+ var request = URLRequest(url: url)
457
+ request.httpMethod = "POST"
458
+ request.setValue(apiKey, forHTTPHeaderField: "X-BillDog-API-Key")
459
+ request.setValue("application/json", forHTTPHeaderField: "Content-Type")
460
+ request.httpBody = try? JSONSerialization.data(withJSONObject: body)
461
+
462
+ URLSession.shared.dataTask(with: request) { data, response, error in
463
+ if let error = error {
464
+ completion(.failure(error))
465
+ return
466
+ }
467
+
468
+ guard let json = self.payload(data) else {
469
+ completion(.failure(NSError(domain: "BillDog", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid response"])))
470
+ return
471
+ }
472
+
473
+ completion(.success(json))
474
+ }.resume()
475
+ }
476
+
477
+ private func fetchAllCurrencies(customerId: String, completion: @escaping (Result<[[String: Any]], Error>) -> Void) {
478
+ guard let baseUrl = self.baseUrl, let apiKey = self.apiKey else {
479
+ completion(.failure(NSError(domain: "BillDog", code: -1, userInfo: [NSLocalizedDescriptionKey: "Configuration missing"])))
480
+ return
481
+ }
482
+
483
+ let urlString = "\(baseUrl)/virtual-currency/balances?customer_id=\(customerId)"
484
+ guard let url = URL(string: urlString) else {
485
+ completion(.failure(NSError(domain: "BillDog", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])))
486
+ return
487
+ }
488
+
489
+ var request = URLRequest(url: url)
490
+ request.httpMethod = "GET"
491
+ request.setValue(apiKey, forHTTPHeaderField: "X-BillDog-API-Key")
492
+ request.setValue("application/json", forHTTPHeaderField: "Content-Type")
493
+
494
+ URLSession.shared.dataTask(with: request) { data, response, error in
495
+ if let error = error {
496
+ completion(.failure(error))
497
+ return
498
+ }
499
+
500
+ // All-balances route returns `data.balances = [{currency:{…}, balance}]`
501
+ // (NOT `currencies`); the JS layer normalizes the nested shape.
502
+ guard let json = self.payload(data),
503
+ let balances = json["balances"] as? [[String: Any]] else {
504
+ completion(.failure(NSError(domain: "BillDog", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid response"])))
505
+ return
506
+ }
507
+
508
+ completion(.success(balances))
509
+ }.resume()
510
+ }
511
+ }
package/junit.xml ADDED
@@ -0,0 +1,25 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <testsuites name="jest tests" tests="9" failures="0" errors="0" time="0.677">
3
+ <testsuite name="BillDog.purchase — canonical PurchaseResult contract" errors="0" failures="0" skipped="0" timestamp="2026-05-01T12:33:27" time="0.527" tests="6">
4
+ <testcase classname="BillDog.purchase — canonical PurchaseResult contract returns cancelled when no native bridge is wired" name="BillDog.purchase — canonical PurchaseResult contract returns cancelled when no native bridge is wired" time="0.001">
5
+ </testcase>
6
+ <testcase classname="BillDog.purchase — canonical PurchaseResult contract routes through host PurchaseController when registered" name="BillDog.purchase — canonical PurchaseResult contract routes through host PurchaseController when registered" time="0.001">
7
+ </testcase>
8
+ <testcase classname="BillDog.purchase — canonical PurchaseResult contract controller exception becomes a typed failure (NOT a throw)" name="BillDog.purchase — canonical PurchaseResult contract controller exception becomes a typed failure (NOT a throw)" time="0">
9
+ </testcase>
10
+ <testcase classname="BillDog.purchase — canonical PurchaseResult contract detaching the controller restores native-bridge fallback" name="BillDog.purchase — canonical PurchaseResult contract detaching the controller restores native-bridge fallback" time="0">
11
+ </testcase>
12
+ <testcase classname="BillDog.purchaseCustom — controller-only API without controller returns a typed failure" name="BillDog.purchaseCustom — controller-only API without controller returns a typed failure" time="0.001">
13
+ </testcase>
14
+ <testcase classname="BillDog.purchaseCustom — controller-only API with controller routes through it" name="BillDog.purchaseCustom — controller-only API with controller routes through it" time="0">
15
+ </testcase>
16
+ </testsuite>
17
+ <testsuite name="BillDog.restore — RestoreResult contract" errors="0" failures="0" skipped="0" timestamp="2026-05-01T12:33:28" time="0.028" tests="3">
18
+ <testcase classname="BillDog.restore — RestoreResult contract routes through host controller when registered" name="BillDog.restore — RestoreResult contract routes through host controller when registered" time="0.001">
19
+ </testcase>
20
+ <testcase classname="BillDog.restore — RestoreResult contract controller exception becomes a typed failure" name="BillDog.restore — RestoreResult contract controller exception becomes a typed failure" time="0">
21
+ </testcase>
22
+ <testcase classname="BillDog.restore — RestoreResult contract without controller and no bridge returns empty restored" name="BillDog.restore — RestoreResult contract without controller and no bridge returns empty restored" time="0.011">
23
+ </testcase>
24
+ </testsuite>
25
+ </testsuites>
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@billdog.io/react-native",
3
+ "version": "1.0.0-beta.2",
4
+ "description": "React Native paywall rendering library for BillDog",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "prepare": "npm run build",
10
+ "test": "jest"
11
+ },
12
+ "keywords": [
13
+ "react-native",
14
+ "paywall",
15
+ "billdog",
16
+ "subscriptions",
17
+ "in-app-purchases"
18
+ ],
19
+ "author": "BillDog",
20
+ "license": "MIT",
21
+ "homepage": "https://billdog.app",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/billdogai/billdog.git",
25
+ "directory": "native-sdks/react-native"
26
+ },
27
+ "bugs": {
28
+ "url": "https://github.com/billdogai/billdog/issues"
29
+ },
30
+ "peerDependencies": {
31
+ "react": ">=16.8.0",
32
+ "react-native": ">=0.60.0",
33
+ "react-native-in-app-review": ">=4.0.0",
34
+ "react-native-webview": "^13.0.0"
35
+ },
36
+ "peerDependenciesMeta": {
37
+ "react-native-in-app-review": {
38
+ "optional": true
39
+ }
40
+ },
41
+ "dependencies": {
42
+ "@react-native-async-storage/async-storage": "^1.19.0",
43
+ "pako": "^2.1.0",
44
+ "react-native-fs": "^2.20.0",
45
+ "react-native-svg": "^13.0.0"
46
+ },
47
+ "devDependencies": {
48
+ "@types/jest": "^29.5.12",
49
+ "@types/pako": "^2.0.3",
50
+ "@types/react": "^18.0.0",
51
+ "@types/react-native": "^0.70.0",
52
+ "jest": "^29.7.0",
53
+ "jest-junit": "^16.0.0",
54
+ "react": "^18.2.0",
55
+ "react-native": "0.72.17",
56
+ "react-native-webview": "^13.17.0",
57
+ "react-test-renderer": "^18.2.0",
58
+ "ts-jest": "^29.1.2",
59
+ "typescript": "^5.0.0"
60
+ }
61
+ }
@@ -0,0 +1,13 @@
1
+ module.exports = {
2
+ dependency: {
3
+ platforms: {
4
+ ios: {
5
+ podspecPath: './BillDogVirtualCurrency.podspec',
6
+ },
7
+ android: {
8
+ sourceDir: './android',
9
+ packageImportPath: 'import io.billdog.virtualcurrency.BillDogVirtualCurrencyPackage;',
10
+ },
11
+ },
12
+ },
13
+ };