diff --git a/CHANGELOG.md b/CHANGELOG.md index e1e2670b9b7..9cfbda94b60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## XX.Y.Z +* Adds `STPPaymentMethod` protocol implementation for `STPSource`. You can now call `image`/`templatedImage`/`label` on a source. [#976](https://github.com/stripe/stripe-ios/pull/976) + ## 13.1.0 2018-07-13 * Adds `STPPaymentIntent` to support PaymentIntents. [#985](https://github.com/stripe/stripe-ios/pull/985), [#986](https://github.com/stripe/stripe-ios/pull/986), [#987](https://github.com/stripe/stripe-ios/pull/987), [#988](https://github.com/stripe/stripe-ios/pull/988) * Reduce `NSURLSession` memory footprint. [#969](https://github.com/stripe/stripe-ios/pull/969) diff --git a/Stripe.xcodeproj/project.pbxproj b/Stripe.xcodeproj/project.pbxproj index c056e4bb5c9..b3b146217d4 100644 --- a/Stripe.xcodeproj/project.pbxproj +++ b/Stripe.xcodeproj/project.pbxproj @@ -325,6 +325,12 @@ 04F94DD41D22A242004FC826 /* NSBundle+Stripe_AppName.m in Sources */ = {isa = PBXBuildFile; fileRef = 049A3F981CC76A2400F57DE7 /* NSBundle+Stripe_AppName.m */; }; 04FCFA191BD59A8C00297732 /* STPCategoryLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 04FCFA171BD59A8C00297732 /* STPCategoryLoader.h */; }; 8B013C891F1E784A00DD831B /* STPPaymentConfigurationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B013C881F1E784A00DD831B /* STPPaymentConfigurationTest.m */; }; + 8B39128220E2F99600098401 /* EPSSource.json in Resources */ = {isa = PBXBuildFile; fileRef = 8B39128120E2F99600098401 /* EPSSource.json */; }; + 8B39128320E2F9A100098401 /* BancontactSource.json in Resources */ = {isa = PBXBuildFile; fileRef = 8B39127F20E2F6A500098401 /* BancontactSource.json */; }; + 8B39128520E2F9C400098401 /* GiropaySource.json in Resources */ = {isa = PBXBuildFile; fileRef = 8B39128420E2F9C400098401 /* GiropaySource.json */; }; + 8B39128720E2F9D300098401 /* MultibancoSource.json in Resources */ = {isa = PBXBuildFile; fileRef = 8B39128620E2F9D300098401 /* MultibancoSource.json */; }; + 8B39128920E2F9E000098401 /* P24Source.json in Resources */ = {isa = PBXBuildFile; fileRef = 8B39128820E2F9E000098401 /* P24Source.json */; }; + 8B39128B20E2F9F500098401 /* SOFORTSource.json in Resources */ = {isa = PBXBuildFile; fileRef = 8B39128A20E2F9F500098401 /* SOFORTSource.json */; }; 8B429AD81EF9D4B400F95F34 /* STPBankAccountParams+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B429AD71EF9D4A300F95F34 /* STPBankAccountParams+Private.h */; }; 8B429AD91EF9D4B500F95F34 /* STPBankAccountParams+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B429AD71EF9D4A300F95F34 /* STPBankAccountParams+Private.h */; }; 8B429ADE1EF9EFF900F95F34 /* STPFile+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B429ADD1EF9EFF600F95F34 /* STPFile+Private.h */; }; @@ -1039,6 +1045,12 @@ 4A0D74F918F6106100966D7B /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 7E0B1132203572FB00271AD3 /* fi */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = fi; path = Localizations/fi.lproj/Localizable.strings; sourceTree = ""; }; 8B013C881F1E784A00DD831B /* STPPaymentConfigurationTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPPaymentConfigurationTest.m; sourceTree = ""; }; + 8B39127F20E2F6A500098401 /* BancontactSource.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = BancontactSource.json; sourceTree = ""; }; + 8B39128120E2F99600098401 /* EPSSource.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = EPSSource.json; sourceTree = ""; }; + 8B39128420E2F9C400098401 /* GiropaySource.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = GiropaySource.json; sourceTree = ""; }; + 8B39128620E2F9D300098401 /* MultibancoSource.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = MultibancoSource.json; sourceTree = ""; }; + 8B39128820E2F9E000098401 /* P24Source.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = P24Source.json; sourceTree = ""; }; + 8B39128A20E2F9F500098401 /* SOFORTSource.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = SOFORTSource.json; sourceTree = ""; }; 8B429AD71EF9D4A300F95F34 /* STPBankAccountParams+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "STPBankAccountParams+Private.h"; sourceTree = ""; }; 8B429ADD1EF9EFF600F95F34 /* STPFile+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "STPFile+Private.h"; sourceTree = ""; }; 8B5B4B431EFDD925005CF475 /* STPSourceOwnerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPSourceOwnerTest.m; sourceTree = ""; }; @@ -1584,11 +1596,17 @@ isa = PBXGroup; children = ( F1BA241F1E57BEC600E4A1CF /* 3DSSource.json */, + F16AA26D1F5A05A100207FFF /* AlipaySource.json */, + 8B39127F20E2F6A500098401 /* BancontactSource.json */, F1BA241C1E57BE5700E4A1CF /* CardSource.json */, + 8B39128120E2F99600098401 /* EPSSource.json */, + 8B39128420E2F9C400098401 /* GiropaySource.json */, F152322E1EA9344000D65C67 /* iDEALSource.json */, + 8B39128620E2F9D300098401 /* MultibancoSource.json */, B3BDCADE20F0142C0034F7F5 /* PaymentIntent.json */, + 8B39128820E2F9E000098401 /* P24Source.json */, 8BD2133D1F045D31007F6FD1 /* SEPADebitSource.json */, - F16AA26D1F5A05A100207FFF /* AlipaySource.json */, + 8B39128A20E2F9F500098401 /* SOFORTSource.json */, ); name = Source; sourceTree = ""; @@ -1597,10 +1615,10 @@ isa = PBXGroup; children = ( F156753F1DB544D3004468E3 /* STPAddCardViewControllerLocalizationTests.m */, + C1054F901FE197AE0033C87E /* STPPaymentContextSnapshotTests.m */, F1B980931DB550E60075332E /* STPPaymentMethodsViewControllerLocalizationTests.m */, C1EF04491DD2396200FBF452 /* STPShippingAddressViewControllerLocalizationTests.m */, C1EF044A1DD2396200FBF452 /* STPShippingMethodsViewControllerLocalizationTests.m */, - C1054F901FE197AE0033C87E /* STPPaymentContextSnapshotTests.m */, ); name = Snapshot; sourceTree = ""; @@ -2573,11 +2591,17 @@ C1C02CCC1ECCD0ED00DF5643 /* EphemeralKey.json in Resources */, F1BA24211E57BECA00E4A1CF /* 3DSSource.json in Resources */, F1BA241E1E57BE5E00E4A1CF /* CardSource.json in Resources */, + 8B39128520E2F9C400098401 /* GiropaySource.json in Resources */, F1343BE91D652CAB00F102D8 /* Card.json in Resources */, + 8B39128720E2F9D300098401 /* MultibancoSource.json in Resources */, 8BD213391F0457A1007F6FD1 /* FileUpload.json in Resources */, C1CFCB7A1ED5F88D00BE45DF /* stp_test_upload_image.jpeg in Resources */, F152322F1EA9344600D65C67 /* iDEALSource.json in Resources */, F16AA26F1F5A0F1700207FFF /* AlipaySource.json in Resources */, + 8B39128920E2F9E000098401 /* P24Source.json in Resources */, + 8B39128220E2F99600098401 /* EPSSource.json in Resources */, + 8B39128320E2F9A100098401 /* BancontactSource.json in Resources */, + 8B39128B20E2F9F500098401 /* SOFORTSource.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Stripe/PublicHeaders/STPPaymentMethod.h b/Stripe/PublicHeaders/STPPaymentMethod.h index 8a94163ffb3..33e9725f6b4 100644 --- a/Stripe/PublicHeaders/STPPaymentMethod.h +++ b/Stripe/PublicHeaders/STPPaymentMethod.h @@ -36,9 +36,12 @@ typedef NS_OPTIONS(NSUInteger, STPPaymentMethodType) { /** This protocol represents a payment method that a user can select and use to pay. Currently the only classes that conform to it are `STPCard`, which - represents that the user wants to pay with a specific card, and + represents that the user wants to pay with a specific card, `STPApplePayPaymentMethod`, which represents that the user wants to pay with - Apple Pay. + Apple Pay, and `STPSource`. Only `STPSource.type == STPSourceTypeCard` payment + methods are supported by `STPPaymentContext` and `STPPaymentMethodViewController`, + but the other types do have basic support for this protocol for use in a custom + integration. */ @protocol STPPaymentMethod diff --git a/Stripe/PublicHeaders/STPSource.h b/Stripe/PublicHeaders/STPSource.h index 7e362390b61..98ce410365d 100644 --- a/Stripe/PublicHeaders/STPSource.h +++ b/Stripe/PublicHeaders/STPSource.h @@ -25,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN /** Representation of a customer's payment instrument created with the Stripe API. @see https://stripe.com/docs/api#sources */ -@interface STPSource : NSObject +@interface STPSource : NSObject /** You cannot directly instantiate an `STPSource`. You should only use one that diff --git a/Stripe/Resources/Localizations/en.lproj/Localizable.strings b/Stripe/Resources/Localizations/en.lproj/Localizable.strings index c5b51f931a8..0677d66bf73 100644 --- a/Stripe/Resources/Localizations/en.lproj/Localizable.strings +++ b/Stripe/Resources/Localizations/en.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* {card brand} ending in {last4} */ "%@ Ending In %@" = "%1$@ Ending In %2$@"; +/* Source type brand name */ +"3D Secure" = "3D Secure"; + /* Title for Add a Card view */ "Add a Card" = "Add a Card"; @@ -10,12 +13,18 @@ /* Caption for Address field on address form */ "Address" = "Address"; +/* Source type brand name */ +"Alipay" = "Alipay"; + /* Text for Apple Pay payment method */ "Apple Pay" = "Apple Pay"; /* Caption for Apartment/Address line 2 field on address form */ "Apt." = "Apt."; +/* Source type brand name */ +"Bancontact" = "Bancontact"; + /* Title for billing address entry section */ "Billing Address" = "Billing Address"; @@ -46,27 +55,42 @@ /* Caption for Email field on address form */ "Email" = "Email"; +/* Source type brand name */ +"EPS" = "EPS"; + /* accessibility label for text field */ "expiration date" = "expiration date"; /* Label for free shipping method */ "Free" = "Free"; +/* Source type brand name */ +"Giropay" = "Giropay"; + +/* Source type brand name */ +"iDEAL" = "iDEAL"; + /* Shipping form error message */ "Invalid Shipping Address" = "Invalid Shipping Address"; /* Title for screen when data is still loading from the network. */ "Loading…" = "Loading…"; +/* Source type brand name */ +"Multibanco" = "Multibanco"; + /* Caption for Name field on address form */ "Name" = "Name"; /* Button to move to the next text entry field */ "Next" = "Next"; -/* No comment provided by engineer. */ +/* ok button */ "OK" = "OK"; +/* Source type brand name */ +"P24" = "P24"; + /* Title for Payment Method screen */ "Payment Method" = "Payment Method"; @@ -85,6 +109,9 @@ /* Text for button to scan a credit card */ "Scan Card" = "Scan Card"; +/* Source type brand name */ +"SEPA Direct Debit" = "SEPA Direct Debit"; + /* Title for shipping info form */ "Shipping" = "Shipping"; @@ -94,6 +121,9 @@ /* Label for shipping method form */ "Shipping Method" = "Shipping Method"; +/* Source type brand name */ +"SOFORT" = "SOFORT"; + /* Caption for State field on address form (only countries that use state , like United States) */ "State" = "State"; @@ -106,6 +136,9 @@ /* Unexpected error, such as a 500 from Stripe or a JSON parse error */ "There was an unexpected error -- try again in a few seconds" = "There was an unexpected error -- try again in a few seconds"; +/* Default missing source type label */ +"Unknown" = "Unknown"; + /* Button to fill shipping address from billing address. */ "Use Billing" = "Use Billing"; diff --git a/Stripe/STPSource+Private.h b/Stripe/STPSource+Private.h index cc1043ea5c7..5ff93d9efaf 100644 --- a/Stripe/STPSource+Private.h +++ b/Stripe/STPSource+Private.h @@ -11,7 +11,7 @@ NS_ASSUME_NONNULL_BEGIN -@interface STPSource () +@interface STPSource () + (STPSourceType)typeFromString:(NSString *)string; + (nullable NSString *)stringFromType:(STPSourceType)type; diff --git a/Stripe/STPSource.m b/Stripe/STPSource.m index cca225a9f52..160096f35e7 100644 --- a/Stripe/STPSource.m +++ b/Stripe/STPSource.m @@ -269,8 +269,7 @@ + (instancetype)decodedObjectFromAPIResponse:(NSDictionary *)response { #pragma mark - STPPaymentMethod - (UIImage *)image { - if (self.type == STPSourceTypeCard - && self.cardDetails != nil) { + if (self.type == STPSourceTypeCard && self.cardDetails != nil) { return [STPImageLibrary brandImageForCardBrand:self.cardDetails.brand]; } else { @@ -279,8 +278,7 @@ - (UIImage *)image { } - (UIImage *)templateImage { - if (self.type == STPSourceTypeCard - && self.cardDetails != nil) { + if (self.type == STPSourceTypeCard && self.cardDetails != nil) { return [STPImageLibrary templatedBrandImageForCardBrand:self.cardDetails.brand]; } else { @@ -289,15 +287,38 @@ - (UIImage *)templateImage { } - (NSString *)label { - if (self.type == STPSourceTypeCard - && self.cardDetails != nil) { - NSString *brand = [STPCard stringFromBrand:self.cardDetails.brand]; - return [NSString stringWithFormat:@"%@ %@", brand, self.cardDetails.last4];; - } - else { - return [STPCard stringFromBrand:STPCardBrandUnknown]; + switch (self.type) { + case STPSourceTypeBancontact: + return STPLocalizedString(@"Bancontact", @"Source type brand name"); + case STPSourceTypeCard: + if (self.cardDetails != nil) { + NSString *brand = [STPCard stringFromBrand:self.cardDetails.brand]; + return [NSString stringWithFormat:@"%@ %@", brand, self.cardDetails.last4]; + } + else { + return [STPCard stringFromBrand:STPCardBrandUnknown]; + } + case STPSourceTypeGiropay: + return STPLocalizedString(@"Giropay", @"Source type brand name"); + case STPSourceTypeIDEAL: + return STPLocalizedString(@"iDEAL", @"Source type brand name"); + case STPSourceTypeSEPADebit: + return STPLocalizedString(@"SEPA Direct Debit", @"Source type brand name"); + case STPSourceTypeSofort: + return STPLocalizedString(@"SOFORT", @"Source type brand name"); + case STPSourceTypeThreeDSecure: + return STPLocalizedString(@"3D Secure", @"Source type brand name"); + case STPSourceTypeAlipay: + return STPLocalizedString(@"Alipay", @"Source type brand name"); + case STPSourceTypeP24: + return STPLocalizedString(@"P24", @"Source type brand name"); + case STPSourceTypeEPS: + return STPLocalizedString(@"EPS", @"Source type brand name"); + case STPSourceTypeMultibanco: + return STPLocalizedString(@"Multibanco", @"Source type brand name"); + case STPSourceTypeUnknown: + return STPLocalizedString(@"Unknown", @"Default missing source type label"); } } - @end diff --git a/Tests/Tests/AlipaySource.json b/Tests/Tests/AlipaySource.json index 1f0ad79a014..a38ae90622f 100644 --- a/Tests/Tests/AlipaySource.json +++ b/Tests/Tests/AlipaySource.json @@ -1,3 +1,4 @@ +// Source: https://stripe.com/docs/sources/alipay { "id": "src_123", "object": "source", @@ -8,19 +9,19 @@ "flow": "redirect", "livemode": true, "owner": { - "address": null, - "email": null, - "name": null, - "phone": null, - "verified_address": null, - "verified_email": null, - "verified_name": null, - "verified_phone": null, + "address": null, + "email": null, + "name": null, + "phone": null, + "verified_address": null, + "verified_email": null, + "verified_name": null, + "verified_phone": null, }, "redirect": { - "return_url": "https://shop.foo.com/crtABC", - "status": "pending", - "url": "https://pay.stripe.com/redirect/src_123?client_secret=src_client_secret_123" + "return_url": "https://shop.foo.com/crtABC", + "status": "pending", + "url": "https://pay.stripe.com/redirect/src_123?client_secret=src_client_secret_123" }, "statement_descriptor": null, "status": "pending", diff --git a/Tests/Tests/BancontactSource.json b/Tests/Tests/BancontactSource.json new file mode 100644 index 00000000000..4a9cf38406a --- /dev/null +++ b/Tests/Tests/BancontactSource.json @@ -0,0 +1,38 @@ +// Source: https://stripe.com/docs/sources/bancontact +{ + "id": "src_16xhynE8WzK49JbAs9M21jaR", + "object": "source", + "amount": 1099, + "client_secret": "src_client_secret_UfwvW2WHpZ0s3QEn9g5x7waU", + "created": 1445277809, + "currency": "eur", + "statement_descriptor": null, + "flow": "redirect", + "livemode": true, + "owner": { + "address": null, + "email": null, + "name": "Jenny Rosen", + "phone": null, + "verified_address": null, + "verified_email": null, + "verified_name": "Jenny Rosen", + "verified_phone": null + }, + "redirect": { + "return_url": "https://shop.example.com/crtA6B28E1", + "status": "pending", + "url": "https://pay.stripe.com/redirect/src_16xhynE8WzK49JbAs9M21jaR?client_secret=src_client_secret_UfwvW2WHpZ0s3QEn9g5x7waU" + }, + "status": "pending", + "type": "bancontact", + "usage": "single_use", + "bancontact": { + "bank_code": null, + "bic": null, + "bank_name": null, + "iban_last4": null, + "statement_descriptor": null, + "preferred_language": null + } +} diff --git a/Tests/Tests/CardSource.json b/Tests/Tests/CardSource.json index 92275234e8e..ff45cae3d99 100644 --- a/Tests/Tests/CardSource.json +++ b/Tests/Tests/CardSource.json @@ -1,3 +1,4 @@ +// Source: https://stripe.com/docs/sources/cards { "id": "src_123", "object": "source", diff --git a/Tests/Tests/EPSSource.json b/Tests/Tests/EPSSource.json new file mode 100644 index 00000000000..36e2da04075 --- /dev/null +++ b/Tests/Tests/EPSSource.json @@ -0,0 +1,34 @@ +// Source: https://stripe.com/docs/sources/eps +{ + "id": "src_16xhynE8WzK49JbAs9M21jaR", + "object": "source", + "amount": 1099, + "client_secret": "src_client_secret_UfwvW2WHpZ0s3QEn9g5x7waU", + "created": 1445277809, + "currency": "eur", + "flow": "redirect", + "livemode": true, + "owner": { + "address": null, + "email": null, + "name": "Jenny Rosen", + "phone": null, + "verified_address": null, + "verified_email": null, + "verified_name": "Jenny Rosen", + "verified_phone": null + }, + "redirect": { + "return_url": "https://shop.example.com/crtA6B28E1", + "status": "pending", + "url": "https://pay.stripe.com/redirect/src_16xhynE8WzK49JbAs9M21jaR?client_secret=src_client_secret_UfwvW2WHpZ0s3QEn9g5x7waU" + }, + "statement_descriptor": null, + "status": "pending", + "type": "eps", + "usage": "single_use", + "eps": { + "reference": null, + "statement_descriptor": null, + } +} diff --git a/Tests/Tests/GiropaySource.json b/Tests/Tests/GiropaySource.json new file mode 100644 index 00000000000..a95b7df1db1 --- /dev/null +++ b/Tests/Tests/GiropaySource.json @@ -0,0 +1,36 @@ +// Source: https://stripe.com/docs/sources/giropay +{ + "id": "src_16xhynE8WzK49JbAs9M21jaR", + "object": "source", + "amount": 1099, + "client_secret": "src_client_secret_UfwvW2WHpZ0s3QEn9g5x7waU", + "created": 1445277809, + "currency": "eur", + "flow": "redirect", + "livemode": true, + "owner": { + "address": null, + "email": null, + "name": null, + "phone": null, + "verified_address": null, + "verified_email": null, + "verified_name": "Jenny Rosen", + "verified_phone": null + }, + "redirect": { + "return_url": "https://shop.example.com/crtA6B28E1", + "status": "pending", + "url": "https://pay.stripe.com/redirect/src_16xhynE8WzK49JbAs9M21jaR?client_secret=src_client_secret_UfwvW2WHpZ0s3QEn9g5x7waU" + }, + "statement_descriptor": null, + "status": "pending", + "type": "giropay", + "usage": "single_use", + "giropay": { + "bank_code": null, + "bic": null, + "bank_name": null, + "statement_descriptor": null + } +} diff --git a/Tests/Tests/MultibancoSource.json b/Tests/Tests/MultibancoSource.json new file mode 100644 index 00000000000..05250c28555 --- /dev/null +++ b/Tests/Tests/MultibancoSource.json @@ -0,0 +1,42 @@ +// Source: https://stripe.com/docs/sources/multibanco +{ + "id": "src_16xhynE8WzK49JbAs9M21jaR", + "object": "source", + "amount": 1099, + "client_secret": "src_client_secret_UfwvW2WHpZ0s3QEn9g5x7waU", + "created": 1445277809, + "currency": "eur", + "flow": "receiver", + "livemode": true, + "owner": { + "address": null, + "email": null, + "name": "Jenny Rosen", + "phone": null, + "verified_address": null, + "verified_email": null, + "verified_name": "Jenny Rosen", + "verified_phone": null + }, + "redirect": { + "return_url": "https://shop.example.com/crtA6B28E1", + "status": "pending", + "url": "https://pay.stripe.com/redirect/src_16xhynE8WzK49JbAs9M21jaR?client_secret=src_client_secret_UfwvW2WHpZ0s3QEn9g5x7waU" + }, + "receiver": { + "address": "12345-123456789", + "amount_charged": 0, + "amount_received": 0, + "amount_returned": 0, + "refund_attributes_method": "email", + "refund_attributes_status": "missing" + }, + "statement_descriptor": null, + "status": "pending", + "type": "multibanco", + "usage": "single_use", + "multibanco": { + "reference": "12345", + "entity": "123456789", + } +} diff --git a/Tests/Tests/P24Source.json b/Tests/Tests/P24Source.json new file mode 100644 index 00000000000..5948340cbbb --- /dev/null +++ b/Tests/Tests/P24Source.json @@ -0,0 +1,33 @@ +// Source: https://stripe.com/docs/sources/p24 +{ + "id": "src_16xhynE8WzK49JbAs9M21jaR", + "object": "source", + "amount": 1099, + "client_secret": "src_client_secret_UfwvW2WHpZ0s3QEn9g5x7waU", + "created": 1445277809, + "currency": "eur", + "flow": "redirect", + "livemode": true, + "owner": { + "address": null, + "email": "jenny.rosen@example.com", + "name": "Jenny Rosen", + "phone": null, + "verified_address": null, + "verified_email": "jenny.rosen@example.com", + "verified_name": "Jenny Rosen", + "verified_phone": null + }, + "redirect": { + "return_url": "https://shop.example.com/crtA6B28E1", + "status": "pending", + "url": "https://pay.stripe.com/redirect/src_16xhynE8WzK49JbAs9M21jaR?client_secret=src_client_secret_UfwvW2WHpZ0s3QEn9g5x7waU" + }, + "statement_descriptor": null, + "status": "pending", + "type": "p24", + "usage": "single_use", + "p24": { + "reference": "P24-000-111-222" + } +} diff --git a/Tests/Tests/SOFORTSource.json b/Tests/Tests/SOFORTSource.json new file mode 100644 index 00000000000..83349507c8a --- /dev/null +++ b/Tests/Tests/SOFORTSource.json @@ -0,0 +1,39 @@ +// Source: https://stripe.com/docs/sources/sofort +{ + "id": "src_16xhynE8WzK49JbAs9M21jaR", + "object": "source", + "amount": 1099, + "client_secret": "src_client_secret_UfwvW2WHpZ0s3QEn9g5x7waU", + "created": 1445277809, + "currency": "eur", + "flow": "redirect", + "livemode": true, + "owner": { + "address": null, + "email": null, + "name": "Jenny Rosen", + "phone": null, + "verified_address": null, + "verified_email": null, + "verified_name": "Jenny Rosen", + "verified_phone": null + }, + "redirect": { + "return_url": "https://shop.example.com/crtA6B28E1", + "status": "pending", + "url": "https://pay.stripe.com/redirect/src_16xhynE8WzK49JbAs9M21jaR?client_secret=src_client_secret_UfwvW2WHpZ0s3QEn9g5x7waU" + }, + "statement_descriptor": null, + "status": "pending", + "type": "sofort", + "usage": "single_use", + "sofort": { + "country": "DE", + "bank_code": null, + "bic": null, + "bank_name": null, + "iban_last4": null, + "preferred_language": null, + "statement_descriptor": null + } +} diff --git a/Tests/Tests/STPFixtures.h b/Tests/Tests/STPFixtures.h index d037768f299..9be9839a4dd 100644 --- a/Tests/Tests/STPFixtures.h +++ b/Tests/Tests/STPFixtures.h @@ -17,11 +17,17 @@ extern NSString *const STPTestJSONCard; extern NSString *const STPTestJSONPaymentIntent; +extern NSString *const STPTestJSONSource3DS; extern NSString *const STPTestJSONSourceAlipay; +extern NSString *const STPTestJSONSourceBancontact; extern NSString *const STPTestJSONSourceCard; -extern NSString *const STPTestJSONSource3DS; +extern NSString *const STPTestJSONSourceEPS; +extern NSString *const STPTestJSONSourceGiropay; extern NSString *const STPTestJSONSourceiDEAL; +extern NSString *const STPTestJSONSourceMultibanco; +extern NSString *const STPTestJSONSourceP24; extern NSString *const STPTestJSONSourceSEPADebit; +extern NSString *const STPTestJSONSourceSOFORT; @interface STPFixtures : NSObject diff --git a/Tests/Tests/STPFixtures.m b/Tests/Tests/STPFixtures.m index 1b4a139fa4a..f5db930b3d1 100644 --- a/Tests/Tests/STPFixtures.m +++ b/Tests/Tests/STPFixtures.m @@ -16,11 +16,18 @@ NSString *const STPTestJSONPaymentIntent = @"PaymentIntent"; +NSString *const STPTestJSONSource3DS = @"3DSSource"; NSString *const STPTestJSONSourceAlipay = @"AlipaySource"; +NSString *const STPTestJSONSourceBancontact = @"BancontactSource"; NSString *const STPTestJSONSourceCard = @"CardSource"; -NSString *const STPTestJSONSource3DS = @"3DSSource"; +NSString *const STPTestJSONSourceEPS = @"EPSSource"; +NSString *const STPTestJSONSourceGiropay = @"GiropaySource"; NSString *const STPTestJSONSourceiDEAL = @"iDEALSource"; +NSString *const STPTestJSONSourceMultibanco = @"MultibancoSource"; +NSString *const STPTestJSONSourceP24 = @"P24Source"; NSString *const STPTestJSONSourceSEPADebit = @"SEPADebitSource"; +NSString *const STPTestJSONSourceSOFORT = @"SOFORTSource"; + @implementation STPFixtures diff --git a/Tests/Tests/STPSourceTest.m b/Tests/Tests/STPSourceTest.m index e2c8f431256..eb6e33b5899 100644 --- a/Tests/Tests/STPSourceTest.m +++ b/Tests/Tests/STPSourceTest.m @@ -455,4 +455,96 @@ - (void)testDecodingSource_sepa_debit { XCTAssertEqualObjects(source.allResponseFields, [response stp_dictionaryByRemovingNulls]); } +#pragma mark - STPPaymentMethod Tests + +- (NSArray *)possibleAPIResponses { + return @[[STPTestUtils jsonNamed:STPTestJSONSourceCard], + [STPTestUtils jsonNamed:STPTestJSONSource3DS], + [STPTestUtils jsonNamed:STPTestJSONSourceAlipay], + [STPTestUtils jsonNamed:STPTestJSONSourceBancontact], + [STPTestUtils jsonNamed:STPTestJSONSourceEPS], + [STPTestUtils jsonNamed:STPTestJSONSourceGiropay], + [STPTestUtils jsonNamed:STPTestJSONSourceiDEAL], + [STPTestUtils jsonNamed:STPTestJSONSourceMultibanco], + [STPTestUtils jsonNamed:STPTestJSONSourceP24], + [STPTestUtils jsonNamed:STPTestJSONSourceSEPADebit], + [STPTestUtils jsonNamed:STPTestJSONSourceSOFORT]]; +} + +- (void)testPaymentMethodImage { + for (NSDictionary *response in [self possibleAPIResponses]) { + STPSource *source = [STPSource decodedObjectFromAPIResponse:response]; + + switch (source.type) { + case STPSourceTypeCard: + AssertEqualImages(source.image, [STPImageLibrary brandImageForCardBrand:source.cardDetails.brand]); + break; + default: + AssertEqualImages(source.image, [STPImageLibrary brandImageForCardBrand:STPCardBrandUnknown]); + break; + } + } +} + +- (void)testPaymentMethodTemplateImage { + for (NSDictionary *response in [self possibleAPIResponses]) { + STPSource *source = [STPSource decodedObjectFromAPIResponse:response]; + + switch (source.type) { + case STPSourceTypeCard: + AssertEqualImages(source.templateImage, [STPImageLibrary templatedBrandImageForCardBrand:source.cardDetails.brand]); + break; + default: + AssertEqualImages(source.templateImage, [STPImageLibrary templatedBrandImageForCardBrand:STPCardBrandUnknown]); + break; + } + } +} + +- (void)testPaymentMethodLabel { + for (NSDictionary *response in [self possibleAPIResponses]) { + STPSource *source = [STPSource decodedObjectFromAPIResponse:response]; + + switch (source.type) { + case STPSourceTypeBancontact: + XCTAssertEqualObjects(source.label, @"Bancontact"); + break; + case STPSourceTypeCard: + XCTAssertEqualObjects(source.label, @"Visa 5556"); + break; + case STPSourceTypeGiropay: + XCTAssertEqualObjects(source.label, @"Giropay"); + break; + case STPSourceTypeIDEAL: + XCTAssertEqualObjects(source.label, @"iDEAL"); + break; + case STPSourceTypeSEPADebit: + XCTAssertEqualObjects(source.label, @"SEPA Direct Debit"); + break; + case STPSourceTypeSofort: + XCTAssertEqualObjects(source.label, @"SOFORT"); + break; + case STPSourceTypeThreeDSecure: + XCTAssertEqualObjects(source.label, @"3D Secure"); + break; + case STPSourceTypeAlipay: + XCTAssertEqualObjects(source.label, @"Alipay"); + break; + case STPSourceTypeP24: + XCTAssertEqualObjects(source.label, @"P24"); + break; + case STPSourceTypeEPS: + XCTAssertEqualObjects(source.label, @"EPS"); + break; + case STPSourceTypeMultibanco: + XCTAssertEqualObjects(source.label, @"Multibanco"); + break; + case STPSourceTypeUnknown: + XCTAssertEqualObjects(source.label, [STPCard stringFromBrand:STPCardBrandUnknown]); + break; + } + + } +} + @end diff --git a/Tests/Tests/STPTestUtils.h b/Tests/Tests/STPTestUtils.h index ffe769e3c4a..42ff56363db 100644 --- a/Tests/Tests/STPTestUtils.h +++ b/Tests/Tests/STPTestUtils.h @@ -13,3 +13,20 @@ + (NSDictionary *)jsonNamed:(NSString *)name; @end + + +/** + Custom assertion macro to compare to UIImage instances. + + On iOS 9, `XCTAssertEqualObjects` incorrectly fails when provided with identical images. + + This just calls `XCTAssertEqualObjects` with the `UIImagePNGRepresentation` of each + image. Can be removed when we drop support for iOS 9. + + @param image1 First UIImage to compare + @param image2 Second UIImage to compare + */ +#define AssertEqualImages(image1, image2) \ + do { \ + XCTAssertEqualObjects(UIImagePNGRepresentation(image1), UIImagePNGRepresentation(image2)); \ + } while (0) diff --git a/Tests/Tests/iDEALSource.json b/Tests/Tests/iDEALSource.json index 335e814fd9f..2ea86eea44d 100644 --- a/Tests/Tests/iDEALSource.json +++ b/Tests/Tests/iDEALSource.json @@ -9,24 +9,24 @@ "flow": "redirect", "livemode": true, "owner": { - "address": null, - "email": null, - "name": "Jenny Rosen", - "phone": null, - "verified_address": null, - "verified_email": null, - "verified_name": "Jenny Rosen", - "verified_phone": null, + "address": null, + "email": null, + "name": "Jenny Rosen", + "phone": null, + "verified_address": null, + "verified_email": null, + "verified_name": "Jenny Rosen", + "verified_phone": null, }, "redirect": { - "return_url": "https://shop.foo.com/crtABC", - "status": "pending", - "url": "https://pay.stripe.com/redirect/src_123?client_secret=src_client_secret_123" + "return_url": "https://shop.foo.com/crtABC", + "status": "pending", + "url": "https://pay.stripe.com/redirect/src_123?client_secret=src_client_secret_123" }, "status": "pending", "type": "ideal", "usage": "single_use", "ideal": { - "bank": "ing" + "bank": "ing" } }