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

Make STPSource conform to STPPaymentMethod, exposing labels & images #976

Merged
merged 9 commits into from
Aug 3, 2018
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
* Adds `STPPaymentMethod` protocol implementation for `STPSource` [#976](https://github.com/stripe/stripe-ios/pull/976)
Copy link
Contributor

Choose a reason for hiding this comment

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

wdyt about: mentioning what this accomplishes? I suspect this won't mean much to our users


## 13.0.3 2018-06-11
* Fixes payment method label overlapping the checkmark, for Amex on small devices [#952](https://github.com/stripe/stripe-ios/pull/952)
* Adds EPS and Multibanco support to `STPSourceParams` [#961](https://github.com/stripe/stripe-ios/pull/961)
Expand Down
30 changes: 26 additions & 4 deletions Stripe.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -1016,6 +1022,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 = "<group>"; };
8B013C881F1E784A00DD831B /* STPPaymentConfigurationTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPPaymentConfigurationTest.m; sourceTree = "<group>"; };
8B39127F20E2F6A500098401 /* BancontactSource.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = BancontactSource.json; sourceTree = "<group>"; };
8B39128120E2F99600098401 /* EPSSource.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = EPSSource.json; sourceTree = "<group>"; };
8B39128420E2F9C400098401 /* GiropaySource.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = GiropaySource.json; sourceTree = "<group>"; };
8B39128620E2F9D300098401 /* MultibancoSource.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = MultibancoSource.json; sourceTree = "<group>"; };
8B39128820E2F9E000098401 /* P24Source.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = P24Source.json; sourceTree = "<group>"; };
8B39128A20E2F9F500098401 /* SOFORTSource.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = SOFORTSource.json; sourceTree = "<group>"; };
8B429AD71EF9D4A300F95F34 /* STPBankAccountParams+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "STPBankAccountParams+Private.h"; sourceTree = "<group>"; };
8B429ADD1EF9EFF600F95F34 /* STPFile+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "STPFile+Private.h"; sourceTree = "<group>"; };
8B5B4B431EFDD925005CF475 /* STPSourceOwnerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPSourceOwnerTest.m; sourceTree = "<group>"; };
Expand All @@ -1038,7 +1050,6 @@
8BCB6E552053389800629978 /* stp_card_unionpay_en@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "stp_card_unionpay_en@3x.png"; sourceTree = "<group>"; };
8BD213361F044B57007F6FD1 /* BankAccount.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = BankAccount.json; sourceTree = "<group>"; };
8BD213381F0457A1007F6FD1 /* FileUpload.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = FileUpload.json; sourceTree = "<group>"; };
8BD2133B1F0458F5007F6FD1 /* BitcoinSource.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = BitcoinSource.json; sourceTree = "<group>"; };
8BD2133D1F045D31007F6FD1 /* SEPADebitSource.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = SEPADebitSource.json; sourceTree = "<group>"; };
8BD87B871EFB131400269C2B /* STPSourceCardDetails+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "STPSourceCardDetails+Private.h"; sourceTree = "<group>"; };
8BD87B8A1EFB136F00269C2B /* STPSourceCardDetailsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPSourceCardDetailsTest.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1548,11 +1559,16 @@
isa = PBXGroup;
children = (
F1BA241F1E57BEC600E4A1CF /* 3DSSource.json */,
8BD2133B1F0458F5007F6FD1 /* BitcoinSource.json */,
F16AA26D1F5A05A100207FFF /* AlipaySource.json */,
8B39127F20E2F6A500098401 /* BancontactSource.json */,
F1BA241C1E57BE5700E4A1CF /* CardSource.json */,
8B39128120E2F99600098401 /* EPSSource.json */,
8B39128420E2F9C400098401 /* GiropaySource.json */,
F152322E1EA9344000D65C67 /* iDEALSource.json */,
8B39128620E2F9D300098401 /* MultibancoSource.json */,
8B39128820E2F9E000098401 /* P24Source.json */,
8BD2133D1F045D31007F6FD1 /* SEPADebitSource.json */,
F16AA26D1F5A05A100207FFF /* AlipaySource.json */,
8B39128A20E2F9F500098401 /* SOFORTSource.json */,
);
name = Source;
sourceTree = "<group>";
Expand All @@ -1561,10 +1577,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 = "<group>";
Expand Down Expand Up @@ -2511,11 +2527,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;
};
Expand Down
2 changes: 1 addition & 1 deletion Stripe/PublicHeaders/STPSource.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<STPAPIResponseDecodable, STPSourceProtocol>
@interface STPSource : NSObject<STPAPIResponseDecodable, STPSourceProtocol, STPPaymentMethod>

/**
You cannot directly instantiate an `STPSource`. You should only use one that
Expand Down
2 changes: 1 addition & 1 deletion Stripe/STPSource+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

NS_ASSUME_NONNULL_BEGIN

@interface STPSource () <STPInternalAPIResponseDecodable, STPPaymentMethod>
@interface STPSource () <STPInternalAPIResponseDecodable>

+ (STPSourceType)typeFromString:(NSString *)string;
+ (nullable NSString *)stringFromType:(STPSourceType)type;
Expand Down
45 changes: 33 additions & 12 deletions Stripe/STPSource.m
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 @"Bancontact";
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 @"Giropay";
case STPSourceTypeIDEAL:
return @"iDEAL";
case STPSourceTypeSEPADebit:
return @"SEPA Direct Debit";
case STPSourceTypeSofort:
return @"SOFORT";
case STPSourceTypeThreeDSecure:
return @"3D Secure";
Copy link
Contributor

Choose a reason for hiding this comment

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

Did you consider including more details from the source in the label strings?

3DS is an obvious one: it's wrapping a card, and the customer is probably more likely to recognize the card that's used in the Source than the fact that it was charged through 3DS. It'd also be hard for them to disambiguate if they had more than one.

I suspect the STPSourceParam factory methods are good references for what fields might be interesting on a per-SourceType basis.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I did want to include more information if available but it looks like we don't make API Version guarantees on what information is included in the source specific blocks. If I'm wrong about the guarantees, then yeah we should add them appropriately.

case STPSourceTypeAlipay:
return @"Alipay";
case STPSourceTypeP24:
return @"P24";
case STPSourceTypeEPS:
return @"EPS";
case STPSourceTypeMultibanco:
return @"Multibanco";
Copy link
Contributor

Choose a reason for hiding this comment

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

Do any of these need to be localizable strings?

My first thought was no, because they're brand/product names. On the other hand, I suspect it'd be weird to see "Alipay" instead of "支付宝" (according to wikipedia) in an app localized to Chinese.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point, we should probably wrap them in NSLocalizedString calls in case users want to localize it themselves in a particular way.

case STPSourceTypeUnknown:
return [STPCard stringFromBrand:STPCardBrandUnknown];
Copy link
Contributor

Choose a reason for hiding this comment

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

wdyt about using @"Unknown" here instead of this layer of indirection with STPCardBrandUnknown?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah why not. I first put this here because I was focused on card source types but it is a little out of place in the STPSourceTypeUnknown block.

}
}


@end
23 changes: 12 additions & 11 deletions Tests/Tests/AlipaySource.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Source: https://stripe.com/docs/sources/alipay
{
"id": "src_123",
"object": "source",
Expand All @@ -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",
Expand Down
38 changes: 38 additions & 0 deletions Tests/Tests/BancontactSource.json
Original file line number Diff line number Diff line change
@@ -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
}
}
1 change: 1 addition & 0 deletions Tests/Tests/CardSource.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Source: https://stripe.com/docs/sources/cards
{
"id": "src_123",
"object": "source",
Expand Down
34 changes: 34 additions & 0 deletions Tests/Tests/EPSSource.json
Original file line number Diff line number Diff line change
@@ -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,
}
}
36 changes: 36 additions & 0 deletions Tests/Tests/GiropaySource.json
Original file line number Diff line number Diff line change
@@ -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
}
}
42 changes: 42 additions & 0 deletions Tests/Tests/MultibancoSource.json
Original file line number Diff line number Diff line change
@@ -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",
}
}
Loading