增加ios内购
This commit is contained in:
parent
13bd51e38e
commit
1c5faa5afd
@ -66,11 +66,11 @@ bool AppDelegate::applicationDidFinishLaunching()
|
|||||||
|
|
||||||
se->start();
|
se->start();
|
||||||
se::AutoHandleScope hs;
|
se::AutoHandleScope hs;
|
||||||
jsb_run_script("js/jsb-adapter/jsb-builtin.js");
|
jsb_run_script("Data/js/jsb-adapter/jsb-builtin.js");
|
||||||
jsb_run_script("js/jcwallet.js");
|
jsb_run_script("Data/js/jcwallet.js");
|
||||||
jsb_run_script("js/platform.js");
|
jsb_run_script("Data/js/platform.js");
|
||||||
jsb_run_script("js/main.js");
|
jsb_run_script("Data/js/main.js");
|
||||||
jsb_run_script("js/wallet.js");
|
jsb_run_script("Data/js/wallet.js");
|
||||||
se->addAfterCleanupHook([]() {
|
se->addAfterCleanupHook([]() {
|
||||||
JSBClassType::destroy();
|
JSBClassType::destroy();
|
||||||
});
|
});
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "AppDelegate.h"
|
#include "AppDelegate.h"
|
||||||
#import "UIViewController+Wallet.h"
|
#import "UIViewController+Wallet.h"
|
||||||
#import "UIViewController+QR.h"
|
#import "UIViewController+QR.h"
|
||||||
|
#import "UIViewController+Purchase.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
|
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
|
||||||
@ -460,6 +461,110 @@ bool jsb_showWebPage(se::State& s) {
|
|||||||
}
|
}
|
||||||
SE_BIND_FUNC(jsb_showWebPage)
|
SE_BIND_FUNC(jsb_showWebPage)
|
||||||
|
|
||||||
|
static bool JSB_queryProducts(se::State& s)
|
||||||
|
{
|
||||||
|
const auto& args = s.args();
|
||||||
|
size_t argc = args.size();
|
||||||
|
CC_UNUSED bool ok = true;
|
||||||
|
if (argc > 1) {
|
||||||
|
std::string funid;
|
||||||
|
ok = seval_to_std_string(args[0], &funid);
|
||||||
|
SE_PRECONDITION2(ok, false, "funid is invalid!");
|
||||||
|
std::string param0;
|
||||||
|
ok = seval_to_std_string(args[1], ¶m0);
|
||||||
|
SE_PRECONDITION2(ok, false, "param0 is invalid!");
|
||||||
|
NSString *nParams = [NSString stringWithCString:param0.c_str() encoding: NSUTF8StringEncoding];
|
||||||
|
NSString *nfunid = [NSString stringWithCString:funid.c_str() encoding: NSUTF8StringEncoding];
|
||||||
|
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
UIWindow* window = [[[UIApplication sharedApplication] delegate] window];
|
||||||
|
[window.rootViewController queryProducts:nfunid products: nParams];
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 2);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SE_BIND_FUNC(JSB_queryProducts)
|
||||||
|
|
||||||
|
static bool JSB_queryPurchase(se::State& s)
|
||||||
|
{
|
||||||
|
const auto& args = s.args();
|
||||||
|
size_t argc = args.size();
|
||||||
|
CC_UNUSED bool ok = true;
|
||||||
|
if (argc > 0) {
|
||||||
|
std::string funid;
|
||||||
|
ok = seval_to_std_string(args[0], &funid);
|
||||||
|
SE_PRECONDITION2(ok, false, "funid is invalid!");
|
||||||
|
NSString *nfunid = [NSString stringWithCString:funid.c_str() encoding: NSUTF8StringEncoding];
|
||||||
|
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
UIWindow* window = [[[UIApplication sharedApplication] delegate] window];
|
||||||
|
[window.rootViewController queryPurchase:nfunid ];
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SE_BIND_FUNC(JSB_queryPurchase)
|
||||||
|
|
||||||
|
static bool JSB_finishTransaction(se::State& s)
|
||||||
|
{
|
||||||
|
const auto& args = s.args();
|
||||||
|
size_t argc = args.size();
|
||||||
|
CC_UNUSED bool ok = true;
|
||||||
|
if (argc > 1) {
|
||||||
|
std::string funid;
|
||||||
|
ok = seval_to_std_string(args[0], &funid);
|
||||||
|
SE_PRECONDITION2(ok, false, "funid is invalid!");
|
||||||
|
std::string transactionid;
|
||||||
|
ok = seval_to_std_string(args[1], &transactionid);
|
||||||
|
SE_PRECONDITION2(ok, false, "transactionid is invalid!");
|
||||||
|
NSString *nfunid = [NSString stringWithCString:funid.c_str() encoding: NSUTF8StringEncoding];
|
||||||
|
NSString *ntransactionid = [NSString stringWithCString:transactionid.c_str() encoding: NSUTF8StringEncoding];
|
||||||
|
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
UIWindow* window = [[[UIApplication sharedApplication] delegate] window];
|
||||||
|
[window.rootViewController finishTransaction:nfunid transactionId:ntransactionid ];
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 2);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SE_BIND_FUNC(JSB_finishTransaction)
|
||||||
|
|
||||||
|
static bool JSB_beginBuy(se::State& s)
|
||||||
|
{
|
||||||
|
const auto& args = s.args();
|
||||||
|
size_t argc = args.size();
|
||||||
|
CC_UNUSED bool ok = true;
|
||||||
|
if (argc > 2) {
|
||||||
|
std::string funid;
|
||||||
|
ok = seval_to_std_string(args[0], &funid);
|
||||||
|
SE_PRECONDITION2(ok, false, "funid is invalid!");
|
||||||
|
std::string param0;
|
||||||
|
ok = seval_to_std_string(args[1], ¶m0);
|
||||||
|
SE_PRECONDITION2(ok, false, "param0 is invalid!");
|
||||||
|
std::string param1;
|
||||||
|
ok = seval_to_std_string(args[2], ¶m1);
|
||||||
|
SE_PRECONDITION2(ok, false, "param1 is invalid!");
|
||||||
|
NSString *nParam0 = [NSString stringWithCString:param0.c_str() encoding: NSUTF8StringEncoding];
|
||||||
|
NSString *nParam1 = [NSString stringWithCString:param1.c_str() encoding: NSUTF8StringEncoding];
|
||||||
|
NSString *nfunid = [NSString stringWithCString:funid.c_str() encoding: NSUTF8StringEncoding];
|
||||||
|
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
UIWindow* window = [[[UIApplication sharedApplication] delegate] window];
|
||||||
|
[window.rootViewController beginBuy:nfunid productId: nParam0 orderId:nParam1];
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 3);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SE_BIND_FUNC(JSB_beginBuy)
|
||||||
|
|
||||||
|
|
||||||
bool jsb_register_walletevent_modules(se::Object* global) {
|
bool jsb_register_walletevent_modules(se::Object* global) {
|
||||||
getOrCreatePlainObject_r("jsb", global, &__jsbObj);
|
getOrCreatePlainObject_r("jsb", global, &__jsbObj);
|
||||||
@ -473,6 +578,10 @@ bool jsb_register_walletevent_modules(se::Object* global) {
|
|||||||
__jsbObj->defineFunction("signOutGoogle", _SE(jsb_signOutGoogle));
|
__jsbObj->defineFunction("signOutGoogle", _SE(jsb_signOutGoogle));
|
||||||
__jsbObj->defineFunction("showQRCode", _SE(JSB_showQRCode));
|
__jsbObj->defineFunction("showQRCode", _SE(JSB_showQRCode));
|
||||||
__jsbObj->defineFunction("showWebPage", _SE(jsb_showWebPage));
|
__jsbObj->defineFunction("showWebPage", _SE(jsb_showWebPage));
|
||||||
|
__jsbObj->defineFunction("queryProducts", _SE(JSB_queryProducts));
|
||||||
|
__jsbObj->defineFunction("queryPurchase", _SE(JSB_queryPurchase));
|
||||||
|
__jsbObj->defineFunction("finishTransaction", _SE(JSB_finishTransaction));
|
||||||
|
__jsbObj->defineFunction("beginBuy", _SE(JSB_beginBuy));
|
||||||
// JSB_signWithEmail
|
// JSB_signWithEmail
|
||||||
// JSB_beginBuyJNI
|
// JSB_beginBuyJNI
|
||||||
return true;
|
return true;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// UIViewController+Logger.h
|
// UIViewController+Purchase.h
|
||||||
// Unity-iPhone
|
// Unity-iPhone
|
||||||
//
|
//
|
||||||
// Created by Hl Zhang on 2023/3/21.
|
// Created by Hl Zhang on 2023/3/21.
|
||||||
@ -9,8 +9,10 @@
|
|||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
|
||||||
@interface UIViewController (Purchase)
|
@interface UIViewController (Purchase)
|
||||||
- (void)initPurchaseEnv;
|
- (void)initPurchaseEnv;
|
||||||
|
- (void)queryProducts:(NSString *)funId products:(NSString *)products;
|
||||||
|
- (void)queryPurchase:(NSString *)funId;
|
||||||
|
- (void)finishTransaction:(NSString *)funId transactionId: (NSString *) transactionId;
|
||||||
|
- (void)beginBuy:(NSString *)funId productId:(NSString *)productId orderId:(NSString *)orderId;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -1,116 +1,203 @@
|
|||||||
//
|
//
|
||||||
// UIViewController+Logger.cpp
|
// UIViewController+Purchase.cpp
|
||||||
// Unity-iPhone
|
// Unity-iPhone
|
||||||
//
|
//
|
||||||
// Created by Hl Zhang on 2023/3/21.
|
// Created by Hl Zhang on 2023/3/21.
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "UIViewController+Purchase.h"
|
#import "UIViewController+Purchase.h"
|
||||||
|
#import "UIViewController+Wallet.h"
|
||||||
#import "Utilities.h"
|
#import "Utilities.h"
|
||||||
#import "StoreManager.h"
|
#import "StoreManager.h"
|
||||||
#import "StoreObserver.h"
|
#import "StoreObserver.h"
|
||||||
#import "AppConfiguration.h"
|
#import "AppConfiguration.h"
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "SKProduct+SKProductAdditions.h"
|
||||||
|
#include <string>
|
||||||
|
#import "NSString+Customer.h"
|
||||||
|
#include "JcWallet.h"
|
||||||
|
|
||||||
static Utilities *utility;
|
static Utilities *utility = nil;
|
||||||
|
|
||||||
|
@interface UIViewController (Purchase)
|
||||||
|
@property (nonatomic) NSString *currentFunId;
|
||||||
|
@end
|
||||||
|
|
||||||
@implementation UIViewController (Purchase)
|
@implementation UIViewController (Purchase)
|
||||||
|
|
||||||
-(void)initPurchaseEnv {
|
-(void)initPurchaseEnv {
|
||||||
utility = [[Utilities alloc] init];
|
utility = [[Utilities alloc] init];
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
// [[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
selector:@selector(handleProductRequestNotification:)
|
// selector:@selector(handleProductRequestNotification:)
|
||||||
name:PCSProductRequestNotification
|
// name:PCSProductRequestNotification
|
||||||
object:[StoreManager sharedInstance]];
|
// object:[StoreManager sharedInstance]];
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
selector:@selector(handlePurchaseNotification:)
|
selector:@selector(handlePurchaseNotification:)
|
||||||
name:PCSPurchaseNotification
|
name:PCSPurchaseNotification
|
||||||
object:[StoreObserver sharedInstance]];
|
object:[StoreObserver sharedInstance]];
|
||||||
}
|
}
|
||||||
|
#pragma mark - Query products
|
||||||
/// Retrieves product information from the App Store.
|
/// Retrieves product information from the App Store.
|
||||||
-(void)fetchProductInformation {
|
- (void)queryProducts:(NSString *) funId products: (NSString *) products {
|
||||||
|
if (utility == nil) {
|
||||||
|
[self initPurchaseEnv];
|
||||||
|
}
|
||||||
|
if (products == nil || [products isEqualToString:@""]) {
|
||||||
|
NSLog(@"queryProducts with empty products: %@", products);
|
||||||
|
[self nativeCb:funId hasErr:YES dataStr:@"queryProducts with empty products"];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// split products to array
|
||||||
|
NSArray *identifiers = [products componentsSeparatedByString:@","];
|
||||||
// First, let's check whether the user is allowed to make purchases. Proceed if they are allowed. Display an alert, otherwise.
|
// First, let's check whether the user is allowed to make purchases. Proceed if they are allowed. Display an alert, otherwise.
|
||||||
if ([StoreObserver sharedInstance].isAuthorizedForPayments) {
|
if ([StoreObserver sharedInstance].isAuthorizedForPayments) {
|
||||||
NSArray *identifiers = utility.identifiers;
|
// Fetch the product information.
|
||||||
|
[[StoreManager sharedInstance] fetchProductsMatchingIdentifiers:identifiers completionBlock: ^(PCSProductRequestStatus status, NSMutableArray *storeResponse){
|
||||||
if (identifiers != nil) {
|
if (status == PCSStoreResponse) {
|
||||||
if (identifiers.count > 0) {
|
// define json array to return
|
||||||
Section *section = [[Section alloc] initWithName:PCSProductsInvalidIdentifiers elements:identifiers];
|
NSMutableArray *jsonArray = [[NSMutableArray alloc] init];
|
||||||
|
for (Section *section in storeResponse) {
|
||||||
// Refresh the UI with identifiers to be queried.
|
NSArray *content = section.elements;
|
||||||
// [self switchToViewController:ParentViewControllerSegmentProducts];
|
if ([section.name isEqualToString:PCSProductsAvailableProducts]) {
|
||||||
// [self.products reloadWithData:[NSMutableArray arrayWithObject:section]];
|
for (SKProduct *product in content) {
|
||||||
|
NSMutableDictionary *json = [[NSMutableDictionary alloc] init];
|
||||||
// Fetch the product information.
|
[json setObject:product.productIdentifier forKey:@"id"];
|
||||||
[[StoreManager sharedInstance] startProductRequestWithIdentifiers:identifiers];
|
[json setObject:product.localizedTitle forKey:@"name"];
|
||||||
} else {
|
[json setObject:product.localizedDescription forKey:@"description"];
|
||||||
// Warn the user that the resource file does not contain anything.
|
[json setObject:product.priceLocale.currencyCode forKey:@"currencyCode"];
|
||||||
[self alertWithTitle:PCSMessagesStatus message:[NSString stringWithFormat:@"%@.%@ %@", PCSProductIdsPlistName, PCSProductIdsPlistFileExtension, PCSMessagesEmptyResource]];
|
[json setObject:product.price.stringValue forKey:@"priceValue"];
|
||||||
|
[json setObject:product.regularPrice forKey:@"priceShow"];
|
||||||
|
[jsonArray addObject:json];
|
||||||
|
}
|
||||||
|
} else if ([section.name isEqualToString:PCSProductsInvalidIdentifiers]) {
|
||||||
|
// if there are invalid product identifiers, show them.
|
||||||
|
NSLog(@"Invalid product identifiers: %@", section.name);
|
||||||
|
// [self nativeCb:funId hasErr:YES dataStr: section.name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NSError *error;
|
||||||
|
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonArray options:NSJSONWritingPrettyPrinted error:&error];
|
||||||
|
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||||
|
if (!jsonData) {
|
||||||
|
NSLog(@"Got an error: %@", error);
|
||||||
|
[self nativeCb:funId hasErr:YES dataStr: [NSString stringWithFormat:@"%@", error]];
|
||||||
|
} else {
|
||||||
|
NSLog(@"jsonString: %@", jsonString);
|
||||||
|
[self nativeCb:funId hasErr:NO dataStr: jsonString];
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (status == PCSRequestFailed) {
|
||||||
|
NSLog(@"Product request failed with message: %@", [StoreManager sharedInstance].message);
|
||||||
|
[self nativeCb:funId hasErr:YES dataStr: [StoreManager sharedInstance].message];
|
||||||
}
|
}
|
||||||
} else {
|
}];
|
||||||
// Warn the user that the resource file could not be found.
|
|
||||||
[self alertWithTitle:PCSMessagesStatus message:[NSString stringWithFormat:@"%@ %@.%@.", PCSMessagesResourceNotFound, PCSProductIdsPlistName, PCSProductIdsPlistFileExtension]];
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Warn the user that they are not allowed to make purchases.
|
// Warn the user that they are not allowed to make purchases.
|
||||||
[self alertWithTitle:PCSMessagesStatus message:[NSString stringWithFormat:@"%@", PCSMessagesCannotMakePayments]];
|
NSLog(@"User is not allowed to make payments (Payments are disabled in Settings).");
|
||||||
|
[self nativeCb:funId hasErr:YES dataStr: [NSString stringWithFormat:@"%@", PCSMessagesCannotMakePayments]];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - Query Purchase
|
||||||
|
- (void)queryPurchase:(NSString *)funId {
|
||||||
|
if (utility == nil) {
|
||||||
|
[self initPurchaseEnv];
|
||||||
|
}
|
||||||
|
NSArray *transations = [[StoreObserver sharedInstance] queryPendingTransactions];
|
||||||
|
// convert to json string
|
||||||
|
NSError *error;
|
||||||
|
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:transations options:NSJSONWritingPrettyPrinted error:&error];
|
||||||
|
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||||
|
[self nativeCb:funId hasErr:NO dataStr:jsonString];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Begin Buy Product
|
||||||
|
- (void)beginBuy:(NSString *)funId productId:(NSString *)productId orderId:(NSString *)orderId {
|
||||||
|
if (utility == nil) {
|
||||||
|
[self initPurchaseEnv];
|
||||||
|
}
|
||||||
|
if (productId == nil || [productId isEqualToString:@""]) {
|
||||||
|
NSLog(@"beginBy with empty productId: %@", productId);
|
||||||
|
[self nativeCb:funId hasErr:YES dataStr:@"beginBy with empty productId"];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// check if currentFunId is empty
|
||||||
|
if (self.currentFunId != nil && ![self.currentFunId isEqualToString:@""]) {
|
||||||
|
NSLog(@"beginBy with currentFunId: %@", self.currentFunId);
|
||||||
|
[self nativeCb:funId hasErr:YES dataStr:@"other purchase is processing"];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.currentFunId = funId;
|
||||||
|
SKProduct *product = [[StoreManager sharedInstance] productMatchingIdentifier: productId];
|
||||||
|
if (product == nil) {
|
||||||
|
NSLog(@"beginBy with empty product: %@", productId);
|
||||||
|
self.currentFunId = nil;
|
||||||
|
[self nativeCb:funId hasErr:YES dataStr:@"beginBy with empty product"];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (orderId == nil || [orderId isEqualToString:@""]) {
|
||||||
|
NSLog(@"beginBy with empty orderId: %@", orderId);
|
||||||
|
self.currentFunId = nil;
|
||||||
|
[self nativeCb:funId hasErr:YES dataStr:@"beginBy with empty orderId"];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
[[StoreObserver sharedInstance] buy:product orderId: orderId];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Finish Transaction
|
||||||
|
- (void)finishTransaction:(NSString *)funId transactionId: (NSString *) transactionId {
|
||||||
|
[[StoreObserver sharedInstance] finishTransaction: transactionId];
|
||||||
|
[self nativeCb:funId hasErr:NO dataStr:@""];
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates and displays an alert.
|
/// Creates and displays an alert.
|
||||||
-(void)alertWithTitle:(NSString *)title message:(NSString *)message {
|
-(void)alertWithTitle:(NSString *)title message:(NSString *)message {
|
||||||
UIAlertController *alertController = [utility alertWithTitle:title message:message];
|
UIAlertController *alertController = [utility alertWithTitle:title message:message];
|
||||||
[self.navigationController presentViewController:alertController animated:YES completion:nil];
|
[self.navigationController presentViewController:alertController animated:YES completion:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Handle PCSProductRequest Notification
|
#pragma mark - Handle PCSProductRequest Notification
|
||||||
|
|
||||||
/// Updates the UI according to the product request notification result.
|
/// Updates the UI according to the product request notification result.
|
||||||
-(void)handleProductRequestNotification:(NSNotification *)notification {
|
-(void)handleProductRequestNotification:(NSNotification *)notification {
|
||||||
StoreManager *productRequestNotification = (StoreManager*)notification.object;
|
StoreManager *productRequestNotification = (StoreManager*)notification.object;
|
||||||
PCSProductRequestStatus status = (PCSProductRequestStatus)productRequestNotification.status;
|
PCSProductRequestStatus status = (PCSProductRequestStatus)productRequestNotification.status;
|
||||||
|
NSLog(@"handleProductRequestNotification status: %ld", (long)status);
|
||||||
if (status == PCSStoreResponse) {
|
|
||||||
// Switch to the Products view controller.
|
|
||||||
// [self switchToViewController:ParentViewControllerSegmentProducts];
|
|
||||||
// [self.products reloadWithData:productRequestNotification.storeResponse];
|
|
||||||
// self.segmentedControl.selectedSegmentIndex = ParentViewControllerSegmentProducts;
|
|
||||||
} else if (status == PCSRequestFailed) {
|
|
||||||
[self alertWithTitle:PCSMessagesProductRequestStatus message:productRequestNotification.message];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Handle PCSPurchase Notification
|
#pragma mark - Handle PCSPurchase Notification
|
||||||
|
|
||||||
/// Updates the UI according to the purchase request notification result.
|
/// Updates the UI according to the purchase request notification result.
|
||||||
-(void)handlePurchaseNotification:(NSNotification *)notification {
|
-(void)handlePurchaseNotification:(NSNotification *)notification {
|
||||||
StoreObserver *purchasesNotification = (StoreObserver *)notification.object;
|
NSDictionary* dic = notification.userInfo;
|
||||||
PCSPurchaseStatus status = (PCSPurchaseStatus)purchasesNotification.status;
|
NSNumber *errcode = dic[@"errcode"];
|
||||||
|
NSLog(@"handlePurchaseNotification status: %@", errcode);
|
||||||
switch (status) {
|
// check if errcode is zero or not
|
||||||
case PCSNoRestorablePurchases:
|
self.currentFunId = nil;
|
||||||
case PCSPurchaseFailed:
|
if (errcode != nil && [errcode intValue] != 0) {
|
||||||
case PCSRestoreFailed: [self alertWithTitle:PCSMessagesPurchaseStatus message:purchasesNotification.message];
|
[self nativeCb:self.currentFunId hasErr:YES dataStr: [NSString stringWithFormat:@"%@", dic[@"errmsg"]]];
|
||||||
break;
|
} else {
|
||||||
// Switch to the Purchases view when receiving a successful restore notification.
|
NSString *tid = dic[@"dataid"];
|
||||||
case PCSRestoreSucceeded: [self handleRestoredSucceededTransaction];
|
NSMutableArray *transactions = [[NSMutableArray alloc] init];
|
||||||
break;
|
[transactions addObject:tid];
|
||||||
default: break;
|
NSError *error;
|
||||||
}
|
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:transactions options:NSJSONWritingPrettyPrinted error:&error];
|
||||||
|
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||||
|
[self nativeCb:self.currentFunId hasErr:NO dataStr: jsonString];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Handle Restored Transactions
|
#pragma mark - Handle Restored Transactions
|
||||||
|
|
||||||
/// Handles succesful restored transactions. Switches to the Purchases view.
|
/// Handles succesful restored transactions. Switches to the Purchases view.
|
||||||
-(void)handleRestoredSucceededTransaction {
|
-(void)handleRestoredSucceededTransaction {
|
||||||
utility.restoreWasCalled = YES;
|
utility.restoreWasCalled = YES;
|
||||||
|
|
||||||
// Refresh the Purchases view with the restored purchases.
|
// Refresh the Purchases view with the restored purchases.
|
||||||
// [self switchToViewController:ParentViewControllerSegmentPurchases];
|
// [self switchToViewController:ParentViewControllerSegmentPurchases];
|
||||||
// [self.purchases reloadWithData:self.utility.dataSourceForPurchasesUI];
|
// [self.purchases reloadWithData:self.utility.dataSourceForPurchasesUI];
|
||||||
// self.segmentedControl.selectedSegmentIndex = ParentViewControllerSegmentPurchases;
|
// self.segmentedControl.selectedSegmentIndex = ParentViewControllerSegmentPurchases;
|
||||||
}
|
}
|
||||||
@end
|
|
||||||
|
|
||||||
|
@end
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import "UIViewController+QR.h"
|
#import "UIViewController+QR.h"
|
||||||
|
#import "UIViewController+Wallet.h"
|
||||||
#import "QRCodeReaderViewController.h"
|
#import "QRCodeReaderViewController.h"
|
||||||
#import "QRCodeReader.h"
|
#import "QRCodeReader.h"
|
||||||
#import "QRCodeReaderDelegate.h"
|
#import "QRCodeReaderDelegate.h"
|
||||||
@ -132,21 +133,5 @@ static SimpleQRViewController *sqrVC = nil;
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void)nativeCb:(NSString *)funid hasErr: (BOOL) hasErr dataStr:(NSString *) dataStr {
|
|
||||||
if ([NSString isStringEmpty:funid]) {
|
|
||||||
NSLog(@"nativeCallBack with empty funid: %@", funid);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::string methodName = "nativeCallBack";
|
|
||||||
NSString *paramStr;
|
|
||||||
if (hasErr) {
|
|
||||||
paramStr = [NSString stringWithFormat:@"{\"errcode\": 1, \"errmessage\": \"%@\"}", dataStr];
|
|
||||||
} else {
|
|
||||||
paramStr = [NSString stringWithFormat:@"{\"errcode\": 0, \"data\": \"%@\"}", dataStr];
|
|
||||||
}
|
|
||||||
std::string sfunid = std::string([funid UTF8String], [funid lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
|
|
||||||
std::string sparam = std::string([paramStr UTF8String], [paramStr lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
|
|
||||||
cocos2d::nativeCallBack(sfunid.c_str(), methodName.c_str(), sparam.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -22,5 +22,5 @@
|
|||||||
-(void)saveKey:(NSString *) account key:(NSString *) key;
|
-(void)saveKey:(NSString *) account key:(NSString *) key;
|
||||||
-(NSString *)loadKey:(NSString *) account;
|
-(NSString *)loadKey:(NSString *) account;
|
||||||
-(void)showPage:(NSString *)url;
|
-(void)showPage:(NSString *)url;
|
||||||
//-(void)nativeCb:(NSString *)funid hasErr: (BOOL) hasErr dataStr:(NSString *) dataStr;
|
-(void)nativeCb:(NSString *)funid hasErr: (BOOL) hasErr dataStr:(NSString *) dataStr;
|
||||||
@end
|
@end
|
||||||
|
@ -176,14 +176,26 @@ static WebPageViewController *webpageVC = nil;
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::string methodName = "nativeCallBack";
|
std::string methodName = "nativeCallBack";
|
||||||
NSString *paramStr;
|
NSMutableDictionary *json = [[NSMutableDictionary alloc] init];
|
||||||
if (hasErr) {
|
if (hasErr) {
|
||||||
paramStr = [NSString stringWithFormat:@"{\"errcode\": 1, \"errmessage\": \"%@\"}", dataStr];
|
json[@"errcode"] = @1;
|
||||||
|
json[@"errmessage"] = dataStr;
|
||||||
} else {
|
} else {
|
||||||
paramStr = [NSString stringWithFormat:@"{\"errcode\": 0, \"data\": \"%@\"}", dataStr];
|
json[@"errcode"] = @0;
|
||||||
|
json[@"data"] = dataStr;
|
||||||
}
|
}
|
||||||
|
NSError *error;
|
||||||
|
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:json options:NSJSONWritingPrettyPrinted error:&error];
|
||||||
|
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||||
std::string sfunid = std::string([funid UTF8String], [funid lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
|
std::string sfunid = std::string([funid UTF8String], [funid lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
|
||||||
std::string sparam = std::string([paramStr UTF8String], [paramStr lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
|
if (error) {
|
||||||
|
NSLog(@"Got an error: %@", error);
|
||||||
|
NSString *errorStr = [NSString stringWithFormat:@"{\"errcode\": 1, \"errmessage\": \"%@\"}", error];
|
||||||
|
std::string sparam = std::string([errorStr UTF8String], [errorStr lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
|
||||||
|
cocos2d::nativeCallBack(sfunid.c_str(), methodName.c_str(), sparam.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::string sparam = std::string([jsonString UTF8String], [jsonString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
|
||||||
cocos2d::nativeCallBack(sfunid.c_str(), methodName.c_str(), sparam.c_str());
|
cocos2d::nativeCallBack(sfunid.c_str(), methodName.c_str(), sparam.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,6 +322,7 @@
|
|||||||
D59AB8522A68FEB700433200 /* Section.m in Sources */ = {isa = PBXBuildFile; fileRef = D59AB8502A68FEB700433200 /* Section.m */; };
|
D59AB8522A68FEB700433200 /* Section.m in Sources */ = {isa = PBXBuildFile; fileRef = D59AB8502A68FEB700433200 /* Section.m */; };
|
||||||
D59AB8552A690BC300433200 /* UIViewController+Purchase.mm in Sources */ = {isa = PBXBuildFile; fileRef = D59AB8542A690BC300433200 /* UIViewController+Purchase.mm */; };
|
D59AB8552A690BC300433200 /* UIViewController+Purchase.mm in Sources */ = {isa = PBXBuildFile; fileRef = D59AB8542A690BC300433200 /* UIViewController+Purchase.mm */; };
|
||||||
D59AB8582A690C9900433200 /* Utilities.m in Sources */ = {isa = PBXBuildFile; fileRef = D59AB8572A690C9900433200 /* Utilities.m */; };
|
D59AB8582A690C9900433200 /* Utilities.m in Sources */ = {isa = PBXBuildFile; fileRef = D59AB8572A690C9900433200 /* Utilities.m */; };
|
||||||
|
D59AB85E2A6A500900433200 /* SKProduct+SKProductAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D59AB85C2A6A500900433200 /* SKProduct+SKProductAdditions.m */; };
|
||||||
D5BF397629C9B79400EC6351 /* UIViewController+Logger.mm in Sources */ = {isa = PBXBuildFile; fileRef = D5BF397529C9B79400EC6351 /* UIViewController+Logger.mm */; };
|
D5BF397629C9B79400EC6351 /* UIViewController+Logger.mm in Sources */ = {isa = PBXBuildFile; fileRef = D5BF397529C9B79400EC6351 /* UIViewController+Logger.mm */; };
|
||||||
D5BF397829C9B8C000EC6351 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = D5BF397729C9B8C000EC6351 /* GoogleService-Info.plist */; };
|
D5BF397829C9B8C000EC6351 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = D5BF397729C9B8C000EC6351 /* GoogleService-Info.plist */; };
|
||||||
D5C03B702A49A808002E758D /* Data in Resources */ = {isa = PBXBuildFile; fileRef = D5C03B6F2A49A808002E758D /* Data */; };
|
D5C03B702A49A808002E758D /* Data in Resources */ = {isa = PBXBuildFile; fileRef = D5C03B6F2A49A808002E758D /* Data */; };
|
||||||
@ -1369,6 +1370,9 @@
|
|||||||
D59AB8542A690BC300433200 /* UIViewController+Purchase.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = "UIViewController+Purchase.mm"; sourceTree = "<group>"; };
|
D59AB8542A690BC300433200 /* UIViewController+Purchase.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = "UIViewController+Purchase.mm"; sourceTree = "<group>"; };
|
||||||
D59AB8562A690C9900433200 /* Utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Utilities.h; sourceTree = "<group>"; };
|
D59AB8562A690C9900433200 /* Utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Utilities.h; sourceTree = "<group>"; };
|
||||||
D59AB8572A690C9900433200 /* Utilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Utilities.m; sourceTree = "<group>"; };
|
D59AB8572A690C9900433200 /* Utilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Utilities.m; sourceTree = "<group>"; };
|
||||||
|
D59AB85A2A6A387A00433200 /* test-in-app-purchase.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = "test-in-app-purchase.storekit"; sourceTree = "<group>"; };
|
||||||
|
D59AB85C2A6A500900433200 /* SKProduct+SKProductAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "SKProduct+SKProductAdditions.m"; sourceTree = "<group>"; };
|
||||||
|
D59AB85D2A6A500900433200 /* SKProduct+SKProductAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SKProduct+SKProductAdditions.h"; sourceTree = "<group>"; };
|
||||||
D5BF397429C9B77E00EC6351 /* UIViewController+Logger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Logger.h"; sourceTree = "<group>"; };
|
D5BF397429C9B77E00EC6351 /* UIViewController+Logger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Logger.h"; sourceTree = "<group>"; };
|
||||||
D5BF397529C9B79400EC6351 /* UIViewController+Logger.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = "UIViewController+Logger.mm"; sourceTree = "<group>"; };
|
D5BF397529C9B79400EC6351 /* UIViewController+Logger.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = "UIViewController+Logger.mm"; sourceTree = "<group>"; };
|
||||||
D5BF397729C9B8C000EC6351 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
|
D5BF397729C9B8C000EC6351 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
|
||||||
@ -1469,6 +1473,7 @@
|
|||||||
29B97314FDCFA39411CA2CEA /* CustomTemplate */ = {
|
29B97314FDCFA39411CA2CEA /* CustomTemplate */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
D59AB85A2A6A387A00433200 /* test-in-app-purchase.storekit */,
|
||||||
D59AB8482A68FC2900433200 /* purchase */,
|
D59AB8482A68FC2900433200 /* purchase */,
|
||||||
D5C03B6F2A49A808002E758D /* Data */,
|
D5C03B6F2A49A808002E758D /* Data */,
|
||||||
D526FA3C299498E3002A2290 /* cocos2d_libs.xcodeproj */,
|
D526FA3C299498E3002A2290 /* cocos2d_libs.xcodeproj */,
|
||||||
@ -3500,6 +3505,8 @@
|
|||||||
D59AB8482A68FC2900433200 /* purchase */ = {
|
D59AB8482A68FC2900433200 /* purchase */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
D59AB85D2A6A500900433200 /* SKProduct+SKProductAdditions.h */,
|
||||||
|
D59AB85C2A6A500900433200 /* SKProduct+SKProductAdditions.m */,
|
||||||
D59AB8492A68FC5200433200 /* StoreObserver.h */,
|
D59AB8492A68FC5200433200 /* StoreObserver.h */,
|
||||||
D59AB8462A68FC2200433200 /* StoreObserver.m */,
|
D59AB8462A68FC2200433200 /* StoreObserver.m */,
|
||||||
D59AB84A2A68FC7200433200 /* AppConfiguration.h */,
|
D59AB84A2A68FC7200433200 /* AppConfiguration.h */,
|
||||||
@ -3817,6 +3824,7 @@
|
|||||||
D55759902A5FAC16009369FC /* Generics5.cpp in Sources */,
|
D55759902A5FAC16009369FC /* Generics5.cpp in Sources */,
|
||||||
D59AB84F2A68FE8E00433200 /* StoreManager.m in Sources */,
|
D59AB84F2A68FE8E00433200 /* StoreManager.m in Sources */,
|
||||||
D55759D12A5FAC16009369FC /* UnityEngine.InputLegacyModule.cpp in Sources */,
|
D55759D12A5FAC16009369FC /* UnityEngine.InputLegacyModule.cpp in Sources */,
|
||||||
|
D59AB85E2A6A500900433200 /* SKProduct+SKProductAdditions.m in Sources */,
|
||||||
D507C9412994A0A800CF3953 /* UIView+Toast.m in Sources */,
|
D507C9412994A0A800CF3953 /* UIView+Toast.m in Sources */,
|
||||||
D55759E12A5FAC16009369FC /* UnityEngine.TextCoreTextEngineModule.cpp in Sources */,
|
D55759E12A5FAC16009369FC /* UnityEngine.TextCoreTextEngineModule.cpp in Sources */,
|
||||||
D507D5E92994C62A00CF3953 /* UnityAdsUtilities.m in Sources */,
|
D507D5E92994C62A00CF3953 /* UnityAdsUtilities.m in Sources */,
|
||||||
|
Binary file not shown.
@ -142,8 +142,8 @@
|
|||||||
filePath = "Classes_cocos/JcWallet.mm"
|
filePath = "Classes_cocos/JcWallet.mm"
|
||||||
startingColumnNumber = "9223372036854775807"
|
startingColumnNumber = "9223372036854775807"
|
||||||
endingColumnNumber = "9223372036854775807"
|
endingColumnNumber = "9223372036854775807"
|
||||||
startingLineNumber = "182"
|
startingLineNumber = "183"
|
||||||
endingLineNumber = "182"
|
endingLineNumber = "183"
|
||||||
landmarkName = "JcWallet::jsToUnity(funId, msg)"
|
landmarkName = "JcWallet::jsToUnity(funId, msg)"
|
||||||
landmarkType = "7">
|
landmarkType = "7">
|
||||||
</BreakpointContent>
|
</BreakpointContent>
|
||||||
@ -238,8 +238,8 @@
|
|||||||
filePath = "Classes_cocos/JcWallet.mm"
|
filePath = "Classes_cocos/JcWallet.mm"
|
||||||
startingColumnNumber = "9223372036854775807"
|
startingColumnNumber = "9223372036854775807"
|
||||||
endingColumnNumber = "9223372036854775807"
|
endingColumnNumber = "9223372036854775807"
|
||||||
startingLineNumber = "188"
|
startingLineNumber = "189"
|
||||||
endingLineNumber = "188"
|
endingLineNumber = "189"
|
||||||
landmarkName = "initEnv()"
|
landmarkName = "initEnv()"
|
||||||
landmarkType = "9">
|
landmarkType = "9">
|
||||||
</BreakpointContent>
|
</BreakpointContent>
|
||||||
@ -254,11 +254,267 @@
|
|||||||
filePath = "Classes_cocos/JcWallet.mm"
|
filePath = "Classes_cocos/JcWallet.mm"
|
||||||
startingColumnNumber = "9223372036854775807"
|
startingColumnNumber = "9223372036854775807"
|
||||||
endingColumnNumber = "9223372036854775807"
|
endingColumnNumber = "9223372036854775807"
|
||||||
startingLineNumber = "114"
|
startingLineNumber = "115"
|
||||||
endingLineNumber = "114"
|
endingLineNumber = "115"
|
||||||
landmarkName = "JcWallet::initEnv()"
|
landmarkName = "JcWallet::initEnv()"
|
||||||
landmarkType = "7">
|
landmarkType = "7">
|
||||||
</BreakpointContent>
|
</BreakpointContent>
|
||||||
</BreakpointProxy>
|
</BreakpointProxy>
|
||||||
|
<BreakpointProxy
|
||||||
|
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||||
|
<BreakpointContent
|
||||||
|
uuid = "4D59F0BA-B8D8-4997-82C4-0E290B339761"
|
||||||
|
shouldBeEnabled = "No"
|
||||||
|
ignoreCount = "0"
|
||||||
|
continueAfterRunningActions = "No"
|
||||||
|
filePath = "Classes_cocos/UIViewController+Purchase.mm"
|
||||||
|
startingColumnNumber = "9223372036854775807"
|
||||||
|
endingColumnNumber = "9223372036854775807"
|
||||||
|
startingLineNumber = "44"
|
||||||
|
endingLineNumber = "44"
|
||||||
|
landmarkName = "-queryProducts:products:"
|
||||||
|
landmarkType = "7">
|
||||||
|
</BreakpointContent>
|
||||||
|
</BreakpointProxy>
|
||||||
|
<BreakpointProxy
|
||||||
|
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||||
|
<BreakpointContent
|
||||||
|
uuid = "EFF0CD4A-8153-4757-B4A4-357559BA0D1B"
|
||||||
|
shouldBeEnabled = "No"
|
||||||
|
ignoreCount = "0"
|
||||||
|
continueAfterRunningActions = "No"
|
||||||
|
filePath = "Classes_cocos/UIViewController+Purchase.mm"
|
||||||
|
startingColumnNumber = "9223372036854775807"
|
||||||
|
endingColumnNumber = "9223372036854775807"
|
||||||
|
startingLineNumber = "97"
|
||||||
|
endingLineNumber = "97"
|
||||||
|
landmarkName = "-queryProducts:products:"
|
||||||
|
landmarkType = "7">
|
||||||
|
</BreakpointContent>
|
||||||
|
</BreakpointProxy>
|
||||||
|
<BreakpointProxy
|
||||||
|
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||||
|
<BreakpointContent
|
||||||
|
uuid = "4FBF6B08-08A0-4695-995A-211586F5A6CF"
|
||||||
|
shouldBeEnabled = "No"
|
||||||
|
ignoreCount = "0"
|
||||||
|
continueAfterRunningActions = "No"
|
||||||
|
filePath = "Classes_cocos/UIViewController+Purchase.mm"
|
||||||
|
startingColumnNumber = "9223372036854775807"
|
||||||
|
endingColumnNumber = "9223372036854775807"
|
||||||
|
startingLineNumber = "25"
|
||||||
|
endingLineNumber = "25"
|
||||||
|
landmarkName = "unknown"
|
||||||
|
landmarkType = "0">
|
||||||
|
</BreakpointContent>
|
||||||
|
</BreakpointProxy>
|
||||||
|
<BreakpointProxy
|
||||||
|
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||||
|
<BreakpointContent
|
||||||
|
uuid = "6CDA02A2-8D28-4324-BFE6-7B2F4037BCB7"
|
||||||
|
shouldBeEnabled = "No"
|
||||||
|
ignoreCount = "0"
|
||||||
|
continueAfterRunningActions = "No"
|
||||||
|
filePath = "Classes_cocos/UIViewController+Purchase.mm"
|
||||||
|
startingColumnNumber = "9223372036854775807"
|
||||||
|
endingColumnNumber = "9223372036854775807"
|
||||||
|
startingLineNumber = "74"
|
||||||
|
endingLineNumber = "74"
|
||||||
|
landmarkName = "-queryProducts:products:"
|
||||||
|
landmarkType = "7">
|
||||||
|
</BreakpointContent>
|
||||||
|
</BreakpointProxy>
|
||||||
|
<BreakpointProxy
|
||||||
|
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||||
|
<BreakpointContent
|
||||||
|
uuid = "E24AA442-B796-47C7-B2EC-28713FB595F3"
|
||||||
|
shouldBeEnabled = "No"
|
||||||
|
ignoreCount = "0"
|
||||||
|
continueAfterRunningActions = "No"
|
||||||
|
filePath = "Classes_cocos/UIViewController+Purchase.mm"
|
||||||
|
startingColumnNumber = "9223372036854775807"
|
||||||
|
endingColumnNumber = "9223372036854775807"
|
||||||
|
startingLineNumber = "46"
|
||||||
|
endingLineNumber = "46"
|
||||||
|
landmarkName = "-queryProducts:products:"
|
||||||
|
landmarkType = "7">
|
||||||
|
</BreakpointContent>
|
||||||
|
</BreakpointProxy>
|
||||||
|
<BreakpointProxy
|
||||||
|
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||||
|
<BreakpointContent
|
||||||
|
uuid = "41E5D281-1A9D-4031-AF76-9BDCF83C9611"
|
||||||
|
shouldBeEnabled = "No"
|
||||||
|
ignoreCount = "0"
|
||||||
|
continueAfterRunningActions = "No"
|
||||||
|
filePath = "Classes_cocos/UIViewController+Purchase.mm"
|
||||||
|
startingColumnNumber = "9223372036854775807"
|
||||||
|
endingColumnNumber = "9223372036854775807"
|
||||||
|
startingLineNumber = "80"
|
||||||
|
endingLineNumber = "80"
|
||||||
|
landmarkName = "-queryProducts:products:"
|
||||||
|
landmarkType = "7">
|
||||||
|
</BreakpointContent>
|
||||||
|
</BreakpointProxy>
|
||||||
|
<BreakpointProxy
|
||||||
|
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||||
|
<BreakpointContent
|
||||||
|
uuid = "79F18C2C-0D3F-41EE-BCEC-0A1B0D6DDC2F"
|
||||||
|
shouldBeEnabled = "No"
|
||||||
|
ignoreCount = "0"
|
||||||
|
continueAfterRunningActions = "No"
|
||||||
|
filePath = "Classes_cocos/UIViewController+Wallet.mm"
|
||||||
|
startingColumnNumber = "9223372036854775807"
|
||||||
|
endingColumnNumber = "9223372036854775807"
|
||||||
|
startingLineNumber = "174"
|
||||||
|
endingLineNumber = "174"
|
||||||
|
landmarkName = "-nativeCb:hasErr:dataStr:"
|
||||||
|
landmarkType = "7">
|
||||||
|
</BreakpointContent>
|
||||||
|
</BreakpointProxy>
|
||||||
|
<BreakpointProxy
|
||||||
|
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||||
|
<BreakpointContent
|
||||||
|
uuid = "8B87105F-C5EE-489F-BF1E-00FDF62602D0"
|
||||||
|
shouldBeEnabled = "No"
|
||||||
|
ignoreCount = "0"
|
||||||
|
continueAfterRunningActions = "No"
|
||||||
|
filePath = "purchase/StoreObserver.m"
|
||||||
|
startingColumnNumber = "9223372036854775807"
|
||||||
|
endingColumnNumber = "9223372036854775807"
|
||||||
|
startingLineNumber = "131"
|
||||||
|
endingLineNumber = "131"
|
||||||
|
landmarkName = "-paymentQueue:updatedTransactions:"
|
||||||
|
landmarkType = "7">
|
||||||
|
</BreakpointContent>
|
||||||
|
</BreakpointProxy>
|
||||||
|
<BreakpointProxy
|
||||||
|
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||||
|
<BreakpointContent
|
||||||
|
uuid = "6E8F3D37-5AC6-454A-8526-57B1C343F168"
|
||||||
|
shouldBeEnabled = "No"
|
||||||
|
ignoreCount = "0"
|
||||||
|
continueAfterRunningActions = "No"
|
||||||
|
filePath = "Classes_cocos/UIViewController+Purchase.mm"
|
||||||
|
startingColumnNumber = "9223372036854775807"
|
||||||
|
endingColumnNumber = "9223372036854775807"
|
||||||
|
startingLineNumber = "111"
|
||||||
|
endingLineNumber = "111"
|
||||||
|
landmarkName = "-queryPurchase:"
|
||||||
|
landmarkType = "7">
|
||||||
|
</BreakpointContent>
|
||||||
|
</BreakpointProxy>
|
||||||
|
<BreakpointProxy
|
||||||
|
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||||
|
<BreakpointContent
|
||||||
|
uuid = "298A81F7-4B66-40D1-8EA3-BE7606FB25D2"
|
||||||
|
shouldBeEnabled = "Yes"
|
||||||
|
ignoreCount = "0"
|
||||||
|
continueAfterRunningActions = "No"
|
||||||
|
filePath = "purchase/StoreObserver.m"
|
||||||
|
startingColumnNumber = "9223372036854775807"
|
||||||
|
endingColumnNumber = "9223372036854775807"
|
||||||
|
startingLineNumber = "190"
|
||||||
|
endingLineNumber = "190"
|
||||||
|
landmarkName = "-handleTransactionPurchased:"
|
||||||
|
landmarkType = "7">
|
||||||
|
</BreakpointContent>
|
||||||
|
</BreakpointProxy>
|
||||||
|
<BreakpointProxy
|
||||||
|
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||||
|
<BreakpointContent
|
||||||
|
uuid = "E989D350-F926-417F-BB88-2C17D5DDE9F2"
|
||||||
|
shouldBeEnabled = "No"
|
||||||
|
ignoreCount = "0"
|
||||||
|
continueAfterRunningActions = "No"
|
||||||
|
filePath = "purchase/StoreObserver.m"
|
||||||
|
startingColumnNumber = "9223372036854775807"
|
||||||
|
endingColumnNumber = "9223372036854775807"
|
||||||
|
startingLineNumber = "50"
|
||||||
|
endingLineNumber = "50"
|
||||||
|
landmarkName = "-getAppReceipt"
|
||||||
|
landmarkType = "7">
|
||||||
|
</BreakpointContent>
|
||||||
|
</BreakpointProxy>
|
||||||
|
<BreakpointProxy
|
||||||
|
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||||
|
<BreakpointContent
|
||||||
|
uuid = "69659E6C-1EDA-428A-A479-A7139FF808A3"
|
||||||
|
shouldBeEnabled = "No"
|
||||||
|
ignoreCount = "0"
|
||||||
|
continueAfterRunningActions = "No"
|
||||||
|
filePath = "purchase/StoreObserver.m"
|
||||||
|
startingColumnNumber = "9223372036854775807"
|
||||||
|
endingColumnNumber = "9223372036854775807"
|
||||||
|
startingLineNumber = "56"
|
||||||
|
endingLineNumber = "56"
|
||||||
|
landmarkName = "-getAppReceipt"
|
||||||
|
landmarkType = "7">
|
||||||
|
</BreakpointContent>
|
||||||
|
</BreakpointProxy>
|
||||||
|
<BreakpointProxy
|
||||||
|
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||||
|
<BreakpointContent
|
||||||
|
uuid = "E2F34C60-2F85-4D8F-8A0A-75BD5B33C607"
|
||||||
|
shouldBeEnabled = "No"
|
||||||
|
ignoreCount = "0"
|
||||||
|
continueAfterRunningActions = "No"
|
||||||
|
filePath = "Classes_cocos/UIViewController+Purchase.mm"
|
||||||
|
startingColumnNumber = "9223372036854775807"
|
||||||
|
endingColumnNumber = "9223372036854775807"
|
||||||
|
startingLineNumber = "158"
|
||||||
|
endingLineNumber = "158"
|
||||||
|
landmarkName = "-alertWithTitle:message:"
|
||||||
|
landmarkType = "7">
|
||||||
|
</BreakpointContent>
|
||||||
|
</BreakpointProxy>
|
||||||
|
<BreakpointProxy
|
||||||
|
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||||
|
<BreakpointContent
|
||||||
|
uuid = "9119790E-EDB6-49DA-B186-43B0CE2E3876"
|
||||||
|
shouldBeEnabled = "No"
|
||||||
|
ignoreCount = "0"
|
||||||
|
continueAfterRunningActions = "No"
|
||||||
|
filePath = "Classes_cocos/UIViewController+Purchase.mm"
|
||||||
|
startingColumnNumber = "9223372036854775807"
|
||||||
|
endingColumnNumber = "9223372036854775807"
|
||||||
|
startingLineNumber = "159"
|
||||||
|
endingLineNumber = "159"
|
||||||
|
landmarkName = "-alertWithTitle:message:"
|
||||||
|
landmarkType = "7">
|
||||||
|
</BreakpointContent>
|
||||||
|
</BreakpointProxy>
|
||||||
|
<BreakpointProxy
|
||||||
|
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||||
|
<BreakpointContent
|
||||||
|
uuid = "A9CA3DDC-6205-4BD4-8933-EA7A3B91A6F3"
|
||||||
|
shouldBeEnabled = "No"
|
||||||
|
ignoreCount = "0"
|
||||||
|
continueAfterRunningActions = "No"
|
||||||
|
filePath = "purchase/StoreObserver.m"
|
||||||
|
startingColumnNumber = "9223372036854775807"
|
||||||
|
endingColumnNumber = "9223372036854775807"
|
||||||
|
startingLineNumber = "206"
|
||||||
|
endingLineNumber = "206"
|
||||||
|
landmarkName = "-handleTransactionFailed:"
|
||||||
|
landmarkType = "7">
|
||||||
|
</BreakpointContent>
|
||||||
|
</BreakpointProxy>
|
||||||
|
<BreakpointProxy
|
||||||
|
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||||
|
<BreakpointContent
|
||||||
|
uuid = "15FCA0D4-7E42-43F6-9DC2-FB0643322A2D"
|
||||||
|
shouldBeEnabled = "Yes"
|
||||||
|
ignoreCount = "0"
|
||||||
|
continueAfterRunningActions = "No"
|
||||||
|
filePath = "purchase/StoreObserver.m"
|
||||||
|
startingColumnNumber = "9223372036854775807"
|
||||||
|
endingColumnNumber = "9223372036854775807"
|
||||||
|
startingLineNumber = "264"
|
||||||
|
endingLineNumber = "264"
|
||||||
|
landmarkName = "-onPurchaseResult:errorCode:errorDescription:"
|
||||||
|
landmarkType = "7">
|
||||||
|
</BreakpointContent>
|
||||||
|
</BreakpointProxy>
|
||||||
</Breakpoints>
|
</Breakpoints>
|
||||||
</Bucket>
|
</Bucket>
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<VariablesViewState
|
||||||
|
version = "1.0">
|
||||||
|
<ContextStates>
|
||||||
|
<ContextState
|
||||||
|
contextName = "-[StoreObserver handlePurchasedTransaction:]:StoreObserver.m">
|
||||||
|
<PersistentStrings>
|
||||||
|
<PersistentString
|
||||||
|
value = " transaction.originalID">
|
||||||
|
</PersistentString>
|
||||||
|
</PersistentStrings>
|
||||||
|
</ContextState>
|
||||||
|
</ContextStates>
|
||||||
|
</VariablesViewState>
|
File diff suppressed because one or more lines are too long
38
js/main.js
38
js/main.js
@ -363,6 +363,20 @@ function tokenPrice(funId, tokenName, amount) {
|
|||||||
function fiatList(funId) {
|
function fiatList(funId) {
|
||||||
promiseCb(funId, jc.wallet.paySvr.fetchFiatList());
|
promiseCb(funId, jc.wallet.paySvr.fetchFiatList());
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* query price of crypto -> usd
|
||||||
|
* @param {string} crypto
|
||||||
|
* @param {number} chain chain id,
|
||||||
|
*/
|
||||||
|
function getCryptoPriceOfUSD(funId, crypto, chain) {
|
||||||
|
let chainData = jc.wallet.currentChain;
|
||||||
|
if (chain) {
|
||||||
|
chainData = jc.wallet.chainList.find((v) => v.chainId === +chain);
|
||||||
|
}
|
||||||
|
let network = chainData.type !== 'Testnet' ? chainData.network || chainData.symbol : 'ARBITRUM';
|
||||||
|
network = network || 'ARBITRUM';
|
||||||
|
promiseCb(funId, jc.wallet.paySvr.queryTokenPrice(network, crypto));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* format price
|
* format price
|
||||||
@ -493,20 +507,34 @@ function nftMallBuy(funId, currency, addresses, ids, amounts, values, signature,
|
|||||||
|
|
||||||
// end of NFT mall
|
// end of NFT mall
|
||||||
|
|
||||||
|
// begin of token mall
|
||||||
|
// buy ceg with usdt, usdc
|
||||||
|
function buyTokenWithErc20(funId, currency, amount, gas, estimate) {
|
||||||
|
promiseCb(
|
||||||
|
funId,
|
||||||
|
jc.wallet.jcStandard.buyTokenWithErc20({
|
||||||
|
currency,
|
||||||
|
amount,
|
||||||
|
estimate,
|
||||||
|
}),
|
||||||
|
(v) => JSON.stringify(v)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// begin of token mall
|
||||||
|
|
||||||
// begin of google pay
|
// begin of google pay
|
||||||
function queryGoogleProducts(funId, productIds) {
|
function queryGoogleProducts(funId, productIds) {
|
||||||
let ids = JSON.parse(productIds);
|
let ids = JSON.parse(productIds);
|
||||||
console.log('queryGoogleProducts:: ' + productIds);
|
console.log('queryAppleProducts:: ' + productIds);
|
||||||
promiseCb(funId, jc.wallet.paySvr.queryGoogleProducts(ids));
|
promiseCb(funId, jc.wallet.paySvr.queryIOSProducts(ids));
|
||||||
}
|
}
|
||||||
|
|
||||||
function queryGooglePurchases(funId) {
|
function queryGooglePurchases(funId) {
|
||||||
promiseCb(funId, jc.wallet.paySvr.queryGooglePurchases());
|
promiseCb(funId, jc.wallet.paySvr.queryIOSPurchases());
|
||||||
}
|
}
|
||||||
|
|
||||||
function beginGoogleBuy(funId, productId, orderId) {
|
function beginGoogleBuy(funId, productId, orderId) {
|
||||||
promiseCb(funId, jc.wallet.paySvr.buyGoogleProduct(productId, orderId));
|
promiseCb(funId, jc.wallet.paySvr.beginIOSPurchase(productId, orderId));
|
||||||
}
|
}
|
||||||
|
|
||||||
// end of google pay
|
// end of google pay
|
||||||
|
|
||||||
|
13
purchase/SKProduct+SKProductAdditions.h
Normal file
13
purchase/SKProduct+SKProductAdditions.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
See LICENSE folder for this sample’s licensing information.
|
||||||
|
|
||||||
|
Abstract:
|
||||||
|
Creates a category for the SKProduct class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@import StoreKit;
|
||||||
|
|
||||||
|
@interface SKProduct (SKProductAdditions)
|
||||||
|
/// - returns: The cost of the product formatted in the local currency.
|
||||||
|
-(NSString *)regularPrice;
|
||||||
|
@end
|
19
purchase/SKProduct+SKProductAdditions.m
Normal file
19
purchase/SKProduct+SKProductAdditions.m
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
See LICENSE folder for this sample’s licensing information.
|
||||||
|
|
||||||
|
Abstract:
|
||||||
|
Creates a category for the SKProduct class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "SKProduct+SKProductAdditions.h"
|
||||||
|
|
||||||
|
@implementation SKProduct (SKProductAdditions)
|
||||||
|
/// - returns: The cost of the product formatted in the local currency.
|
||||||
|
-(NSString *)regularPrice {
|
||||||
|
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
|
||||||
|
[formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
|
||||||
|
[formatter setLocale: self.priceLocale];
|
||||||
|
return [formatter stringFromNumber:self.price];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
@ -23,11 +23,13 @@ Retrieves product information from the App Store using SKRequestDelegate, SKProd
|
|||||||
@property (strong) NSMutableArray *storeResponse;
|
@property (strong) NSMutableArray *storeResponse;
|
||||||
|
|
||||||
/// Starts the product request with the specified identifiers.
|
/// Starts the product request with the specified identifiers.
|
||||||
-(void)startProductRequestWithIdentifiers:(NSArray *)identifiers;
|
-(void)fetchProductsMatchingIdentifiers:(NSArray *)identifiers completionBlock: (nullable void (^) (PCSProductRequestStatus status, NSMutableArray * _Nullable storeResponse))completionBlock;
|
||||||
|
|
||||||
/// - returns: Existing product's title matching the specified product identifier.
|
/// - returns: Existing product's title matching the specified product identifier.
|
||||||
-(NSString *)titleMatchingIdentifier:(NSString *)identifier;
|
-(NSString *)titleMatchingIdentifier:(NSString *)identifier;
|
||||||
|
|
||||||
|
-(SKProduct *)productMatchingIdentifier:(NSString *)identifier;
|
||||||
|
|
||||||
/// - returns: Existing product's title associated with the specified payment transaction.
|
/// - returns: Existing product's title associated with the specified payment transaction.
|
||||||
-(NSString *)titleMatchingPaymentTransaction:(SKPaymentTransaction *)transaction;
|
-(NSString *)titleMatchingPaymentTransaction:(SKPaymentTransaction *)transaction;
|
||||||
@end
|
@end
|
||||||
|
@ -19,6 +19,9 @@ Retrieves product information from the App Store using SKRequestDelegate, SKProd
|
|||||||
|
|
||||||
/// Keeps a strong reference to the product request.
|
/// Keeps a strong reference to the product request.
|
||||||
@property (strong) SKProductsRequest *productRequest;
|
@property (strong) SKProductsRequest *productRequest;
|
||||||
|
|
||||||
|
@property (copy, nonatomic) void (^completionBlock) (PCSProductRequestStatus, NSMutableArray *);
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation StoreManager
|
@implementation StoreManager
|
||||||
@ -47,13 +50,10 @@ Retrieves product information from the App Store using SKRequestDelegate, SKProd
|
|||||||
|
|
||||||
#pragma mark - Request Information
|
#pragma mark - Request Information
|
||||||
|
|
||||||
/// Starts the product request with the specified identifiers.
|
|
||||||
-(void)startProductRequestWithIdentifiers:(NSArray *)identifiers {
|
|
||||||
[self fetchProductsMatchingIdentifiers:identifiers];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches information about your products from the App Store.
|
/// Fetches information about your products from the App Store.
|
||||||
-(void)fetchProductsMatchingIdentifiers:(NSArray *)identifiers {
|
-(void)fetchProductsMatchingIdentifiers:(NSArray *)identifiers completionBlock: (void (^) (PCSProductRequestStatus status, NSMutableArray *resultAsString))completionBlock{
|
||||||
|
self.completionBlock = completionBlock;
|
||||||
// Create a set for the product identifiers.
|
// Create a set for the product identifiers.
|
||||||
NSSet *productIdentifiers = [NSSet setWithArray:identifiers];
|
NSSet *productIdentifiers = [NSSet setWithArray:identifiers];
|
||||||
|
|
||||||
@ -89,7 +89,9 @@ Retrieves product information from the App Store using SKRequestDelegate, SKProd
|
|||||||
|
|
||||||
if (self.storeResponse.count > 0) {
|
if (self.storeResponse.count > 0) {
|
||||||
self.status = PCSStoreResponse;
|
self.status = PCSStoreResponse;
|
||||||
|
if (_completionBlock) {
|
||||||
|
_completionBlock(self.status, self.storeResponse);
|
||||||
|
}
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:PCSProductRequestNotification object:self];
|
[[NSNotificationCenter defaultCenter] postNotificationName:PCSProductRequestNotification object:self];
|
||||||
});
|
});
|
||||||
@ -123,6 +125,16 @@ Retrieves product information from the App Store using SKRequestDelegate, SKProd
|
|||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-(SKProduct *) productMatchingIdentifier:(NSString *)identifier {
|
||||||
|
SKProduct *result = nil;
|
||||||
|
for (SKProduct *product in self.availableProducts) {
|
||||||
|
if ([product.productIdentifier isEqualToString:identifier]) {
|
||||||
|
result = product;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/// - returns: Existing product's title associated with the specified payment transaction.
|
/// - returns: Existing product's title associated with the specified payment transaction.
|
||||||
-(NSString *)titleMatchingPaymentTransaction:(SKPaymentTransaction *) transaction {
|
-(NSString *)titleMatchingPaymentTransaction:(SKPaymentTransaction *) transaction {
|
||||||
NSString *title = [self titleMatchingIdentifier:transaction.payment.productIdentifier];
|
NSString *title = [self titleMatchingIdentifier:transaction.payment.productIdentifier];
|
||||||
|
@ -16,24 +16,34 @@ Implements the SKPaymentTransactionObserver protocol. Handles purchasing and res
|
|||||||
- returns: true if the user is allowed to make payments and false, otherwise. Tell StoreManager to query the App Store when the user is allowed to make payments and there are product identifiers to be
|
- returns: true if the user is allowed to make payments and false, otherwise. Tell StoreManager to query the App Store when the user is allowed to make payments and there are product identifiers to be
|
||||||
queried.
|
queried.
|
||||||
*/
|
*/
|
||||||
@property (NS_NONATOMIC_IOSONLY, readonly) BOOL isAuthorizedForPayments;
|
@property(NS_NONATOMIC_IOSONLY, readonly) BOOL isAuthorizedForPayments;
|
||||||
|
|
||||||
/// Indicates the cause of the purchase failure.
|
/// Indicates the cause of the purchase failure.
|
||||||
@property (nonatomic, copy) NSString *message;
|
@property(nonatomic, copy) NSString *message;
|
||||||
|
|
||||||
/// Keeps track of all purchases.
|
/// Keeps track of all purchases.
|
||||||
@property (strong) NSMutableArray *productsPurchased;
|
@property(strong) NSMutableArray *productsPurchased;
|
||||||
|
|
||||||
/// Keeps track of all restored purchases.
|
/// Keeps track of all restored purchases.
|
||||||
@property (strong) NSMutableArray *productsRestored;
|
@property(strong) NSMutableArray *productsRestored;
|
||||||
|
|
||||||
|
@property(strong) NSMutableDictionary *pendingTransactions;
|
||||||
|
|
||||||
|
@property(strong) NSMutableSet *finishedTransactions;
|
||||||
|
|
||||||
/// Indicates the purchase status.
|
/// Indicates the purchase status.
|
||||||
@property (nonatomic) PCSPurchaseStatus status;
|
@property(nonatomic) PCSPurchaseStatus status;
|
||||||
|
|
||||||
/// Implements the purchase of a product.
|
/// Implements the purchase of a product.
|
||||||
-(void)buy:(SKProduct *)product;
|
- (void)buy:(SKProduct *)product orderId:(NSString *)orderId;
|
||||||
|
|
||||||
|
- (void)finishTransaction:(NSString *)transactionIdentifier;
|
||||||
|
|
||||||
|
- (NSArray *)queryPendingTransactions;
|
||||||
|
|
||||||
/// Implements the restoration of previously completed purchases.
|
/// Implements the restoration of previously completed purchases.
|
||||||
-(void)restore;
|
- (void)restore;
|
||||||
@end
|
|
||||||
|
|
||||||
|
- (NSString *)getAppReceipt;
|
||||||
|
|
||||||
|
@end
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
See LICENSE folder for this sample’s licensing information.
|
See LICENSE folder for this sample’s licensing information.
|
||||||
|
|
||||||
Abstract:
|
Abstract:
|
||||||
Implements the SKPaymentTransactionObserver protocol. Handles purchasing and restoring products using paymentQueue:updatedTransactions: .
|
Implements the SKPaymentTransactionObserver protocol. Handles purchasing and restoring products using paymentQueue:updatedTransactions: .
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "StoreObserver.h"
|
#import "StoreObserver.h"
|
||||||
|
|
||||||
@ -14,155 +14,262 @@ Implements the SKPaymentTransactionObserver protocol. Handles purchasing and res
|
|||||||
|
|
||||||
@implementation StoreObserver
|
@implementation StoreObserver
|
||||||
+ (StoreObserver *)sharedInstance {
|
+ (StoreObserver *)sharedInstance {
|
||||||
static dispatch_once_t onceToken;
|
static dispatch_once_t onceToken;
|
||||||
static StoreObserver * storeObserverSharedInstance;
|
static StoreObserver * storeObserverSharedInstance;
|
||||||
|
|
||||||
dispatch_once(&onceToken, ^{
|
dispatch_once(&onceToken, ^{
|
||||||
storeObserverSharedInstance = [[StoreObserver alloc] init];
|
storeObserverSharedInstance = [[StoreObserver alloc] init];
|
||||||
});
|
});
|
||||||
return storeObserverSharedInstance;
|
return storeObserverSharedInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)init {
|
- (instancetype)init {
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if (self != nil) {
|
if (self != nil) {
|
||||||
_hasRestorablePurchases = NO;
|
_hasRestorablePurchases = NO;
|
||||||
_productsPurchased = [[NSMutableArray alloc] initWithCapacity:0];
|
_productsRestored = [[NSMutableArray alloc] initWithCapacity:0];
|
||||||
_productsRestored = [[NSMutableArray alloc] initWithCapacity:0];
|
_pendingTransactions = [[NSMutableDictionary alloc] init];
|
||||||
_status = PCSPurchaseStatusNone;
|
_finishedTransactions = [[NSMutableSet alloc] init];
|
||||||
}
|
_status = PCSPurchaseStatusNone;
|
||||||
return self;
|
}
|
||||||
|
return self;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
Indicates whether the user is allowed to make payments.
|
Indicates whether the user is allowed to make payments.
|
||||||
- returns: true if the user is allowed to make payments and false, otherwise. Tell StoreManager to query the App Store when the user is allowed to make payments and there are product identifiers to be
|
- returns: true if the user is allowed to make payments and false, otherwise. Tell StoreManager to query the App Store when the user is allowed to make payments and there are product identifiers to be
|
||||||
queried.
|
queried.
|
||||||
*/
|
*/
|
||||||
-(BOOL)isAuthorizedForPayments {
|
-(BOOL)isAuthorizedForPayments {
|
||||||
return [SKPaymentQueue canMakePayments];
|
return [SKPaymentQueue canMakePayments];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString*)getAppReceipt
|
||||||
|
{
|
||||||
|
NSBundle* bundle = [NSBundle mainBundle];
|
||||||
|
if ([bundle respondsToSelector: @selector(appStoreReceiptURL)])
|
||||||
|
{
|
||||||
|
NSURL *receiptURL = [bundle appStoreReceiptURL];
|
||||||
|
if ([[NSFileManager defaultManager] fileExistsAtPath: [receiptURL path]])
|
||||||
|
{
|
||||||
|
NSData *receipt = [NSData dataWithContentsOfURL: receiptURL];
|
||||||
|
|
||||||
|
#if MAC_APPSTORE
|
||||||
|
// The base64EncodedStringWithOptions method was only added in OSX 10.9.
|
||||||
|
NSString* result = [receipt mgb64_base64EncodedString];
|
||||||
|
#else
|
||||||
|
NSString* result = [receipt base64EncodedStringWithOptions: 0];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NSLog(@"No App Receipt found");
|
||||||
|
return @"";
|
||||||
|
}
|
||||||
|
#if !MAC_APPSTORE
|
||||||
|
- (BOOL)isiOS6OrEarlier
|
||||||
|
{
|
||||||
|
float version = [[[UIDevice currentDevice] systemVersion] floatValue];
|
||||||
|
return version < 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
// Retrieve a receipt for the transaction, which will either
|
||||||
|
// be the old style transaction receipt on <= iOS 6,
|
||||||
|
// or the App Receipt in OSX and iOS 7+.
|
||||||
|
- (NSString*)selectReceipt:(SKPaymentTransaction*)transaction
|
||||||
|
{
|
||||||
|
#if MAC_APPSTORE
|
||||||
|
return [self getAppReceipt];
|
||||||
|
#else
|
||||||
|
if ([self isiOS6OrEarlier])
|
||||||
|
{
|
||||||
|
if (nil == transaction)
|
||||||
|
{
|
||||||
|
return @"";
|
||||||
|
}
|
||||||
|
NSString* receipt;
|
||||||
|
receipt = [[NSString alloc] initWithData: transaction.transactionReceipt encoding: NSUTF8StringEncoding];
|
||||||
|
|
||||||
|
return receipt;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return [self getAppReceipt];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Submit Payment Request
|
#pragma mark - Submit Payment Request
|
||||||
|
|
||||||
/// Creates and adds a payment request to the payment queue.
|
/// Creates and adds a payment request to the payment queue.
|
||||||
-(void)buy:(SKProduct *)product {
|
-(void)buy:(SKProduct *)product orderId: (NSString *) orderId {
|
||||||
SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];
|
SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];
|
||||||
[[SKPaymentQueue defaultQueue] addPayment:payment];
|
// payment.applicationUsername = orderId;
|
||||||
|
// generate UUID for order id
|
||||||
|
NSString *testOrderId = [[NSUUID UUID] UUIDString];
|
||||||
|
[payment performSelector: @selector(setApplicationUsername:) withObject: testOrderId];
|
||||||
|
[[SKPaymentQueue defaultQueue] addPayment:payment];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Restore All Restorable Purchases
|
#pragma mark - Restore All Restorable Purchases
|
||||||
|
|
||||||
/// Restores all previously completed purchases.
|
/// Restores all previously completed purchases.
|
||||||
-(void)restore {
|
-(void)restore {
|
||||||
if (self.productsRestored.count > 0) {
|
if (self.productsRestored.count > 0) {
|
||||||
[self.productsRestored removeAllObjects];
|
[self.productsRestored removeAllObjects];
|
||||||
}
|
}
|
||||||
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
|
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - SKPaymentTransactionObserver Methods
|
#pragma mark - SKPaymentTransactionObserver Methods
|
||||||
|
|
||||||
/// Called when there are transactions in the payment queue.
|
/// Called when there are transactions in the payment queue.
|
||||||
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
|
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
|
||||||
for(SKPaymentTransaction *transaction in transactions) {
|
for(SKPaymentTransaction *transaction in transactions) {
|
||||||
switch (transaction.transactionState) {
|
switch (transaction.transactionState) {
|
||||||
case SKPaymentTransactionStatePurchasing: break;
|
case SKPaymentTransactionStatePurchasing: break;
|
||||||
// Do not block your UI. Allow the user to continue using your app.
|
// Do not block your UI. Allow the user to continue using your app.
|
||||||
case SKPaymentTransactionStateDeferred: NSLog(@"%@", PCSMessagesDeferred);
|
case SKPaymentTransactionStateDeferred: NSLog(@"%@", PCSMessagesDeferred);
|
||||||
break;
|
break;
|
||||||
// The purchase was successful.
|
// The purchase was successful.
|
||||||
case SKPaymentTransactionStatePurchased: [self handlePurchasedTransaction:transaction];
|
case SKPaymentTransactionStatePurchased: [self handleTransactionPurchased:transaction];
|
||||||
break;
|
break;
|
||||||
// The transaction failed.
|
// The transaction failed.
|
||||||
case SKPaymentTransactionStateFailed: [self handleFailedTransaction:transaction];
|
case SKPaymentTransactionStateFailed: [self handleTransactionFailed:transaction];
|
||||||
break;
|
break;
|
||||||
// There are restored products.
|
// There are restored products.
|
||||||
case SKPaymentTransactionStateRestored: [self handleRestoredTransaction:transaction];
|
case SKPaymentTransactionStateRestored: [self handleTransactionRestored:transaction];
|
||||||
break;
|
break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Logs all transactions that have been removed from the payment queue.
|
/// Logs all transactions that have been removed from the payment queue.
|
||||||
- (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions {
|
- (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions {
|
||||||
for(SKPaymentTransaction *transaction in transactions) {
|
for(SKPaymentTransaction *transaction in transactions) {
|
||||||
NSLog(@"%@ %@", transaction.payment.productIdentifier, PCSMessagesRemove);
|
NSLog(@"%@ %@", transaction.payment.productIdentifier, PCSMessagesRemove);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called when an error occur while restoring purchases. Notifies the user about the error.
|
/// Called when an error occur while restoring purchases. Notifies the user about the error.
|
||||||
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error {
|
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error {
|
||||||
if (error.code != SKErrorPaymentCancelled) {
|
NSLog(@"%@\n%@", PCSMessagesFailed, error.localizedDescription);
|
||||||
self.status = PCSRestoreFailed;
|
// if (error.code != SKErrorPaymentCancelled) {
|
||||||
self.message = error.localizedDescription;
|
// self.status = PCSRestoreFailed;
|
||||||
|
// self.message = error.localizedDescription;
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
//
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:PCSPurchaseNotification object:self];
|
// dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
});
|
// [[NSNotificationCenter defaultCenter] postNotificationName:PCSPurchaseNotification object:self];
|
||||||
}
|
// });
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called when all restorable transactions have been processed by the payment queue.
|
/// Called when all restorable transactions have been processed by the payment queue.
|
||||||
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
|
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
|
||||||
NSLog(@"%@", PCSMessagesRestorable);
|
NSLog(@"%@\n%@", PCSMessagesNoRestorablePurchases, PCSMessagesPreviouslyBought);
|
||||||
|
// if (!self.hasRestorablePurchases) {
|
||||||
if (!self.hasRestorablePurchases) {
|
// self.status = PCSNoRestorablePurchases;
|
||||||
self.status = PCSNoRestorablePurchases;
|
// self.message = [NSString stringWithFormat:@"%@\n%@", PCSMessagesNoRestorablePurchases, PCSMessagesPreviouslyBought];
|
||||||
self.message = [NSString stringWithFormat:@"%@\n%@", PCSMessagesNoRestorablePurchases, PCSMessagesPreviouslyBought];
|
//
|
||||||
|
// dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
// [[NSNotificationCenter defaultCenter] postNotificationName:PCSPurchaseNotification object:self];
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:PCSPurchaseNotification object:self];
|
// });
|
||||||
});
|
// }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Handle Payment Transactions
|
#pragma mark - Handle Payment Transactions
|
||||||
|
|
||||||
/// Handles successful purchase transactions.
|
/// Handles successful purchase transactions.
|
||||||
-(void)handlePurchasedTransaction:(SKPaymentTransaction*)transaction {
|
-(void)handleTransactionPurchased:(SKPaymentTransaction*)transaction {
|
||||||
[self.productsPurchased addObject:transaction];
|
NSString *transactionId = transaction.transactionIdentifier;
|
||||||
NSLog(@"%@ %@.", PCSMessagesDeliverContent, transaction.payment.productIdentifier);
|
NSLog(@"%@ %@.", PCSMessagesDeliverContent, transaction.payment.productIdentifier);
|
||||||
|
NSLog(@"original transaction id: %@", transactionId);
|
||||||
// Finish the successful transaction.
|
NSLog(@"order id: %@", transaction.payment.applicationUsername);
|
||||||
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
|
[self onTransactionSucceeded: transaction];
|
||||||
}
|
|
||||||
|
|
||||||
/// Handles failed purchase transactions.
|
|
||||||
-(void)handleFailedTransaction:(SKPaymentTransaction*)transaction {
|
|
||||||
self.status = PCSPurchaseFailed;
|
|
||||||
self.message = [NSString stringWithFormat:@"%@ %@ %@",PCSMessagesPurchaseOf, transaction.payment.productIdentifier, PCSMessagesFailed];
|
|
||||||
|
|
||||||
if (transaction.error) {
|
|
||||||
[self.message stringByAppendingString:[NSString stringWithFormat:@"\n%@ %@", PCSMessagesError, transaction.error.localizedDescription]];
|
|
||||||
NSLog(@"%@ %@", PCSMessagesError, transaction.error.localizedDescription);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not send any notifications when the user cancels the purchase.
|
|
||||||
if (transaction.error.code != SKErrorPaymentCancelled) {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:PCSPurchaseNotification object:self];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finish the failed transaction.
|
|
||||||
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles restored purchase transactions.
|
/// Handles restored purchase transactions.
|
||||||
-(void)handleRestoredTransaction:(SKPaymentTransaction*)transaction {
|
-(void)handleTransactionRestored:(SKPaymentTransaction*)transaction {
|
||||||
self.status = PCSRestoreSucceeded;
|
self.status = PCSRestoreSucceeded;
|
||||||
self.hasRestorablePurchases = true;
|
self.hasRestorablePurchases = true;
|
||||||
[self.productsRestored addObject:transaction];
|
[self onTransactionSucceeded: transaction];
|
||||||
NSLog(@"%@ %@.", PCSMessagesRestoreContent, transaction.payment.productIdentifier);
|
}
|
||||||
|
|
||||||
|
/// Handles failed purchase transactions.
|
||||||
|
-(void)handleTransactionFailed:(SKPaymentTransaction*)transaction {
|
||||||
|
self.status = PCSPurchaseFailed;
|
||||||
|
self.message = [NSString stringWithFormat:@"%@ %@ %@",PCSMessagesPurchaseOf, transaction.payment.productIdentifier, PCSMessagesFailed];
|
||||||
|
|
||||||
|
[self onPurchaseResult: transaction.payment.productIdentifier errorCode: 200 + transaction.error.code errorDescription: [NSString stringWithFormat:@"\n%@ %@", PCSMessagesError, transaction.error.localizedDescription]];
|
||||||
|
|
||||||
|
// Finish the failed transaction.
|
||||||
|
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle a new or restored purchase transaction by informing Unity.
|
||||||
|
- (void)onTransactionSucceeded:(SKPaymentTransaction*)transaction
|
||||||
|
{
|
||||||
|
NSString* transactionId = transaction.transactionIdentifier;
|
||||||
|
|
||||||
|
// This should never happen according to Apple's docs, but it does!
|
||||||
|
if (nil == transactionId)
|
||||||
|
{
|
||||||
|
// Make something up, allowing us to identifiy the transaction when finishing it.
|
||||||
|
transactionId = [[NSUUID UUID] UUIDString];
|
||||||
|
NSLog(@"Missing transaction Identifier!");
|
||||||
|
[self onPurchaseResult: transactionId errorCode: 101 errorDescription: @"Missing transaction Identifier!"];
|
||||||
|
}
|
||||||
|
|
||||||
|
// This transaction was marked as finished, but was not cleared from the queue. Try to clear it now, then pass the error up the stack as a DuplicateTransaction
|
||||||
|
if ([self.finishedTransactions containsObject: transactionId])
|
||||||
|
{
|
||||||
|
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
|
||||||
|
NSLog(@"DuplicateTransaction error with product %@ and transactionId %@", transaction.payment.productIdentifier, transactionId);
|
||||||
|
[self onPurchaseResult: transaction.payment.productIdentifier errorCode: 100 errorDescription: @"Duplicate transaction occurred"];
|
||||||
|
return; // EARLY RETURN
|
||||||
|
}
|
||||||
|
|
||||||
|
// Item was successfully purchased or restored.
|
||||||
|
if (nil == [self.pendingTransactions objectForKey: transactionId])
|
||||||
|
{
|
||||||
|
[self.pendingTransactions setObject: transaction forKey: transactionId];
|
||||||
|
}
|
||||||
|
[self onPurchaseResult: transactionId errorCode: 0 errorDescription: @""];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray *)queryPendingTransactions{
|
||||||
|
return [self.pendingTransactions allKeys];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)finishTransaction:(NSString *)transactionIdentifier{
|
||||||
|
SKPaymentTransaction* transaction = [self.pendingTransactions objectForKey: transactionIdentifier];
|
||||||
|
if (nil != transaction) {
|
||||||
|
NSLog(@"Finishing transaction %@", transactionIdentifier);
|
||||||
|
[[SKPaymentQueue defaultQueue] finishTransaction: transaction]; // If this fails (user not logged into the store?), transaction is already removed from pendingTransactions, so future calls to finishTransaction will not retry
|
||||||
|
[self.pendingTransactions removeObjectForKey: transactionIdentifier];
|
||||||
|
[self.finishedTransactions addObject: transactionIdentifier];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
NSLog(@"Transaction %@ not pending, nothing to finish here", transactionIdentifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)onPurchaseResult:(NSString*)productId errorCode:(NSInteger)errorCode errorDescription:(NSString*)errorDescription
|
||||||
|
{
|
||||||
|
NSMutableDictionary* dic = [[NSMutableDictionary alloc] init];
|
||||||
|
[dic setObject: productId forKey: @"dataid"];
|
||||||
|
[dic setObject: [NSNumber numberWithInteger: errorCode] forKey: @"errcode"];
|
||||||
|
[dic setObject: errorDescription forKey: @"errmsg"];
|
||||||
|
|
||||||
|
// NSData* data = [NSJSONSerialization dataWithJSONObject: dic options: 0 error: nil];
|
||||||
|
// NSString* result = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:PCSPurchaseNotification object:self];
|
[[NSNotificationCenter defaultCenter] postNotificationName:PCSPurchaseNotification object: self userInfo:dic];
|
||||||
});
|
});
|
||||||
|
|
||||||
// Finish the restored transaction.
|
|
||||||
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
33
test-in-app-purchase.storekit
Normal file
33
test-in-app-purchase.storekit
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"identifier" : "E7768E5D",
|
||||||
|
"nonRenewingSubscriptions" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"products" : [
|
||||||
|
{
|
||||||
|
"displayPrice" : "0.99",
|
||||||
|
"familyShareable" : false,
|
||||||
|
"internalID" : "07066426",
|
||||||
|
"localizations" : [
|
||||||
|
{
|
||||||
|
"description" : "buy 100ceg",
|
||||||
|
"displayName" : "100ceg",
|
||||||
|
"locale" : "en_US"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"productID" : "2999",
|
||||||
|
"referenceName" : "100d",
|
||||||
|
"type" : "Consumable"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"subscriptionGroups" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"version" : {
|
||||||
|
"major" : 2,
|
||||||
|
"minor" : 0
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user