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

Add method to API that takes STPPaymentIntentParams and confirms them #986

Merged
merged 12 commits into from
Jul 11, 2018

Conversation

danj-stripe
Copy link
Contributor

Summary

Adding support for /v1/payment_intents/:id/confirm

This method takes a STPPaymentIntentParams object, which may have STPSourceParams or a
Source id, among other fields.

Motivation

IOS-792

Testing

Added tests. This is part of a series of PRs, starting with #985 (which this relies on),
and this was also tested in the sample app.

@danj-stripe danj-stripe force-pushed the danj/feature/retrieve-paymentintent branch from 29867d2 to dc9c943 Compare July 6, 2018 01:28
@danj-stripe danj-stripe force-pushed the danj/feature/confirm-paymentintent branch from 9baa1b9 to 9b7bfa9 Compare July 6, 2018 01:29
…entSecret

Still needs implementation of `STPPaymentIntent description`, and better tests for
decoding from JSON response
@danj-stripe danj-stripe force-pushed the danj/feature/retrieve-paymentintent branch from dc9c943 to 1ed8e33 Compare July 6, 2018 02:03
@danj-stripe danj-stripe force-pushed the danj/feature/confirm-paymentintent branch from 9b7bfa9 to 9ea11c7 Compare July 6, 2018 02:04
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.
Still missing `description` implementation and test for it
Includes functional test of the API method. Does not include analytics call
@danj-stripe danj-stripe force-pushed the danj/feature/confirm-paymentintent branch from 9ea11c7 to 42b009f Compare July 6, 2018 22:45
@danj-stripe
Copy link
Contributor Author

This also needs review.

Copy link
Contributor

@csabol-stripe csabol-stripe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overall looks good. Just a couple small comments

/**
Confirms the PaymentIntent object with the provided params object.

At a minimum, the params object must include the `clientSecret`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe add docs as to what happens if it does not? completion called immediately with failure?

Copy link
Contributor Author

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 want to promise/document what'll happen. This is a nonnull property, there's an assertion that it's not nil, and if they got past both of those I believe the web service call would happen and fail with an error.


This should be a boolean NSNumber, so that it can be `nil`
*/
@property (nonatomic, strong, nullable, readwrite) NSNumber *saveSourceToCustomer;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the behavior if it is nil? Same as @no? If that's the case could this just be a bool, if not add docs describing behavior

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is important this is NSNumber - I started with bool because that's way friendlier, but quickly realized my mistake.

If nil, then it leaves the PaymentIntent.save_source_to_customer field unchanged.

If we forced it to be YES/NO, then the client app would have to decide what the value is, and it takes control away from their backend (or it forces the app developer to retrieve the PaymentIntent just to read this value to prevent overwriting the existing value).

This is true of every parameter that's nullable, and I don't think that's something that needs to be documented specifically on this one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

- (void)confirmPaymentIntentWithParams:(STPPaymentIntentParams *)paymentIntentParams
completion:(STPPaymentIntentCompletionBlock)completion {
NSString *secret = paymentIntentParams.clientSecret;
NSCAssert(secret != nil, @"'secret' is required to confirm a PaymentIntent");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so is the intended behavior in release to just let the api request fail because of missing clientSecret instead of manually short-circuiting at this point?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is following the same pattern used by createSourceWithParams:, createTokenWithParameters:, retrieveSourceWithId:, etc.

I think assertions may be enabled in Release builds.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joeydong-stripe had a similar question about the retrieve method, and it might be that we want to change the way we do this in a future PR. https://github.com/stripe/stripe-ios/pull/985/files#r201145333

#import "STPPaymentIntentParams.h"
#import "STPPaymentIntent+Private.h"

@implementation STPPaymentIntentParams
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll cast a vote to call this STPPaymentIntentParameters but happy either way

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to be consistent with the other "Params" classes for Connect Account, Legal Entity, Source, Bank Account, and Card

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah noticed that later so that makes sense

- (instancetype)initWithClientSecret:(NSString *)clientSecret {
self = [super init];
if (self) {
_clientSecret = clientSecret;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be a copy

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great catch, thanks!


#import "STPPaymentIntentParams.h"
#import "STPPaymentIntent+Private.h"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it useful to include NS_ASSUME_NONNULL_BEGIN/END in the .m files as well. It makes sure your implementation matches your interface!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that'd be a good addition to our STYLEGUIDE.md and a nice enhancement to make.

I found very few implementation files in this project actually had it, and I didn't find any documentation about sticking it into the implementation. The stuff from Apple that I found only recommends putting it into headers.

- (void)testInit {
for (STPPaymentIntentParams *params in @[[[STPPaymentIntentParams alloc] initWithClientSecret:@"secret"],
[[STPPaymentIntentParams alloc] init],
[STPPaymentIntentParams new],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no personal preference but mixing alloc] init] and new] right next to each other reads a bit weird :P

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this explicit to test that new actually calls alloc] init]? Seems like that's out of scope of something we need to test

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intention is to verify that all the ways that an STPPaymentIntentParams can be constructed are available, and yield an object with a non-nil clientSecret (since nil triggers an assert in confirm).

I don't think it adds a lot of value here, but it's also really cheap to include.

new sometimes behaves differently from init in this codebase (even though it does end up calling it). I was thinking of #886 in particular, where we mark STPCard init unavailable with the intent of preventing instantiation, but using new is a workaround/hole.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Contributor Author

@danj-stripe danj-stripe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the great review.

I think my answer to most of these is "that's the way the existing code does things", so my preference is to land as-is and write up any improvements as separate JIRAs to be prioritized against our other work, but let me know if you think any of them are high enough priority to include.

/**
Confirms the PaymentIntent object with the provided params object.

At a minimum, the params object must include the `clientSecret`.
Copy link
Contributor Author

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 want to promise/document what'll happen. This is a nonnull property, there's an assertion that it's not nil, and if they got past both of those I believe the web service call would happen and fail with an error.


This should be a boolean NSNumber, so that it can be `nil`
*/
@property (nonatomic, strong, nullable, readwrite) NSNumber *saveSourceToCustomer;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is important this is NSNumber - I started with bool because that's way friendlier, but quickly realized my mistake.

If nil, then it leaves the PaymentIntent.save_source_to_customer field unchanged.

If we forced it to be YES/NO, then the client app would have to decide what the value is, and it takes control away from their backend (or it forces the app developer to retrieve the PaymentIntent just to read this value to prevent overwriting the existing value).

This is true of every parameter that's nullable, and I don't think that's something that needs to be documented specifically on this one.

- (void)confirmPaymentIntentWithParams:(STPPaymentIntentParams *)paymentIntentParams
completion:(STPPaymentIntentCompletionBlock)completion {
NSString *secret = paymentIntentParams.clientSecret;
NSCAssert(secret != nil, @"'secret' is required to confirm a PaymentIntent");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is following the same pattern used by createSourceWithParams:, createTokenWithParameters:, retrieveSourceWithId:, etc.

I think assertions may be enabled in Release builds.

- (void)confirmPaymentIntentWithParams:(STPPaymentIntentParams *)paymentIntentParams
completion:(STPPaymentIntentCompletionBlock)completion {
NSString *secret = paymentIntentParams.clientSecret;
NSCAssert(secret != nil, @"'secret' is required to confirm a PaymentIntent");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joeydong-stripe had a similar question about the retrieve method, and it might be that we want to change the way we do this in a future PR. https://github.com/stripe/stripe-ios/pull/985/files#r201145333


#import "STPPaymentIntentParams.h"
#import "STPPaymentIntent+Private.h"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that'd be a good addition to our STYLEGUIDE.md and a nice enhancement to make.

I found very few implementation files in this project actually had it, and I didn't find any documentation about sticking it into the implementation. The stuff from Apple that I found only recommends putting it into headers.

#import "STPPaymentIntentParams.h"
#import "STPPaymentIntent+Private.h"

@implementation STPPaymentIntentParams
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to be consistent with the other "Params" classes for Connect Account, Legal Entity, Source, Bank Account, and Card

- (instancetype)initWithClientSecret:(NSString *)clientSecret {
self = [super init];
if (self) {
_clientSecret = clientSecret;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great catch, thanks!

- (void)testInit {
for (STPPaymentIntentParams *params in @[[[STPPaymentIntentParams alloc] initWithClientSecret:@"secret"],
[[STPPaymentIntentParams alloc] init],
[STPPaymentIntentParams new],
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intention is to verify that all the ways that an STPPaymentIntentParams can be constructed are available, and yield an object with a non-nil clientSecret (since nil triggers an assert in confirm).

I don't think it adds a lot of value here, but it's also really cheap to include.

new sometimes behaves differently from init in this codebase (even though it does end up calling it). I was thinking of #886 in particular, where we mark STPCard init unavailable with the intent of preventing instantiation, but using new is a workaround/hole.

@danj-stripe danj-stripe changed the base branch from danj/feature/retrieve-paymentintent to master July 10, 2018 22:02
…m-paymentintent

Conflicts:
	Stripe.xcodeproj/project.pbxproj
	Stripe/PublicHeaders/STPAPIClient.h
	Stripe/PublicHeaders/STPBlocks.h
	Stripe/PublicHeaders/STPPaymentIntent.h
	Stripe/PublicHeaders/Stripe.h
	Stripe/STPAPIClient.m
	Tests/Tests/STPPaymentIntentFunctionalTest.m
@csabol-stripe
Copy link
Contributor

I think my answer to most of these is "that's the way the existing code does things", so my preference is to land as-is and write up any improvements as separate JIRAs to be prioritized against our other work, but let me know if you think any of them are high enough priority to include.

Sounds good to me. Land away!

@danj-stripe danj-stripe merged commit a531129 into master Jul 11, 2018
@danj-stripe danj-stripe deleted the danj/feature/confirm-paymentintent branch July 11, 2018 18:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants