| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581 |
- //
- // PPOTConfiguration.m
- // PayPalOneTouch
- //
- // Copyright © 2015 PayPal, Inc. All rights reserved.
- //
- #import "PPOTConfiguration.h"
- #if __has_include("PayPalUtils.h")
- #import "PPOTJSONHelper.h"
- #import "PPOTMacros.h"
- #import "PPOTSimpleKeychain.h"
- #import "PPOTURLSession.h"
- #else
- #import <PayPalUtils/PPOTJSONHelper.h>
- #import <PayPalUtils/PPOTMacros.h>
- #import <PayPalUtils/PPOTSimpleKeychain.h>
- #import <PayPalUtils/PPOTURLSession.h>
- #endif
- #if __has_include("BraintreeCore.h")
- #import "BTLogger_Internal.h"
- #else
- #import <BraintreeCore/BTLogger_Internal.h>
- #endif
- #import <libkern/OSAtomic.h>
- #include "PPDefaultConfigurationJSON.h"
- // `PPDefaultConfigurationJSON.h` is generated by a build script from `otc-config.ios.json`;
- // it defines these two variables:
- // unsigned char configuration_otc_config_ios_json[];
- // unsigned int configuration_otc_config_ios_json_len;
- // `configuration_otc_config_ios_json` holds the contents of the `otc-config.ios.json` file.
- #define PPEnvironmentProduction @"live"
- #define kConfigurationFileDownloadURL CARDIO_STR(@"https://www.paypalobjects.com/webstatic/otc/otc-config.ios.json")
- #define kConfigurationFileDownloadTimeout 60
- #define kConfigurationFileDownloadRetryInterval (5 * 60) // 5 minutes
- #define kPPOTConfigurationFileMaximumAcceptableObsolescence (4 * 60 * 60) // 4 hours
- #define kPPOTConfigurationSupportedProtocolVersionsForWallet @[@1, @2, @3]
- #define kPPOTConfigurationSupportedProtocolVersionsForBrowser @[@0, @3]
- #define kPPOTConfigurationKeyOs CARDIO_STR(@"os")
- #define kPPOTConfigurationKeyFileTimestamp CARDIO_STR(@"file_timestamp")
- #define kPPOTConfigurationKeyTarget CARDIO_STR(@"target")
- #define kPPOTConfigurationKeyProtocolVersion CARDIO_STR(@"protocol")
- #define kPPOTConfigurationKeySupportedLocales CARDIO_STR(@"supported_locales")
- #define kPPOTConfigurationKeyScope CARDIO_STR(@"scope")
- #define kPPOTConfigurationKeyURLScheme CARDIO_STR(@"scheme")
- #define kPPOTConfigurationKeyApplications CARDIO_STR(@"applications")
- #define kPPOTConfigurationKeyEndpoints CARDIO_STR(@"endpoints")
- #define kPPOTConfigurationKeyURL CARDIO_STR(@"url")
- #define kPPOTConfigurationKeyCertificateSerialNumber CARDIO_STR(@"certificate_serial_number")
- #define kPPOTConfigurationKeyCertificate CARDIO_STR(@"certificate")
- #define kPPOTConfigurationKeyOAuthRecipes CARDIO_STR(@"oauth2_recipes_in_decreasing_priority_order")
- #define kPPOTConfigurationKeyCheckoutRecipes CARDIO_STR(@"checkout_recipes_in_decreasing_priority_order")
- #define kPPOTConfigurationKeyBillingAgreementRecipes CARDIO_STR(@"billing_agreement_recipes_in_decreasing_priority_order")
- #define kPPOTConfigurationValueWallet CARDIO_STR(@"wallet")
- #define kPPOTConfigurationValueBrowser CARDIO_STR(@"browser")
- #define kPPOTCoderKeyConfigurationRecipeTarget CARDIO_STR(@"target")
- #define kPPOTCoderKeyConfigurationRecipeProtocolVersion CARDIO_STR(@"protocol")
- #define kPPOTCoderKeyConfigurationRecipeSupportedLocales CARDIO_STR(@"supportedLocales")
- #define kPPOTCoderKeyConfigurationRecipeTargetAppURLScheme CARDIO_STR(@"targetAppURLScheme")
- #define kPPOTCoderKeyConfigurationRecipeTargetAppBundleIDs CARDIO_STR(@"targetAppBundleIDs")
- #define kPPOTCoderKeyConfigurationRecipeEndpoints CARDIO_STR(@"endpoints")
- #define kPPOTCoderKeyConfigurationRecipeScope CARDIO_STR(@"scope")
- #define kPPOTCoderKeyConfigurationRecipeURL CARDIO_STR(@"url")
- #define kPPOTCoderKeyConfigurationRecipeCertificateSerialNumber CARDIO_STR(@"certificate_serial_number")
- #define kPPOTCoderKeyConfigurationRecipeCertificate CARDIO_STR(@"certificate")
- #define kPPOTCoderKeyConfigurationDownloadTime CARDIO_STR(@"downloadTime")
- #define kPPOTCoderKeyConfigurationTimestamp CARDIO_STR(@"timestamp")
- #define kPPOTCoderKeyConfigurationOAuthRecipes CARDIO_STR(@"oAuthRecipes")
- #define kPPOTCoderKeyConfigurationCheckoutRecipes CARDIO_STR(@"checkoutRecipes")
- #define kPPOTCoderKeyConfigurationBillingAgreementRecipes CARDIO_STR(@"billingAgreementRecipes")
- #define kPPOTKeychainConfiguration CARDIO_STR(@"PayPal_OTC_Configuration")
- #define LOG_ERROR_AND_RETURN_NIL { PPSDKLog(@"Bad configuration: error %d", __LINE__); return nil; }
- #define STRING_FROM_DICTIONARY(STRING, DICTIONARY, KEY) \
- NSString *STRING = [PPOTJSONHelper stringFromDictionary:DICTIONARY withKey:KEY]; \
- if (!STRING) LOG_ERROR_AND_RETURN_NIL
- #define DICTIONARY_FROM_DICTIONARY(DICTIONARY1, DICTIONARY2, KEY, REQUIRED) \
- NSDictionary *DICTIONARY1 = [PPOTJSONHelper dictionaryFromDictionary:DICTIONARY2 withKey:KEY]; \
- if (REQUIRED && !DICTIONARY1) LOG_ERROR_AND_RETURN_NIL
- #define STRING_ARRAY_FROM_DICTIONARY(ARRAY, DICTIONARY, KEY, REQUIRED) \
- NSArray *ARRAY = [PPOTJSONHelper stringArrayFromDictionary:DICTIONARY withKey:KEY]; \
- if (REQUIRED && !ARRAY) LOG_ERROR_AND_RETURN_NIL
- #define DICTIONARY_ARRAY_FROM_DICTIONARY(ARRAY, DICTIONARY, KEY, REQUIRED) \
- NSArray *ARRAY = [PPOTJSONHelper dictionaryArrayFromDictionary:DICTIONARY withKey:KEY]; \
- if (REQUIRED && !ARRAY) LOG_ERROR_AND_RETURN_NIL
- #pragma mark - PPOTConfigurationRecipe
- @implementation PPOTConfigurationRecipe
- - (instancetype)initWithDictionary:(NSDictionary *)dictionary {
- STRING_FROM_DICTIONARY(targetString, dictionary, kPPOTConfigurationKeyTarget)
- STRING_FROM_DICTIONARY(protocolVersionString, dictionary, kPPOTConfigurationKeyProtocolVersion)
- NSNumber *protocolVersionNumber = [NSNumber numberWithInteger:[protocolVersionString integerValue]];
- if ((self = [super init])) {
- if ([targetString isEqualToString:kPPOTConfigurationValueWallet]) {
- _target = PPOTRequestTargetOnDeviceApplication;
- if (![kPPOTConfigurationSupportedProtocolVersionsForWallet containsObject:protocolVersionNumber]) {
- LOG_ERROR_AND_RETURN_NIL
- }
- _protocolVersion = protocolVersionNumber;
- STRING_ARRAY_FROM_DICTIONARY(supportedLocalesArray, dictionary, kPPOTConfigurationKeySupportedLocales, NO)
- // protect against capitalization mistakes:
- NSMutableArray *uppercasedSupportedLocalesArray = [NSMutableArray arrayWithCapacity:[supportedLocalesArray count]];
- for (NSString *locale in supportedLocalesArray) {
- [uppercasedSupportedLocalesArray addObject:[locale uppercaseString]];
- }
- _supportedLocales = uppercasedSupportedLocalesArray;
- STRING_FROM_DICTIONARY(targetAppURLScheme, dictionary, kPPOTConfigurationKeyURLScheme)
- if ([targetAppURLScheme rangeOfString:@":"].location != NSNotFound ||
- [targetAppURLScheme rangeOfString:@"/"].location != NSNotFound) {
- LOG_ERROR_AND_RETURN_NIL
- }
- _targetAppURLScheme = targetAppURLScheme;
- STRING_ARRAY_FROM_DICTIONARY(targetsArray, dictionary, kPPOTConfigurationKeyApplications, YES)
- _targetAppBundleIDs = targetsArray;
- }
- else if ([targetString isEqualToString:kPPOTConfigurationValueBrowser]) {
- _target = PPOTRequestTargetBrowser;
- if (![kPPOTConfigurationSupportedProtocolVersionsForBrowser containsObject:protocolVersionNumber]) {
- LOG_ERROR_AND_RETURN_NIL
- }
- _protocolVersion = protocolVersionNumber;
- }
- else {
- LOG_ERROR_AND_RETURN_NIL
- }
- }
- return self;
- }
- #pragma mark - NSCoding
- - (instancetype)initWithCoder:(NSCoder *)aDecoder {
- if ((self = [self init])) {
- _target = ((NSNumber *)[aDecoder decodeObjectForKey:kPPOTCoderKeyConfigurationRecipeTarget]).unsignedIntegerValue;
- _protocolVersion = [aDecoder decodeObjectForKey:kPPOTCoderKeyConfigurationRecipeProtocolVersion];
- if (_target == PPOTRequestTargetOnDeviceApplication) {
- _targetAppURLScheme = [aDecoder decodeObjectForKey:kPPOTCoderKeyConfigurationRecipeTargetAppURLScheme];
- _targetAppBundleIDs = [aDecoder decodeObjectForKey:kPPOTCoderKeyConfigurationRecipeTargetAppBundleIDs];
- _supportedLocales = [aDecoder decodeObjectForKey:kPPOTCoderKeyConfigurationRecipeSupportedLocales];
- }
- }
- return self;
- }
- - (void)encodeWithCoder:(NSCoder *)aCoder {
- [aCoder encodeObject:@(self.target) forKey:kPPOTCoderKeyConfigurationRecipeTarget];
- [aCoder encodeObject:self.protocolVersion forKey:kPPOTCoderKeyConfigurationRecipeProtocolVersion];
- if (self.target == PPOTRequestTargetOnDeviceApplication) {
- [aCoder encodeObject:self.targetAppURLScheme forKey:kPPOTCoderKeyConfigurationRecipeTargetAppURLScheme];
- [aCoder encodeObject:self.targetAppBundleIDs forKey:kPPOTCoderKeyConfigurationRecipeTargetAppBundleIDs];
- [aCoder encodeObject:self.supportedLocales forKey:kPPOTCoderKeyConfigurationRecipeSupportedLocales];
- }
- }
- @end
- #pragma mark - PPOTConfigurationRecipeEndpoint
- @implementation PPOTConfigurationRecipeEndpoint
- - (instancetype)initWithURL:(NSString *)url withCertificateSerialNumber:(NSString *)certificateSerialNumber withBase64EncodedCertificate:(NSString *)base64EncodedCertificate {
- if ((self = [super init])) {
- if (![url length] || ![certificateSerialNumber length] || ![base64EncodedCertificate length]) {
- LOG_ERROR_AND_RETURN_NIL
- }
- if (![url hasPrefix:@"https://"] && ![url hasPrefix:@"http://"]) {
- LOG_ERROR_AND_RETURN_NIL
- }
- _url = url;
- _certificateSerialNumber = certificateSerialNumber;
- _base64EncodedCertificate = base64EncodedCertificate;
- }
- return self;
- }
- #pragma mark - NSCoding
- - (instancetype)initWithCoder:(NSCoder *)aDecoder {
- if ((self = [super init])) {
- _url = [aDecoder decodeObjectForKey:kPPOTCoderKeyConfigurationRecipeURL];
- _certificateSerialNumber = [aDecoder decodeObjectForKey:kPPOTCoderKeyConfigurationRecipeCertificateSerialNumber];
- _base64EncodedCertificate = [aDecoder decodeObjectForKey:kPPOTCoderKeyConfigurationRecipeCertificate];
- }
- return self;
- }
- - (void)encodeWithCoder:(NSCoder *)aCoder {
- [aCoder encodeObject:self.url forKey:kPPOTCoderKeyConfigurationRecipeURL];
- [aCoder encodeObject:self.certificateSerialNumber forKey:kPPOTCoderKeyConfigurationRecipeCertificateSerialNumber];
- [aCoder encodeObject:self.base64EncodedCertificate forKey:kPPOTCoderKeyConfigurationRecipeCertificate];
- }
- @end
- #pragma mark - PPOTConfigurationOAuthRecipe
- @implementation PPOTConfigurationOAuthRecipe
- - (instancetype)initWithDictionary:(NSDictionary *)dictionary {
- if ((self = [super initWithDictionary:dictionary])) {
- STRING_ARRAY_FROM_DICTIONARY(scopeStrings, dictionary, kPPOTConfigurationKeyScope, YES)
- _scope = [NSSet setWithArray:scopeStrings];
- DICTIONARY_FROM_DICTIONARY(jsonEndpoints, dictionary, kPPOTConfigurationKeyEndpoints,
- self.target == PPOTRequestTargetBrowser && [self.protocolVersion isEqual:@(3)])
- if (![jsonEndpoints count]) {
- _endpoints = nil;
- }
- else {
- NSMutableDictionary *endpoints = [NSMutableDictionary dictionaryWithCapacity:[jsonEndpoints count]];
- for (NSString *environment in jsonEndpoints) {
- NSString *url = jsonEndpoints[environment][kPPOTConfigurationKeyURL];
- NSString *certificateSerialNumber = jsonEndpoints[environment][kPPOTConfigurationKeyCertificateSerialNumber];
- NSString *base64EncodedCertificate = jsonEndpoints[environment][kPPOTConfigurationKeyCertificate];
- PPOTConfigurationRecipeEndpoint *endpoint = [[PPOTConfigurationRecipeEndpoint alloc] initWithURL:url
- withCertificateSerialNumber:certificateSerialNumber
- withBase64EncodedCertificate:base64EncodedCertificate];
- if (!endpoint) {
- LOG_ERROR_AND_RETURN_NIL
- }
- endpoints[environment] = endpoint;
- }
- if (!endpoints[PPEnvironmentProduction]) {
- LOG_ERROR_AND_RETURN_NIL
- }
- _endpoints = endpoints;
- }
- }
- return self;
- }
- #pragma mark - NSCoding
- - (instancetype)initWithCoder:(NSCoder *)aDecoder {
- if ((self = [super initWithCoder:aDecoder])) {
- _scope = [aDecoder decodeObjectForKey:kPPOTCoderKeyConfigurationRecipeScope];
- if (self.target == PPOTRequestTargetBrowser) {
- _endpoints = [aDecoder decodeObjectForKey:kPPOTCoderKeyConfigurationRecipeEndpoints];
- }
- }
- return self;
- }
- - (void)encodeWithCoder:(NSCoder *)aCoder {
- [super encodeWithCoder:aCoder];
- [aCoder encodeObject:self.scope forKey:kPPOTCoderKeyConfigurationRecipeScope];
- if (self.target == PPOTRequestTargetBrowser) {
- [aCoder encodeObject:self.endpoints forKey:kPPOTCoderKeyConfigurationRecipeEndpoints];
- }
- }
- @end
- #pragma mark - PPOTConfigurationCheckoutRecipe
- @implementation PPOTConfigurationCheckoutRecipe
- - (instancetype)initWithDictionary:(NSDictionary *)dict {
- if ((self = [super initWithDictionary:dict])) {
- // no subclass-specific properties, so far
- }
- return self;
- }
- #pragma mark - NSCoding
- - (instancetype)initWithCoder:(NSCoder *)aDecoder {
- if ((self = [super initWithCoder:aDecoder])) {
- // no subclass-specific properties, so far
- }
- return self;
- }
- - (void)encodeWithCoder:(NSCoder *)aCoder {
- [super encodeWithCoder:aCoder];
- // no subclass-specific properties, so far
- }
- @end
- #pragma mark - PPOTConfigurationBillingAgreementRecipe
- @implementation PPOTConfigurationBillingAgreementRecipe
- - (instancetype)initWithDictionary:(NSDictionary *)dict {
- if ((self = [super initWithDictionary:dict])) {
- // no subclass-specific properties, so far
- }
- return self;
- }
- #pragma mark - NSCoding
- - (instancetype)initWithCoder:(NSCoder *)aDecoder {
- if ((self = [super initWithCoder:aDecoder])) {
- // no subclass-specific properties, so far
- }
- return self;
- }
- - (void)encodeWithCoder:(NSCoder *)aCoder {
- [super encodeWithCoder:aCoder];
- // no subclass-specific properties, so far
- }
- @end
- #pragma mark - PPOTConfiguration
- typedef void (^PPOTConfigurationFileDownloadCompletionBlock)(NSData *fileData);
- @interface PPOTConfiguration ()
- @property (nonatomic, strong, readwrite) NSDate *downloadTime;
- @end
- @implementation PPOTConfiguration
- #pragma mark - debug-only stuff
- #ifdef DEBUG
- static BOOL alwaysUseHardcodedConfiguration = NO;
- + (void)useHardcodedConfiguration:(BOOL)useHardcodedConfiguration {
- alwaysUseHardcodedConfiguration = useHardcodedConfiguration;
- }
- #endif
- #pragma mark - public methods
- + (void)updateCacheAsNecessary {
- // If there is no persisted configuration, or if it's stale,
- // then download a fresh configuration file and persist it.
- static int nobodyIsWorkingOnThisAtTheMoment = 1;
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wdeprecated-declarations"
- if (OSAtomicCompareAndSwapInt(1, 0, &nobodyIsWorkingOnThisAtTheMoment)) {
- PPOTConfiguration *currentConfiguration = [PPOTConfiguration fetchPersistentConfiguration];
- if (!currentConfiguration || fabs([currentConfiguration.downloadTime timeIntervalSinceNow]) > kPPOTConfigurationFileMaximumAcceptableObsolescence) {
- static NSDate *lastConfigurationFileDownloadAttemptTime = nil;
- if (!lastConfigurationFileDownloadAttemptTime ||
- fabs([lastConfigurationFileDownloadAttemptTime timeIntervalSinceNow]) > kConfigurationFileDownloadRetryInterval) {
- lastConfigurationFileDownloadAttemptTime = [NSDate date];
- NSURL *url = [NSURL URLWithString:kConfigurationFileDownloadURL];
- NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
- [request setHTTPMethod:@"GET"];
- // TODO: Can simplify by not specifying timeout interval. This might be better anyway to not specify because of slow networks.
- PPOTURLSession *session = [PPOTURLSession sessionWithTimeoutIntervalForRequest:kConfigurationFileDownloadTimeout];
- [session sendRequest:request
- completionBlock:^(NSData *data, __attribute__((unused)) NSHTTPURLResponse *response, __attribute__((unused)) NSError *error) {
- #ifdef DEBUG
- NSString *dataString = nil;
- if (data) {
- dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
- }
- else {
- dataString = @"<no data received>";
- }
- [[BTLogger sharedLogger] debug:@"Downloaded JSON config\n-> HTTP status: %ld\n-> file contents:\n%@\n", (long)response.statusCode, dataString];
- #endif
- PPOTConfiguration *configuration = data ? [[PPOTConfiguration alloc] initWithJSON:data] : nil;
- if (configuration) {
- configuration.downloadTime = [NSDate date];
- [PPOTConfiguration storePersistentConfiguration:configuration];
- }
- [session finishTasksAndInvalidate];
- }];
- }
- }
- nobodyIsWorkingOnThisAtTheMoment = 1;
- }
- #pragma clang diagnostic pop
- }
- + (PPOTConfiguration *)getCurrentConfiguration {
- #ifdef DEBUG
- if (alwaysUseHardcodedConfiguration) {
- return [self defaultConfiguration];
- }
- #endif
- PPOTConfiguration *currentConfiguration = [PPOTConfiguration fetchPersistentConfiguration];
- if (!currentConfiguration) {
- currentConfiguration = [self defaultConfiguration];
- }
- return currentConfiguration;
- }
- + (PPOTConfiguration *)configurationWithDictionary:(NSDictionary *)dictionary {
- return [[PPOTConfiguration alloc] initWithDictionary:dictionary];
- }
- #pragma mark - private methods
- + (void)initialize {
- #ifdef DEBUG
- NSAssert([PPOTConfiguration defaultConfiguration] != nil, @"otc-config.ios.json is invalid");
- #endif
- if (self == [PPOTConfiguration class]) {
- [self updateCacheAsNecessary];
- }
- }
- + (PPOTConfiguration *)defaultConfiguration {
- NSData *defaultConfigurationJSON = [NSData dataWithBytes:configuration_otc_config_ios_json
- length:configuration_otc_config_ios_json_len];
- #ifdef DEBUG
- NSString *str = [[NSString alloc] initWithData:defaultConfigurationJSON encoding:NSUTF8StringEncoding];
- [[BTLogger sharedLogger] debug:@"Using default JSON config %@\n", str];
- #endif
- PPOTConfiguration *defaultConfiguration = [[PPOTConfiguration alloc] initWithJSON:defaultConfigurationJSON];
- return defaultConfiguration;
- }
- - (instancetype)initWithDictionary:(NSDictionary *)dictionary {
- STRING_FROM_DICTIONARY(os, dictionary, kPPOTConfigurationKeyOs)
- if (![os isEqualToString:@"iOS"]) {
- LOG_ERROR_AND_RETURN_NIL
- }
- STRING_FROM_DICTIONARY(fileTimestamp, dictionary, kPPOTConfigurationKeyFileTimestamp)
- // Currently we only support config file format 1.0.
- // If we ever need to update the file format, then the code here would presumably
- // first look for sub-dictionary "2.0" (or whatever) and then fallback to "1.0" as needed.
- DICTIONARY_FROM_DICTIONARY(subDictionary, dictionary, @"1.0", YES)
- DICTIONARY_ARRAY_FROM_DICTIONARY(prioritizedOAuthRecipesDictionaries, subDictionary, kPPOTConfigurationKeyOAuthRecipes, NO)
- DICTIONARY_ARRAY_FROM_DICTIONARY(prioritizedCheckoutRecipesDictionaries, subDictionary, kPPOTConfigurationKeyCheckoutRecipes, NO)
- DICTIONARY_ARRAY_FROM_DICTIONARY(prioritizedBillingAgreementRecipesDictionaries, subDictionary, kPPOTConfigurationKeyBillingAgreementRecipes, NO)
- if ((self = [super init])) {
- _downloadTime = [NSDate dateWithTimeIntervalSince1970:0]; // by default, mark file as obsolete
- _fileTimestamp = fileTimestamp;
- _prioritizedOAuthRecipes = [self prioritizedRecipesFromArray:prioritizedOAuthRecipesDictionaries withRecipeAdapter:^PPOTConfigurationRecipe *(NSDictionary *recipeDictionary) {
- return [[PPOTConfigurationOAuthRecipe alloc] initWithDictionary:recipeDictionary];
- }];
- _prioritizedCheckoutRecipes = [self prioritizedRecipesFromArray:prioritizedCheckoutRecipesDictionaries withRecipeAdapter:^PPOTConfigurationRecipe* (NSDictionary* recipeDictionary) {
- return [[PPOTConfigurationCheckoutRecipe alloc] initWithDictionary:recipeDictionary];
- }];
- _prioritizedBillingAgreementRecipes = [self prioritizedRecipesFromArray:prioritizedBillingAgreementRecipesDictionaries withRecipeAdapter:^PPOTConfigurationRecipe* (NSDictionary* recipeDictionary) {
- return [[PPOTConfigurationBillingAgreementRecipe alloc] initWithDictionary:recipeDictionary];
- }];
- if (!_prioritizedOAuthRecipes || !_prioritizedCheckoutRecipes || !_prioritizedBillingAgreementRecipes) {
- return nil;
- }
- }
- return self;
- }
- - (NSArray*)prioritizedRecipesFromArray:(NSArray*)recipes withRecipeAdapter:(PPOTConfigurationRecipe* (^)(NSDictionary*))recipeAdapter {
- NSMutableArray *prioritizedRecipes = [NSMutableArray arrayWithCapacity:[recipes count]];
- for (NSDictionary *recipeDictionary in recipes) {
- PPOTConfigurationRecipe *recipe = recipeAdapter(recipeDictionary);
- if (recipe) {
- [prioritizedRecipes addObject:recipe];
- }
- }
- return prioritizedRecipes;
- }
- - (instancetype)initWithJSON:(NSData *)jsonData {
- NSError *error = nil;
- id jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
- if (error || ![jsonObject isKindOfClass:[NSDictionary class]]) {
- LOG_ERROR_AND_RETURN_NIL
- }
- self = [self initWithDictionary:((NSDictionary *)jsonObject)];
- return self;
- }
- #pragma mark - description
- - (NSString *)description {
- return [NSString stringWithFormat:@"PPOTConfiguration: %ld Authorization recipes, %ld Checkout recipes, %ld Billing Agreement recipes",
- (unsigned long)[self.prioritizedOAuthRecipes count],
- (unsigned long)[self.prioritizedCheckoutRecipes count],
- (unsigned long)[self.prioritizedBillingAgreementRecipes count]];
- }
- #pragma mark - NSCoding
- - (instancetype)initWithCoder:(NSCoder *)aDecoder {
- if ((self = [self init])) {
- _downloadTime = [aDecoder decodeObjectForKey:kPPOTCoderKeyConfigurationDownloadTime];
- _fileTimestamp = [aDecoder decodeObjectForKey:kPPOTCoderKeyConfigurationTimestamp];
- _prioritizedOAuthRecipes = [aDecoder decodeObjectForKey:kPPOTCoderKeyConfigurationOAuthRecipes];
- _prioritizedCheckoutRecipes = [aDecoder decodeObjectForKey:kPPOTCoderKeyConfigurationCheckoutRecipes];
- _prioritizedBillingAgreementRecipes = [aDecoder decodeObjectForKey:kPPOTCoderKeyConfigurationBillingAgreementRecipes];
- }
- return self;
- }
- - (void)encodeWithCoder:(NSCoder *)aCoder {
- [aCoder encodeObject:self.downloadTime forKey:kPPOTCoderKeyConfigurationDownloadTime];
- [aCoder encodeObject:self.fileTimestamp forKey:kPPOTCoderKeyConfigurationTimestamp];
- [aCoder encodeObject:self.prioritizedOAuthRecipes forKey:kPPOTCoderKeyConfigurationOAuthRecipes];
- [aCoder encodeObject:self.prioritizedCheckoutRecipes forKey:kPPOTCoderKeyConfigurationCheckoutRecipes];
- [aCoder encodeObject:self.prioritizedBillingAgreementRecipes forKey:kPPOTCoderKeyConfigurationBillingAgreementRecipes];
- }
- #pragma mark - keychain persistence
- + (PPOTConfiguration *)fetchPersistentConfiguration {
- return (PPOTConfiguration *) [PPOTSimpleKeychain unarchiveObjectWithDataForKey:kPPOTKeychainConfiguration];
- }
- + (void)storePersistentConfiguration:(PPOTConfiguration *)configuration {
- NSData *data = [NSKeyedArchiver archivedDataWithRootObject:configuration];
- [PPOTSimpleKeychain setData:data forKey:kPPOTKeychainConfiguration];
- }
- @end
- @implementation PPConfiguration
- @end
- @implementation PPConfigurationCheckoutRecipe
- @end
- @implementation PPConfigurationBillingAgreementRecipe
- @end
- @implementation PPConfigurationOAuthRecipe
- @end
- @implementation PPConfigurationRecipeEndpoint
- @end
|