diff --git a/Stripe.xcodeproj/project.pbxproj b/Stripe.xcodeproj/project.pbxproj index 82f93a64107..db18c2dfa7a 100644 --- a/Stripe.xcodeproj/project.pbxproj +++ b/Stripe.xcodeproj/project.pbxproj @@ -24,6 +24,22 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 0413CB22233FECD5006429EA /* STPPushProvisioningContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 0413CB1A233FECD4006429EA /* STPPushProvisioningContext.m */; }; + 0413CB23233FECD5006429EA /* STPPushProvisioningContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 0413CB1A233FECD4006429EA /* STPPushProvisioningContext.m */; }; + 0413CB24233FECD5006429EA /* STPPushProvisioningDetailsParams.m in Sources */ = {isa = PBXBuildFile; fileRef = 0413CB1B233FECD4006429EA /* STPPushProvisioningDetailsParams.m */; }; + 0413CB25233FECD5006429EA /* STPPushProvisioningDetailsParams.m in Sources */ = {isa = PBXBuildFile; fileRef = 0413CB1B233FECD4006429EA /* STPPushProvisioningDetailsParams.m */; }; + 0413CB26233FECD5006429EA /* STPPushProvisioningDetails.m in Sources */ = {isa = PBXBuildFile; fileRef = 0413CB1C233FECD4006429EA /* STPPushProvisioningDetails.m */; }; + 0413CB27233FECD5006429EA /* STPPushProvisioningDetails.m in Sources */ = {isa = PBXBuildFile; fileRef = 0413CB1C233FECD4006429EA /* STPPushProvisioningDetails.m */; }; + 0413CB28233FECD5006429EA /* STPPushProvisioningDetailsParams.h in Headers */ = {isa = PBXBuildFile; fileRef = 0413CB1D233FECD4006429EA /* STPPushProvisioningDetailsParams.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0413CB29233FECD5006429EA /* STPPushProvisioningDetailsParams.h in Headers */ = {isa = PBXBuildFile; fileRef = 0413CB1D233FECD4006429EA /* STPPushProvisioningDetailsParams.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0413CB2A233FECD5006429EA /* STPPushProvisioningContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 0413CB1E233FECD4006429EA /* STPPushProvisioningContext.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0413CB2B233FECD5006429EA /* STPPushProvisioningContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 0413CB1E233FECD4006429EA /* STPPushProvisioningContext.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0413CB2C233FECD5006429EA /* STPAPIClient+PushProvisioning.h in Headers */ = {isa = PBXBuildFile; fileRef = 0413CB1F233FECD4006429EA /* STPAPIClient+PushProvisioning.h */; }; + 0413CB2D233FECD5006429EA /* STPAPIClient+PushProvisioning.h in Headers */ = {isa = PBXBuildFile; fileRef = 0413CB1F233FECD4006429EA /* STPAPIClient+PushProvisioning.h */; }; + 0413CB2E233FECD5006429EA /* STPPushProvisioningDetails.h in Headers */ = {isa = PBXBuildFile; fileRef = 0413CB20233FECD5006429EA /* STPPushProvisioningDetails.h */; }; + 0413CB2F233FECD5006429EA /* STPPushProvisioningDetails.h in Headers */ = {isa = PBXBuildFile; fileRef = 0413CB20233FECD5006429EA /* STPPushProvisioningDetails.h */; }; + 0413CB30233FECD5006429EA /* STPAPIClient+PushProvisioning.m in Sources */ = {isa = PBXBuildFile; fileRef = 0413CB21233FECD5006429EA /* STPAPIClient+PushProvisioning.m */; }; + 0413CB31233FECD5006429EA /* STPAPIClient+PushProvisioning.m in Sources */ = {isa = PBXBuildFile; fileRef = 0413CB21233FECD5006429EA /* STPAPIClient+PushProvisioning.m */; }; 0426B96E1CEADC98006AC8DD /* STPColorUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 0426B96C1CEADC98006AC8DD /* STPColorUtils.h */; }; 0426B96F1CEADC98006AC8DD /* STPColorUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 0426B96D1CEADC98006AC8DD /* STPColorUtils.m */; }; 0426B9721CEAE3EB006AC8DD /* UITableViewCell+Stripe_Borders.h in Headers */ = {isa = PBXBuildFile; fileRef = 0426B9701CEAE3EB006AC8DD /* UITableViewCell+Stripe_Borders.h */; }; @@ -283,6 +299,15 @@ 04E39F5C1CECFAFD00AF3B96 /* STPPaymentContext+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 04E39F5A1CECFAFD00AF3B96 /* STPPaymentContext+Private.h */; }; 04E39F6A1CED48D500AF3B96 /* UIBarButtonItem+Stripe.h in Headers */ = {isa = PBXBuildFile; fileRef = 04E39F681CED48D500AF3B96 /* UIBarButtonItem+Stripe.h */; }; 04E39F6B1CED48D500AF3B96 /* UIBarButtonItem+Stripe.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E39F691CED48D500AF3B96 /* UIBarButtonItem+Stripe.m */; }; + 04E7D4BA233FEFDF00FC8C02 /* STPPushProvisioningDetailsFunctionalTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E7D4B9233FEFDF00FC8C02 /* STPPushProvisioningDetailsFunctionalTest.m */; }; + 04E7D4BD233FF29100FC8C02 /* STPFakeAddPaymentPassViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 04E7D4BB233FF29100FC8C02 /* STPFakeAddPaymentPassViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 04E7D4BE233FF29100FC8C02 /* STPFakeAddPaymentPassViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 04E7D4BB233FF29100FC8C02 /* STPFakeAddPaymentPassViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 04E7D4BF233FF29100FC8C02 /* STPFakeAddPaymentPassViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E7D4BC233FF29100FC8C02 /* STPFakeAddPaymentPassViewController.m */; }; + 04E7D4C0233FF29100FC8C02 /* STPFakeAddPaymentPassViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E7D4BC233FF29100FC8C02 /* STPFakeAddPaymentPassViewController.m */; }; + 04E7D4C32340717400FC8C02 /* PKAddPaymentPassRequest+Stripe_Error.h in Headers */ = {isa = PBXBuildFile; fileRef = 04E7D4C12340717400FC8C02 /* PKAddPaymentPassRequest+Stripe_Error.h */; }; + 04E7D4C42340717400FC8C02 /* PKAddPaymentPassRequest+Stripe_Error.h in Headers */ = {isa = PBXBuildFile; fileRef = 04E7D4C12340717400FC8C02 /* PKAddPaymentPassRequest+Stripe_Error.h */; }; + 04E7D4C52340717400FC8C02 /* PKAddPaymentPassRequest+Stripe_Error.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E7D4C22340717400FC8C02 /* PKAddPaymentPassRequest+Stripe_Error.m */; }; + 04E7D4C62340717400FC8C02 /* PKAddPaymentPassRequest+Stripe_Error.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E7D4C22340717400FC8C02 /* PKAddPaymentPassRequest+Stripe_Error.m */; }; 04EBC7531B7533C300A0E6AE /* STPCardValidationState.h in Headers */ = {isa = PBXBuildFile; fileRef = 04EBC7511B7533C300A0E6AE /* STPCardValidationState.h */; settings = {ATTRIBUTES = (Public, ); }; }; 04EBC7561B7533C300A0E6AE /* STPCardValidationState.h in Headers */ = {isa = PBXBuildFile; fileRef = 04EBC7511B7533C300A0E6AE /* STPCardValidationState.h */; settings = {ATTRIBUTES = (Public, ); }; }; 04EBC7571B7533C300A0E6AE /* STPCardValidator.h in Headers */ = {isa = PBXBuildFile; fileRef = 04EBC7521B7533C300A0E6AE /* STPCardValidator.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1345,6 +1370,14 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 0413CB1A233FECD4006429EA /* STPPushProvisioningContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPPushProvisioningContext.m; sourceTree = ""; }; + 0413CB1B233FECD4006429EA /* STPPushProvisioningDetailsParams.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPPushProvisioningDetailsParams.m; sourceTree = ""; }; + 0413CB1C233FECD4006429EA /* STPPushProvisioningDetails.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPPushProvisioningDetails.m; sourceTree = ""; }; + 0413CB1D233FECD4006429EA /* STPPushProvisioningDetailsParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STPPushProvisioningDetailsParams.h; path = PublicHeaders/STPPushProvisioningDetailsParams.h; sourceTree = ""; }; + 0413CB1E233FECD4006429EA /* STPPushProvisioningContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STPPushProvisioningContext.h; path = PublicHeaders/STPPushProvisioningContext.h; sourceTree = ""; }; + 0413CB1F233FECD4006429EA /* STPAPIClient+PushProvisioning.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "STPAPIClient+PushProvisioning.h"; sourceTree = ""; }; + 0413CB20233FECD5006429EA /* STPPushProvisioningDetails.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPPushProvisioningDetails.h; sourceTree = ""; }; + 0413CB21233FECD5006429EA /* STPAPIClient+PushProvisioning.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "STPAPIClient+PushProvisioning.m"; sourceTree = ""; }; 0426B96C1CEADC98006AC8DD /* STPColorUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPColorUtils.h; sourceTree = ""; }; 0426B96D1CEADC98006AC8DD /* STPColorUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPColorUtils.m; sourceTree = ""; }; 0426B9701CEAE3EB006AC8DD /* UITableViewCell+Stripe_Borders.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableViewCell+Stripe_Borders.h"; sourceTree = ""; }; @@ -1517,6 +1550,11 @@ 04E39F5A1CECFAFD00AF3B96 /* STPPaymentContext+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "STPPaymentContext+Private.h"; sourceTree = ""; }; 04E39F681CED48D500AF3B96 /* UIBarButtonItem+Stripe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIBarButtonItem+Stripe.h"; sourceTree = ""; }; 04E39F691CED48D500AF3B96 /* UIBarButtonItem+Stripe.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIBarButtonItem+Stripe.m"; sourceTree = ""; }; + 04E7D4B9233FEFDF00FC8C02 /* STPPushProvisioningDetailsFunctionalTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPPushProvisioningDetailsFunctionalTest.m; sourceTree = ""; }; + 04E7D4BB233FF29100FC8C02 /* STPFakeAddPaymentPassViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = STPFakeAddPaymentPassViewController.h; path = PublicHeaders/STPFakeAddPaymentPassViewController.h; sourceTree = ""; }; + 04E7D4BC233FF29100FC8C02 /* STPFakeAddPaymentPassViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STPFakeAddPaymentPassViewController.m; sourceTree = ""; }; + 04E7D4C12340717400FC8C02 /* PKAddPaymentPassRequest+Stripe_Error.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PKAddPaymentPassRequest+Stripe_Error.h"; sourceTree = ""; }; + 04E7D4C22340717400FC8C02 /* PKAddPaymentPassRequest+Stripe_Error.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "PKAddPaymentPassRequest+Stripe_Error.m"; sourceTree = ""; }; 04EBC7511B7533C300A0E6AE /* STPCardValidationState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STPCardValidationState.h; path = PublicHeaders/STPCardValidationState.h; sourceTree = ""; }; 04EBC7521B7533C300A0E6AE /* STPCardValidator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STPCardValidator.h; path = PublicHeaders/STPCardValidator.h; sourceTree = ""; }; 04F213301BCEAB61001D6F22 /* STPFormEncodable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STPFormEncodable.h; path = PublicHeaders/STPFormEncodable.h; sourceTree = ""; }; @@ -2636,6 +2674,7 @@ C1CFCB6F1ED5E11500BE45DF /* STPFileFunctionalTest.m */, B3BDCACE20EEF4640034F7F5 /* STPPaymentIntentFunctionalTest.m */, B600F3C2223088F900264403 /* STPPaymentMethodFunctionalTest.m */, + 04E7D4B9233FEFDF00FC8C02 /* STPPushProvisioningDetailsFunctionalTest.m */, C1CFCB711ED5E11500BE45DF /* STPPIIFunctionalTest.m */, C1D7B5241E36C70D002181F5 /* STPSourceFunctionalTest.m */, 0731329A2277AA200019CE3F /* STPPinManagementServiceFunctionalTest.m */, @@ -2827,6 +2866,8 @@ 04633B091CD44F6C009D4FB5 /* PKPayment+Stripe.m */, C11810931CC6C4700022FB55 /* PKPaymentAuthorizationViewController+Stripe_Blocks.h */, C11810941CC6C4700022FB55 /* PKPaymentAuthorizationViewController+Stripe_Blocks.m */, + 04E7D4C12340717400FC8C02 /* PKAddPaymentPassRequest+Stripe_Error.h */, + 04E7D4C22340717400FC8C02 /* PKAddPaymentPassRequest+Stripe_Error.m */, 04E39F681CED48D500AF3B96 /* UIBarButtonItem+Stripe.h */, 04E39F691CED48D500AF3B96 /* UIBarButtonItem+Stripe.m */, F1D3A2581EB014BD0095BFA9 /* UIImage+Stripe.h */, @@ -2884,6 +2925,10 @@ 04633B061CD44F47009D4FB5 /* STPAPIClient+ApplePay.h */, 04633B041CD44F1C009D4FB5 /* STPAPIClient+ApplePay.m */, C184107A1EC2539F00178149 /* STPEphemeralKeyProvider.h */, + 0413CB1E233FECD4006429EA /* STPPushProvisioningContext.h */, + 0413CB1A233FECD4006429EA /* STPPushProvisioningContext.m */, + 0413CB1D233FECD4006429EA /* STPPushProvisioningDetailsParams.h */, + 0413CB1B233FECD4006429EA /* STPPushProvisioningDetailsParams.m */, F152321F1EA92FCF00D65C67 /* STPRedirectContext.h */, F152321C1EA92FC100D65C67 /* STPRedirectContext.m */, 0731328D2277A3F60019CE3F /* STPPinManagementService.h */, @@ -2961,6 +3006,8 @@ 36239BAC2295EA23004FB1A5 /* STP3DS2AuthenticateResponse.h */, 36239BAD2295EA23004FB1A5 /* STP3DS2AuthenticateResponse.m */, 049952D11BCF13DD0088C703 /* STPAPIClient+Private.h */, + 0413CB1F233FECD4006429EA /* STPAPIClient+PushProvisioning.h */, + 0413CB21233FECD5006429EA /* STPAPIClient+PushProvisioning.m */, 049952CD1BCF13510088C703 /* STPAPIRequest.h */, 049952CE1BCF13510088C703 /* STPAPIRequest.m */, 8B429AD71EF9D4A300F95F34 /* STPBankAccountParams+Private.h */, @@ -2991,6 +3038,8 @@ B6B41F74223481BA0020BA7F /* STPPaymentMethodCardWallet+Private.h */, B665CE45228DE4C4008B546F /* STPPaymentMethodListDeserializer.h */, B665CE46228DE4C4008B546F /* STPPaymentMethodListDeserializer.m */, + 0413CB20233FECD5006429EA /* STPPushProvisioningDetails.h */, + 0413CB1C233FECD4006429EA /* STPPushProvisioningDetails.m */, B32B176420F80442000D6EF8 /* STPRedirectContext+Private.h */, B640DB1622C69A8E003C8810 /* STPSetupIntent+Private.h */, C1C1012C1E57A26F00C7BFAE /* STPSource+Private.h */, @@ -3225,6 +3274,8 @@ 31B9608F22FE128C00DC6FD9 /* STPBankSelectionViewController.m */, C15993261D8808490047950D /* STPShippingAddressViewController.h */, C159932C1D8808680047950D /* STPShippingAddressViewController.m */, + 04E7D4BB233FF29100FC8C02 /* STPFakeAddPaymentPassViewController.h */, + 04E7D4BC233FF29100FC8C02 /* STPFakeAddPaymentPassViewController.m */, ); name = "View Controllers"; sourceTree = ""; @@ -3289,6 +3340,7 @@ C159933D1D8808970047950D /* STPShippingMethodsViewController.h in Headers */, F15232251EA9303800D65C67 /* STPURLCallbackHandler.h in Headers */, B3A2413A1FFEB57400A2F00D /* STPConnectAccountParams.h in Headers */, + 0413CB2D233FECD5006429EA /* STPAPIClient+PushProvisioning.h in Headers */, B6F16097223351C20088C970 /* STPPaymentIntentActionRedirectToURL.h in Headers */, B32B176620F80442000D6EF8 /* STPRedirectContext+Private.h in Headers */, 319A608722E9186B00AACF66 /* STDSChallengeStatusReceiver.h in Headers */, @@ -3319,6 +3371,7 @@ B6A46F7722FCDB81001991B2 /* STPPaymentIntentLastPaymentError.h in Headers */, 0439B9881C454F97005A1ED5 /* STPPaymentOptionsViewController.h in Headers */, 319A609D22E9186B00AACF66 /* STDSTransaction.h in Headers */, + 04E7D4BE233FF29100FC8C02 /* STPFakeAddPaymentPassViewController.h in Headers */, 3604007B22C18DAE004CF80B /* STPThreeDSTextFieldCustomization.h in Headers */, B6F16091223350640088C970 /* STPPaymentIntentAction.h in Headers */, F1A2F92D1EEB6A70006B0456 /* NSCharacterSet+Stripe.h in Headers */, @@ -3333,10 +3386,12 @@ 319A609122E9186B00AACF66 /* STDSJSONEncoder.h in Headers */, 319A608222E9186B00AACF66 /* STDSAlreadyInitializedException.h in Headers */, 04F94DD11D22A239004FC826 /* STPPromise.h in Headers */, + 0413CB2F233FECD5006429EA /* STPPushProvisioningDetails.h in Headers */, C1BD9B351E3940C400CEE925 /* STPSourceVerification.h in Headers */, 36E582FD22B456760044F82C /* STPAuthenticationContext.h in Headers */, F1A0197D1EA5733200354301 /* STPSourceParams+Private.h in Headers */, 04633B161CD45222009D4FB5 /* STPAPIClient+ApplePay.h in Headers */, + 0413CB2B233FECD5006429EA /* STPPushProvisioningContext.h in Headers */, 0438EF2E1B7416BB00D506CC /* STPFormTextField.h in Headers */, B66AC61522C6E6590064C551 /* STPPaymentHandlerActionParams.h in Headers */, 04A4C38A1C4F25F900B3B290 /* NSArray+Stripe.h in Headers */, @@ -3358,6 +3413,7 @@ C1717DB11CC00ED60009CF4A /* STPAddress.h in Headers */, 04B31DE71D09D25F00EF1631 /* STPPaymentOptionsInternalViewController.h in Headers */, 3604007A22C18DA9004CF80B /* STPThreeDSSelectionCustomization.h in Headers */, + 0413CB29233FECD5006429EA /* STPPushProvisioningDetailsParams.h in Headers */, 3604007D22C18DBA004CF80B /* STPThreeDSButtonCustomization.h in Headers */, 3604007622C18D93004CF80B /* STPThreeDSCustomization+Private.h in Headers */, F152322B1EA9306100D65C67 /* NSURLComponents+Stripe.h in Headers */, @@ -3384,6 +3440,7 @@ B690DDF9222F0564000B902D /* STPPaymentMethodCard.h in Headers */, 3604007822C18DA1004CF80B /* STPThreeDSLabelCustomization.h in Headers */, C1BD9B2F1E3940A200CEE925 /* STPSourceRedirect.h in Headers */, + 04E7D4C42340717400FC8C02 /* PKAddPaymentPassRequest+Stripe_Error.h in Headers */, B690DDF3222F0211000B902D /* STPPaymentMethodAddress.h in Headers */, B36C6D742193676600D17575 /* STPPaymentIntentSourceActionAuthorizeWithURL.h in Headers */, 8B429AD91EF9D4B500F95F34 /* STPBankAccountParams+Private.h in Headers */, @@ -3530,6 +3587,7 @@ 04EBC7571B7533C300A0E6AE /* STPCardValidator.h in Headers */, 046FE9A11CE55D1D00DA6A7B /* STPPaymentActivityIndicatorView.h in Headers */, C11810861CC6AF4C0022FB55 /* STPPaymentOption.h in Headers */, + 0413CB28233FECD5006429EA /* STPPushProvisioningDetailsParams.h in Headers */, B640DB1222C58E82003C8810 /* STPSetupIntentConfirmParams.h in Headers */, 3635C33322B03E00004298B8 /* STPEmptyStripeResponse.h in Headers */, B690DDEC222F01BF000B902D /* STPPaymentMethodBillingDetails.h in Headers */, @@ -3562,6 +3620,7 @@ C1A06F101E1D8A7F004DCA06 /* STPCard+Private.h in Headers */, F1D3A24C1EB012010095BFA9 /* STPMultipartFormDataEncoder.h in Headers */, C113D2191EBB9A36006FACC2 /* STPEphemeralKey.h in Headers */, + 0413CB2A233FECD5006429EA /* STPPushProvisioningContext.h in Headers */, 04E39F6A1CED48D500AF3B96 /* UIBarButtonItem+Stripe.h in Headers */, 31B9609022FE128C00DC6FD9 /* STPBankSelectionViewController.h in Headers */, F1FA6F951E25960500EB444D /* STPCoreScrollViewController+Private.h in Headers */, @@ -3588,6 +3647,7 @@ 049A3FB21CC9FEFC00F57DE7 /* UIToolbar+Stripe_InputAccessory.h in Headers */, F1FA6F981E25970F00EB444D /* STPCoreTableViewController+Private.h in Headers */, F1DEB8901E2052150066B8E8 /* STPCoreScrollViewController.h in Headers */, + 04E7D4BD233FF29100FC8C02 /* STPFakeAddPaymentPassViewController.h in Headers */, 04A488331CA34D3000506E53 /* STPEmailAddressValidator.h in Headers */, C192269C1EBA99F900BED563 /* STPCustomerContext.h in Headers */, 04A4C38D1C4F25F900B3B290 /* UIViewController+Stripe_ParentViewController.h in Headers */, @@ -3612,6 +3672,7 @@ 315CB8C422E7D96000E612A3 /* STDSButtonCustomization.h in Headers */, 315CB8C522E7D96000E612A3 /* STDSWarning.h in Headers */, 315CB8C622E7D96000E612A3 /* STDSSelectionCustomization.h in Headers */, + 04E7D4C32340717400FC8C02 /* PKAddPaymentPassRequest+Stripe_Error.h in Headers */, 315CB8C722E7D96000E612A3 /* STDSException.h in Headers */, 315CB8C822E7D96000E612A3 /* STDSTextFieldCustomization.h in Headers */, 315CB8C922E7D96000E612A3 /* STDSUICustomization.h in Headers */, @@ -3622,11 +3683,13 @@ 315CB8CE22E7D96000E612A3 /* STDSAuthenticationRequestParameters.h in Headers */, 315CB8CF22E7D96000E612A3 /* STDSRuntimeErrorEvent.h in Headers */, 315CB8D022E7D96000E612A3 /* STDSErrorMessage.h in Headers */, + 0413CB2C233FECD5006429EA /* STPAPIClient+PushProvisioning.h in Headers */, 315CB8D122E7D96000E612A3 /* STDSCompletionEvent.h in Headers */, 315CB8D222E7D96000E612A3 /* STDSThreeDSProtocolVersion.h in Headers */, 315CB8D322E7D96000E612A3 /* STDSAuthenticationResponse.h in Headers */, 315CB8D422E7D96100E612A3 /* STDSThreeDS2Service.h in Headers */, 315CB8D522E7D96100E612A3 /* STDSFooterCustomization.h in Headers */, + 0413CB2E233FECD5006429EA /* STPPushProvisioningDetails.h in Headers */, 315CB8D622E7D96100E612A3 /* STDSAlreadyInitializedException.h in Headers */, 315CB8D722E7D96100E612A3 /* STDSJSONEncoder.h in Headers */, 31F5A50B22F0EFB10033663B /* STPPaymentMethodFPX.h in Headers */, @@ -4434,6 +4497,7 @@ 04415C6C1A6605B5001225ED /* STPBankAccountTest.m in Sources */, B664D65322B813EC00E6354B /* STPThreeDSButtonCustomizationTest.m in Sources */, C19D09931EAEAE5E00A4AB3E /* STPTelemetryClientTest.m in Sources */, + 04E7D4BA233FEFDF00FC8C02 /* STPPushProvisioningDetailsFunctionalTest.m in Sources */, F152321B1EA92F9D00D65C67 /* STPRedirectContextTest.m in Sources */, B6B41F73223476B90020BA7F /* STPPaymentMethodCardWalletVisaCheckoutTest.m in Sources */, 04415C6D1A6605B5001225ED /* STPCardFunctionalTest.m in Sources */, @@ -4473,6 +4537,7 @@ 04F94DA91D229F32004FC826 /* STPPaymentOptionTuple.m in Sources */, C15608E01FE08F2E0032AE66 /* UIView+Stripe_SafeAreaBounds.m in Sources */, F1A2F92F1EEB6A70006B0456 /* NSCharacterSet+Stripe.m in Sources */, + 04E7D4C0233FF29100FC8C02 /* STPFakeAddPaymentPassViewController.m in Sources */, 36A734282121F8A700784615 /* STPCardValidator+Private.m in Sources */, B604CF2422C56E9B00A23CC4 /* STPIntentActionRedirectToURL.m in Sources */, F19491E51E60DD72001E1FC2 /* STPSourceSEPADebitDetails.m in Sources */, @@ -4509,6 +4574,7 @@ 04A4C3901C4F25F900B3B290 /* UIViewController+Stripe_ParentViewController.m in Sources */, 04F94DA01D229F0B004FC826 /* STPPostalCodeValidator.m in Sources */, C124A17F1CCAA0C2007D42EE /* NSMutableURLRequest+Stripe.m in Sources */, + 0413CB31233FECD5006429EA /* STPAPIClient+PushProvisioning.m in Sources */, C15993411D8808A10047950D /* STPShippingMethodTableViewCell.m in Sources */, B690DDEF222F01BF000B902D /* STPPaymentMethodBillingDetails.m in Sources */, B690DDFB222F0564000B902D /* STPPaymentMethodCard.m in Sources */, @@ -4553,6 +4619,7 @@ 04633B011CD129CB009D4FB5 /* STPPhoneNumberValidator.m in Sources */, F152322D1EA9306100D65C67 /* NSURLComponents+Stripe.m in Sources */, C159933F1D88089B0047950D /* STPShippingMethodsViewController.m in Sources */, + 0413CB27233FECD5006429EA /* STPPushProvisioningDetails.m in Sources */, 04A488451CA3580700506E53 /* UINavigationController+Stripe_Completion.m in Sources */, 049E84CF1A605DE0000B66CD /* STPBankAccount.m in Sources */, 049E84D01A605DE0000B66CD /* STPCard.m in Sources */, @@ -4588,15 +4655,18 @@ 0451CC471C49AE1C003B2CA6 /* STPPaymentResult.m in Sources */, 049E84D11A605DE0000B66CD /* STPToken.m in Sources */, C1BD9B371E3940C400CEE925 /* STPSourceVerification.m in Sources */, + 0413CB23233FECD5006429EA /* STPPushProvisioningContext.m in Sources */, B68D52E622A739FF00D4E8BA /* STPSourceWeChatPayDetails.m in Sources */, 049E84D21A605DE0000B66CD /* StripeError.m in Sources */, 04B31DD71D08E6E200EF1631 /* STPCustomer.m in Sources */, C19226A01EBA9A0B00BED563 /* STPCustomerContext.m in Sources */, B690DDF5222F0211000B902D /* STPPaymentMethodAddress.m in Sources */, 049952D81BCF14990088C703 /* STPAPIRequest.m in Sources */, + 0413CB25233FECD5006429EA /* STPPushProvisioningDetailsParams.m in Sources */, 04F94DBE1D229F98004FC826 /* UITableViewCell+Stripe_Borders.m in Sources */, 0426B9791CEBD001006AC8DD /* UINavigationBar+Stripe_Theme.m in Sources */, C192268A1EBA228900BED563 /* STPTelemetryClient.m in Sources */, + 04E7D4C62340717400FC8C02 /* PKAddPaymentPassRequest+Stripe_Error.m in Sources */, 045D71231CEFA57000F6CD65 /* UIViewController+Stripe_Promises.m in Sources */, 04BC29A11CD8412000318357 /* STPPaymentContext.m in Sources */, 04CDE5C71BC20AF800548833 /* STPBankAccountParams.m in Sources */, @@ -4622,6 +4692,7 @@ C1080F4A1CBECF7B007B2D89 /* STPAddress.m in Sources */, 04B31DE81D09D25F00EF1631 /* STPPaymentOptionsInternalViewController.m in Sources */, F1D3A25C1EB014BD0095BFA9 /* UIImage+Stripe.m in Sources */, + 0413CB30233FECD5006429EA /* STPAPIClient+PushProvisioning.m in Sources */, 31F5A51122F0EFDC0033663B /* STPPaymentMethodFPXParams.m in Sources */, B613DD3322C536C900C7603F /* STPSetupIntent.m in Sources */, F1DEB8921E2052150066B8E8 /* STPCoreScrollViewController.m in Sources */, @@ -4629,6 +4700,7 @@ 0438EF2F1B7416BB00D506CC /* STPFormTextField.m in Sources */, 045D71101CEEE30500F6CD65 /* STPAspects.m in Sources */, F152321D1EA92FC100D65C67 /* STPRedirectContext.m in Sources */, + 0413CB24233FECD5006429EA /* STPPushProvisioningDetailsParams.m in Sources */, B32B176020F6D2C4000D6EF8 /* STPGenericStripeObject.m in Sources */, B621F055223454E9002141B7 /* STPPaymentMethodCardWallet.m in Sources */, 04B31E011D131D9000EF1631 /* STPPaymentCardTextFieldCell.m in Sources */, @@ -4681,6 +4753,7 @@ 0426B9731CEAE3EB006AC8DD /* UITableViewCell+Stripe_Borders.m in Sources */, B664D67222B96A1300E6354B /* STPThreeDSTextFieldCustomization.m in Sources */, C1BD9B241E393FFE00CEE925 /* STPSourceReceiver.m in Sources */, + 04E7D4C52340717400FC8C02 /* PKAddPaymentPassRequest+Stripe_Error.m in Sources */, 04B31DD61D08E6E200EF1631 /* STPCustomer.m in Sources */, 367B46D722A0969000730BE0 /* STPThreeDSCustomizationSettings.m in Sources */, B69FEC3F222EE8FE00273A16 /* STPPaymentMethod.m in Sources */, @@ -4697,6 +4770,7 @@ 049880FE1CED5A2300EA4FFD /* STPPaymentConfiguration.m in Sources */, 046FE9A21CE55D1D00DA6A7B /* STPPaymentActivityIndicatorView.m in Sources */, 31B9609622FE20DF00DC6FD9 /* STPBankSelectionTableViewCell.m in Sources */, + 04E7D4BF233FF29100FC8C02 /* STPFakeAddPaymentPassViewController.m in Sources */, B621F061223465EE002141B7 /* STPPaymentMethodCardWalletVisaCheckout.m in Sources */, 045D71221CEFA57000F6CD65 /* UIViewController+Stripe_Promises.m in Sources */, C124A1721CCA968B007D42EE /* STPAnalyticsClient.m in Sources */, @@ -4704,6 +4778,7 @@ F148ABC81D5D334B0014FD92 /* STPLocalizationUtils.m in Sources */, 04A4C38B1C4F25F900B3B290 /* NSArray+Stripe.m in Sources */, 3691EB722119111A008C49E1 /* STPCardValidator+Private.m in Sources */, + 0413CB22233FECD5006429EA /* STPPushProvisioningContext.m in Sources */, F19491E41E60DD72001E1FC2 /* STPSourceSEPADebitDetails.m in Sources */, 04A4C38F1C4F25F900B3B290 /* UIViewController+Stripe_ParentViewController.m in Sources */, 31B9609222FE128C00DC6FD9 /* STPBankSelectionViewController.m in Sources */, @@ -4737,6 +4812,7 @@ 3635C33422B03E00004298B8 /* STPEmptyStripeResponse.m in Sources */, 0451CC461C49AE1C003B2CA6 /* STPPaymentResult.m in Sources */, 04E39F551CECF7A100AF3B96 /* STPPaymentOptionTuple.m in Sources */, + 0413CB26233FECD5006429EA /* STPPushProvisioningDetails.m in Sources */, C11810961CC6C4700022FB55 /* PKPaymentAuthorizationViewController+Stripe_Blocks.m in Sources */, 0439B9891C454F97005A1ED5 /* STPPaymentOptionsViewController.m in Sources */, 04A488441CA3580700506E53 /* UINavigationController+Stripe_Completion.m in Sources */, diff --git a/Stripe/PKAddPaymentPassRequest+Stripe_Error.h b/Stripe/PKAddPaymentPassRequest+Stripe_Error.h new file mode 100644 index 00000000000..4037c9709bd --- /dev/null +++ b/Stripe/PKAddPaymentPassRequest+Stripe_Error.h @@ -0,0 +1,17 @@ +// +// PKAddPaymentPassRequest+Stripe_Error.h +// Stripe +// +// Created by Jack Flintermann on 9/29/19. +// Copyright © 2019 Stripe, Inc. All rights reserved. +// + +#import + +// This is used to store an error on a PKAddPaymentPassRequest +// so that STPFakeAddPaymentPassViewController can inspect it for debugging. +@interface PKAddPaymentPassRequest (Stripe_Error) +@property (nonatomic) NSError *stp_error; +@end + +void linkPKAddPaymentPassRequestCategory(void); diff --git a/Stripe/PKAddPaymentPassRequest+Stripe_Error.m b/Stripe/PKAddPaymentPassRequest+Stripe_Error.m new file mode 100644 index 00000000000..2e160c44629 --- /dev/null +++ b/Stripe/PKAddPaymentPassRequest+Stripe_Error.m @@ -0,0 +1,22 @@ +// +// PKAddPaymentPassRequest+Stripe_Error.m +// Stripe +// +// Created by Jack Flintermann on 9/29/19. +// Copyright © 2019 Stripe, Inc. All rights reserved. +// + +#import "PKAddPaymentPassRequest+Stripe_Error.h" +#import + +@implementation PKAddPaymentPassRequest (Stripe_Error) +- (NSError *)stp_error { + return objc_getAssociatedObject(self, @selector(stp_error)); +} + +- (void)setStp_error:(NSError *)stp_error { + objc_setAssociatedObject(self, @selector(stp_error), stp_error, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} +@end + +void linkPKAddPaymentPassRequestCategory(void){} diff --git a/Stripe/PublicHeaders/STPFakeAddPaymentPassViewController.h b/Stripe/PublicHeaders/STPFakeAddPaymentPassViewController.h new file mode 100644 index 00000000000..bbc7925d551 --- /dev/null +++ b/Stripe/PublicHeaders/STPFakeAddPaymentPassViewController.h @@ -0,0 +1,30 @@ +// +// STPFakeAddPaymentPassViewController.h +// Stripe +// +// Created by Jack Flintermann on 9/28/19. +// Copyright © 2019 Stripe, Inc. All rights reserved. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** +This class is a piece of fake UI that is intended to mimic `PKAddPaymentPassViewController`. That class is restricted to apps with a special entitlement from Apple, and as such can be difficult to build and test against. This class implements the same public API as `PKAddPaymentPassViewController`, and can be used to develop against the Stripe API in *testmode only*. (Obviously it will not actually place cards into the user's Apple Pay wallet either.) When it's time to go to production, you may simply replace all references to `STPFakeAddPaymentPassViewController` in your app with `PKAddPaymentPassViewController` and it will continue to function. For more information on developing against this API, please see https://stripe.com/docs/issuing/cards/digital-wallets . + */ +@interface STPFakeAddPaymentPassViewController : UIViewController + +/// @see PKAddPaymentPassViewController ++ (BOOL)canAddPaymentPass; + +/// @see PKAddPaymentPassViewController +- (nullable instancetype)initWithRequestConfiguration:(PKAddPaymentPassRequestConfiguration *)configuration + delegate:(nullable id)delegate NS_DESIGNATED_INITIALIZER; +/// @see PKAddPaymentPassViewController +@property (nonatomic, weak, nullable) id delegate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Stripe/PublicHeaders/STPPushProvisioningContext.h b/Stripe/PublicHeaders/STPPushProvisioningContext.h new file mode 100644 index 00000000000..f84b7d10124 --- /dev/null +++ b/Stripe/PublicHeaders/STPPushProvisioningContext.h @@ -0,0 +1,50 @@ +// +// STPPushProvisioningContext.h +// Stripe +// +// Created by Jack Flintermann on 9/27/18. +// Copyright © 2018 Stripe, Inc. All rights reserved. +// + +#import +#import +#import "STPEphemeralKeyProvider.h" +#import "STPCard.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + This class makes it easier to implement "Push Provisioning", the process by which an end-user can add a card to their Apple Pay wallet without having to type their number. This process is mediated by an Apple class called `PKAddPaymentPassViewController`; this class will help you implement that class' delegate methods. Note that this flow requires a special entitlement from Apple; for more information please see https://stripe.com/docs/issuing/cards/digital-wallets . + */ +@interface STPPushProvisioningContext : NSObject + +/** + This is a helper method to generate a PKAddPaymentPassRequestConfiguration that will work with + Stripe's Issuing APIs. Pass the returned configuration object to `PKAddPaymentPassViewController`'s `initWithRequestConfiguration:delegate:` initializer. + + @param name Your cardholder's name. Example: John Appleseed + @param description A localized description of your card's name. This will appear in Apple's UI as "{description} will be available in Wallet". Example: Platinum Rewards Card + @param last4 The last 4 of the card to be added to the user's Apple Pay wallet. Example: 4242 + @param brand The brand of the card. Example: `STPCardBrandVisa` + */ ++ (PKAddPaymentPassRequestConfiguration *)requestConfigurationWithName:(NSString *)name + description:(nullable NSString *)description + last4:(nullable NSString *)last4 + brand:(STPCardBrand)brand; + +/** + In order to retreive the encrypted payload that PKAddPaymentPassViewController expects, the Stripe SDK must talk to the Stripe API. As this requires privileged access, you must write a "key provider" that generates an Ephemeral Key on your backend and provides it to the SDK when requested. For more information, see https://stripe.com/docs/mobile/ios/standard#prepare-your-api + */ +- (instancetype)initWithKeyProvider:(id)keyProvider; + +/** + This method lines up with the method of the same name on `PKAddPaymentPassViewControllerDelegate`. You should implement that protocol in your own app, and when that method is called, call this method on your `STPPushProvisioningContext`. This in turn will first initiate a call to your `keyProvider` (see above) to obtain an Ephemeral Key, then make a call to the Stripe Issuing API to fetch an encrypted payload for the card in question, then return that payload to iOS. + */ +- (void)addPaymentPassViewController:(PKAddPaymentPassViewController *)controller + generateRequestWithCertificateChain:(NSArray *)certificates + nonce:(NSData *)nonce + nonceSignature:(NSData *)nonceSignature + completionHandler:(void (^)(PKAddPaymentPassRequest *))handler; +@end + +NS_ASSUME_NONNULL_END diff --git a/Stripe/PublicHeaders/STPPushProvisioningDetailsParams.h b/Stripe/PublicHeaders/STPPushProvisioningDetailsParams.h new file mode 100644 index 00000000000..fc2b13efae0 --- /dev/null +++ b/Stripe/PublicHeaders/STPPushProvisioningDetailsParams.h @@ -0,0 +1,43 @@ +// +// STPPushProvisioningDetailsParams.h +// Stripe +// +// Created by Jack Flintermann on 9/26/18. +// Copyright © 2018 Stripe, Inc. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + A helper class for turning the raw certificate array, nonce, and nonce signature emitted by PKAddPaymentPassViewController into a format that is understandable by the Stripe API. + If you are using STPPushProvisioningContext to implement your integration, you do not need to use this class. + */ +@interface STPPushProvisioningDetailsParams : NSObject + +/// The Stripe ID of the Issuing card object to retrieve details for. +@property (nonatomic, readonly) NSString *cardId; +/// An array of certificates that should be used to encrypt the card details. +@property (nonatomic, readonly) NSArray *certificates; +/// A nonce that should be used during the encryption of the card details. +@property (nonatomic, readonly) NSData *nonce; +/// A nonce signature that should be used during the encryption of the card details. +@property (nonatomic, readonly) NSData *nonceSignature; + +/// Implemented for convenience - the Stripe API expects the certificate chain as an array of base64-encoded strings. +@property (nonatomic, readonly) NSArray *certificatesBase64; +/// Implemented for convenience - the Stripe API expects the nonce as a hex-encoded string. +@property (nonatomic, readonly) NSString *nonceHex; +/// Implemented for convenience - the Stripe API expects the nonce signature as a hex-encoded string. +@property (nonatomic, readonly) NSString *nonceSignatureHex; + +/// Instantiates a new params object with the provided attributes. ++(instancetype)paramsWithCardId:(NSString *)cardId + certificates:(NSArray*)certificates + nonce:(NSData *)nonce + nonceSignature:(NSData *)nonceSignature; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Stripe/PublicHeaders/Stripe.h b/Stripe/PublicHeaders/Stripe.h index 5614f644fb5..be497a7b64a 100644 --- a/Stripe/PublicHeaders/Stripe.h +++ b/Stripe/PublicHeaders/Stripe.h @@ -37,6 +37,7 @@ #import "STPCustomer.h" #import "STPCustomerContext.h" #import "STPEphemeralKeyProvider.h" +#import "STPFakeAddPaymentPassViewController.h" #import "STPFile.h" #import "STPFormEncodable.h" #import "STPFPXBankBrand.h" @@ -76,6 +77,8 @@ #import "STPPaymentOption.h" #import "STPPaymentOptionsViewController.h" #import "STPPaymentResult.h" +#import "STPPushProvisioningContext.h" +#import "STPPushProvisioningDetailsParams.h" #import "STPRedirectContext.h" #import "STPSetupIntent.h" #import "STPSetupIntentConfirmParams.h" diff --git a/Stripe/STPAPIClient+PushProvisioning.h b/Stripe/STPAPIClient+PushProvisioning.h new file mode 100644 index 00000000000..58d05096ab2 --- /dev/null +++ b/Stripe/STPAPIClient+PushProvisioning.h @@ -0,0 +1,33 @@ +// +// STPAPIClient+PushProvisioning.h +// Stripe +// +// Created by Jack Flintermann on 9/27/18. +// Copyright © 2018 Stripe, Inc. All rights reserved. +// + +#import +#import "STPPushProvisioningDetails.h" +#import "STPPushProvisioningDetailsParams.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef void (^STPPushProvisioningDetailsCompletionBlock)(STPPushProvisioningDetails * __nullable details, NSError * __nullable error); + +@interface STPAPIClient (PushProvisioning) + +- (void)retrievePushProvisioningDetailsWithParams:(STPPushProvisioningDetailsParams *)params + completion:(STPPushProvisioningDetailsCompletionBlock)completion; + +@end + +NS_ASSUME_NONNULL_END + +/** + This function should not be called directly. + + It is used by the SDK when it is built as a static library to force the + compiler to link in category methods regardless of the integrating + app's compiler flags. + */ +void linkSTPAPIClientPushProvisioningCategory(void); diff --git a/Stripe/STPAPIClient+PushProvisioning.m b/Stripe/STPAPIClient+PushProvisioning.m new file mode 100644 index 00000000000..571b996677d --- /dev/null +++ b/Stripe/STPAPIClient+PushProvisioning.m @@ -0,0 +1,37 @@ +// +// STPAPIClient+PushProvisioning.m +// Stripe +// +// Created by Jack Flintermann on 9/27/18. +// Copyright © 2018 Stripe, Inc. All rights reserved. +// + +#import "STPAPIClient+PushProvisioning.h" +#import "STPAPIRequest.h" + +@implementation STPAPIClient (PushProvisioning) + +- (void)retrievePushProvisioningDetailsWithParams:(STPPushProvisioningDetailsParams *)params + completion:(STPPushProvisioningDetailsCompletionBlock)completion { + + NSString *endpoint = [NSString stringWithFormat:@"issuing/cards/%@/push_provisioning_details", params.cardId]; + NSDictionary *parameters = @{ + @"ios": @{ + @"certificates": params.certificatesBase64, + @"nonce": params.nonceHex, + @"nonce_signature": params.nonceSignatureHex, + }, + }; + + [STPAPIRequest getWithAPIClient:self + endpoint:endpoint + parameters:parameters + deserializer:[STPPushProvisioningDetails new] + completion:^(STPPushProvisioningDetails *details, __unused NSHTTPURLResponse *response, NSError *error) { + completion(details, error); + }]; +} + +@end + +void linkSTPAPIClientPushProvisioningCategory(void){} diff --git a/Stripe/STPCategoryLoader.m b/Stripe/STPCategoryLoader.m index bd5d6f654c2..5e952b7e2aa 100644 --- a/Stripe/STPCategoryLoader.m +++ b/Stripe/STPCategoryLoader.m @@ -21,7 +21,9 @@ #import "NSURLComponents+Stripe.h" #import "PKPayment+Stripe.h" #import "PKPaymentAuthorizationViewController+Stripe_Blocks.h" +#import "PKAddPaymentPassRequest+Stripe_Error.h" #import "STPAPIClient+ApplePay.h" +#import "STPAPIClient+PushProvisioning.h" #import "STPAspects.h" #import "STPCardValidator+Private.h" #import "StripeError.h" @@ -53,7 +55,9 @@ + (void)loadCategories { linkNSURLComponentsCategory(); linkPKPaymentAuthorizationViewControllerBlocksCategory(); linkPKPaymentCategory(); + linkPKAddPaymentPassRequestCategory(); linkSTPAPIClientApplePayCategory(); + linkSTPAPIClientPushProvisioningCategory(); linkSTPCardValidatorPrivateCategory(); linkUIBarButtonItemCategory(); linkUIImageCategory(); diff --git a/Stripe/STPEphemeralKeyManager.m b/Stripe/STPEphemeralKeyManager.m index c0a393dd7f1..4ce35678562 100644 --- a/Stripe/STPEphemeralKeyManager.m +++ b/Stripe/STPEphemeralKeyManager.m @@ -89,7 +89,7 @@ - (void)_createKey { if ([self.keyProvider conformsToProtocol:@protocol(STPCustomerEphemeralKeyProvider)]) { NSAssert(NO, @"Could not parse the ephemeral key response following protocol STPCustomerEphemeralKeyProvider. Make sure your backend is sending the unmodified JSON of the ephemeral key to your app. For more info, see https://stripe.com/docs/mobile/ios/standard#prepare-your-api"); } else if ([self.keyProvider conformsToProtocol:@protocol(STPIssuingCardEphemeralKeyProvider)]) { - NSAssert(NO, @"Could not parse the ephemeral key response following protocol STPCustomerEphemeralKeyProvider. Make sure your backend is sending the unmodified JSON of the ephemeral key to your app. For more info, see https://stripe.com/docs/mobile/ios/standard#prepare-your-api"); + NSAssert(NO, @"Could not parse the ephemeral key response following protocol STPIssuingCardEphemeralKeyProvider. Make sure your backend is sending the unmodified JSON of the ephemeral key to your app. For more info, see https://stripe.com/docs/mobile/ios/standard#prepare-your-api"); } NSAssert(NO, @"Could not parse the ephemeral key response. Make sure your backend is sending the unmodified JSON of the ephemeral key to your app. For more info, see https://stripe.com/docs/mobile/ios/standard#prepare-your-api"); } diff --git a/Stripe/STPFakeAddPaymentPassViewController.m b/Stripe/STPFakeAddPaymentPassViewController.m new file mode 100644 index 00000000000..7de0d2030be --- /dev/null +++ b/Stripe/STPFakeAddPaymentPassViewController.m @@ -0,0 +1,217 @@ +// +// STPFakeAddPaymentPassViewController.m +// Stripe +// +// Created by Jack Flintermann on 9/28/19. +// Copyright © 2019 Stripe, Inc. All rights reserved. +// + +#import "STPFakeAddPaymentPassViewController.h" +#import "PKAddPaymentPassRequest+Stripe_Error.h" +#import "StripeError.h" +#import "STPLocalizationUtils.h" + +typedef NS_ENUM(NSUInteger, STPFakeAddPaymentPassViewControllerState) { + STPFakeAddPaymentPassViewControllerStateInitial, + STPFakeAddPaymentPassViewControllerStateLoading, + STPFakeAddPaymentPassViewControllerStateError, + STPFakeAddPaymentPassViewControllerStateSuccess, +}; + +@interface STPFakeAddPaymentPassViewController () +@property(nonatomic)PKAddPaymentPassRequestConfiguration *configuration; +@property(nonatomic)STPFakeAddPaymentPassViewControllerState state; +@property(nonatomic)UILabel *contentLabel; +@property(nonatomic)NSString *errorText; +@end + +@implementation STPFakeAddPaymentPassViewController + ++ (BOOL)canAddPaymentPass { + return YES; +} + +- (nullable instancetype)initWithRequestConfiguration:(PKAddPaymentPassRequestConfiguration *)configuration + delegate:(nullable id)delegate { + self = [super initWithNibName:nil bundle:nil]; + NSCParameterAssert(delegate); + if (self) { + _state = STPFakeAddPaymentPassViewControllerStateInitial; + _delegate = delegate; + _configuration = configuration; + if (!configuration.primaryAccountSuffix && !configuration.cardholderName) { + NSCAssert(NO, @"Your PKAddPaymentPassRequestConfiguration must provide either a cardholderName or a primaryAccountSuffix."); + } + } + return self; +} + +- (instancetype)initWithNibName:(__unused NSString *)nibNameOrNil bundle:(__unused NSBundle *)nibBundleOrNil { + return [self initWithRequestConfiguration:[PKAddPaymentPassRequestConfiguration new] delegate:nil]; +} + +- (instancetype)initWithCoder:(__unused NSCoder *)aDecoder { + return [self initWithRequestConfiguration:[PKAddPaymentPassRequestConfiguration new] delegate:nil]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + self.view.backgroundColor = [UIColor whiteColor]; + + UINavigationBar *navBar = [[UINavigationBar alloc] init]; + [self.view addSubview:navBar]; + navBar.translucent = NO; + navBar.backgroundColor = [UIColor whiteColor]; + navBar.items = @[self.navigationItem]; + navBar.translatesAutoresizingMaskIntoConstraints = NO; + [navBar.leftAnchor constraintEqualToAnchor:self.view.leftAnchor].active = YES; + [navBar.rightAnchor constraintEqualToAnchor:self.view.rightAnchor].active = YES; + if (@available(iOS 11.0, *)) { + [navBar.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor].active = YES; + } else { + [navBar.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES; + } + + UILabel *contentLabel = [[UILabel alloc] initWithFrame:CGRectZero]; + self.contentLabel = contentLabel; + contentLabel.textAlignment = NSTextAlignmentCenter; + contentLabel.textColor = [UIColor blackColor]; + contentLabel.numberOfLines = 0; + contentLabel.font = [UIFont systemFontOfSize:18]; + contentLabel.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:contentLabel]; + [contentLabel.topAnchor constraintEqualToAnchor:navBar.bottomAnchor].active = YES; + [contentLabel.leftAnchor constraintEqualToAnchor:self.view.leftAnchor constant:10.0f].active = YES; + [contentLabel.rightAnchor constraintEqualToAnchor:self.view.rightAnchor constant:-10.0f].active = YES; + [contentLabel.heightAnchor constraintEqualToConstant:150].active = YES; + + NSMutableArray *pairs = [NSMutableArray array]; + if (self.configuration.cardholderName) { + [pairs addObject:@[@"Name", self.configuration.cardholderName]]; + } + if (self.configuration.primaryAccountSuffix) { + [pairs addObject:@[@"Card Number", [NSString stringWithFormat:@"···· %@", self.configuration.primaryAccountSuffix]]]; + } + NSMutableArray *rows = [NSMutableArray array]; + for (NSArray *pair in pairs) { + UILabel *left = [[UILabel alloc] init]; + left.text = pair[0]; + left.textAlignment = NSTextAlignmentLeft; + [left setFont:[UIFont boldSystemFontOfSize:16]]; + UILabel *right = [[UILabel alloc] init]; + right.text = pair[1]; + right.textAlignment = NSTextAlignmentLeft; + right.textColor = [UIColor lightGrayColor]; + UIStackView *row = [[UIStackView alloc] initWithArrangedSubviews:@[left, right]]; + row.axis = UILayoutConstraintAxisHorizontal; + row.distribution = UIStackViewDistributionFillEqually; + row.alignment = UIStackViewAlignmentFill; + row.translatesAutoresizingMaskIntoConstraints = NO; + [rows addObject:row]; + } + UIStackView *pairsTable = [[UIStackView alloc] initWithArrangedSubviews:rows]; + pairsTable.layoutMarginsRelativeArrangement = YES; + pairsTable.layoutMargins = UIEdgeInsetsMake(20, 20, 20, 20); + pairsTable.axis = UILayoutConstraintAxisVertical; + pairsTable.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:pairsTable]; + + [pairsTable.leftAnchor constraintEqualToAnchor:self.view.leftAnchor].active = YES; + [pairsTable.rightAnchor constraintEqualToAnchor:self.view.rightAnchor].active = YES; + [pairsTable.topAnchor constraintEqualToAnchor:contentLabel.bottomAnchor].active = YES; + [pairsTable.heightAnchor constraintEqualToConstant:(rows.count * 50)].active = YES; + [self setState:STPFakeAddPaymentPassViewControllerStateInitial]; +} + +- (void)setState:(STPFakeAddPaymentPassViewControllerState)state { + _state = state; + UIBarButtonItem *cancelItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancel:)]; + UIButton *nextButton = [UIButton buttonWithType:UIButtonTypeSystem]; + [nextButton addTarget:self action:@selector(next:) forControlEvents:UIControlEventTouchUpInside]; + UIActivityIndicatorView *indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; + [indicatorView startAnimating]; + UIBarButtonItem *loadingItem = [[UIBarButtonItem alloc] initWithCustomView:indicatorView]; + [nextButton setTitle:STPNonLocalizedString(@"Next") forState:UIControlStateNormal]; + UIBarButtonItem *nextItem = [[UIBarButtonItem alloc] initWithCustomView:nextButton]; + UIBarButtonItem *doneItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(done:)]; + + switch (state) { + case STPFakeAddPaymentPassViewControllerStateInitial: + self.contentLabel.text = STPNonLocalizedString(@"This class simulates the delegate methods that PKAddPaymentPassViewController will call in your app. Press next to continue."); + self.navigationItem.leftBarButtonItem = cancelItem; + self.navigationItem.rightBarButtonItem = nextItem; + break; + case STPFakeAddPaymentPassViewControllerStateLoading: + self.contentLabel.text = STPNonLocalizedString(@"Fetching encrypted card details..."); + cancelItem.enabled = NO; + self.navigationItem.leftBarButtonItem = cancelItem; + self.navigationItem.rightBarButtonItem = loadingItem; + break; + case STPFakeAddPaymentPassViewControllerStateError: + self.contentLabel.text = STPNonLocalizedString([@"Error: " stringByAppendingString:self.errorText]); + doneItem.enabled = NO; + self.navigationItem.leftBarButtonItem = cancelItem; + self.navigationItem.rightBarButtonItem = doneItem; + break; + case STPFakeAddPaymentPassViewControllerStateSuccess: + self.contentLabel.text = STPNonLocalizedString(@"Success! In production, your card would now have been added to your Apple Pay wallet. Your app's success callback will be triggered when the user presses 'Done'."); + cancelItem.enabled = NO; + self.navigationItem.leftBarButtonItem = cancelItem; + self.navigationItem.rightBarButtonItem = doneItem; + } +} + +- (void)cancel:(__unused id)sender { + [self.delegate addPaymentPassViewController:(PKAddPaymentPassViewController *)self didFinishAddingPaymentPass:nil error:[NSError errorWithDomain:PKPassKitErrorDomain code:PKAddPaymentPassErrorUserCancelled userInfo:nil]]; +} + +- (void)next:(__unused id)sender { + [self setState:STPFakeAddPaymentPassViewControllerStateLoading]; + NSArray *certificates = @[ + [@"cert1" dataUsingEncoding:NSUTF8StringEncoding], + [@"cert2" dataUsingEncoding:NSUTF8StringEncoding], + ]; + NSData *nonce = [@"nonce" dataUsingEncoding:NSUTF8StringEncoding]; + NSData *nonceSignature = [@"nonceSignature" dataUsingEncoding:NSUTF8StringEncoding]; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + if (self.state == STPFakeAddPaymentPassViewControllerStateLoading) { + self.errorText = @"You exceeded the timeout of 10 seconds to call the request completion handler. Please check your PKAddPaymentPassViewControllerDelegate implementation, and make sure you are calling the `completionHandler` in `addPaymentPassViewController:generateRequestWithCertificateChain:nonce:nonceSignature:completionHandler`."; + [self setState:STPFakeAddPaymentPassViewControllerStateError]; + } + }); + [self.delegate addPaymentPassViewController:(PKAddPaymentPassViewController *)self + generateRequestWithCertificateChain:certificates + nonce:nonce + nonceSignature:nonceSignature + completionHandler:^(PKAddPaymentPassRequest * _Nonnull request) { + if (self.state == STPFakeAddPaymentPassViewControllerStateLoading) { + NSString *contents; + if (request.encryptedPassData) { + contents = [[NSString alloc] initWithData:request.encryptedPassData encoding:NSUTF8StringEncoding]; + } + if (request.stp_error) { + NSString *error = request.stp_error.userInfo[STPErrorMessageKey]; + if (!error) { + error = request.stp_error.userInfo[NSLocalizedDescriptionKey]; + } + self.errorText = error; + [self setState:STPFakeAddPaymentPassViewControllerStateError]; + } + // This specific string is returned by the Stripe API in testmode. + else if ([contents isEqualToString:@"TESTMODE_CONTENTS"]){ + [self setState:STPFakeAddPaymentPassViewControllerStateSuccess]; + } else { + self.errorText = @"Your server response contained the wrong encrypted card details. Please ensure that you are not modifying the response from the Stripe API in any way, and that your request is in testmode."; + [self setState:STPFakeAddPaymentPassViewControllerStateError]; + } + } +}]; +} + +- (void)done:(__unused id)sender { + PKPaymentPass *pass = [PKPaymentPass new]; + [self.delegate addPaymentPassViewController:(PKAddPaymentPassViewController *)self didFinishAddingPaymentPass:pass error:nil]; +} + +@end diff --git a/Stripe/STPPushProvisioningContext.m b/Stripe/STPPushProvisioningContext.m new file mode 100644 index 00000000000..6dcd0993c82 --- /dev/null +++ b/Stripe/STPPushProvisioningContext.m @@ -0,0 +1,78 @@ +// +// STPPushProvisioningContext.m +// Stripe +// +// Created by Jack Flintermann on 9/27/18. +// Copyright © 2018 Stripe, Inc. All rights reserved. +// + +#import "STPPushProvisioningContext.h" +#import "STPEphemeralKeyManager.h" +#import "STPAPIClient+Private.h" +#import "STPAPIClient+PushProvisioning.h" +#import "STPEphemeralKey.h" +#import "PKAddPaymentPassRequest+Stripe_Error.h" + +@interface STPPushProvisioningContext() +@property (nonatomic, strong) STPEphemeralKeyManager *keyManager; +@property (nonatomic, strong, nullable) STPEphemeralKey *ephemeralKey; +@end + +@implementation STPPushProvisioningContext + +- (instancetype)initWithKeyProvider:(id)keyProvider { + self = [super init]; + if (self) { + _keyManager = [[STPEphemeralKeyManager alloc] initWithKeyProvider:keyProvider apiVersion:[STPAPIClient apiVersion] performsEagerFetching:NO]; + } + return self; +} + ++ (PKAddPaymentPassRequestConfiguration *)requestConfigurationWithName:(NSString *)name + description:(nullable NSString *)description + last4:(nullable NSString *)last4 + brand:(STPCardBrand)brand { + PKAddPaymentPassRequestConfiguration *config = [[PKAddPaymentPassRequestConfiguration alloc] initWithEncryptionScheme:PKEncryptionSchemeECC_V2]; + config.cardholderName = name; + config.primaryAccountSuffix = last4; + config.localizedDescription = description; + if (@available(iOS 12.0, *)) { + config.style = PKAddPaymentPassStylePayment; + } + if (brand == STPCardBrandVisa) { + config.paymentNetwork = PKPaymentNetworkVisa; + } + if (brand == STPCardBrandMasterCard) { + config.paymentNetwork = PKPaymentNetworkMasterCard; + } + return config; +} + +- (void)addPaymentPassViewController:(__unused PKAddPaymentPassViewController *)controller generateRequestWithCertificateChain:(NSArray *)certificates nonce:(NSData *)nonce nonceSignature:(NSData *)nonceSignature completionHandler:(void (^)(PKAddPaymentPassRequest *))handler { + [self.keyManager getOrCreateKey:^(STPEphemeralKey * _Nullable ephemeralKey, NSError * _Nullable keyError) { + if (keyError != nil) { + PKAddPaymentPassRequest *request = [PKAddPaymentPassRequest new]; + request.stp_error = keyError; + // handler, bizarrely, cannot take an NSError, but passing an empty PKAddPaymentPassRequest causes roughly equivalent behavior. + handler(request); + return; + } + STPPushProvisioningDetailsParams *params = [STPPushProvisioningDetailsParams paramsWithCardId:ephemeralKey.issuingCardID certificates:certificates nonce:nonce nonceSignature:nonceSignature]; + STPAPIClient *client = [STPAPIClient apiClientWithEphemeralKey:ephemeralKey]; + [client retrievePushProvisioningDetailsWithParams:params completion:^(STPPushProvisioningDetails * _Nullable details, NSError * _Nullable error) { + if (error != nil) { + PKAddPaymentPassRequest *request = [PKAddPaymentPassRequest new]; + request.stp_error = error; + handler(request); + return; + } + PKAddPaymentPassRequest *request = [[PKAddPaymentPassRequest alloc] init]; + request.activationData = details.activationData; + request.encryptedPassData = details.encryptedPassData; + request.ephemeralPublicKey = details.ephemeralPublicKey; + handler(request); + }]; + }]; +} + +@end diff --git a/Stripe/STPPushProvisioningDetails.h b/Stripe/STPPushProvisioningDetails.h new file mode 100644 index 00000000000..99394d8bb45 --- /dev/null +++ b/Stripe/STPPushProvisioningDetails.h @@ -0,0 +1,30 @@ +// +// STPPushProvisioningDetails.h +// Stripe +// +// Created by Jack Flintermann on 9/26/18 +// Copyright © 2018 Stripe, Inc. All rights reserved. +// + +#import +#import "STPAPIResponseDecodable.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface STPPushProvisioningDetails : NSObject + +@property (nonatomic, readonly) NSString *cardId; +@property (nonatomic, readonly) BOOL livemode; +@property (nonatomic, readonly) NSData *encryptedPassData; +@property (nonatomic, readonly) NSData *activationData; +@property (nonatomic, readonly) NSData *ephemeralPublicKey; + ++ (instancetype)detailsWithCardId:(NSString *)cardId + livemode:(BOOL)livemode + encryptedPassData:(NSData *)encryptedPassData + activationData:(NSData *)activationData + ephemeralPublicKey:(NSData *)ephemeralPublicKey; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Stripe/STPPushProvisioningDetails.m b/Stripe/STPPushProvisioningDetails.m new file mode 100644 index 00000000000..45a1f59b252 --- /dev/null +++ b/Stripe/STPPushProvisioningDetails.m @@ -0,0 +1,97 @@ +// +// STPPushProvisioningDetails.m +// Stripe +// +// Created by Jack Flintermann on 9/26/18. +// Copyright © 2018 Stripe, Inc. All rights reserved. +// + +#import "STPPushProvisioningDetails.h" + +#import "NSDictionary+Stripe.h" + +@interface STPPushProvisioningDetails () + +@property (nonatomic, readwrite) NSString *cardId; +@property (nonatomic, readwrite) BOOL livemode; +@property (nonatomic, readwrite) NSData *encryptedPassData; +@property (nonatomic, readwrite) NSData *activationData; +@property (nonatomic, readwrite) NSData *ephemeralPublicKey; +@property (nonatomic, readwrite, copy) NSDictionary *allResponseFields; + +@end + +@implementation STPPushProvisioningDetails + ++ (instancetype)detailsWithCardId:(NSString *)cardId + livemode:(BOOL)livemode + encryptedPassData:(NSData *)encryptedPassData + activationData:(NSData *)activationData + ephemeralPublicKey:(NSData *)ephemeralPublicKey { + STPPushProvisioningDetails *details = [[self alloc] init]; + details.cardId = cardId; + details.livemode = livemode; + details.encryptedPassData = encryptedPassData; + details.activationData = activationData; + details.ephemeralPublicKey = ephemeralPublicKey; + return details; +} + +#pragma mark - STPAPIResponseDecodable + ++ (instancetype)decodedObjectFromAPIResponse:(NSDictionary *)response { + NSDictionary *dict = [response stp_dictionaryByRemovingNulls]; + if (!dict) { + return nil; + } + + // required fields + NSString *cardId = [dict stp_stringForKey:@"card"]; + BOOL livemode = [dict stp_boolForKey:@"livemode" or:NO]; + NSString *encryptedPassString = [dict stp_stringForKey:@"contents"]; + NSData *encryptedPassData = encryptedPassString ? [[NSData alloc] initWithBase64EncodedString:encryptedPassString options:0] : nil; + + NSString *activationString = [dict stp_stringForKey:@"activation_data"]; + NSData *activationData = activationString ? [[NSData alloc] initWithBase64EncodedString:activationString options:0] : nil; + + NSString *ephemeralPublicKeyString = [dict stp_stringForKey:@"ephemeral_public_key"]; + NSData *ephemeralPublicKeyData = ephemeralPublicKeyString ? [[NSData alloc] initWithBase64EncodedString:ephemeralPublicKeyString options:0] : nil; + + if (cardId == nil || encryptedPassData == nil || activationData == nil || ephemeralPublicKeyData == nil) { + return nil; + } + + STPPushProvisioningDetails *details = [self detailsWithCardId:cardId + livemode:livemode + encryptedPassData:encryptedPassData + activationData:activationData + ephemeralPublicKey:ephemeralPublicKeyData]; + details.allResponseFields = dict; + + return details; +} + + +#pragma mark - Equality + +- (BOOL)isEqual:(STPPushProvisioningDetails *)details { + return [self isEqualToDetails:details]; +} + +- (NSUInteger)hash { + return [self.activationData hash]; +} + +- (BOOL)isEqualToDetails:(STPPushProvisioningDetails *)details { + if (self == details) { + return YES; + } + + if (!details || ![details isKindOfClass:self.class]) { + return NO; + } + + return [self.activationData isEqualToData:details.activationData]; +} + +@end diff --git a/Stripe/STPPushProvisioningDetailsParams.m b/Stripe/STPPushProvisioningDetailsParams.m new file mode 100644 index 00000000000..8194fc4eae7 --- /dev/null +++ b/Stripe/STPPushProvisioningDetailsParams.m @@ -0,0 +1,68 @@ +// +// STPPushProvisioningDetailsParams.m +// Stripe +// +// Created by Jack Flintermann on 9/26/18. +// Copyright © 2018 Stripe, Inc. All rights reserved. +// + +#import "STPPushProvisioningDetailsParams.h" + +@interface STPPushProvisioningDetailsParams () + +@property (nonatomic, readwrite) NSString *cardId; +@property (nonatomic, readwrite) NSArray *certificates; +@property (nonatomic, readwrite) NSData *nonce; +@property (nonatomic, readwrite) NSData *nonceSignature; + +@end + +@implementation STPPushProvisioningDetailsParams + ++(instancetype)paramsWithCardId:(NSString *)cardId + certificates:(NSArray*)certificates + nonce:(NSData *)nonce + nonceSignature:(NSData *)nonceSignature { + STPPushProvisioningDetailsParams *params = [[self alloc] init]; + params.cardId = cardId; + params.certificates = certificates; + params.nonce = nonce; + params.nonceSignature = nonceSignature; + return params; +} + +- (NSArray *)certificatesBase64 { + NSMutableArray *base64Certificates = [NSMutableArray array]; + for (NSData *certificate in self.certificates) { + [base64Certificates addObject:[certificate base64EncodedStringWithOptions:kNilOptions]]; + } + return base64Certificates; +} + +- (NSString *)nonceHex { + return [self.class hexadecimalStringForData:self.nonce]; +} + +- (NSString *)nonceSignatureHex { + return [self.class hexadecimalStringForData:self.nonceSignature]; +} + +// Adapted from https://stackoverflow.com/questions/1305225/best-way-to-serialize-an-nsdata-into-a-hexadeximal-string ++ (NSString *)hexadecimalStringForData:(NSData *)data { + const unsigned char *dataBuffer = (const unsigned char *)[data bytes]; + + if (!dataBuffer) { + return [NSString string]; + } + + NSUInteger dataLength = [data length]; + NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)]; + + for (NSUInteger i = 0; i < dataLength; ++i) { + [hexString appendString:[NSString stringWithFormat:@"%02lx", (unsigned long)dataBuffer[i]]]; + } + + return [NSString stringWithString:hexString]; +} + +@end diff --git a/Tests/Tests/STPPushProvisioningDetailsFunctionalTest.m b/Tests/Tests/STPPushProvisioningDetailsFunctionalTest.m new file mode 100644 index 00000000000..9a1ce54edc7 --- /dev/null +++ b/Tests/Tests/STPPushProvisioningDetailsFunctionalTest.m @@ -0,0 +1,45 @@ +// +// STPPushProvisioningDetailsFunctionalTest.m +// StripeiOS Tests +// +// Created by Jack Flintermann on 11/30/18. +// Copyright © 2018 Stripe, Inc. All rights reserved. +// + +#import +#import +#import "STPAPIClient+PushProvisioning.h" +#import "STPNetworkStubbingTestCase.h" + +@interface STPPushProvisioningDetailsFunctionalTest : STPNetworkStubbingTestCase +@end + +@implementation STPPushProvisioningDetailsFunctionalTest + +- (void)setUp { + [super setUp]; +} + +- (void)testRetrievePushProvisioningDetails { + // this API requires a secret key - replace the key below if you need to re-record the network traffic. + STPAPIClient *client = [[STPAPIClient alloc] initWithPublishableKey:@"pk_test_REPLACEME"]; + NSString *cardId = @"ic_1C0Xig4JYtv6MPZK91WoXa9u"; + NSString *cert1 = @"MIID/TCCA6OgAwIBAgIIGM2CpiS9WyYwCgYIKoZIzj0EAwIwgYAxNDAyBgNVBAMMK0FwcGxlIFdvcmxkd2lkZSBEZXZlbG9wZXIgUmVsYXRpb25zIENBIC0gRzIxJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzAeFw0xODA2MDEyMjE0MTVaFw0yMDA2MzAyMjE0MTVaMGwxMjAwBgNVBAMMKWVjYy1jcnlwdG8tc2VydmljZXMtZW5jaXBoZXJtZW50X1VDNi1QUk9EMRQwEgYDVQQLDAtpT1MgU3lzdGVtczETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASzCVyQGX3syyW2aI6nyfNQe+vjjzjU4rLO0ZiWiVZZSmEzYfACFI8tuDFiDLv9XWrHEeX0/yNtGVjwAzpanWb/o4ICGDCCAhQwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBSEtoTMOoZichZZlOgao71I3zrfCzBHBggrBgEFBQcBAQQ7MDkwNwYIKwYBBQUHMAGGK2h0dHA6Ly9vY3NwLmFwcGxlLmNvbS9vY3NwMDMtYXBwbGV3d2RyY2EyMDUwggEdBgNVHSAEggEUMIIBEDCCAQwGCSqGSIb3Y2QFATCB/jCBwwYIKwYBBQUHAgIwgbYMgbNSZWxpYW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRlIHBvbGljeSBhbmQgY2VydGlmaWNhdGlvbiBwcmFjdGljZSBzdGF0ZW1lbnRzLjA2BggrBgEFBQcCARYqaHR0cDovL3d3dy5hcHBsZS5jb20vY2VydGlmaWNhdGVhdXRob3JpdHkvMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwuYXBwbGUuY29tL2FwcGxld3dkcmNhMi5jcmwwHQYDVR0OBBYEFI5aYtQKaJCRpvI1Dgh+Ra4x2iCrMA4GA1UdDwEB/wQEAwIDKDASBgkqhkiG92NkBicBAf8EAgUAMAoGCCqGSM49BAMCA0gAMEUCIAY/9gwN/KAAw3EtW3NyeX1UVM3fO+wVt0cbeHL8eM/mAiEAppLm5O/2Ox8uHkxI4U/kU5vDhJA21DRbzm2rsYN+EcQ="; + NSString *cert2 = @"MIIC9zCCAnygAwIBAgIIb+/Y9emjp+4wCgYIKoZIzj0EAwIwZzEbMBkGA1UEAwwSQXBwbGUgUm9vdCBDQSAtIEczMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwHhcNMTQwNTA2MjM0MzI0WhcNMjkwNTA2MjM0MzI0WjCBgDE0MDIGA1UEAwwrQXBwbGUgV29ybGR3aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ0EgLSBHMjEmMCQGA1UECwwdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxEzARBgNVBAoMCkFwcGxlIEluYy4xCzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE3fC3BkvP3XMEE8RDiQOTgPte9nStQmFSWAImUxnIYyIHCVJhysTZV+9tJmiLdJGMxPmAaCj8CWjwENrp0C7JGqOB9zCB9DBGBggrBgEFBQcBAQQ6MDgwNgYIKwYBBQUHMAGGKmh0dHA6Ly9vY3NwLmFwcGxlLmNvbS9vY3NwMDQtYXBwbGVyb290Y2FnMzAdBgNVHQ4EFgQUhLaEzDqGYnIWWZToGqO9SN863wswDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS7sN6hWDOImqSKmd6+veuv2sskqzA3BgNVHR8EMDAuMCygKqAohiZodHRwOi8vY3JsLmFwcGxlLmNvbS9hcHBsZXJvb3RjYWczLmNybDAOBgNVHQ8BAf8EBAMCAQYwEAYKKoZIhvdjZAYCDwQCBQAwCgYIKoZIzj0EAwIDaQAwZgIxANmxxzHGI/ZPTdDZR8V9GGkRh3En02it4Jtlmr5s3z9GppAJvm6hOyywUYlBPIfSvwIxAPxkUolLPF2/axzCiZgvcq61m6oaCyNUd1ToFUOixRLal1BzfF7QbrJcYlDXUfE6Wg=="; + NSString *nonce = @"ea85a73a"; + NSString *nonceSignature = @"QBfCqTvDhmRcwqxJF3fDqzhXezIpwrpHFcOMw7/DvGVBwpfCuicwwqHCmMKYMD06w754wrjChcObwqjDr8K9wqxxUydQaMOyfsKGZMK4AcKMwqNfwoHDlcKLHsO5w7JqQiHDln7Du8KUNMOnwqpGwq/CqcKswo1Lw7s="; + NSArray *certs = @[ + [[NSData alloc] initWithBase64EncodedString:cert1 options:0], + [[NSData alloc] initWithBase64EncodedString:cert2 options:0], + ]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Push provisioning details"]; + STPPushProvisioningDetailsParams *params = [STPPushProvisioningDetailsParams paramsWithCardId:@"ic_1C0Xig4JYtv6MPZK91WoXa9u" certificates:certs nonce:[[NSData alloc] initWithBase64EncodedString:nonce options:0] nonceSignature:[[NSData alloc] initWithBase64EncodedString:nonceSignature options:0]]; + [client retrievePushProvisioningDetailsWithParams:params completion:^(STPPushProvisioningDetails * _Nullable details, NSError * _Nullable error) { + [expectation fulfill]; + XCTAssertNil(error); + XCTAssert([details.cardId isEqualToString:cardId]); + }]; + [self waitForExpectationsWithTimeout:5.0f handler:nil]; +} + +@end diff --git a/Tests/recorded_network_traffic/STPPushProvisioningDetailsFunctionalTest/testRetrievePushProvisioningDetails/get_v1_issuing_cards_ic_1C0Xig4JYtv6MPZK91WoXa9u_push_provisioning_details_0.tail b/Tests/recorded_network_traffic/STPPushProvisioningDetailsFunctionalTest/testRetrievePushProvisioningDetails/get_v1_issuing_cards_ic_1C0Xig4JYtv6MPZK91WoXa9u_push_provisioning_details_0.tail new file mode 100644 index 00000000000..6095a2845d8 --- /dev/null +++ b/Tests/recorded_network_traffic/STPPushProvisioningDetailsFunctionalTest/testRetrievePushProvisioningDetails/get_v1_issuing_cards_ic_1C0Xig4JYtv6MPZK91WoXa9u_push_provisioning_details_0.tail @@ -0,0 +1,27 @@ +GET +/v1/issuing/cards/ic_1C0Xig4JYtv6MPZK91WoXa9u/push_provisioning_details\?ios%5Bcertificates%5D%5B0%5D=.*&ios%5Bcertificates%5D%5B1%5D=.*&ios%5Bnonce%5D=.*&ios%5Bnonce_signature%5D=.*$ +200 +application/json +Content-Type: application/json +Access-Control-Allow-Origin: * +Access-Control-Allow-Methods: GET, POST, HEAD, OPTIONS, DELETE +Server: nginx +Access-Control-Expose-Headers: Request-Id, Stripe-Manage-Version, X-Stripe-External-Auth-Required, X-Stripe-Privileged-Session-Required +Access-Control-Max-Age: 300 +Stripe-Issuing-Push-Provisioning-Platform: ios +Cache-Control: no-cache, no-store +Date: Fri, 30 Nov 2018 23:12:54 GMT +Stripe-Version: 2015-10-12 +Access-Control-Allow-Credentials: true +Content-Length: 1278 +Connection: keep-alive +Strict-Transport-Security: max-age=31556926; includeSubDomains; preload +Request-Id: req_QTDJy3AYaxI1tL + +{ + "activation_data" : "TUJQQUMtMS1GSy00MDU1NTEuMS0tVERFQS1FNUVCNUJCQjhGMENDMjdCQjU5MUNCRTdCMTVDN0U2RjBENTQ5RDM1NkZERTRDQUZBNDBGOUNCMTA1MzI5NUQ4N0RBRTk0MTE0QTQyNENDQTY0NDAxRTFCQTExOTRBRDM=", + "object" : "issuing.push_provisioning_details", + "ephemeral_public_key" : "BHdLb5BNpoPrh9Btay8LwQ5oELoziQwJL7HagE3xB5mrbdgLa5iiwogu34Y32\/xBEviaN31s\/ONRXSetYT745ig=", + "card" : "ic_1C0Xig4JYtv6MPZK91WoXa9u", + "contents" : "UYeMxRqiYrjVzwqKcCGRVbgFXRspbDIKkWWly8e55caWkHYmO2DNnFtqD3y5S6bvGbe+bkNPCIkT1UzQQChCQOkb0P+cbVDBLaFGaDeJW0Mhca8\/6GwbUx9lp4H3czqszG504PkiA89dNvnbtwUmlQOpk+B\/IAMnkaXjD2xUBUtPX9xEr5EvckkDSHHFmpy5rbGfqnWsPbJNPwUiE+6mYbt643DqF9RpmgdFN84DImuMU1W0xshbkN7voq63L\/6UgTW7liTzWVzKUT36TtaTw5TGKVf1Niqu5CHNu2NpDEnzrvcwUCgphxRVgezJyFfq1NjhZVlGA2nUKZuRvc\/XjBwdE0fr4Enw5XfHbRQHorpv\/S2rX4Cmn4VHJE1JDHWK3Wrn4HmMOCH+psVi+T5hPvy6+\/v+0zRRmGGeFKQEx0soItHiQauN2\/zO4QoC2DCQOAKGj1KSzqHhTgdxBcBu4TIOQRsIXu6zk1ItenHIdq4thD8vF8m3wgJ8Y3KcG3TwwgbjomxOjO4rX0AA9q2V6w0TXWQ1eWC8WfX11J30Zt\/SbyYHoU8KrIdM2ANcOvIFHENnUNBcL7AO0+tjv9lVO7M9w7hKMiVJOnDGMeH2OQfnTBOJI8SEHGm2kDRNw\/5+VGJ7pHA1wZ9y2IS6EvY8IeNhqZ7HdBXhn18X4AWThZqmNvGuZoEU\/ZlPmfed\/c0BgqSw5x6K9GuC5G9Nyee3O1E6wETf4Z3goNbFnRYNJ8m+A4DxUKbvhhRSNva4d\/lRkoNuQj2ztPiLSAPVt1H7Dk3ryeyXCrqprHsjn5T35jXFOlm5whuyeGgeXWt3DUxsYXZGTiohZyU3bZELqW3EUSwjwQ==" +} \ No newline at end of file