-
Notifications
You must be signed in to change notification settings - Fork 997
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
New type of ephemeral keys #1131
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall a nice refactor to add the new type of ephemeral keys!. I'll defer the rest of review to someone who understands the SDK internals better than I do.
Stripe/STPEphemeralKeyManager.m
Outdated
@@ -64,18 +68,44 @@ - (void)handleWillForegroundNotification { | |||
// eager refreshses to once per hour. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor typo:
// eager refreshes to once per hour.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
// the ephemeral key could not be decoded | ||
else { | ||
[self.createKeyPromise fail:[NSError stp_ephemeralKeyDecodingError]]; | ||
NSAssert(NO, @"Could not parse the ephemeral key response. Make sure your backend is sending the unmodified JSON of the ephemeral key to your app. For more info, see https://stripe.com/docs/mobile/ios/standard#prepare-your-api"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be handy to log the type of key here (Issuing vs Customer), not sure if that's available in this context. My thought is the user will contact us when they see that error, and it'd be nice to know which key provider is causing the issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
@@ -35,7 +35,7 @@ NS_ASSUME_NONNULL_BEGIN | |||
@param keyProvider The key provider the customer context will use. | |||
@return the newly-instantiated customer context. | |||
*/ | |||
- (instancetype)initWithKeyProvider:(id<STPEphemeralKeyProvider>)keyProvider; | |||
- (instancetype)initWithKeyProvider:(id<STPCustomerEphemeralKeyProvider>)keyProvider; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renaming this class means:
- We're also going to have to update the Examples, the stripe.com docs, and the generated docs
- We'll need coordinate a release between all three.
- Users will have to update their existing code when they upgrade, even if they're not using Issuing.
I don't think any of this is a blocker, just thinking through the user-facing consequences here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jack-stripe Just to make sure: is it in line with what you expected?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe I safely deprecated STPEphemeralKeyProvider
by changing it to subclass STPCustomerEphemeralKeyProvider
- this would trigger a deprecation warning in existing integrations but not a compiler error. Yes, this will mean a docs update though.
} | ||
|
||
- (BOOL)isEqualToResourceKey:(STPEphemeralKey *)other { | ||
return [self.stripeID isEqualToString:other.stripeID] | ||
&& [self.secret isEqualToString:other.secret] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why were these removed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I renamed it to isEqualToEphemeralKey
("Resource Key" was an early and terrible name for ephemeral keys)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah sorry, I mean why was it changed so it only compares the stripeIDs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ping on this @jack-stripe ? (not going to block patch because id equivalence makes sense as sufficient to me but would be useful to know the reasoning
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, sorry, missed your comment @csabol-stripe!
I honestly don't remember why I changed that. I don't see a world in which stripeID
is the same but secret
is different. I'm happy with either implementation.
Stripe/STPEphemeralKeyManager.m
Outdated
self.customerKey = key; | ||
} | ||
[self getOrCreateKey:^(__unused STPEphemeralKey * _Nullable ephemeralKey, __unused NSError * _Nullable error) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like self.ephemeralKey
gets set in the actual to getOrCreateKey
. Could we add some comments here explaining why this is called and why we do nothing with the response here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Stripe/STPEphemeralKeyManager.m
Outdated
[self.createKeyPromise fail:error]; | ||
} | ||
// the ephemeral key could not be decoded | ||
else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style-nit: I prefer to have the else
on the same line as the closing brace, } else {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Stripe/STPEphemeralKeyManager.m
Outdated
id<STPCustomerEphemeralKeyProvider> provider = self.keyProvider; | ||
[provider createCustomerKeyWithAPIVersion:self.apiVersion completion:jsonCompletion]; | ||
} | ||
else if ([self.keyProvider conformsToProtocol:@protocol(STPIssuingCardEphemeralKeyProvider)]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style-nit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Stripe/STPEphemeralKeyManager.m
Outdated
}]; | ||
} | ||
} | ||
|
||
- (void)getCustomerKey:(STPEphemeralKeyCompletionBlock)completion { | ||
- (void)createKey { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style-nit: (this is a new one I'm trying to enforce so a lot of existing code doesn't follow it). Internal methods should start with an underscore to differentiate from public methods - (void)_createKey
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
@@ -32,12 +31,16 @@ - (void)setUp { | |||
self.apiVersion = @"2015-03-03"; | |||
} | |||
|
|||
#pragma clang diagnostic push | |||
#pragma clang diagnostic ignored "-Wdeprecated" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what are we using here that's deprecated?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My mistake, I added this accidentally in my initial testing, feel free to remove these ignores
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
- (id)mockKeyProviderWithKeyResponse:(NSDictionary *)keyResponse { | ||
XCTestExpectation *exp = [self expectationWithDescription:@"createCustomerKey"]; | ||
id mockKeyProvider = OCMProtocolMock(@protocol(STPEphemeralKeyProvider)); | ||
OCMStub([mockKeyProvider createCustomerKeyWithAPIVersion:[OCMArg isEqual:self.apiVersion] | ||
completion:[OCMArg any]]) | ||
.andDo(^(NSInvocation *invocation) { | ||
[invocation retainArguments]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a bug using OCMock
that this avoids: erikdoe/ocmock#147
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 a comment referencing this would be helpfule
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me add this
It provides a way for Stripe utility classes to request a new ephemeral key from | ||
your backend, which it will use to retrieve and update Stripe API objects. | ||
*/ | ||
@protocol STPIssuingCardEphemeralKeyProvider <NSObject> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure these need to be two separate protocols. The documentation says that the API client should conform to both of these, which means checking for keyProvider
protocol conformance will always succeed for both.
It's not totally clear to me how or why we intend to differentiate between creating customer and card ephemeral keys? Is the intention for the integrator to create two instances of STPEphemeralKeyManager
and call the right one as needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's the way I imagined it, yes. You might imagine one API that calls POST https://myapi.com/users/:id/ephemeral_keys
and one that calls POST https://myapi.com/cards/:id/ephemeral_keys
. Then you might have one API client client-side that calls the first, and another that calls the second.
The other alternative, as you suggest, is to make a single API - POST https://myapi.com/ephemeral_keys
(with user
or card
parameters) and a single API client to talk to both. The issue is that now the method signature needs to change - we need to tell the app what they should be making an ephemeral key for exactly. So the protocol method would have to be something generic like createKey:forAssociatedStripeObject:
which just feels like turning a thing to figure out at compile-time into a thing to figure out at runtime.
You raise a good point, though, that this breaks if someone's API client conforms to both protocols. Sadly, I think we'll need to add a property to STPEphemeralKeyManager
to indicate what kind of key it is intended to make (not the worst thing, as it's a private class). Thoughts @csabol-stripe ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, what about adding a new method to STPEphemeralKeyManager
so we have getCustomerKey:
and getCardKey:
(or similar)? That way we only have one key manager but the requests are still "typed" at compile time.
Similarly, what are your thoughts about keeping STPEphemeralKeyProvider
and just adding createIssuingCardKeyWithAPIVersion:completion:
to the existing protocol (and marking as optional if we think that would be useful?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'd prefer to keep a property on STPEphemeralKeyManager
, because I think it'd be a programmer error to call getCustomerKey:
and then later getCardKey:
on the same instance. Nice thing is, assuming that property is set in the initializer, we could assert that the given keyProvider
conforms to the necessary protocol.
I don't feel as strongly about whether or not to split STPEphemeralKeyProvider
into 2 protocols, but realistically users are going to be implementing only one of createCustomerKeyWithAPIVersion:completion
or createIssuingCardKeyWithAPIVersion:completion`, so both would have to be optional. In that case, I think it's a little nicer to just have two protocols so that you can enforce at compile-time (instead of runtime) that the necessary methods are implemented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thoughts @csabol-stripe? Can also discuss in slack on monday : )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
closing the loop: @jack-stripe and I met to discuss this today and we are happy moving forward with the patch as is :)
It provides a way for Stripe utility classes to request a new ephemeral key from | ||
your backend, which it will use to retrieve and update Stripe API objects. | ||
*/ | ||
@protocol STPIssuingCardEphemeralKeyProvider <NSObject> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
closing the loop: @jack-stripe and I met to discuss this today and we are happy moving forward with the patch as is :)
} | ||
|
||
- (BOOL)isEqualToResourceKey:(STPEphemeralKey *)other { | ||
return [self.stripeID isEqualToString:other.stripeID] | ||
&& [self.secret isEqualToString:other.secret] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ping on this @jack-stripe ? (not going to block patch because id equivalence makes sense as sufficient to me but would be useful to know the reasoning
Thanks everyone |
* Extract UI logic into a view model * Implement name collection * Add test for `String.isBlank` * Cleanup
Summary
Allowing for ephemeral keys that are not customer
Motivation
Stripe Issuing will need those
Testing
Existing ephemeral keys test apply