react-native-nami-sdk 2.0.4 → 3.0.8
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.
- package/.github/workflows/app_stg.yaml +203 -0
- package/.github/workflows/build.yml +2 -2
- package/.pre-commit-config.yaml +25 -0
- package/android/build.gradle +24 -13
- package/android/gradle/wrapper/gradle-wrapper.properties +5 -1
- package/android/src/main/java/com/nami/reactlibrary/Constants.kt +1 -1
- package/android/src/main/java/com/nami/reactlibrary/NamiBridgeModule.kt +6 -50
- package/android/src/main/java/com/nami/reactlibrary/NamiBridgePackage.java +1 -3
- package/android/src/main/java/com/nami/reactlibrary/NamiCampaignManagerBridge.kt +133 -0
- package/android/src/main/java/com/nami/reactlibrary/NamiCustomerManagerBridge.kt +89 -20
- package/android/src/main/java/com/nami/reactlibrary/NamiEmitter.kt +25 -25
- package/android/src/main/java/com/nami/reactlibrary/NamiEntitlementManagerBridgeModule.kt +31 -130
- package/android/src/main/java/com/nami/reactlibrary/NamiMLManagerBridgeModule.kt +1 -1
- package/android/src/main/java/com/nami/reactlibrary/NamiPaywallManagerBridgeModule.kt +36 -147
- package/android/src/main/java/com/nami/reactlibrary/NamiPurchaseManagerBridge.kt +37 -39
- package/android/src/main/java/com/nami/reactlibrary/NamiUtil.kt +50 -180
- package/build-utils/get_version_code.py +140 -0
- package/index.d.ts +20 -0
- package/index.js +7 -6
- package/ios/Nami.m +18 -72
- package/ios/NamiBridgeUtil.h +4 -6
- package/ios/NamiBridgeUtil.m +37 -99
- package/ios/NamiCampaignManagerBridge.m +26 -0
- package/ios/NamiCampaignManagerBridge.swift +107 -0
- package/ios/NamiCustomerManager.m +19 -24
- package/ios/NamiCustomerManager.swift +122 -0
- package/ios/NamiEmitter.m +85 -95
- package/ios/NamiEntitlementManagerBridge.m +7 -107
- package/ios/NamiEntitlementManagerBridge.swift +74 -0
- package/ios/NamiMLManagerBridge.m +2 -2
- package/ios/NamiPaywallManagerBridge.m +22 -94
- package/ios/NamiPaywallManagerBridge.swift +93 -0
- package/ios/NamiPurchaseManagerBridge.m +40 -71
- package/ios/NamiPurchaseManagerBridge.swift +174 -0
- package/ios/Podfile +2 -2
- package/ios/RNNami-Bridging-Header.h +5 -0
- package/ios/RNNami.h +0 -1
- package/ios/RNNami.m +1 -1
- package/ios/RNNami.xcodeproj/project.pbxproj +84 -8
- package/ios/RNNami.xcodeproj/xcshareddata/xcschemes/RNNami.xcscheme +67 -0
- package/package.json +1 -1
- package/react-native-nami-sdk.podspec +3 -3
- package/src/Nami.d.ts +112 -0
- package/src/Nami.js +10 -0
- package/src/NamiCampaignManager.d.ts +54 -0
- package/src/NamiCampaignManager.js +43 -0
- package/src/NamiCustomerManager.d.ts +39 -0
- package/src/NamiCustomerManager.js +43 -0
- package/src/NamiEntitlementManager.d.ts +24 -0
- package/src/NamiEntitlementManager.js +23 -0
- package/src/NamiMLManager.d.ts +5 -0
- package/src/NamiMLManager.js +7 -0
- package/src/NamiPaywallManager.d.ts +57 -0
- package/src/NamiPaywallManager.js +28 -0
- package/src/NamiPurchaseManager.d.ts +57 -0
- package/src/NamiPurchaseManager.js +37 -0
- package/src/types.ts +36 -0
- package/android/src/main/java/com/nami/reactlibrary/NamiAnalyticsEmitter.kt +0 -121
- package/ios/NamiAnalyticsEmitter.m +0 -146
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
// RNNami
|
|
4
4
|
//
|
|
5
5
|
// Created by Kendall Gelner on 4/6/20.
|
|
6
|
-
// Copyright © 2020
|
|
6
|
+
// Copyright © 2020-2023 Nami ML Inc. All rights reserved.
|
|
7
7
|
//
|
|
8
8
|
#import <Foundation/Foundation.h>
|
|
9
|
-
#import <
|
|
9
|
+
#import <NamiApple/NamiApple.h>
|
|
10
10
|
#import "NamiBridgeUtil.h"
|
|
11
11
|
|
|
12
12
|
#import <React/RCTBridgeModule.h>
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
//
|
|
8
8
|
|
|
9
9
|
#import <Foundation/Foundation.h>
|
|
10
|
-
#import <
|
|
10
|
+
#import <NamiApple/NamiApple.h>
|
|
11
11
|
#import "NamiBridgeUtil.h"
|
|
12
12
|
|
|
13
13
|
#import <React/RCTBridgeModule.h>
|
|
@@ -15,6 +15,24 @@
|
|
|
15
15
|
|
|
16
16
|
#import "React/RCTViewManager.h"
|
|
17
17
|
|
|
18
|
+
@interface RCT_EXTERN_MODULE(RNNamiPaywallManager, NSObject)
|
|
19
|
+
|
|
20
|
+
RCT_EXTERN_METHOD(buySkuComplete:(NSDictionary)dict)
|
|
21
|
+
|
|
22
|
+
RCT_EXTERN_METHOD(registerBuySkuHandler)
|
|
23
|
+
|
|
24
|
+
RCT_EXTERN_METHOD(registerCloseHandler:(BOOL)blockDismiss)
|
|
25
|
+
|
|
26
|
+
RCT_EXTERN_METHOD(dismiss:(BOOL)animated completion:(RCTResponseSenderBlock)callback)
|
|
27
|
+
|
|
28
|
+
RCT_EXTERN_METHOD(displayedViewController)
|
|
29
|
+
|
|
30
|
+
+ (BOOL)requiresMainQueueSetup {
|
|
31
|
+
return YES;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@end
|
|
35
|
+
|
|
18
36
|
@interface NamiEmitter : RCTEventEmitter
|
|
19
37
|
- (void)sendEventPreparePaywallForDisplayFinishedWithResult:(BOOL)success developerPaywallID: (NSString * _Nullable) developerPaywallID error:(NSError * _Nullable) error;
|
|
20
38
|
+ (NamiEmitter *) reactInstance;
|
|
@@ -32,22 +50,18 @@
|
|
|
32
50
|
self = [super init];
|
|
33
51
|
if (self) {
|
|
34
52
|
[self setBlockPaywallRaise:false];
|
|
35
|
-
[NamiPaywallManager registerAllowAutoRaisePaywallHandler:^BOOL{
|
|
36
|
-
NSLog(@"Block paywall raise set to %d", [self blockPaywallRaise]);
|
|
37
|
-
return ![self blockPaywallRaise];
|
|
38
|
-
}];
|
|
39
53
|
}
|
|
40
54
|
return self;
|
|
41
55
|
}
|
|
42
56
|
|
|
43
57
|
RCT_EXTERN_METHOD(raisePaywall)
|
|
44
58
|
- (void)raisePaywall {
|
|
45
|
-
|
|
59
|
+
[NamiCampaignManager launch];
|
|
46
60
|
}
|
|
47
61
|
|
|
48
|
-
RCT_EXPORT_METHOD(raisePaywallByDeveloperPaywallId
|
|
62
|
+
RCT_EXPORT_METHOD(raisePaywallByDeveloperPaywallId)
|
|
49
63
|
{
|
|
50
|
-
[
|
|
64
|
+
[NamiCampaignManager launch];
|
|
51
65
|
}
|
|
52
66
|
|
|
53
67
|
RCT_EXPORT_METHOD(blockPaywallRaise:(BOOL)blockRaise)
|
|
@@ -55,92 +69,6 @@ RCT_EXPORT_METHOD(blockPaywallRaise:(BOOL)blockRaise)
|
|
|
55
69
|
[self setBlockPaywallRaise:blockRaise];
|
|
56
70
|
}
|
|
57
71
|
|
|
58
|
-
RCT_EXPORT_METHOD(canRaisePaywall:(RCTResponseSenderBlock)completion)
|
|
59
|
-
{
|
|
60
|
-
BOOL canRaise = [NamiPaywallManager canRaisePaywall];
|
|
61
|
-
completion(@[[NSNumber numberWithBool:canRaise]]);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
RCT_EXPORT_METHOD(presentNamiPaywall:(NSArray *)skuIDs metapaywallDefinition:(NSDictionary *)paywallDict)
|
|
66
|
-
{
|
|
67
|
-
NSString *paywallDeveloperID = paywallDict[@"developerPaywallID"];
|
|
68
|
-
if ( paywallDeveloperID != nil ) {
|
|
69
|
-
[NamiPaywallManager fetchCustomPaywallMetaForDeveloperID:paywallDeveloperID :^(NSArray<NamiSKU *> * _Nullable products, NSString * _Nonnull paywallDevloperID, NamiPaywall * _Nullable namiMetaPaywall) {
|
|
70
|
-
[NamiPaywallManager presentNamiPaywallFromVC:nil products:products paywallMetadata:namiMetaPaywall backgroundImage:namiMetaPaywall.backgroundImage forNami:false];
|
|
71
|
-
}];
|
|
72
|
-
} else {
|
|
73
|
-
// No way to handle this case for now as we cannot cretae a NamiMetaPaywall
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
RCT_EXPORT_METHOD(fetchCustomPaywallMetaForDeveloperID:(NSString *)developerPaywallID completion:(RCTResponseSenderBlock)completion)
|
|
78
|
-
{
|
|
79
|
-
[NamiPaywallManager fetchCustomPaywallMetaForDeveloperID:developerPaywallID :^(NSArray<NamiSKU *> * _Nullable products, NSString * _Nonnull developerPaywallID, NamiPaywall * _Nullable paywallMetadata) {
|
|
80
|
-
NSMutableArray<NSDictionary<NSString *,NSString *> *> *productDicts = [NSMutableArray new];
|
|
81
|
-
for (NamiSKU *product in products) {
|
|
82
|
-
[productDicts addObject:[NamiBridgeUtil skuToSKUDict:product]];
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
NSMutableDictionary *paywallMeta = [NSMutableDictionary dictionaryWithDictionary:paywallMetadata.namiPaywallInfoDict];
|
|
86
|
-
// This part is really meant to be internally facing, scrub from dictionary
|
|
87
|
-
// Strip out presention_position from all listed sku items
|
|
88
|
-
NSArray *cleanedOrderdMetadata = [NamiBridgeUtil stripPresentationPositionFromOrderedMetadataForPaywallMetaDict:paywallMeta];
|
|
89
|
-
[paywallMeta setObject:cleanedOrderdMetadata forKey:@"formatted_skus"];
|
|
90
|
-
|
|
91
|
-
[paywallMeta removeObjectForKey:@"sku_ordered_metadata"];
|
|
92
|
-
[paywallMeta removeObjectForKey:@"skus"];
|
|
93
|
-
|
|
94
|
-
NSDictionary *paywallStylingDict = [NamiBridgeUtil paywallStylingToPaywallStylingDict:[paywallMetadata styleData]];
|
|
95
|
-
paywallMeta[@"styleData"] = paywallStylingDict;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
NSArray *wrapperArray = @[@{ @"namiSkus": productDicts,
|
|
99
|
-
@"developerPaywallID": developerPaywallID,
|
|
100
|
-
@"paywallMetadata": paywallMeta }];
|
|
101
|
-
completion(wrapperArray);
|
|
102
|
-
}];
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
RCT_EXPORT_METHOD(paywallImpression:(NSString *)developerPaywallID)
|
|
107
|
-
{
|
|
108
|
-
[NamiPaywallManager paywallImpressionWithDeveloperID:developerPaywallID];
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
RCT_EXPORT_METHOD( preparePaywallForDisplay:(BOOL)backgroundImageRequired
|
|
112
|
-
imageFetchTimeout:(double)imageFetchTimeout )
|
|
113
|
-
{
|
|
114
|
-
[NamiPaywallManager preparePaywallForDisplayWithBackgroundImageRequired:backgroundImageRequired imageFetchTimeout:imageFetchTimeout prepareHandler:^(BOOL success, NSError * _Nullable error) {
|
|
115
|
-
[[NamiEmitter reactInstance] sendEventPreparePaywallForDisplayFinishedWithResult:success developerPaywallID:nil error:error];
|
|
116
|
-
}];
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
RCT_EXPORT_METHOD(preparePaywallForDisplayByDeveloperPaywallId:(NSString *)developerPaywallID
|
|
120
|
-
backgroundImageRequired: (BOOL)backgroundImageRequired
|
|
121
|
-
imageFetchTimeout:(double)imageFetchTimeout )
|
|
122
|
-
{
|
|
123
|
-
[NamiPaywallManager preparePaywallForDisplayWithDeveloperPaywallID:developerPaywallID backgroundImageRequired:backgroundImageRequired imageFetchTimeout:imageFetchTimeout prepareHandler:^(BOOL success, NSError * _Nullable error) {
|
|
124
|
-
[[NamiEmitter reactInstance] sendEventPreparePaywallForDisplayFinishedWithResult:success developerPaywallID:developerPaywallID error:error];
|
|
125
|
-
}];
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
RCT_EXPORT_METHOD(processSmartTextForProducts:(NSString *)smartText skuIDs:(NSArray<NSString *> *)skuIDs completion:(RCTResponseSenderBlock)completion)
|
|
130
|
-
{
|
|
131
|
-
[NamiPurchaseManager skusForSKUIDsWithSkuIDs:skuIDs productHandler:^(BOOL success, NSArray<NamiSKU *> * _Nullable skus, NSArray<NSString *> * _Nullable invalidSkuIDs, NSError * _Nullable error) {
|
|
132
|
-
if (skus != NULL) {
|
|
133
|
-
// Found some of the skus they were looking for, process text
|
|
134
|
-
NSString *processedText = [NamiPaywallManager processSmartTextWithText:smartText dataStores:skus];
|
|
135
|
-
completion(@[processedText]);
|
|
136
|
-
} else {
|
|
137
|
-
// No products found so cannot process smart text, just send back.
|
|
138
|
-
completion(@[smartText]);
|
|
139
|
-
}
|
|
140
|
-
}];
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
|
|
144
72
|
@end
|
|
145
73
|
|
|
146
74
|
@implementation NamiPaywallManagerBridge
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
//
|
|
2
|
+
// NamiPaywallManagerBridge.swift
|
|
3
|
+
// RNNami
|
|
4
|
+
//
|
|
5
|
+
// Created by macbook on 07.04.2023.
|
|
6
|
+
// Copyright © 2023 Nami ML INc.. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
import Foundation
|
|
10
|
+
import NamiApple
|
|
11
|
+
import React
|
|
12
|
+
|
|
13
|
+
@objc(RNNamiPaywallManager)
|
|
14
|
+
class RNNamiPaywallManager: RCTEventEmitter {
|
|
15
|
+
override func supportedEvents() -> [String]! {
|
|
16
|
+
return ["RegisterBuySKU", "BlockingPaywallClosed"]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@objc(buySkuComplete:)
|
|
20
|
+
func buySkuComplete(dict: NSDictionary) {
|
|
21
|
+
guard let jsonData = try? JSONSerialization.data(withJSONObject: dict["product"] as Any, options: []) else {
|
|
22
|
+
print("Error converting dictionary to JSON data")
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
do {
|
|
26
|
+
let decoder = JSONDecoder()
|
|
27
|
+
let product = try decoder.decode(NamiSKU.self, from: jsonData)
|
|
28
|
+
|
|
29
|
+
let dateFormatter = DateFormatter()
|
|
30
|
+
dateFormatter.locale = .init(identifier: "en_US_POSIX")
|
|
31
|
+
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
|
|
32
|
+
|
|
33
|
+
if
|
|
34
|
+
let transactionID = dict["transactionID"] as? String,
|
|
35
|
+
let originalTransactionID = dict["originalTransactionID"] as? String,
|
|
36
|
+
let priceDecimal = Decimal(string: dict["price"] as! String),
|
|
37
|
+
let currencyCode = dict["currencyCode"] as? String,
|
|
38
|
+
let localeString = dict["locale"] as? String
|
|
39
|
+
{
|
|
40
|
+
let expiresDate = Date(timeIntervalSince1970: dict["purchaseDate"] as! Double? ?? 0)
|
|
41
|
+
let originalPurchaseDate = Date(timeIntervalSince1970: dict["originalPurchaseDate"] as! Double)
|
|
42
|
+
let purchaseDate = Date(timeIntervalSince1970: dict["purchaseDate"] as! Double)
|
|
43
|
+
let locale = Locale(identifier: localeString)
|
|
44
|
+
let purchaseSuccess = NamiPurchaseSuccess(
|
|
45
|
+
product: product,
|
|
46
|
+
transactionID: transactionID,
|
|
47
|
+
originalTransactionID: originalTransactionID,
|
|
48
|
+
originalPurchaseDate: originalPurchaseDate,
|
|
49
|
+
purchaseDate: purchaseDate,
|
|
50
|
+
expiresDate: expiresDate,
|
|
51
|
+
price: priceDecimal,
|
|
52
|
+
currencyCode: currencyCode,
|
|
53
|
+
locale: locale
|
|
54
|
+
)
|
|
55
|
+
NamiPaywallManager.buySkuComplete(purchaseSuccess: purchaseSuccess)
|
|
56
|
+
}
|
|
57
|
+
} catch {
|
|
58
|
+
print("Error decoding JSON: \(error)")
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@objc(registerBuySkuHandler)
|
|
63
|
+
func registerBuySkuHandler() {
|
|
64
|
+
NamiPaywallManager.registerBuySkuHandler { sku in
|
|
65
|
+
let dictionary = RNNamiPurchaseManager.skuToSKUDict(sku)
|
|
66
|
+
self.sendEvent(withName: "RegisterBuySKU", body: dictionary)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@objc(registerCloseHandler:)
|
|
71
|
+
func registerCloseHandler(blockDismiss: Bool) {
|
|
72
|
+
NamiPaywallManager.registerCloseHandler { _ in
|
|
73
|
+
let dictionary = NSDictionary(dictionary: ["blockingPaywallClosed": true].compactMapValues { $0 })
|
|
74
|
+
self.sendEvent(withName: "BlockingPaywallClosed", body: dictionary)
|
|
75
|
+
if (blockDismiss) {
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
NamiPaywallManager.dismiss(animated: true, completion: {})
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@objc(dismiss:callback:)
|
|
83
|
+
func dismiss(animated: Bool, callback: @escaping RCTResponseSenderBlock) {
|
|
84
|
+
NamiPaywallManager.dismiss(animated: animated) {
|
|
85
|
+
callback([])
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@objc(displayedViewController)
|
|
90
|
+
func displayedViewController() {
|
|
91
|
+
_ = NamiPaywallManager.displayedViewController()
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
//
|
|
8
8
|
|
|
9
9
|
#import <Foundation/Foundation.h>
|
|
10
|
-
#import <
|
|
10
|
+
#import <NamiApple/NamiApple.h>
|
|
11
11
|
#import "NamiBridgeUtil.h"
|
|
12
12
|
|
|
13
13
|
#import <React/RCTBridgeModule.h>
|
|
@@ -15,6 +15,26 @@
|
|
|
15
15
|
|
|
16
16
|
#import "React/RCTViewManager.h"
|
|
17
17
|
|
|
18
|
+
@interface RCT_EXTERN_MODULE(RNNamiPurchaseManager, NSObject)
|
|
19
|
+
|
|
20
|
+
RCT_EXTERN_METHOD(allPurchases:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
21
|
+
|
|
22
|
+
RCT_EXTERN_METHOD(skuPurchased:(NSString *)skuId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
23
|
+
|
|
24
|
+
RCT_EXTERN_METHOD(anySkuPurchased:(NSArray*)skuIds resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
25
|
+
|
|
26
|
+
RCT_EXTERN_METHOD(consumePurchasedSku:(NSString *)skuId)
|
|
27
|
+
|
|
28
|
+
RCT_EXTERN_METHOD(registerPurchasesChangedHandler)
|
|
29
|
+
|
|
30
|
+
RCT_EXTERN_METHOD(registerRestorePurchasesHandler)
|
|
31
|
+
|
|
32
|
+
+ (BOOL)requiresMainQueueSetup {
|
|
33
|
+
return YES;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@end
|
|
37
|
+
|
|
18
38
|
@interface NamiEmitter : RCTEventEmitter
|
|
19
39
|
- (void)sendRestorePurchasesStateChanged: (enum NamiRestorePurchasesState) state
|
|
20
40
|
newPurchases: (NSArray<NamiPurchase *> * _Nonnull) newPurchases
|
|
@@ -37,32 +57,11 @@ RCT_EXTERN_METHOD(clearBypassStorePurchases)
|
|
|
37
57
|
[NamiPurchaseManager clearBypassStorePurchases];
|
|
38
58
|
}
|
|
39
59
|
|
|
40
|
-
RCT_EXPORT_METHOD(purchases:(RCTResponseSenderBlock)completion)
|
|
41
|
-
{
|
|
42
|
-
NSArray <NamiPurchase *> *purchases = [NamiPurchaseManager allPurchases];
|
|
43
|
-
NSMutableArray *convertedPurchaseDicts = [NSMutableArray new];
|
|
44
|
-
BOOL anyProductNil = NO;
|
|
45
|
-
for ( NamiPurchase *purchaseRecord in purchases ) {
|
|
46
|
-
if ( purchaseRecord.skuID == nil ) {
|
|
47
|
-
anyProductNil = YES;
|
|
48
|
-
}
|
|
49
|
-
NSDictionary *purchaseDict = [NamiBridgeUtil purchaseToPurchaseDict:purchaseRecord];
|
|
50
|
-
[convertedPurchaseDicts addObject:purchaseDict];
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
completion(@[convertedPurchaseDicts]);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
RCT_EXPORT_METHOD(isSKUIDPurchased:(nonnull NSString*)skuID completion:(RCTResponseSenderBlock)completion)
|
|
57
|
-
{
|
|
58
|
-
BOOL active = [NamiPurchaseManager isSKUIDPurchased:skuID];
|
|
59
|
-
completion(@[[NSNumber numberWithBool:active]]);
|
|
60
|
-
}
|
|
61
60
|
|
|
62
61
|
RCT_EXPORT_METHOD(restorePurchasesWithCompletionHandler:(RCTResponseSenderBlock)completion)
|
|
63
62
|
{
|
|
64
63
|
NSLog(@"NamiBridge: Info: Calling RestorePurchasesWithCompletionHandler");
|
|
65
|
-
|
|
64
|
+
|
|
66
65
|
[NamiPurchaseManager restorePurchasesWithStatehandler:^(enum NamiRestorePurchasesState state, NSArray<NamiPurchase *> * _Nonnull newPurchases, NSArray<NamiPurchase *> * _Nonnull oldPurchases, NSError * _Nullable error) {
|
|
67
66
|
NSDictionary *retDict = [[NamiEmitter reactInstance] buildRestorePurchasesStateChangedDict:state newPurchases:newPurchases oldPurchases:oldPurchases error:error];
|
|
68
67
|
completion(@[retDict]);
|
|
@@ -72,31 +71,12 @@ RCT_EXPORT_METHOD(restorePurchasesWithCompletionHandler:(RCTResponseSenderBlock)
|
|
|
72
71
|
RCT_EXPORT_METHOD(restorePurchases)
|
|
73
72
|
{
|
|
74
73
|
NSLog(@"NamiBridge: Info: Calling RestorePurchases");
|
|
75
|
-
|
|
74
|
+
|
|
76
75
|
[NamiPurchaseManager restorePurchasesWithStatehandler:^(enum NamiRestorePurchasesState state, NSArray<NamiPurchase *> * _Nonnull newPurchases, NSArray<NamiPurchase *> * _Nonnull oldPurchases, NSError * _Nullable error) {
|
|
77
76
|
[[NamiEmitter reactInstance] sendRestorePurchasesStateChanged:state newPurchases:newPurchases oldPurchases:oldPurchases error:error];
|
|
78
77
|
}];
|
|
79
78
|
}
|
|
80
79
|
|
|
81
|
-
/// Determines if any one of the passed in SKUID's have been purchased.
|
|
82
|
-
RCT_EXPORT_METHOD(anySKUIDPurchased:(nonnull NSArray*)skuIDs completion:(RCTResponseSenderBlock)completion)
|
|
83
|
-
{
|
|
84
|
-
BOOL active = false;
|
|
85
|
-
for (NamiPurchase *purchase in [NamiPurchaseManager allPurchases]) {
|
|
86
|
-
if ( [skuIDs containsObject:purchase.skuID] ) {
|
|
87
|
-
active = true;
|
|
88
|
-
break;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
completion(@[[NSNumber numberWithBool:active]]);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/// For consumable purchases, removes the SKU from Nami so a product may be purchased again.
|
|
96
|
-
RCT_EXPORT_METHOD(consumePurchasedSKU:(nonnull NSString*)skuID)
|
|
97
|
-
{
|
|
98
|
-
[NamiPurchaseManager consumePurchasedSKUWithSkuID:skuID];
|
|
99
|
-
}
|
|
100
80
|
|
|
101
81
|
/// For consumable purchases, removes the SKU from Nami so a product may be purchased again.
|
|
102
82
|
RCT_EXPORT_METHOD(presentCodeRedemptionSheet)
|
|
@@ -118,34 +98,23 @@ RCT_EXPORT_METHOD(canPresentCodeRedemptionSheet:(RCTResponseSenderBlock)completi
|
|
|
118
98
|
}
|
|
119
99
|
|
|
120
100
|
/// This method does the purchase work, and can optionally be fed a paywall metadata object to pass along to the purchase flow.
|
|
121
|
-
|
|
122
|
-
[NamiPurchaseManager
|
|
123
|
-
NSLog(@"NamiBridge: Info: Products found are %@, product fetch error is %@", products, [error localizedDescription]);
|
|
124
|
-
NamiSKU *useProduct = products.firstObject;
|
|
125
|
-
if (useProduct != nil) {
|
|
126
|
-
[NamiPurchaseManager
|
|
127
|
-
NSLog(@"NamiBridge: Info: Purchase result is %@, purchased is %d, purchaseState is %@, error is %@", purchase, (purchaseState == NamiPurchaseStatePurchased), [NSNumber numberWithInt:(int)purchaseState], [error localizedDescription]);
|
|
128
|
-
if (purchaseState == NamiPurchaseStatePurchased) {
|
|
129
|
-
completion(@[[NSNumber numberWithBool:true]]);
|
|
130
|
-
}
|
|
131
|
-
}];
|
|
132
|
-
} else {
|
|
133
|
-
completion(@[[NSNumber numberWithBool:false]]);
|
|
134
|
-
}
|
|
135
|
-
}];
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
RCT_EXPORT_METHOD(buySKU:(nonnull NSString*)skuID paywallDeveloperID:(nonnull NSString*)paywallDeveloperID completion:(RCTResponseSenderBlock)completion)
|
|
140
|
-
{
|
|
141
|
-
if (paywallDeveloperID.length > 0) {
|
|
142
|
-
[NamiPaywallManager fetchCustomPaywallMetaForDeveloperID:paywallDeveloperID :^(NSArray<NamiSKU *> * _Nullable products, NSString * _Nonnull developerPaywallID, NamiPaywall * _Nullable namiPaywall) {
|
|
143
|
-
[self doSKUPurchaseWithSKUID:skuID namiPaywall:namiPaywall completion:completion];
|
|
144
|
-
}];
|
|
145
|
-
} else {
|
|
146
|
-
[self doSKUPurchaseWithSKUID:skuID namiPaywall:nil completion:completion];
|
|
147
|
-
}
|
|
148
|
-
}
|
|
101
|
+
//- (void) doSKUPurchaseWithSKUID:(nonnull NSString*)skuID namiPaywall:(NamiPaywall * _Nullable)namiPaywall completion:(RCTResponseSenderBlock)completion {
|
|
102
|
+
// [NamiPurchaseManager skusForSKUIdsWithSkuIds:@[skuID] productHandler:^(BOOL success, NSArray<NamiSKU *> * _Nullable products, NSArray<NSString *> * _Nullable invalidProducts, NSError * _Nullable error) {
|
|
103
|
+
// NSLog(@"NamiBridge: Info: Products found are %@, product fetch error is %@", products, [error localizedDescription]);
|
|
104
|
+
// NamiSKU *useProduct = products.firstObject;
|
|
105
|
+
// if (useProduct != nil) {
|
|
106
|
+
// [NamiPurchaseManager buySku:useProduct responseHandler:^(NSArray<NamiPurchase *> * _Nonnull purchase, enum NamiPurchaseState purchaseState, NSError * _Nullable error) {
|
|
107
|
+
// NSLog(@"NamiBridge: Info: Purchase result is %@, purchased is %d, purchaseState is %@, error is %@", purchase, (purchaseState == NamiPurchaseStatePurchased), [NSNumber numberWithInt:(int)purchaseState], [error localizedDescription]);
|
|
108
|
+
// if (purchaseState == NamiPurchaseStatePurchased) {
|
|
109
|
+
// completion(@[[NSNumber numberWithBool:true]]);
|
|
110
|
+
// }
|
|
111
|
+
// }];
|
|
112
|
+
// } else {
|
|
113
|
+
// completion(@[[NSNumber numberWithBool:false]]);
|
|
114
|
+
// }
|
|
115
|
+
// }];
|
|
116
|
+
//
|
|
117
|
+
//}
|
|
149
118
|
|
|
150
119
|
@end
|
|
151
120
|
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
//
|
|
2
|
+
// NamiPurchaseManagerBridge.swift
|
|
3
|
+
// RNNami
|
|
4
|
+
//
|
|
5
|
+
// Copyright © 2023 Nami ML Inc. All rights reserved.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
import NamiApple
|
|
10
|
+
import React
|
|
11
|
+
|
|
12
|
+
@objc(RNNamiPurchaseManager)
|
|
13
|
+
class RNNamiPurchaseManager: RCTEventEmitter {
|
|
14
|
+
override func supportedEvents() -> [String]! {
|
|
15
|
+
return ["PurchasesChanged", "RestorePurchasesStateChanged"]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static func productToDict(_ product: SKProduct) -> NSDictionary {
|
|
19
|
+
let productDict: [String: Any?] = [
|
|
20
|
+
"localizedTitle": product.localizedTitle,
|
|
21
|
+
"localizedDescription": product.localizedDescription,
|
|
22
|
+
"localizedPrice": product.localizedPrice,
|
|
23
|
+
"localizedMultipliedPrice": product.localizedMultipliedPrice,
|
|
24
|
+
"price": product.price.stringValue,
|
|
25
|
+
"priceLanguage": product.priceLocale.languageCode,
|
|
26
|
+
"priceCurrency": product.priceLocale.currencyCode,
|
|
27
|
+
]
|
|
28
|
+
return NSDictionary(dictionary: productDict.compactMapValues { $0 })
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static func skuToSKUDict(_ sku: NamiSKU) -> NSDictionary {
|
|
32
|
+
var productDict: NSDictionary?
|
|
33
|
+
if let product = sku.product {
|
|
34
|
+
productDict = productToDict(product)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let typeString: String
|
|
38
|
+
switch sku.type {
|
|
39
|
+
case .unknown:
|
|
40
|
+
typeString = "unknown"
|
|
41
|
+
case .one_time_purchase:
|
|
42
|
+
typeString = "one_time_purchase"
|
|
43
|
+
case .subscription:
|
|
44
|
+
typeString = "subscription"
|
|
45
|
+
@unknown default:
|
|
46
|
+
typeString = "unknown"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let skuDict: [String: Any?] = [
|
|
50
|
+
"name": sku.name,
|
|
51
|
+
"skuId": sku.skuId,
|
|
52
|
+
"type": typeString,
|
|
53
|
+
"product": productDict,
|
|
54
|
+
// "displayText": sku.localizedDisplayText,
|
|
55
|
+
// "displaySubText": sku.localizedSubDisplayText,
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
return NSDictionary(dictionary: skuDict.compactMapValues { $0 })
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static func purchaseToPurchaseDict(_ purchase: NamiPurchase) -> NSDictionary {
|
|
62
|
+
var skuDictionary: NSDictionary?
|
|
63
|
+
if let sku = purchase.sku {
|
|
64
|
+
skuDictionary = RNNamiPurchaseManager.skuToSKUDict(sku)
|
|
65
|
+
}
|
|
66
|
+
let dateFormatter = DateFormatter()
|
|
67
|
+
dateFormatter.locale = .init(identifier: "en_US_POSIX")
|
|
68
|
+
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
|
|
69
|
+
let expiresString = dateFormatter.string(from: purchase.expires ?? Date())
|
|
70
|
+
let purchaseInitiatedString = dateFormatter.string(from: purchase.purchaseInitiatedTimestamp)
|
|
71
|
+
let purchaseDict: [String: Any?] = [
|
|
72
|
+
"skuId": purchase.skuId,
|
|
73
|
+
"transactionIdentifier": purchase.transactionIdentifier,
|
|
74
|
+
"sku": skuDictionary,
|
|
75
|
+
"expires": expiresString,
|
|
76
|
+
"purchaseInitiatedTimestamp": purchaseInitiatedString,
|
|
77
|
+
]
|
|
78
|
+
return NSDictionary(dictionary: purchaseDict.compactMapValues { $0 })
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@objc(allPurchases:rejecter:)
|
|
82
|
+
func allPurchases(resolve: @escaping RCTPromiseResolveBlock, reject _: @escaping RCTPromiseRejectBlock) {
|
|
83
|
+
let allPurchases = NamiPurchaseManager.allPurchases()
|
|
84
|
+
let purchaseDictionaries = allPurchases.map { purchase in
|
|
85
|
+
RNNamiPurchaseManager.purchaseToPurchaseDict(purchase)
|
|
86
|
+
}
|
|
87
|
+
resolve(purchaseDictionaries)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@objc(skuPurchased:resolver:rejecter:)
|
|
91
|
+
func skuPurchased(skuId: String, resolve: @escaping RCTPromiseResolveBlock, reject _: @escaping RCTPromiseRejectBlock) {
|
|
92
|
+
let isSkuPurchased = NamiPurchaseManager.skuPurchased(skuId)
|
|
93
|
+
resolve(isSkuPurchased)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@objc(anySkuPurchased:resolver:rejecter:)
|
|
97
|
+
func anySkuPurchased(skuIds: [String], resolve: @escaping RCTPromiseResolveBlock, reject _: @escaping RCTPromiseRejectBlock) {
|
|
98
|
+
let isSkusPurchased = NamiPurchaseManager.anySkuPurchased(skuIds)
|
|
99
|
+
resolve(isSkusPurchased)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@objc(consumePurchasedSku:)
|
|
103
|
+
func consumePurchasedSku(skuId: String) {
|
|
104
|
+
NamiPurchaseManager.consumePurchasedSku(skuId: skuId)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@objc(registerPurchasesChangedHandler)
|
|
108
|
+
func registerPurchasesChangedHandler() {
|
|
109
|
+
NamiPurchaseManager.registerPurchasesChangedHandler { purchases, purchaseState, error in
|
|
110
|
+
let purchaseDictionaries = purchases.map { purchase in
|
|
111
|
+
RNNamiPurchaseManager.purchaseToPurchaseDict(purchase)
|
|
112
|
+
}
|
|
113
|
+
let stateString: String
|
|
114
|
+
switch purchaseState {
|
|
115
|
+
case .pending:
|
|
116
|
+
stateString = "pending"
|
|
117
|
+
case .purchased:
|
|
118
|
+
stateString = "purchased"
|
|
119
|
+
case .consumed:
|
|
120
|
+
stateString = "consumed"
|
|
121
|
+
case .resubscribed:
|
|
122
|
+
stateString = "resubscribed"
|
|
123
|
+
case .unsubscribed:
|
|
124
|
+
stateString = "unsubscribed"
|
|
125
|
+
case .deferred:
|
|
126
|
+
stateString = "deferred"
|
|
127
|
+
case .failed:
|
|
128
|
+
stateString = "failed"
|
|
129
|
+
case .cancelled:
|
|
130
|
+
stateString = "cancelled"
|
|
131
|
+
case .unknown:
|
|
132
|
+
stateString = "unknown"
|
|
133
|
+
@unknown default:
|
|
134
|
+
stateString = "unknown"
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
let payload: [String: Any?] = [
|
|
138
|
+
"purchases": purchaseDictionaries,
|
|
139
|
+
"purchaseState": stateString,
|
|
140
|
+
"error": error?.localizedDescription,
|
|
141
|
+
]
|
|
142
|
+
self.sendEvent(withName: "PurchasesChanged", body: payload)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
@objc(registerRestorePurchasesHandler)
|
|
147
|
+
func registerRestorePurchasesHandler() {
|
|
148
|
+
NamiPurchaseManager.registerRestorePurchasesHandler { state, newPurchases, oldPurchases, _ in
|
|
149
|
+
let stateString: String
|
|
150
|
+
switch state {
|
|
151
|
+
case .started:
|
|
152
|
+
stateString = "started"
|
|
153
|
+
case .finished:
|
|
154
|
+
stateString = "finished"
|
|
155
|
+
case .error:
|
|
156
|
+
stateString = "error"
|
|
157
|
+
@unknown default:
|
|
158
|
+
stateString = "error"
|
|
159
|
+
}
|
|
160
|
+
let newPurchasesDictionaries = newPurchases.map { purchase in
|
|
161
|
+
RNNamiPurchaseManager.purchaseToPurchaseDict(purchase)
|
|
162
|
+
}
|
|
163
|
+
let oldPurchasesDictionaries = oldPurchases.map { purchase in
|
|
164
|
+
RNNamiPurchaseManager.purchaseToPurchaseDict(purchase)
|
|
165
|
+
}
|
|
166
|
+
let payload: [String: Any?] = [
|
|
167
|
+
"state": stateString,
|
|
168
|
+
"newPurchases": newPurchasesDictionaries,
|
|
169
|
+
"oldPurchases": oldPurchasesDictionaries,
|
|
170
|
+
]
|
|
171
|
+
self.sendEvent(withName: "RestorePurchasesStateChanged", body: payload)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
package/ios/Podfile
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
require_relative '../node_modules/react-native/scripts/react_native_pods'
|
|
3
3
|
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
|
|
4
4
|
|
|
5
|
-
platform :ios, '
|
|
5
|
+
platform :ios, '14.0'
|
|
6
6
|
|
|
7
7
|
source 'https://cdn.cocoapods.org/'
|
|
8
8
|
|
|
9
9
|
target 'RNNami' do
|
|
10
10
|
config = use_native_modules!
|
|
11
|
-
pod
|
|
11
|
+
pod "Nami", "3.0.8"
|
|
12
12
|
|
|
13
13
|
use_react_native!(
|
|
14
14
|
:path => config[:reactNativePath],
|
package/ios/RNNami.h
CHANGED