-
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
Add STPPaymentIntent, and method to retrieve from the API given a clientSecret #985
Conversation
Not ready to merge yet, still needs implementation of |
29867d2
to
dc9c943
Compare
…entSecret Still needs implementation of `STPPaymentIntent description`, and better tests for decoding from JSON response
dc9c943
to
1ed8e33
Compare
I'm omitting our normal enum to string helper methods, and instead reaching directly into the `allResponseFields` dictionary. I think this is slightly preferable, because `Unknown` enum values will be represented as the actual string value instead, which may help with debugging. Also logging the `shipping` and `nextSourceAction` fields, even though those aren't exposed by the class yet.
Adding a canceled_at time to the JSON, otherwise the test that the allResponseFields matches the json object fails - because we strip null values out.
This is ready for review now. The |
@interface STPAPIClient (PaymentIntents) | ||
|
||
/** | ||
Retrieves the PaymentIntent object using the given secret. @see https://stripe.com/docs/api#retrieve_payment_intent |
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.
This link isn't working for me. Is it going to be soon?
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.
found it :)
@param secret The client secret of the payment intent to be retrieved. Cannot be nil. | ||
@param completion The callback to run with the returned PaymentIntent object, or an error. | ||
*/ | ||
- (void)retrievePaymentIntentWithClientSecret:(NSString *)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.
I'm wondering if there's less ambiguous vocabulary we can use so it's not confused with Stripe client secret? Or is it?
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.
looks like the rest of our documentation uses this ambiguous vocabulary so nevermind :)
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.
if we're confident it's not going to change, maybe we can mention it starts with pi_*
and assert for that fact before putting it on the network
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.
client_secret
is a secret for working with a specific PaymentIntent
or Source
, and is different from the publishable key (or the secret key that should never be part of the SDK). (I think you figured this out, but commenting for posterity)
I'm still not sure what the right level of client-side protection is, vs encoding specifics about the current state of things.
NSNumber *amount = [dict stp_numberForKey:@"amount"]; | ||
NSString *currency = [dict stp_stringForKey:@"currency"]; | ||
NSString *rawStatus = [dict stp_stringForKey:@"status"]; | ||
if (!stripeId || !clientSecret || amount == nil || !currency || !rawStatus || !dict[@"livemode"]) { |
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.
can you do !amount
here too to be consistent? now I'm questioning the behavior for NSNumber
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.
also do we need to fail if rawStatus
or livemode
are missing since we have STPPaymentIntentStatusUnknown
or the default livemode = YES
?
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.
!amount
triggers a Faux Pas warning, protecting us against accidentally checking nil/not-nil instead of YES/NO.
I believe we do want to enforce these being required.
STPPaymentIntentStatusSucceeded, | ||
|
||
/** | ||
Indicates the payment must be captured, for STPPaymentIntentCaptureMethodManual |
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.
"Indicates the payment must be captured. The captureMethod
is also set to STPPaymentIntentCaptureMethodManual
."?
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 guess what you have is fine, I'm just wondering what's the intention behind having a separate captureMethod
field and if we can communicate that reason.
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.
captureMethod
might be interesting to the app's business logic even when the status is not .requiresCapture
, but yeah, I'm not sure how this is most likely to be used. Primarily just exposing what exists in the API.
completion:(STPPaymentIntentCompletionBlock)completion { | ||
NSCAssert(secret != nil, @"'secret' is required to retrieve a PaymentIntent"); | ||
NSCAssert(completion != nil, @"'completion' is required to use the PaymentIntent that is retrieved"); | ||
NSString *identifier = [STPPaymentIntent idFromClientSecret: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.
Do we need to check that identifier
is not nil
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.
I'm not sure.
I didn't want to NSCAssert
, because the most likely way this'd be nil
is a change on the Stripe server to the client_secret
format, and that'd be a really bad experience if someone shipped an app with asserts enabled.
Similarly, we don't have a pattern (that I found) for early exit, instead we seem to pass (potentially) bad data to the server and allow it to generate an appropriate error. So I think consistency is my preferred choice.
The other alternative that comes to mind is constructing a STPInvalidRequestError
error here, and populating the user info with useful values.
/** | ||
A callback to be run with a PaymentIntent response from the Stripe API. | ||
|
||
@param paymentIntent The Stripe PaymentIntent from the response. Will be nil if an error occurs. @see STPPaymentIntent |
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.
do we need to specifically say STPPaymentIntent.h
here like you did for StripeError.h? I'm just imagining this from a very unaware perspective.
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.
IDK? I was following the pattern of every other callback here.
I think this is reasonable, because it's saying "see STPPaymentIntent
" (the class), vs "see the values in StripeError.h
" (the header with a bunch of values).
Stripe/PublicHeaders/STPAPIClient.h
Outdated
#pragma mark Payment Intents | ||
|
||
/** | ||
Stripe extensions for working with PaymentIntent objects. |
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.
Looks like the docs for the other extension interface definitions in this file say "STPAPIClient extensions..."
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's actually a mix of extensions on STPAPIClient
and on Stripe
, which I didn't realize at first. Great catch, thanks!
Stripe/PublicHeaders/STPBlocks.h
Outdated
A callback to be run with a PaymentIntent response from the Stripe API. | ||
|
||
@param paymentIntent The Stripe PaymentIntent from the response. Will be nil if an error occurs. @see STPPaymentIntent | ||
@param error The error returned from the response, or nil in one occurs. @see StripeError.h for possible values. |
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.
if* none* occurs
looks like similar typos elsewhere in this file
@property (nonatomic, readonly) NSString *clientSecret; | ||
|
||
/** | ||
The amount associated with the PaymentIntent |
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.
Our doc has a better sounding description to me: "Amount intended to be collected by this PaymentIntent."
@property (nonatomic, readonly) NSNumber *amount; | ||
|
||
/** | ||
If status is canceled, when the PaymentIntent was canceled. |
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.
"If status is STPPaymentIntentStatusCanceled, when the PaymentIntent was canceled."?
@property (nonatomic, nullable, readonly) NSDate *created; | ||
|
||
/** | ||
The currency associated with the PaymentIntent. |
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.
Our docs nicely mention it's the ISO currency code, I wonder if we mention that elsewhere we reference currency
:
"Three-letter ISO currency code, in lowercase. Must be a supported currency."
https://www.iso.org/iso-4217-currency-codes.html
https://stripe.com/docs/currencies
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 mixed. Most headers that have a currency
don't provide more details, but a couple do. Similarly, we don't explain amount
being the smallest currency unit in most places that have an amount
.
I think it's reasonable to elide this
XCTAssertNotNil(paymentIntent); | ||
NSString *desc = paymentIntent.description; | ||
XCTAssertTrue([desc containsString:NSStringFromClass([paymentIntent class])]); | ||
XCTAssertGreaterThan(desc.length, 500UL, @"Custom description should be long"); |
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.
👍
copy/paste error called them `Stripe` extensions
"in one" & "in none" -> "if none"
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.
👍
* Delete invalid cookies * Add test
Summary
Adding support for
/v1/payment_intent/:id
Motivation
IOS-792
Testing
Added some tests. This is the first in a series of PRs that'll add full PaymentIntent
support, and this was tested in the sample app too.