improve error parsing and add tests

This commit is contained in:
Ben Guo 2017-04-14 18:53:25 -04:00
parent 16eb2f816c
commit 2185fe95cc
4 changed files with 168 additions and 45 deletions

View File

@ -446,6 +446,7 @@
C15993451D8829C00047950D /* stp_shipping_form.png in Resources */ = {isa = PBXBuildFile; fileRef = C15993201D8807930047950D /* stp_shipping_form.png */; };
C15993461D8829C00047950D /* stp_shipping_form@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C15993211D8807930047950D /* stp_shipping_form@2x.png */; };
C15993471D8829C00047950D /* stp_shipping_form@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = C15993221D8807930047950D /* stp_shipping_form@3x.png */; };
C15B02731EA176090026E606 /* StripeErrorTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C15B02721EA176090026E606 /* StripeErrorTest.m */; };
C16F66AB1CA21BAC006A21B5 /* STPFormTextFieldTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C16F66AA1CA21BAC006A21B5 /* STPFormTextFieldTest.m */; };
C1717DB11CC00ED60009CF4A /* STPAddress.h in Headers */ = {isa = PBXBuildFile; fileRef = C1080F471CBECF7B007B2D89 /* STPAddress.h */; settings = {ATTRIBUTES = (Public, ); }; };
C17A030D1CBEE7A2006C819F /* STPAddressFieldTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C17A030B1CBEE7A2006C819F /* STPAddressFieldTableViewCell.h */; };
@ -973,6 +974,7 @@
C15993301D8808680047950D /* STPShippingMethodsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPShippingMethodsViewController.m; sourceTree = "<group>"; };
C15993311D8808680047950D /* STPShippingMethodTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPShippingMethodTableViewCell.h; sourceTree = "<group>"; };
C15993321D8808680047950D /* STPShippingMethodTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPShippingMethodTableViewCell.m; sourceTree = "<group>"; };
C15B02721EA176090026E606 /* StripeErrorTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StripeErrorTest.m; sourceTree = "<group>"; };
C16F66AA1CA21BAC006A21B5 /* STPFormTextFieldTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPFormTextFieldTest.m; sourceTree = "<group>"; };
C17A030B1CBEE7A2006C819F /* STPAddressFieldTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPAddressFieldTableViewCell.h; sourceTree = "<group>"; };
C17A030C1CBEE7A2006C819F /* STPAddressFieldTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPAddressFieldTableViewCell.m; sourceTree = "<group>"; };
@ -1491,6 +1493,7 @@
C124A1801CCAA1BF007D42EE /* NSMutableURLRequest+StripeTest.m */,
C1EEDCC71CA2172700A54582 /* NSString+StripeTest.m */,
04CB86B81BA89CD400E4F61E /* PKPayment+StripeTest.m */,
C15B02721EA176090026E606 /* StripeErrorTest.m */,
C13538071D2C2186003F6157 /* STPAddCardViewControllerTest.m */,
C1080F4B1CBED48A007B2D89 /* STPAddressTests.m */,
C12711091DBA7E490087840D /* STPAddressViewModelTest.m */,
@ -2292,6 +2295,7 @@
C13538081D2C2186003F6157 /* STPAddCardViewControllerTest.m in Sources */,
04415C671A6605B5001225ED /* STPAPIClientTest.m in Sources */,
045D71311CF514BB00F6CD65 /* STPBinRangeTest.m in Sources */,
C15B02731EA176090026E606 /* StripeErrorTest.m in Sources */,
C1EF044E1DD2397C00FBF452 /* STPShippingMethodsViewControllerLocalizationTests.m in Sources */,
04415C681A6605B5001225ED /* STPFormEncoderTest.m in Sources */,
C11810991CC6D46D0022FB55 /* NSDecimalNumber+StripeTest.m in Sources */,

View File

@ -24,8 +24,8 @@ typedef NS_ENUM(NSInteger, STPErrorCode) {
#endif
STPConnectionError = 40, // Trouble connecting to Stripe.
STPInvalidRequestError = 50, // Your request had invalid parameters.
STPAPIError = 60, // General-purpose API error (should be rare).
STPCardError = 70, // Something was wrong with the given card (most common).
STPAPIError = 60, // General-purpose API error.
STPCardError = 70, // Something was wrong with the given card details.
STPCancellationError = 80, // The operation was cancelled.
STPCheckoutUnknownError = 5000, // Checkout failed
STPCheckoutTooManyAttemptsError = 5001, // Too many incorrect code attempts
@ -44,6 +44,14 @@ FOUNDATION_EXPORT NSString * __nonnull const STPCardErrorCodeKey;
// right UI element.
FOUNDATION_EXPORT NSString * __nonnull const STPErrorParameterKey;
// The error code returned by the Stripe API.
// https://stripe.com/docs/api#errors-type
FOUNDATION_EXPORT NSString * __nonnull const STPStripeErrorCodeKey;
// The error type returned by the Stripe API.
// https://stripe.com/docs/api#errors-code
FOUNDATION_EXPORT NSString * __nonnull const STPStripeErrorTypeKey;
#pragma mark STPCardErrorCodeKeys
typedef NSString * STPCardErrorCode

View File

@ -15,6 +15,8 @@ NSString *const StripeDomain = @"com.stripe.lib";
NSString *const STPCardErrorCodeKey = @"com.stripe.lib:CardErrorCodeKey";
NSString *const STPErrorMessageKey = @"com.stripe.lib:ErrorMessageKey";
NSString *const STPErrorParameterKey = @"com.stripe.lib:ErrorParameterKey";
NSString *const STPStripeErrorCodeKey = @"com.stripe.lib:StripeErrorCodeKey";
NSString *const STPStripeErrorTypeKey = @"com.stripe.lib:StripeErrorTypeKey";
NSString *const STPInvalidNumber = @"com.stripe.lib:InvalidNumber";
NSString *const STPInvalidExpMonth = @"com.stripe.lib:InvalidExpiryMonth";
NSString *const STPInvalidExpYear = @"com.stripe.lib:InvalidExpiryYear";
@ -32,57 +34,58 @@ NSString *const STPIncorrectCVC = @"com.stripe.lib:IncorrectCVC";
if (!errorDictionary) {
return nil;
}
NSString *type = errorDictionary[@"type"];
NSString *devMessage = errorDictionary[@"message"];
NSString *parameter = errorDictionary[@"param"];
NSString *errorType = errorDictionary[@"type"];
NSString *errorParam = errorDictionary[@"param"];
NSString *stripeErrorMessage = errorDictionary[@"message"];
NSString *stripeErrorCode = errorDictionary[@"code"];
NSInteger code = 0;
// There should always be a message and type for the error
if (devMessage == nil || type == nil) {
NSDictionary *userInfo = @{
NSLocalizedDescriptionKey: [self stp_unexpectedErrorMessage],
STPErrorMessageKey: @"Could not interpret the error response that was returned from Stripe."
};
return [[self alloc] initWithDomain:StripeDomain code:STPAPIError userInfo:userInfo];
}
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
userInfo[STPErrorMessageKey] = devMessage;
if (parameter) {
userInfo[STPErrorParameterKey] = [STPFormEncoder stringByReplacingSnakeCaseWithCamelCase:parameter];
userInfo[STPStripeErrorCodeKey] = stripeErrorCode;
userInfo[STPStripeErrorTypeKey] = errorType;
if (errorParam) {
userInfo[STPErrorParameterKey] = [STPFormEncoder stringByReplacingSnakeCaseWithCamelCase:errorParam];
}
if ([type isEqualToString:@"api_error"]) {
if (stripeErrorMessage) {
userInfo[NSLocalizedDescriptionKey] = stripeErrorMessage;
userInfo[STPErrorMessageKey] = stripeErrorMessage;
} else {
userInfo[NSLocalizedDescriptionKey] = [self stp_unexpectedErrorMessage];
userInfo[STPErrorMessageKey] = @"Could not interpret the error response that was returned from Stripe.";
}
if ([errorType isEqualToString:@"api_error"]) {
code = STPAPIError;
userInfo[NSLocalizedDescriptionKey] = [self stp_unexpectedErrorMessage];
} else if ([type isEqualToString:@"invalid_request_error"]) {
code = STPInvalidRequestError;
userInfo[NSLocalizedDescriptionKey] = devMessage;
} else if ([type isEqualToString:@"card_error"]) {
code = STPCardError;
NSDictionary *errorCodes = @{
@"incorrect_number": @{@"code": STPIncorrectNumber, @"message": [self stp_cardErrorInvalidNumberUserMessage]},
@"invalid_number": @{@"code": STPInvalidNumber, @"message": [self stp_cardErrorInvalidNumberUserMessage]},
@"invalid_expiry_month": @{@"code": STPInvalidExpMonth, @"message": [self stp_cardErrorInvalidExpMonthUserMessage]},
@"invalid_expiry_year": @{@"code": STPInvalidExpYear, @"message": [self stp_cardErrorInvalidExpYearUserMessage]},
@"invalid_cvc": @{@"code": STPInvalidCVC, @"message": [self stp_cardInvalidCVCUserMessage]},
@"expired_card": @{@"code": STPExpiredCard, @"message": [self stp_cardErrorExpiredCardUserMessage]},
@"incorrect_cvc": @{@"code": STPIncorrectCVC, @"message": [self stp_cardInvalidCVCUserMessage]},
@"card_declined": @{@"code": STPCardDeclined, @"message": [self stp_cardErrorDeclinedUserMessage]},
@"processing_error": @{@"code": STPProcessingError, @"message": [self stp_cardErrorProcessingErrorUserMessage]},
};
NSDictionary *codeMapEntry = errorCodes[errorDictionary[@"code"]];
if (codeMapEntry) {
userInfo[STPCardErrorCodeKey] = codeMapEntry[@"code"];
userInfo[NSLocalizedDescriptionKey] = codeMapEntry[@"message"];
} else {
if ([errorType isEqualToString:@"invalid_request_error"]) {
code = STPInvalidRequestError;
} else if ([errorType isEqualToString:@"card_error"]) {
code = STPCardError;
} else {
userInfo[STPCardErrorCodeKey] = errorDictionary[@"code"];
userInfo[NSLocalizedDescriptionKey] = devMessage;
code = STPAPIError;
}
NSDictionary *codeMap = @{
@"incorrect_number": @{@"code": STPIncorrectNumber, @"message": [self stp_cardErrorInvalidNumberUserMessage]},
@"invalid_number": @{@"code": STPInvalidNumber, @"message": [self stp_cardErrorInvalidNumberUserMessage]},
@"invalid_expiry_month": @{@"code": STPInvalidExpMonth, @"message": [self stp_cardErrorInvalidExpMonthUserMessage]},
@"invalid_expiry_year": @{@"code": STPInvalidExpYear, @"message": [self stp_cardErrorInvalidExpYearUserMessage]},
@"invalid_cvc": @{@"code": STPInvalidCVC, @"message": [self stp_cardInvalidCVCUserMessage]},
@"expired_card": @{@"code": STPExpiredCard, @"message": [self stp_cardErrorExpiredCardUserMessage]},
@"incorrect_cvc": @{@"code": STPIncorrectCVC, @"message": [self stp_cardInvalidCVCUserMessage]},
@"card_declined": @{@"code": STPCardDeclined, @"message": [self stp_cardErrorDeclinedUserMessage]},
@"processing_error": @{@"code": STPProcessingError, @"message": [self stp_cardErrorProcessingErrorUserMessage]},
};
NSDictionary *codeMapEntry = codeMap[stripeErrorCode];
NSDictionary *cardErrorCode = codeMapEntry[@"code"];
NSString *localizedMessage = codeMapEntry[@"message"];
if (cardErrorCode) {
userInfo[STPCardErrorCodeKey] = cardErrorCode;
}
if (localizedMessage) {
userInfo[NSLocalizedDescriptionKey] = codeMapEntry[@"message"];
}
}
return [[self alloc] initWithDomain:StripeDomain code:code userInfo:userInfo];
}

View File

@ -0,0 +1,108 @@
//
// StripeErrorTest.m
// Stripe
//
// Created by Ben Guo on 4/14/17.
// Copyright © 2017 Stripe, Inc. All rights reserved.
//
#import <XCTest/XCTest.h>
#import "StripeError.h"
@interface StripeErrorTest : XCTestCase
@end
@implementation StripeErrorTest
- (void)testEmptyResponse {
NSDictionary *response = @{};
NSError *error = [NSError stp_errorFromStripeResponse:response];
XCTAssertNil(error);
}
- (void)testResponseWithUnknownTypeAndNoMessage {
NSDictionary *response = @{
@"error": @{
@"type": @"foo",
@"code": @"error_code"
}
};
NSError *error = [NSError stp_errorFromStripeResponse:response];
XCTAssertEqual(error.domain, StripeDomain);
XCTAssertEqual(error.code, STPAPIError);
XCTAssertEqualObjects(error.userInfo[NSLocalizedDescriptionKey], [NSError stp_unexpectedErrorMessage]);
XCTAssertEqual(error.userInfo[STPStripeErrorTypeKey], response[@"error"][@"type"]);
XCTAssertEqual(error.userInfo[STPStripeErrorCodeKey], response[@"error"][@"code"]);
XCTAssertTrue([error.userInfo[STPErrorMessageKey] hasPrefix:@"Could not interpret the error response"]);
}
- (void)testAPIError {
NSDictionary *response = @{
@"error": @{
@"type": @"api_error",
@"message": @"some message"
}
};
NSError *error = [NSError stp_errorFromStripeResponse:response];
XCTAssertEqual(error.domain, StripeDomain);
XCTAssertEqual(error.code, STPAPIError);
XCTAssertEqualObjects(error.userInfo[NSLocalizedDescriptionKey], [NSError stp_unexpectedErrorMessage]);
XCTAssertEqualObjects(error.userInfo[STPErrorMessageKey], response[@"error"][@"message"]);
XCTAssertEqualObjects(error.userInfo[STPStripeErrorTypeKey], response[@"error"][@"type"]);
}
- (void)testInvalidRequestErrorMissingParameter {
NSDictionary *response = @{
@"error": @{
@"type": @"invalid_request_error",
@"message": @"The payment method `card` requires the parameter: card[exp_year].",
@"param": @"card[exp_year]"
}
};
NSError *error = [NSError stp_errorFromStripeResponse:response];
XCTAssertEqual(error.domain, StripeDomain);
XCTAssertEqual(error.code, STPInvalidRequestError);
XCTAssertEqualObjects(error.userInfo[NSLocalizedDescriptionKey], response[@"error"][@"message"]);
XCTAssertEqualObjects(error.userInfo[STPErrorMessageKey], response[@"error"][@"message"]);
XCTAssertEqualObjects(error.userInfo[STPStripeErrorTypeKey], response[@"error"][@"type"]);
XCTAssertEqualObjects(error.userInfo[STPErrorParameterKey], @"card[expYear]");
}
- (void)testInvalidRequestErrorIncorrectNumber {
NSDictionary *response = @{
@"error": @{
@"type": @"invalid_request_error",
@"message": @"Your card number is incorrect.",
@"code": @"incorrect_number"
}
};
NSError *error = [NSError stp_errorFromStripeResponse:response];
XCTAssertEqual(error.domain, StripeDomain);
XCTAssertEqual(error.code, STPInvalidRequestError);
XCTAssertEqualObjects(error.userInfo[NSLocalizedDescriptionKey], [NSError stp_cardErrorInvalidNumberUserMessage]);
XCTAssertEqualObjects(error.userInfo[STPCardErrorCodeKey], STPIncorrectNumber);
XCTAssertEqualObjects(error.userInfo[STPStripeErrorTypeKey], response[@"error"][@"type"]);
XCTAssertEqualObjects(error.userInfo[STPStripeErrorCodeKey], response[@"error"][@"code"]);
XCTAssertEqualObjects(error.userInfo[STPErrorMessageKey], response[@"error"][@"message"]);
}
- (void)testCardErrorIncorrectNumber {
NSDictionary *response = @{
@"error": @{
@"type": @"card_error",
@"message": @"Your card number is incorrect.",
@"code": @"incorrect_number"
}
};
NSError *error = [NSError stp_errorFromStripeResponse:response];
XCTAssertEqual(error.domain, StripeDomain);
XCTAssertEqual(error.code, STPCardError);
XCTAssertEqualObjects(error.userInfo[NSLocalizedDescriptionKey], [NSError stp_cardErrorInvalidNumberUserMessage]);
XCTAssertEqualObjects(error.userInfo[STPCardErrorCodeKey], STPIncorrectNumber);
XCTAssertEqualObjects(error.userInfo[STPStripeErrorTypeKey], response[@"error"][@"type"]);
XCTAssertEqualObjects(error.userInfo[STPStripeErrorCodeKey], response[@"error"][@"code"]);
XCTAssertEqualObjects(error.userInfo[STPErrorMessageKey], response[@"error"][@"message"]);
}
@end