@qonversion/react-native-sdk 10.0.1 → 10.1.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.
Files changed (73) hide show
  1. package/android/build.gradle +1 -1
  2. package/android/src/main/java/com/qonversion/reactnativesdk/NoCodesModule.kt +43 -2
  3. package/android/src/main/java/com/qonversion/reactnativesdk/QonversionModule.kt +27 -0
  4. package/ios/RNNoCodes.mm +45 -4
  5. package/ios/RNNoCodesImpl.swift +58 -3
  6. package/ios/RNQonversion.mm +9 -0
  7. package/ios/RNQonversionImpl.swift +7 -0
  8. package/lib/module/NoCodesConfig.js +3 -1
  9. package/lib/module/NoCodesConfig.js.map +1 -1
  10. package/lib/module/NoCodesConfigBuilder.js +27 -1
  11. package/lib/module/NoCodesConfigBuilder.js.map +1 -1
  12. package/lib/module/dto/PurchaseDelegate.js +4 -0
  13. package/lib/module/dto/PurchaseDelegate.js.map +1 -0
  14. package/lib/module/dto/PurchaseResult.js +74 -0
  15. package/lib/module/dto/PurchaseResult.js.map +1 -0
  16. package/lib/module/dto/StoreTransaction.js +48 -0
  17. package/lib/module/dto/StoreTransaction.js.map +1 -0
  18. package/lib/module/dto/enums.js +38 -0
  19. package/lib/module/dto/enums.js.map +1 -1
  20. package/lib/module/index.js +3 -0
  21. package/lib/module/index.js.map +1 -1
  22. package/lib/module/internal/Mapper.js +47 -1
  23. package/lib/module/internal/Mapper.js.map +1 -1
  24. package/lib/module/internal/NoCodesInternal.js +35 -1
  25. package/lib/module/internal/NoCodesInternal.js.map +1 -1
  26. package/lib/module/internal/QonversionInternal.js +25 -1
  27. package/lib/module/internal/QonversionInternal.js.map +1 -1
  28. package/lib/module/internal/specs/NativeNoCodesModule.js.map +1 -1
  29. package/lib/module/internal/specs/NativeQonversionModule.js.map +1 -1
  30. package/lib/typescript/src/NoCodesApi.d.ts +8 -0
  31. package/lib/typescript/src/NoCodesApi.d.ts.map +1 -1
  32. package/lib/typescript/src/NoCodesConfig.d.ts +4 -1
  33. package/lib/typescript/src/NoCodesConfig.d.ts.map +1 -1
  34. package/lib/typescript/src/NoCodesConfigBuilder.d.ts +19 -0
  35. package/lib/typescript/src/NoCodesConfigBuilder.d.ts.map +1 -1
  36. package/lib/typescript/src/QonversionApi.d.ts +15 -2
  37. package/lib/typescript/src/QonversionApi.d.ts.map +1 -1
  38. package/lib/typescript/src/dto/PurchaseDelegate.d.ts +19 -0
  39. package/lib/typescript/src/dto/PurchaseDelegate.d.ts.map +1 -0
  40. package/lib/typescript/src/dto/PurchaseResult.d.ts +55 -0
  41. package/lib/typescript/src/dto/PurchaseResult.d.ts.map +1 -0
  42. package/lib/typescript/src/dto/StoreTransaction.d.ts +38 -0
  43. package/lib/typescript/src/dto/StoreTransaction.d.ts.map +1 -0
  44. package/lib/typescript/src/dto/enums.d.ts +34 -0
  45. package/lib/typescript/src/dto/enums.d.ts.map +1 -1
  46. package/lib/typescript/src/index.d.ts +4 -0
  47. package/lib/typescript/src/index.d.ts.map +1 -1
  48. package/lib/typescript/src/internal/Mapper.d.ts +24 -1
  49. package/lib/typescript/src/internal/Mapper.d.ts.map +1 -1
  50. package/lib/typescript/src/internal/NoCodesInternal.d.ts +6 -0
  51. package/lib/typescript/src/internal/NoCodesInternal.d.ts.map +1 -1
  52. package/lib/typescript/src/internal/QonversionInternal.d.ts +3 -1
  53. package/lib/typescript/src/internal/QonversionInternal.d.ts.map +1 -1
  54. package/lib/typescript/src/internal/specs/NativeNoCodesModule.d.ts +9 -1
  55. package/lib/typescript/src/internal/specs/NativeNoCodesModule.d.ts.map +1 -1
  56. package/lib/typescript/src/internal/specs/NativeQonversionModule.d.ts +7 -1
  57. package/lib/typescript/src/internal/specs/NativeQonversionModule.d.ts.map +1 -1
  58. package/package.json +1 -1
  59. package/qonversion-react-native-sdk.podspec +1 -1
  60. package/src/NoCodesApi.ts +9 -0
  61. package/src/NoCodesConfig.ts +8 -1
  62. package/src/NoCodesConfigBuilder.ts +30 -1
  63. package/src/QonversionApi.ts +16 -2
  64. package/src/dto/PurchaseDelegate.ts +21 -0
  65. package/src/dto/PurchaseResult.ts +88 -0
  66. package/src/dto/StoreTransaction.ts +61 -0
  67. package/src/dto/enums.ts +40 -0
  68. package/src/index.ts +4 -0
  69. package/src/internal/Mapper.ts +94 -0
  70. package/src/internal/NoCodesInternal.ts +44 -3
  71. package/src/internal/QonversionInternal.ts +50 -2
  72. package/src/internal/specs/NativeNoCodesModule.ts +11 -1
  73. package/src/internal/specs/NativeQonversionModule.ts +12 -2
@@ -0,0 +1,21 @@
1
+ import Product from './Product';
2
+
3
+ /**
4
+ * Delegate responsible for custom purchase and restore handling.
5
+ * When this delegate is provided, it replaces the default Qonversion SDK purchase flow.
6
+ */
7
+ export interface PurchaseDelegate {
8
+ /**
9
+ * Handle purchase for the given product.
10
+ * @param product Product to purchase.
11
+ * @returns Promise that completes successfully when purchase finishes, otherwise throws an error.
12
+ */
13
+ purchase(product: Product): Promise<void>;
14
+
15
+ /**
16
+ * Handle restore flow.
17
+ * @returns Promise that completes successfully when restore finishes, otherwise throws an error.
18
+ */
19
+ restore(): Promise<void>;
20
+ }
21
+
@@ -0,0 +1,88 @@
1
+ import Entitlement from './Entitlement';
2
+ import QonversionError from './QonversionError';
3
+ import StoreTransaction from './StoreTransaction';
4
+ import { PurchaseResultStatus, PurchaseResultSource } from './enums';
5
+
6
+ /**
7
+ * Represents the result of a purchase operation.
8
+ * Contains the status of the purchase, entitlements, and store transaction details.
9
+ */
10
+ class PurchaseResult {
11
+ /**
12
+ * The status of the purchase operation.
13
+ */
14
+ status: PurchaseResultStatus;
15
+
16
+ /**
17
+ * The user's entitlements after the purchase.
18
+ * May be null if the purchase failed or is pending.
19
+ */
20
+ entitlements: Map<string, Entitlement> | null;
21
+
22
+ /**
23
+ * The error that occurred during the purchase, if any.
24
+ */
25
+ error: QonversionError | null;
26
+
27
+ /**
28
+ * Indicates whether the entitlements were generated from a fallback source.
29
+ */
30
+ isFallbackGenerated: boolean;
31
+
32
+ /**
33
+ * The source of the purchase result data.
34
+ */
35
+ source: PurchaseResultSource;
36
+
37
+ /**
38
+ * The store transaction details from the native platform.
39
+ * Contains raw transaction information from Apple App Store or Google Play Store.
40
+ */
41
+ storeTransaction: StoreTransaction | null;
42
+
43
+ constructor(
44
+ status: PurchaseResultStatus,
45
+ entitlements: Map<string, Entitlement> | null,
46
+ error: QonversionError | null,
47
+ isFallbackGenerated: boolean,
48
+ source: PurchaseResultSource,
49
+ storeTransaction: StoreTransaction | null,
50
+ ) {
51
+ this.status = status;
52
+ this.entitlements = entitlements;
53
+ this.error = error;
54
+ this.isFallbackGenerated = isFallbackGenerated;
55
+ this.source = source;
56
+ this.storeTransaction = storeTransaction;
57
+ }
58
+
59
+ /**
60
+ * Returns true if the purchase was successful.
61
+ */
62
+ get isSuccess(): boolean {
63
+ return this.status === PurchaseResultStatus.SUCCESS;
64
+ }
65
+
66
+ /**
67
+ * Returns true if the purchase was canceled by the user.
68
+ */
69
+ get isCanceled(): boolean {
70
+ return this.status === PurchaseResultStatus.USER_CANCELED;
71
+ }
72
+
73
+ /**
74
+ * Returns true if the purchase is pending.
75
+ */
76
+ get isPending(): boolean {
77
+ return this.status === PurchaseResultStatus.PENDING;
78
+ }
79
+
80
+ /**
81
+ * Returns true if an error occurred during the purchase.
82
+ */
83
+ get isError(): boolean {
84
+ return this.status === PurchaseResultStatus.ERROR;
85
+ }
86
+ }
87
+
88
+ export default PurchaseResult;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Represents a raw store transaction from the native platform.
3
+ * This is the transaction information as received from Apple App Store or Google Play Store.
4
+ */
5
+ class StoreTransaction {
6
+ /**
7
+ * The unique identifier for this transaction.
8
+ */
9
+ transactionId?: string;
10
+
11
+ /**
12
+ * The original transaction identifier.
13
+ * For subscriptions, this identifies the first transaction in the subscription chain.
14
+ */
15
+ originalTransactionId?: string;
16
+
17
+ /**
18
+ * The date and time when the transaction occurred.
19
+ */
20
+ transactionDate?: Date;
21
+
22
+ /**
23
+ * The store product identifier associated with this transaction.
24
+ */
25
+ productId?: string;
26
+
27
+ /**
28
+ * The quantity of items purchased.
29
+ */
30
+ quantity: number;
31
+
32
+ /**
33
+ * iOS only. The identifier of the promotional offer applied to this purchase.
34
+ */
35
+ promoOfferId?: string;
36
+
37
+ /**
38
+ * Android only. The purchase token from Google Play.
39
+ */
40
+ purchaseToken?: string;
41
+
42
+ constructor(
43
+ transactionId: string | undefined,
44
+ originalTransactionId: string | undefined,
45
+ transactionTimestamp: number | undefined,
46
+ productId: string | undefined,
47
+ quantity: number | undefined,
48
+ promoOfferId: string | undefined,
49
+ purchaseToken: string | undefined,
50
+ ) {
51
+ this.transactionId = transactionId;
52
+ this.originalTransactionId = originalTransactionId;
53
+ this.transactionDate = transactionTimestamp ? new Date(transactionTimestamp) : undefined;
54
+ this.productId = productId;
55
+ this.quantity = quantity ?? 1;
56
+ this.promoOfferId = promoOfferId;
57
+ this.purchaseToken = purchaseToken;
58
+ }
59
+ }
60
+
61
+ export default StoreTransaction;
package/src/dto/enums.ts CHANGED
@@ -388,3 +388,43 @@ export enum NoCodesErrorCode {
388
388
  SCREEN_LOADING_FAILED = "ScreenLoadingFailed", // iOS
389
389
  SDK_INITIALIZATION_ERROR = "SDKInitializationError" // iOS
390
390
  }
391
+
392
+ /**
393
+ * Status of the purchase result.
394
+ */
395
+ export enum PurchaseResultStatus {
396
+ /**
397
+ * The purchase was successful.
398
+ */
399
+ SUCCESS = "Success",
400
+
401
+ /**
402
+ * The purchase was canceled by the user.
403
+ */
404
+ USER_CANCELED = "UserCanceled",
405
+
406
+ /**
407
+ * The purchase is pending (e.g., waiting for parental approval).
408
+ */
409
+ PENDING = "Pending",
410
+
411
+ /**
412
+ * An error occurred during the purchase.
413
+ */
414
+ ERROR = "Error",
415
+ }
416
+
417
+ /**
418
+ * Source of the purchase result data.
419
+ */
420
+ export enum PurchaseResultSource {
421
+ /**
422
+ * The result was obtained from the Qonversion API.
423
+ */
424
+ API = "Api",
425
+
426
+ /**
427
+ * The result was obtained from the local store.
428
+ */
429
+ LOCAL = "Local",
430
+ }
package/src/index.ts CHANGED
@@ -31,6 +31,8 @@ export { default as SKProductDiscount } from './dto/storeProducts/SKProductDisco
31
31
  export { default as SKSubscriptionPeriod } from './dto/storeProducts/SKSubscriptionPeriod';
32
32
  export { default as PurchaseOptionsBuilder } from './dto/PurchaseOptionsBuilder';
33
33
  export { default as PurchaseOptions } from './dto/PurchaseOptionsBuilder';
34
+ export { default as PurchaseResult } from './dto/PurchaseResult';
35
+ export { default as StoreTransaction } from './dto/StoreTransaction';
34
36
 
35
37
  // NoCode exports
36
38
  export { default as ScreenPresentationConfig } from './dto/ScreenPresentationConfig';
@@ -39,4 +41,6 @@ export type { default as NoCodesApi } from './NoCodesApi';
39
41
  export { default as NoCodesConfig } from './NoCodesConfig';
40
42
  export { default as NoCodesConfigBuilder } from './NoCodesConfigBuilder';
41
43
  export type { NoCodesListener } from './dto/NoCodesListener';
44
+ export type { PurchaseDelegate } from './dto/PurchaseDelegate';
42
45
  export { default as NoCodesAction } from './dto/NoCodesAction';
46
+ export { default as NoCodesError } from './dto/NoCodesError';
@@ -8,6 +8,8 @@ import {
8
8
  PricingPhaseRecurrenceMode,
9
9
  PricingPhaseType,
10
10
  ProductType,
11
+ PurchaseResultSource,
12
+ PurchaseResultStatus,
11
13
  QonversionErrorCode,
12
14
  RemoteConfigurationAssignmentType,
13
15
  RemoteConfigurationSourceType,
@@ -52,6 +54,8 @@ import SKPaymentDiscount from '../dto/storeProducts/SKPaymentDiscount';
52
54
  import NoCodesAction from '../dto/NoCodesAction';
53
55
  import QonversionError from '../dto/QonversionError';
54
56
  import NoCodesError from '../dto/NoCodesError';
57
+ import PurchaseResult from '../dto/PurchaseResult';
58
+ import StoreTransaction from '../dto/StoreTransaction';
55
59
 
56
60
  export type QProduct = {
57
61
  id: string;
@@ -298,6 +302,25 @@ export type QNoCodesError = QQonversionError & {
298
302
 
299
303
  export type QNoCodeScreenInfo = { screenId: string };
300
304
 
305
+ export type QStoreTransaction = {
306
+ transactionId?: string | null;
307
+ originalTransactionId?: string | null;
308
+ transactionTimestamp?: number | null;
309
+ productId?: string | null;
310
+ quantity?: number | null;
311
+ promoOfferId?: string | null;
312
+ purchaseToken?: string | null;
313
+ };
314
+
315
+ export type QPurchaseResult = {
316
+ status: string;
317
+ entitlements: Record<string, QEntitlement> | null;
318
+ error: QQonversionError | null | undefined;
319
+ isFallbackGenerated: boolean;
320
+ source: string;
321
+ storeTransaction: QStoreTransaction | null | undefined;
322
+ };
323
+
301
324
  const priceMicrosRatio = 1000000;
302
325
 
303
326
  class Mapper {
@@ -1156,6 +1179,77 @@ class Mapper {
1156
1179
 
1157
1180
  return QonversionErrorCode.UNKNOWN;
1158
1181
  }
1182
+
1183
+ // region PurchaseResult Mappers
1184
+
1185
+ static convertPurchaseResult(
1186
+ purchaseResult: QPurchaseResult | null | undefined
1187
+ ): PurchaseResult | null {
1188
+ if (!purchaseResult) {
1189
+ return null;
1190
+ }
1191
+
1192
+ const status = this.convertPurchaseResultStatus(purchaseResult.status);
1193
+ const entitlements = purchaseResult.entitlements
1194
+ ? this.convertEntitlements(purchaseResult.entitlements)
1195
+ : null;
1196
+ const error = purchaseResult.error ? this.convertQonversionError(purchaseResult.error) : undefined;
1197
+ const source = this.convertPurchaseResultSource(purchaseResult.source);
1198
+ const storeTransaction = this.convertStoreTransaction(purchaseResult.storeTransaction);
1199
+
1200
+ return new PurchaseResult(
1201
+ status,
1202
+ entitlements,
1203
+ error ?? null,
1204
+ purchaseResult.isFallbackGenerated ?? false,
1205
+ source,
1206
+ storeTransaction
1207
+ );
1208
+ }
1209
+
1210
+ static convertPurchaseResultStatus(status: string | null | undefined): PurchaseResultStatus {
1211
+ switch (status) {
1212
+ case "Success":
1213
+ return PurchaseResultStatus.SUCCESS;
1214
+ case "UserCanceled":
1215
+ return PurchaseResultStatus.USER_CANCELED;
1216
+ case "Pending":
1217
+ return PurchaseResultStatus.PENDING;
1218
+ case "Error":
1219
+ default:
1220
+ return PurchaseResultStatus.ERROR;
1221
+ }
1222
+ }
1223
+
1224
+ static convertPurchaseResultSource(source: string | null | undefined): PurchaseResultSource {
1225
+ switch (source) {
1226
+ case "Local":
1227
+ return PurchaseResultSource.LOCAL;
1228
+ case "Api":
1229
+ default:
1230
+ return PurchaseResultSource.API;
1231
+ }
1232
+ }
1233
+
1234
+ static convertStoreTransaction(
1235
+ storeTransaction: QStoreTransaction | null | undefined
1236
+ ): StoreTransaction | null {
1237
+ if (!storeTransaction) {
1238
+ return null;
1239
+ }
1240
+
1241
+ return new StoreTransaction(
1242
+ storeTransaction.transactionId ?? undefined,
1243
+ storeTransaction.originalTransactionId ?? undefined,
1244
+ storeTransaction.transactionTimestamp ?? undefined,
1245
+ storeTransaction.productId ?? undefined,
1246
+ storeTransaction.quantity ?? undefined,
1247
+ storeTransaction.promoOfferId ?? undefined,
1248
+ storeTransaction.purchaseToken ?? undefined
1249
+ );
1250
+ }
1251
+
1252
+ // endregion
1159
1253
  }
1160
1254
 
1161
1255
  export default Mapper;
@@ -1,12 +1,14 @@
1
1
  import type NoCodesApi from "../NoCodesApi";
2
2
  import NoCodesConfig from "../NoCodesConfig";
3
- import Mapper, { type QNoCodeAction, type QNoCodesError, type QNoCodeScreenInfo } from "./Mapper";
3
+ import Mapper, { type QNoCodeAction, type QNoCodesError, type QNoCodeScreenInfo, type QProduct } from "./Mapper";
4
4
  import type {NoCodesListener} from '../dto/NoCodesListener';
5
+ import type {PurchaseDelegate} from '../dto/PurchaseDelegate';
5
6
  import ScreenPresentationConfig from '../dto/ScreenPresentationConfig';
6
7
  import NoCodesError from '../dto/NoCodesError';
7
8
  import {NoCodesErrorCode} from '../dto/enums';
8
9
  import RNNoCodes, {type NoCodeEvent} from './specs/NativeNoCodesModule';
9
10
  import {sdkSource, sdkVersion} from './QonversionInternal';
11
+ import Product from '../dto/Product';
10
12
 
11
13
  const EVENT_SCREEN_SHOWN = "nocodes_screen_shown";
12
14
  const EVENT_FINISHED = "nocodes_finished";
@@ -17,13 +19,18 @@ const EVENT_SCREEN_FAILED_TO_LOAD = "nocodes_screen_failed_to_load";
17
19
 
18
20
  export default class NoCodesInternal implements NoCodesApi {
19
21
  private noCodesListener: NoCodesListener | null = null;
22
+ private purchaseDelegate: PurchaseDelegate | null = null;
20
23
 
21
24
  constructor(config: NoCodesConfig) {
22
- RNNoCodes.initialize(config.projectKey, sdkSource, sdkVersion, config.proxyUrl);
25
+ RNNoCodes.initialize(config.projectKey, sdkSource, sdkVersion, config.proxyUrl, config.locale);
23
26
 
24
27
  if (config.noCodesListener) {
25
28
  this.setNoCodesListener(config.noCodesListener);
26
29
  }
30
+
31
+ if (config.purchaseDelegate) {
32
+ this.setPurchaseDelegate(config.purchaseDelegate);
33
+ }
27
34
  }
28
35
 
29
36
  async setScreenPresentationConfig(config: ScreenPresentationConfig, contextKey?: string) {
@@ -44,7 +51,7 @@ export default class NoCodesInternal implements NoCodesApi {
44
51
  case EVENT_SCREEN_SHOWN:
45
52
  const screenId = (event.payload as QNoCodeScreenInfo)["screenId"] ?? "";
46
53
  this.noCodesListener?.onScreenShown(screenId);
47
- break;
54
+ break;
48
55
  case EVENT_ACTION_STARTED:
49
56
  const actionStarted = Mapper.convertAction(event.payload as QNoCodeAction);
50
57
  this.noCodesListener?.onActionStartedExecuting(actionStarted);
@@ -75,10 +82,44 @@ export default class NoCodesInternal implements NoCodesApi {
75
82
  }
76
83
  }
77
84
 
85
+ private customPurchaseHandler = async (productData: Object) => {
86
+ if (!this.purchaseDelegate) {
87
+ throw new Error('PurchaseDelegate is not set but custom purchase handling initiated');
88
+ }
89
+ const product: Product = Mapper.convertProduct(productData as QProduct);
90
+ this.purchaseDelegate.purchase(product).then(() => {
91
+ RNNoCodes.delegatedPurchaseCompleted();
92
+ }).catch((error: Error) => {
93
+ RNNoCodes.delegatedPurchaseFailed(error.message);
94
+ });
95
+ }
96
+
97
+ private customRestoreHandler = async () => {
98
+ if (!this.purchaseDelegate) {
99
+ throw new Error('PurchaseDelegate is not set but custom restore handling initiated');
100
+ }
101
+ this.purchaseDelegate.restore().then(() => {
102
+ RNNoCodes.delegatedRestoreCompleted();
103
+ }).catch((error: Error) => {
104
+ RNNoCodes.delegatedRestoreFailed(error.message);
105
+ });
106
+ }
107
+
78
108
  setNoCodesListener(listener: NoCodesListener) {
79
109
  if (this.noCodesListener == null) {
80
110
  RNNoCodes.onNoCodeEvent(this.noCodeEventHandler);
81
111
  }
82
112
  this.noCodesListener = listener;
83
113
  }
114
+
115
+ setPurchaseDelegate(delegate: PurchaseDelegate) {
116
+ this.purchaseDelegate = delegate;
117
+ RNNoCodes.onNoCodePurchase(this.customPurchaseHandler);
118
+ RNNoCodes.onNoCodeRestore(this.customRestoreHandler);
119
+ RNNoCodes.setPurchaseDelegate();
120
+ }
121
+
122
+ setLocale(locale: string | null) {
123
+ RNNoCodes.setLocale(locale);
124
+ }
84
125
  }
@@ -1,10 +1,11 @@
1
1
  import {AttributionProvider, QonversionErrorCode, UserPropertyKey} from "../dto/enums";
2
2
  import IntroEligibility from "../dto/IntroEligibility";
3
3
  import Mapper from "./Mapper";
4
- import type {QEntitlement, QEligibilityInfo, QProduct} from "./Mapper";
4
+ import type {QEntitlement, QEligibilityInfo, QProduct, QPurchaseResult} from "./Mapper";
5
5
  import Offerings from "../dto/Offerings";
6
6
  import Entitlement from "../dto/Entitlement";
7
7
  import Product from "../dto/Product";
8
+ import PurchaseResult from "../dto/PurchaseResult";
8
9
  import {isAndroid, isIos} from "./utils";
9
10
  import type {EntitlementsUpdateListener} from '../dto/EntitlementsUpdateListener';
10
11
  import type {PromoPurchasesListener} from '../dto/PromoPurchasesListener';
@@ -23,7 +24,7 @@ import PromotionalOffer from '../dto/PromotionalOffer';
23
24
  import RNQonversion from './specs/NativeQonversionModule';
24
25
  import type { QPromoOfferDetails } from './specs/NativeQonversionModule';
25
26
 
26
- export const sdkVersion = "10.0.1";
27
+ export const sdkVersion = "10.1.0";
27
28
  export const sdkSource = "rn";
28
29
 
29
30
  export default class QonversionInternal implements QonversionApi {
@@ -73,6 +74,53 @@ export default class QonversionInternal implements QonversionApi {
73
74
  return mappedPromoOffer;
74
75
  }
75
76
 
77
+ async purchaseWithResult(product: Product, options: PurchaseOptions | undefined): Promise<PurchaseResult> {
78
+ if (!options) {
79
+ options = new PurchaseOptionsBuilder().build();
80
+ }
81
+
82
+ const promoOffer: QPromoOfferDetails = {
83
+ productDiscountId: options.promotionalOffer?.productDiscount.identifier,
84
+ keyIdentifier: options.promotionalOffer?.paymentDiscount.keyIdentifier,
85
+ nonce: options.promotionalOffer?.paymentDiscount.nonce,
86
+ signature: options.promotionalOffer?.paymentDiscount.signature,
87
+ timestamp: options.promotionalOffer?.paymentDiscount.timestamp
88
+ };
89
+
90
+ let purchaseResultPromise: Promise<QPurchaseResult | null | undefined>;
91
+ if (isIos()) {
92
+ purchaseResultPromise = RNQonversion.purchaseWithResult(
93
+ product.qonversionId,
94
+ options.quantity,
95
+ options.contextKeys,
96
+ promoOffer,
97
+ undefined,
98
+ false,
99
+ undefined,
100
+ undefined,
101
+ );
102
+ } else {
103
+ purchaseResultPromise = RNQonversion.purchaseWithResult(
104
+ product.qonversionId,
105
+ 1,
106
+ options.contextKeys,
107
+ undefined,
108
+ options.offerId,
109
+ options.applyOffer,
110
+ options.oldProduct?.qonversionId,
111
+ options.updatePolicy,
112
+ );
113
+ }
114
+ const purchaseResult = await purchaseResultPromise;
115
+ const mappedResult = Mapper.convertPurchaseResult(purchaseResult);
116
+
117
+ if (!mappedResult) {
118
+ throw new Error("Failed to parse PurchaseResult");
119
+ }
120
+
121
+ return mappedResult;
122
+ }
123
+
76
124
  async purchaseProduct(product: Product, options: PurchaseOptions | undefined): Promise<Map<string, Entitlement>> {
77
125
  try {
78
126
  if (!options) {
@@ -9,12 +9,22 @@ export type NoCodeEvent = {
9
9
  };
10
10
 
11
11
  export interface Spec extends TurboModule {
12
- initialize(projectKey: string, source: string, version: string, proxyUrl?: string): void;
12
+ initialize(projectKey: string, source: string, version: string, proxyUrl?: string, locale?: string): void;
13
13
  setScreenPresentationConfig(configData: Object, contextKey?: string): Promise<boolean>;
14
14
  showScreen(contextKey: string): Promise<boolean>;
15
15
  close(): Promise<boolean>;
16
+ setPurchaseDelegate(): void;
17
+ setLocale(locale: string | null): void;
18
+
19
+ // Methods to notify native code about purchase/restore results
20
+ delegatedPurchaseCompleted(): void;
21
+ delegatedPurchaseFailed(errorMessage: string): void;
22
+ delegatedRestoreCompleted(): void;
23
+ delegatedRestoreFailed(errorMessage: string): void;
16
24
 
17
25
  readonly onNoCodeEvent: EventEmitter<NoCodeEvent>;
26
+ readonly onNoCodePurchase: EventEmitter<Object>; // QProduct
27
+ readonly onNoCodeRestore: EventEmitter<void>;
18
28
  }
19
29
 
20
30
  export default TurboModuleRegistry.getEnforcing<Spec>('RNNoCodes');
@@ -1,6 +1,6 @@
1
1
  import type {TurboModule} from 'react-native';
2
2
  import {TurboModuleRegistry} from 'react-native';
3
- import type { QPromotionalOffer, QOfferings, QUser, QUserProperties, QRemoteConfig, QRemoteConfigList } from '../Mapper';
3
+ import type { QPromotionalOffer, QOfferings, QUser, QUserProperties, QRemoteConfig, QRemoteConfigList, QPurchaseResult } from '../Mapper';
4
4
  import type { EventEmitter } from 'react-native/Libraries/Types/CodegenTypes';
5
5
 
6
6
  export type QPromoOfferDetails = {
@@ -34,7 +34,17 @@ export interface Spec extends TurboModule {
34
34
  applyOffer: boolean, // Android only
35
35
  oldProductId: string | null | undefined, // Android only
36
36
  updatePolicyKey: string | null | undefined, // Android only
37
- ): Promise<Object | null | undefined>; // Record<string, QEntitlement
37
+ ): Promise<Object | null | undefined>; // Record<string, QEntitlement>
38
+ purchaseWithResult(
39
+ productId: string,
40
+ quantity: number, // iOS only
41
+ contextKeys: string[] | null | undefined,
42
+ promoOffer: QPromoOfferDetails | undefined, // iOS only
43
+ offerId: string | null | undefined, // Android only
44
+ applyOffer: boolean, // Android only
45
+ oldProductId: string | null | undefined, // Android only
46
+ updatePolicyKey: string | null | undefined, // Android only
47
+ ): Promise<QPurchaseResult | null | undefined>;
38
48
  updatePurchase(
39
49
  productId: string,
40
50
  offerId: string | null | undefined,