Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Push provisioning take 2 #1396

Merged
merged 10 commits into from
Oct 14, 2019
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
wip
  • Loading branch information
jack-stripe committed Sep 28, 2019
commit e696ff315ef099d525c4b97dd5477ea2feca8667
24 changes: 24 additions & 0 deletions Stripe/PublicHeaders/STPAPIClient+PushProvisioning.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// STPAPIClient+PushProvisioning.h
// Stripe
//
// Created by Jack Flintermann on 9/27/18.
// Copyright © 2018 Stripe, Inc. All rights reserved.
//

#import <Stripe/Stripe.h>
#import "STPPushProvisioningDetails.h"
#import "STPPushProvisioningDetailsParams.h"

NS_ASSUME_NONNULL_BEGIN

typedef void (^STPPushProvisioningDetailsCompletionBlock)(STPPushProvisioningDetails * __nullable details, NSError * __nullable error);

@interface STPAPIClient (PushProvisioning)

- (void)retrievePushProvisioningDetailsWithParams:(STPPushProvisioningDetailsParams *)params
completion:(STPPushProvisioningDetailsCompletionBlock)completion;

@end

NS_ASSUME_NONNULL_END
30 changes: 30 additions & 0 deletions Stripe/PublicHeaders/STPPushProvisioningContext.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// STPPushProvisioningContext.h
// Stripe
//
// Created by Jack Flintermann on 9/27/18.
// Copyright © 2018 Stripe, Inc. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <PassKit/PassKit.h>
#import "STPEphemeralKeyProvider.h"
#import "STPCard.h"

NS_ASSUME_NONNULL_BEGIN

@interface STPPushProvisioningContext : NSObject

+ (PKAddPaymentPassRequestConfiguration *)requestConfigurationWithName:(NSString *)name
description:(nullable NSString *)description
last4:(nullable NSString *)last4
brand:(STPCardBrand)brand;
- (instancetype)initWithKeyProvider:(id<STPIssuingCardEphemeralKeyProvider>)keyProvider;
- (void)addPaymentPassViewController:(PKAddPaymentPassViewController *)controller
generateRequestWithCertificateChain:(NSArray<NSData *> *)certificates
nonce:(NSData *)nonce
nonceSignature:(NSData *)nonceSignature
completionHandler:(void (^)(PKAddPaymentPassRequest *))handler;
@end

NS_ASSUME_NONNULL_END
28 changes: 28 additions & 0 deletions Stripe/PublicHeaders/STPPushProvisioningDetails.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// STPPushProvisioningDetails.h
// Stripe
//
// Created by Jack Flintermann on 9/26/18
// Copyright © 2018 Stripe, Inc. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "STPAPIResponseDecodable.h"

NS_ASSUME_NONNULL_BEGIN

@interface STPPushProvisioningDetails : NSObject <STPAPIResponseDecodable>

@property (nonatomic, readonly) NSString *cardId;
@property (nonatomic, readonly) NSData *encryptedPassData;
@property (nonatomic, readonly) NSData *activationData;
@property (nonatomic, readonly) NSData *ephemeralPublicKey;

+ (instancetype)detailsWithCardId:(NSString *)cardId
encryptedPassData:(NSData *)encryptedPassData
activationData:(NSData *)activationData
ephemeralPublicKey:(NSData *)ephemeralPublicKey;

@end

NS_ASSUME_NONNULL_END
27 changes: 27 additions & 0 deletions Stripe/PublicHeaders/STPPushProvisioningDetailsParams.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// STPPushProvisioningDetailsParams.h
// Stripe
//
// Created by Jack Flintermann on 9/26/18.
// Copyright © 2018 Stripe, Inc. All rights reserved.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface STPPushProvisioningDetailsParams : NSObject

@property (nonatomic, readonly) NSString *cardId;
@property (nonatomic, readonly) NSArray<NSData *> *certificates;
@property (nonatomic, readonly) NSData *nonce;
@property (nonatomic, readonly) NSData *nonceSignature;

+(instancetype)paramsWithCardId:(NSString *)cardId
certificates:(NSArray<NSData *>*)certificates
nonce:(NSData *)nonce
nonceSignature:(NSData *)nonceSignature;

@end

NS_ASSUME_NONNULL_END
4 changes: 4 additions & 0 deletions Stripe/PublicHeaders/Stripe.h
Original file line number Diff line number Diff line change
@@ -76,6 +76,10 @@
#import "STPPaymentOption.h"
#import "STPPaymentOptionsViewController.h"
#import "STPPaymentResult.h"
#import "STPPushProvisioningContext.h"
#import "STPPushProvisioningDetails.h"
#import "STPPushProvisioningDetailsParams.h"
#import "STPAPIClient+PushProvisioning.h"
#import "STPRedirectContext.h"
#import "STPSetupIntent.h"
#import "STPSetupIntentConfirmParams.h"
61 changes: 61 additions & 0 deletions Stripe/STPAPIClient+PushProvisioning.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//
// STPAPIClient+PushProvisioning.m
// Stripe
//
// Created by Jack Flintermann on 9/27/18.
// Copyright © 2018 Stripe, Inc. All rights reserved.
//

#import "STPAPIClient+PushProvisioning.h"
#import "STPAPIRequest.h"

@implementation STPAPIClient (PushProvisioning)

- (void)retrievePushProvisioningDetailsWithParams:(STPPushProvisioningDetailsParams *)params
completion:(STPPushProvisioningDetailsCompletionBlock)completion {

NSString *endpoint = [NSString stringWithFormat:@"issuing/cards/%@/push_provisioning_details", params.cardId];
NSMutableArray<NSString*>* base64Certificates = [NSMutableArray arrayWithCapacity:params.certificates.count];
for (NSData *certificate in params.certificates) {
NSString *base64Certificate = [certificate base64EncodedStringWithOptions:kNilOptions];
[base64Certificates addObject:base64Certificate];
}

NSString *nonceHexString = [self hexadecimalStringForData:params.nonce];
NSString *nonceSignatureHexString = [self hexadecimalStringForData:params.nonceSignature];

NSDictionary *parameters = @{
@"ios": @{
@"certificates": base64Certificates,
@"nonce": nonceHexString,
@"nonce_signature": nonceSignatureHexString,
},
};

[STPAPIRequest<STPPushProvisioningDetails *> getWithAPIClient:self
endpoint:endpoint
parameters:parameters
deserializer:[STPPushProvisioningDetails new]
completion:^(STPPushProvisioningDetails *details, __unused NSHTTPURLResponse *response, NSError *error) {
completion(details, error);
}];
}

- (NSString *)hexadecimalStringForData:(NSData *)data {
/* Returns hexadecimal string of NSData. Empty string if data is empty. */

const unsigned char *dataBuffer = (const unsigned char *)[data bytes];

if (!dataBuffer)
return [NSString string];

NSUInteger dataLength = [data length];
NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)];

for (NSUInteger i = 0; i < dataLength; ++i)
[hexString appendString:[NSString stringWithFormat:@"%02lx", (unsigned long)dataBuffer[i]]];

return [NSString stringWithString:hexString];
}

@end
69 changes: 69 additions & 0 deletions Stripe/STPPushProvisioningContext.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//
// STPPushProvisioningContext.m
// Stripe
//
// Created by Jack Flintermann on 9/27/18.
// Copyright © 2018 Stripe, Inc. All rights reserved.
//

#import "STPPushProvisioningContext.h"
#import "STPEphemeralKeyManager.h"
#import "STPAPIClient+Private.h"
#import "STPAPIClient+PushProvisioning.m"
#import "STPEphemeralKey.h"

@interface STPPushProvisioningContext()
@property (nonatomic, strong) STPEphemeralKeyManager *keyManager;
@property (nonatomic, strong, nullable) STPEphemeralKey *ephemeralKey;
@end

@implementation STPPushProvisioningContext

- (instancetype)initWithKeyProvider:(id<STPIssuingCardEphemeralKeyProvider>)keyProvider {
self = [super init];
if (self) {
_keyManager = [[STPEphemeralKeyManager alloc] initWithKeyProvider:keyProvider apiVersion:[STPAPIClient apiVersion] performsEagerFetching:NO];
}
return self;
}

+ (PKAddPaymentPassRequestConfiguration *)requestConfigurationWithName:(NSString *)name
description:(nullable NSString *)description
last4:(nullable NSString *)last4
brand:(STPCardBrand)brand {
PKAddPaymentPassRequestConfiguration *config = [[PKAddPaymentPassRequestConfiguration alloc] initWithEncryptionScheme:PKEncryptionSchemeECC_V2];
config.cardholderName = name;
config.primaryAccountSuffix = last4;
config.localizedDescription = description;
if (@available(iOS 12.0, *)) {
config.style = PKAddPaymentPassStylePayment;
}
if (brand == STPCardBrandVisa) {
config.paymentNetwork = PKPaymentNetworkVisa;
}
return config;
}

- (void)addPaymentPassViewController:(__unused PKAddPaymentPassViewController *)controller generateRequestWithCertificateChain:(NSArray<NSData *> *)certificates nonce:(NSData *)nonce nonceSignature:(NSData *)nonceSignature completionHandler:(void (^)(PKAddPaymentPassRequest *))handler {
[self.keyManager getOrCreateKey:^(STPEphemeralKey * _Nullable ephemeralKey, NSError * _Nullable keyError) {
if (keyError != nil) {
handler([PKAddPaymentPassRequest new]);
return;
}
STPPushProvisioningDetailsParams *params = [STPPushProvisioningDetailsParams paramsWithCardId:ephemeralKey.issuingCardID certificates:certificates nonce:nonce nonceSignature:nonceSignature];
STPAPIClient *client = [STPAPIClient apiClientWithEphemeralKey:ephemeralKey];
[client retrievePushProvisioningDetailsWithParams:params completion:^(STPPushProvisioningDetails * _Nullable details, NSError * _Nullable error) {
if (error != nil) {
handler([PKAddPaymentPassRequest new]);
return;
}
PKAddPaymentPassRequest *request = [[PKAddPaymentPassRequest alloc] init];
request.activationData = details.activationData;
request.encryptedPassData = details.encryptedPassData;
request.ephemeralPublicKey = details.ephemeralPublicKey;
handler(request);
}];
}];
}

@end
92 changes: 92 additions & 0 deletions Stripe/STPPushProvisioningDetails.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//
// STPPushProvisioningDetails.m
// Stripe
//
// Created by Jack Flintermann on 9/26/18.
// Copyright © 2018 Stripe, Inc. All rights reserved.
//

#import "STPPushProvisioningDetails.h"

#import "NSDictionary+Stripe.h"

@interface STPPushProvisioningDetails ()

@property (nonatomic, readwrite) NSString *cardId;
@property (nonatomic, readwrite) NSData *encryptedPassData;
@property (nonatomic, readwrite) NSData *activationData;
@property (nonatomic, readwrite) NSData *ephemeralPublicKey;
@property (nonatomic, readwrite, copy) NSDictionary *allResponseFields;

@end

@implementation STPPushProvisioningDetails

+ (instancetype)detailsWithCardId:(NSString *)cardId
encryptedPassData:(NSData *)encryptedPassData
activationData:(NSData *)activationData
ephemeralPublicKey:(NSData *)ephemeralPublicKey {
STPPushProvisioningDetails *details = [[self alloc] init];
details.cardId = cardId;
details.encryptedPassData = encryptedPassData;
details.activationData = activationData;
details.ephemeralPublicKey = ephemeralPublicKey;
return details;
}

#pragma mark - STPAPIResponseDecodable

+ (instancetype)decodedObjectFromAPIResponse:(NSDictionary *)response {
NSDictionary *dict = [response stp_dictionaryByRemovingNulls];
if (!dict) {
return nil;
}

// required fields
NSString *cardId = [dict stp_stringForKey:@"card"];
NSString *encryptedPassString = [dict stp_stringForKey:@"contents"];
NSData *encryptedPassData = encryptedPassString ? [[NSData alloc] initWithBase64EncodedString:encryptedPassString options:0] : nil;

NSString *activationString = [dict stp_stringForKey:@"activation_data"];
NSData *activationData = activationString ? [[NSData alloc] initWithBase64EncodedString:activationString options:0] : nil;

NSString *ephemeralPublicKeyString = [dict stp_stringForKey:@"ephemeral_public_key"];
NSData *ephemeralPublicKeyData = ephemeralPublicKeyString ? [[NSData alloc] initWithBase64EncodedString:ephemeralPublicKeyString options:0] : nil;

if (cardId == nil || encryptedPassData == nil || activationData == nil || ephemeralPublicKeyData == nil) {
return nil;
}

STPPushProvisioningDetails *details = [self detailsWithCardId:cardId
encryptedPassData:encryptedPassData
activationData:activationData
ephemeralPublicKey:ephemeralPublicKeyData];
details.allResponseFields = dict;

return details;
}


#pragma mark - Equality

- (BOOL)isEqual:(STPPushProvisioningDetails *)details {
return [self isEqualToDetails:details];
}

- (NSUInteger)hash {
return [self.activationData hash];
}

- (BOOL)isEqualToDetails:(STPPushProvisioningDetails *)details {
if (self == details) {
return YES;
}

if (!details || ![details isKindOfClass:self.class]) {
return NO;
}

return [self.activationData isEqualToData:details.activationData];
}

@end
34 changes: 34 additions & 0 deletions Stripe/STPPushProvisioningDetailsParams.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// STPPushProvisioningDetailsParams.m
// Stripe
//
// Created by Jack Flintermann on 9/26/18.
// Copyright © 2018 Stripe, Inc. All rights reserved.
//

#import "STPPushProvisioningDetailsParams.h"

@interface STPPushProvisioningDetailsParams ()

@property (nonatomic, readwrite) NSString *cardId;
@property (nonatomic, readwrite) NSArray<NSData *> *certificates;
@property (nonatomic, readwrite) NSData *nonce;
@property (nonatomic, readwrite) NSData *nonceSignature;

@end

@implementation STPPushProvisioningDetailsParams

+(instancetype)paramsWithCardId:(NSString *)cardId
certificates:(NSArray<NSData *>*)certificates
nonce:(NSData *)nonce
nonceSignature:(NSData *)nonceSignature {
STPPushProvisioningDetailsParams *params = [[self alloc] init];
params.cardId = cardId;
params.certificates = certificates;
params.nonce = nonce;
params.nonceSignature = nonceSignature;
return params;
}

@end
Loading