Merge pull request #697 from stripe/bg-customer-shipping

Retrieve and update customer.shipping info via ephemeral key
This commit is contained in:
bg-stripe 2017-06-27 17:18:35 -04:00 committed by GitHub
commit 74bf34c7d6
25 changed files with 399 additions and 87 deletions

View File

@ -176,7 +176,10 @@ class CheckoutViewController: UIViewController, STPPaymentContextDelegate {
// MARK: STPPaymentContextDelegate // MARK: STPPaymentContextDelegate
func paymentContext(_ paymentContext: STPPaymentContext, didCreatePaymentResult paymentResult: STPPaymentResult, completion: @escaping STPErrorBlock) { func paymentContext(_ paymentContext: STPPaymentContext, didCreatePaymentResult paymentResult: STPPaymentResult, completion: @escaping STPErrorBlock) {
MyAPIClient.sharedClient.completeCharge(paymentResult, amount: self.paymentContext.paymentAmount, MyAPIClient.sharedClient.completeCharge(paymentResult,
amount: self.paymentContext.paymentAmount,
shippingAddress: self.paymentContext.shippingAddress,
shippingMethod: self.paymentContext.selectedShippingMethod,
completion: completion) completion: completion)
} }

View File

@ -22,12 +22,18 @@ class MyAPIClient: NSObject, STPEphemeralKeyProvider {
} }
} }
func completeCharge(_ result: STPPaymentResult, amount: Int, completion: @escaping STPErrorBlock) { func completeCharge(_ result: STPPaymentResult,
amount: Int,
shippingAddress: STPAddress?,
shippingMethod: PKShippingMethod?,
completion: @escaping STPErrorBlock) {
let url = self.baseURL.appendingPathComponent("charge") let url = self.baseURL.appendingPathComponent("charge")
Alamofire.request(url, method: .post, parameters: [ var params: [String: Any] = [
"source": result.source.stripeID, "source": result.source.stripeID,
"amount": amount "amount": amount
]) ]
params["shipping"] = STPAddress.shippingInfoForCharge(with: shippingAddress, shippingMethod: shippingMethod)
Alamofire.request(url, method: .post, parameters: params)
.validate(statusCode: 200..<300) .validate(statusCode: 200..<300)
.responseString { response in .responseString { response in
switch response.result { switch response.result {
@ -42,7 +48,7 @@ class MyAPIClient: NSObject, STPEphemeralKeyProvider {
func createCustomerKey(withAPIVersion apiVersion: String, completion: @escaping STPJSONResponseCompletionBlock) { func createCustomerKey(withAPIVersion apiVersion: String, completion: @escaping STPJSONResponseCompletionBlock) {
let url = self.baseURL.appendingPathComponent("ephemeral_keys") let url = self.baseURL.appendingPathComponent("ephemeral_keys")
Alamofire.request(url, method: .post, parameters: [ Alamofire.request(url, method: .post, parameters: [
"api_version": apiVersion "api_version": apiVersion,
]) ])
.validate(statusCode: 200..<300) .validate(statusCode: 200..<300)
.responseJSON { responseJSON in .responseJSON { responseJSON in

View File

@ -8,6 +8,7 @@
4. Instead of using the initializers for `STPPaymentContext` or `STPPaymentMethodsViewController` that take an `STPBackendAPIAdapter` parameter, you should use the new initializers that take an `STPCustomerContext` parameter. You'll need to set up your instance of `STPCustomerContext` using the key provider you set up in (2). 4. Instead of using the initializers for `STPPaymentContext` or `STPPaymentMethodsViewController` that take an `STPBackendAPIAdapter` parameter, you should use the new initializers that take an `STPCustomerContext` parameter. You'll need to set up your instance of `STPCustomerContext` using the key provider you set up in (2).
- For a more detailed overview of the new integration, you can refer to our tutorial at https://stripe.com/docs/mobile/ios/standard - For a more detailed overview of the new integration, you can refer to our tutorial at https://stripe.com/docs/mobile/ios/standard
- Fixed nullability annotation for `[STPFile stringFromPurpose:]` which returns `nil` for `STPFilePurposeUnknown`. Will return a non-nil value for all other `STPFilePurpose`. - Fixed nullability annotation for `[STPFile stringFromPurpose:]` which returns `nil` for `STPFilePurposeUnknown`. Will return a non-nil value for all other `STPFilePurpose`.
- We've removed the `email` and `phone` properties in `STPUserInformation`. You can now pre-fill this information in the shipping form using the new `shippingAddress` property.
### Migrating from versions < 10.2.0 ### Migrating from versions < 10.2.0
- `paymentRequestWithMerchantIdentifier:` has been deprecated. You should instead use `paymentRequestWithMerchantIdentifier:country:currency:`. Apple Pay is now available in many countries and currencies, and you should use the appropriate values for your business. - `paymentRequestWithMerchantIdentifier:` has been deprecated. You should instead use `paymentRequestWithMerchantIdentifier:country:currency:`. Apple Pay is now available in many countries and currencies, and you should use the appropriate values for your business.

View File

@ -485,6 +485,8 @@
C15993471D8829C00047950D /* stp_shipping_form@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = C15993221D8807930047950D /* stp_shipping_form@3x.png */; }; C15993471D8829C00047950D /* stp_shipping_form@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = C15993221D8807930047950D /* stp_shipping_form@3x.png */; };
C15B02731EA176090026E606 /* StripeErrorTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C15B02721EA176090026E606 /* StripeErrorTest.m */; }; C15B02731EA176090026E606 /* StripeErrorTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C15B02721EA176090026E606 /* StripeErrorTest.m */; };
C16F66AB1CA21BAC006A21B5 /* STPFormTextFieldTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C16F66AA1CA21BAC006A21B5 /* STPFormTextFieldTest.m */; }; C16F66AB1CA21BAC006A21B5 /* STPFormTextFieldTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C16F66AA1CA21BAC006A21B5 /* STPFormTextFieldTest.m */; };
C16FC79D1EEB39C3001CBD3A /* STPCustomerContext+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C16FC79C1EEB39C3001CBD3A /* STPCustomerContext+Private.h */; };
C16FC79E1EEB39C3001CBD3A /* STPCustomerContext+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C16FC79C1EEB39C3001CBD3A /* STPCustomerContext+Private.h */; };
C1717DB11CC00ED60009CF4A /* STPAddress.h in Headers */ = {isa = PBXBuildFile; fileRef = C1080F471CBECF7B007B2D89 /* STPAddress.h */; settings = {ATTRIBUTES = (Public, ); }; }; C1717DB11CC00ED60009CF4A /* STPAddress.h in Headers */ = {isa = PBXBuildFile; fileRef = C1080F471CBECF7B007B2D89 /* STPAddress.h */; settings = {ATTRIBUTES = (Public, ); }; };
C1785F5C1EC60B5E00E9CFAC /* STPCardIOProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = C1785F5A1EC60B5E00E9CFAC /* STPCardIOProxy.h */; }; C1785F5C1EC60B5E00E9CFAC /* STPCardIOProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = C1785F5A1EC60B5E00E9CFAC /* STPCardIOProxy.h */; };
C1785F5D1EC60B5E00E9CFAC /* STPCardIOProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = C1785F5A1EC60B5E00E9CFAC /* STPCardIOProxy.h */; }; C1785F5D1EC60B5E00E9CFAC /* STPCardIOProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = C1785F5A1EC60B5E00E9CFAC /* STPCardIOProxy.h */; };
@ -518,6 +520,8 @@
C19D09931EAEAE5E00A4AB3E /* STPTelemetryClientTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C19D09911EAEAE5200A4AB3E /* STPTelemetryClientTest.m */; }; C19D09931EAEAE5E00A4AB3E /* STPTelemetryClientTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C19D09911EAEAE5200A4AB3E /* STPTelemetryClientTest.m */; };
C1A06F101E1D8A7F004DCA06 /* STPCard+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C1A06F0F1E1D8A6E004DCA06 /* STPCard+Private.h */; }; C1A06F101E1D8A7F004DCA06 /* STPCard+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C1A06F0F1E1D8A6E004DCA06 /* STPCard+Private.h */; };
C1A06F111E1D8A7F004DCA06 /* STPCard+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C1A06F0F1E1D8A6E004DCA06 /* STPCard+Private.h */; }; C1A06F111E1D8A7F004DCA06 /* STPCard+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C1A06F0F1E1D8A6E004DCA06 /* STPCard+Private.h */; };
C1A5E5F41ED3A71C00C79B0F /* StripeError+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C1A5E5F31ED3A71C00C79B0F /* StripeError+Private.h */; };
C1A5E5F51ED3A71C00C79B0F /* StripeError+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C1A5E5F31ED3A71C00C79B0F /* StripeError+Private.h */; };
C1AED1561EE0C8C6008BEFBF /* STPApplePayTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C1AED1551EE0C8C6008BEFBF /* STPApplePayTest.m */; }; C1AED1561EE0C8C6008BEFBF /* STPApplePayTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C1AED1551EE0C8C6008BEFBF /* STPApplePayTest.m */; };
C1B630BB1D1D860100A05285 /* stp_card_amex.png in Resources */ = {isa = PBXBuildFile; fileRef = 0438EF891B741C2800D506CC /* stp_card_amex.png */; }; C1B630BB1D1D860100A05285 /* stp_card_amex.png in Resources */ = {isa = PBXBuildFile; fileRef = 0438EF891B741C2800D506CC /* stp_card_amex.png */; };
C1B630BC1D1D860100A05285 /* stp_card_amex@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0438EF8A1B741C2800D506CC /* stp_card_amex@2x.png */; }; C1B630BC1D1D860100A05285 /* stp_card_amex@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0438EF8A1B741C2800D506CC /* stp_card_amex@2x.png */; };
@ -1101,6 +1105,7 @@
C16F66AA1CA21BAC006A21B5 /* STPFormTextFieldTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPFormTextFieldTest.m; sourceTree = "<group>"; }; C16F66AA1CA21BAC006A21B5 /* STPFormTextFieldTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPFormTextFieldTest.m; sourceTree = "<group>"; };
C1785F5A1EC60B5E00E9CFAC /* STPCardIOProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPCardIOProxy.h; sourceTree = "<group>"; }; C1785F5A1EC60B5E00E9CFAC /* STPCardIOProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPCardIOProxy.h; sourceTree = "<group>"; };
C1785F5B1EC60B5E00E9CFAC /* STPCardIOProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPCardIOProxy.m; sourceTree = "<group>"; }; C1785F5B1EC60B5E00E9CFAC /* STPCardIOProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPCardIOProxy.m; sourceTree = "<group>"; };
C16FC79C1EEB39C3001CBD3A /* STPCustomerContext+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "STPCustomerContext+Private.h"; sourceTree = "<group>"; };
C17A030B1CBEE7A2006C819F /* STPAddressFieldTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPAddressFieldTableViewCell.h; sourceTree = "<group>"; }; C17A030B1CBEE7A2006C819F /* STPAddressFieldTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPAddressFieldTableViewCell.h; sourceTree = "<group>"; };
C17A030C1CBEE7A2006C819F /* STPAddressFieldTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPAddressFieldTableViewCell.m; sourceTree = "<group>"; }; C17A030C1CBEE7A2006C819F /* STPAddressFieldTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPAddressFieldTableViewCell.m; sourceTree = "<group>"; };
C17D24ED1E37DBAC005CB188 /* STPSourceTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPSourceTest.m; sourceTree = "<group>"; }; C17D24ED1E37DBAC005CB188 /* STPSourceTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPSourceTest.m; sourceTree = "<group>"; };
@ -1118,6 +1123,7 @@
C19D098E1EAEAE4000A4AB3E /* STPTelemetryClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPTelemetryClient.m; sourceTree = "<group>"; }; C19D098E1EAEAE4000A4AB3E /* STPTelemetryClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPTelemetryClient.m; sourceTree = "<group>"; };
C19D09911EAEAE5200A4AB3E /* STPTelemetryClientTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPTelemetryClientTest.m; sourceTree = "<group>"; }; C19D09911EAEAE5200A4AB3E /* STPTelemetryClientTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPTelemetryClientTest.m; sourceTree = "<group>"; };
C1A06F0F1E1D8A6E004DCA06 /* STPCard+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "STPCard+Private.h"; sourceTree = "<group>"; }; C1A06F0F1E1D8A6E004DCA06 /* STPCard+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "STPCard+Private.h"; sourceTree = "<group>"; };
C1A5E5F31ED3A71C00C79B0F /* StripeError+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "StripeError+Private.h"; sourceTree = "<group>"; };
C1AED1551EE0C8C6008BEFBF /* STPApplePayTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPApplePayTest.m; sourceTree = "<group>"; }; C1AED1551EE0C8C6008BEFBF /* STPApplePayTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPApplePayTest.m; sourceTree = "<group>"; };
C1B630B31D1D817900A05285 /* Stripe.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Stripe.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; C1B630B31D1D817900A05285 /* Stripe.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Stripe.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
C1B630B51D1D817900A05285 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; C1B630B51D1D817900A05285 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -1682,6 +1688,8 @@
04A4C3881C4F25F900B3B290 /* UIViewController+Stripe_ParentViewController.m */, 04A4C3881C4F25F900B3B290 /* UIViewController+Stripe_ParentViewController.m */,
045D711E1CEFA57000F6CD65 /* UIViewController+Stripe_Promises.h */, 045D711E1CEFA57000F6CD65 /* UIViewController+Stripe_Promises.h */,
045D711F1CEFA57000F6CD65 /* UIViewController+Stripe_Promises.m */, 045D711F1CEFA57000F6CD65 /* UIViewController+Stripe_Promises.m */,
C1A5E5F31ED3A71C00C79B0F /* StripeError+Private.h */,
C1C1B6E71EFC05F800BD1A36 /* StripeError+Private.m */,
); );
name = Categories; name = Categories;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1837,7 +1845,6 @@
04BC29B41CD9AE0000318357 /* STPCheckoutBootstrapResponse.m */, 04BC29B41CD9AE0000318357 /* STPCheckoutBootstrapResponse.m */,
F1D3A2631EBA5BAE0095BFA9 /* STPPaymentCardTextField+Private.h */, F1D3A2631EBA5BAE0095BFA9 /* STPPaymentCardTextField+Private.h */,
04B31DD81D09A4DC00EF1631 /* STPPaymentConfiguration+Private.h */, 04B31DD81D09A4DC00EF1631 /* STPPaymentConfiguration+Private.h */,
04E39F5A1CECFAFD00AF3B96 /* STPPaymentContext+Private.h */,
F12C8DBE1D63DE9F00ADA0D7 /* STPPaymentContextAmountModel.h */, F12C8DBE1D63DE9F00ADA0D7 /* STPPaymentContextAmountModel.h */,
F12C8DBF1D63DE9F00ADA0D7 /* STPPaymentContextAmountModel.m */, F12C8DBF1D63DE9F00ADA0D7 /* STPPaymentContextAmountModel.m */,
04E39F501CECF7A100AF3B96 /* STPPaymentMethodTuple.h */, 04E39F501CECF7A100AF3B96 /* STPPaymentMethodTuple.h */,
@ -1870,6 +1877,8 @@
F1728CF41EAAA457002E0C29 /* PaymentContext */ = { F1728CF41EAAA457002E0C29 /* PaymentContext */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
04E39F5A1CECFAFD00AF3B96 /* STPPaymentContext+Private.h */,
C16FC79C1EEB39C3001CBD3A /* STPCustomerContext+Private.h */,
); );
name = PaymentContext; name = PaymentContext;
sourceTree = "<group>"; sourceTree = "<group>";
@ -2083,6 +2092,7 @@
C1A06F111E1D8A7F004DCA06 /* STPCard+Private.h in Headers */, C1A06F111E1D8A7F004DCA06 /* STPCard+Private.h in Headers */,
04F94DBA1D229F8A004FC826 /* PKPaymentAuthorizationViewController+Stripe_Blocks.h in Headers */, 04F94DBA1D229F8A004FC826 /* PKPaymentAuthorizationViewController+Stripe_Blocks.h in Headers */,
04F94DD31D22A23F004FC826 /* NSBundle+Stripe_AppName.h in Headers */, 04F94DD31D22A23F004FC826 /* NSBundle+Stripe_AppName.h in Headers */,
C1A5E5F51ED3A71C00C79B0F /* StripeError+Private.h in Headers */,
F1D3A25B1EB014BD0095BFA9 /* UIImage+Stripe.h in Headers */, F1D3A25B1EB014BD0095BFA9 /* UIImage+Stripe.h in Headers */,
04F94DC31D22A1F7004FC826 /* STPCheckoutAccount.h in Headers */, 04F94DC31D22A1F7004FC826 /* STPCheckoutAccount.h in Headers */,
F1852F941D80B6EC00367C86 /* STPStringUtils.h in Headers */, F1852F941D80B6EC00367C86 /* STPStringUtils.h in Headers */,
@ -2119,6 +2129,7 @@
04F94DA51D229F21004FC826 /* STPPaymentContext+Private.h in Headers */, 04F94DA51D229F21004FC826 /* STPPaymentContext+Private.h in Headers */,
F12829DB1D7747E4008B10D6 /* STPBundleLocator.h in Headers */, F12829DB1D7747E4008B10D6 /* STPBundleLocator.h in Headers */,
8BD87B891EFB131800269C2B /* STPSourceCardDetails+Private.h in Headers */, 8BD87B891EFB131800269C2B /* STPSourceCardDetails+Private.h in Headers */,
C16FC79E1EEB39C3001CBD3A /* STPCustomerContext+Private.h in Headers */,
04827D161D257764002DB3E8 /* STPImageLibrary+Private.h in Headers */, 04827D161D257764002DB3E8 /* STPImageLibrary+Private.h in Headers */,
049E84EC1A605EF0000B66CD /* StripeError.h in Headers */, 049E84EC1A605EF0000B66CD /* StripeError.h in Headers */,
046FE9A31CE5608000DA6A7B /* STPPaymentActivityIndicatorView.h in Headers */, 046FE9A31CE5608000DA6A7B /* STPPaymentActivityIndicatorView.h in Headers */,
@ -2258,6 +2269,7 @@
04BC29C21CE2A48000318357 /* STPSMSCodeViewController.h in Headers */, 04BC29C21CE2A48000318357 /* STPSMSCodeViewController.h in Headers */,
C11810891CC6B00D0022FB55 /* STPApplePayPaymentMethod.h in Headers */, C11810891CC6B00D0022FB55 /* STPApplePayPaymentMethod.h in Headers */,
0426B9721CEAE3EB006AC8DD /* UITableViewCell+Stripe_Borders.h in Headers */, 0426B9721CEAE3EB006AC8DD /* UITableViewCell+Stripe_Borders.h in Headers */,
C1A5E5F41ED3A71C00C79B0F /* StripeError+Private.h in Headers */,
04CDE5C91BC20B1D00548833 /* STPBankAccountParams.h in Headers */, 04CDE5C91BC20B1D00548833 /* STPBankAccountParams.h in Headers */,
049A3F891CC73C7100F57DE7 /* STPPaymentContext.h in Headers */, 049A3F891CC73C7100F57DE7 /* STPPaymentContext.h in Headers */,
04E39F541CECF7A100AF3B96 /* STPPaymentMethodTuple.h in Headers */, 04E39F541CECF7A100AF3B96 /* STPPaymentMethodTuple.h in Headers */,
@ -2307,6 +2319,7 @@
04B31DE61D09D25F00EF1631 /* STPPaymentMethodsInternalViewController.h in Headers */, 04B31DE61D09D25F00EF1631 /* STPPaymentMethodsInternalViewController.h in Headers */,
04CDB4FE1A5F30A700B854EE /* STPAPIClient.h in Headers */, 04CDB4FE1A5F30A700B854EE /* STPAPIClient.h in Headers */,
045D712C1CF4ED7600F6CD65 /* STPBINRange.h in Headers */, 045D712C1CF4ED7600F6CD65 /* STPBINRange.h in Headers */,
C16FC79D1EEB39C3001CBD3A /* STPCustomerContext+Private.h in Headers */,
C1D7B51A1E36B8B9002181F5 /* STPSourceParams.h in Headers */, C1D7B51A1E36B8B9002181F5 /* STPSourceParams.h in Headers */,
04827D101D2575C6002DB3E8 /* STPImageLibrary.h in Headers */, 04827D101D2575C6002DB3E8 /* STPImageLibrary.h in Headers */,
F152322A1EA9306100D65C67 /* NSURLComponents+Stripe.h in Headers */, F152322A1EA9306100D65C67 /* NSURLComponents+Stripe.h in Headers */,
@ -2823,6 +2836,7 @@
04F94DA21D229F14004FC826 /* STPAddressFieldTableViewCell.m in Sources */, 04F94DA21D229F14004FC826 /* STPAddressFieldTableViewCell.m in Sources */,
049A3FB71CCA6B8900F57DE7 /* STPAddress.m in Sources */, 049A3FB71CCA6B8900F57DE7 /* STPAddress.m in Sources */,
04F94DD41D22A242004FC826 /* NSBundle+Stripe_AppName.m in Sources */, 04F94DD41D22A242004FC826 /* NSBundle+Stripe_AppName.m in Sources */,
C1C1B6E91EFC05F800BD1A36 /* StripeError+Private.m in Sources */,
F152321E1EA92FC100D65C67 /* STPRedirectContext.m in Sources */, F152321E1EA92FC100D65C67 /* STPRedirectContext.m in Sources */,
C126553A1CAA2392006F7265 /* STPAddCardViewController.m in Sources */, C126553A1CAA2392006F7265 /* STPAddCardViewController.m in Sources */,
04633B151CD4521F009D4FB5 /* STPAPIClient+ApplePay.m in Sources */, 04633B151CD4521F009D4FB5 /* STPAPIClient+ApplePay.m in Sources */,
@ -2968,6 +2982,7 @@
F15232261EA9303800D65C67 /* STPURLCallbackHandler.m in Sources */, F15232261EA9303800D65C67 /* STPURLCallbackHandler.m in Sources */,
049952D01BCF13510088C703 /* STPAPIRequest.m in Sources */, 049952D01BCF13510088C703 /* STPAPIRequest.m in Sources */,
F1D3A24B1EB012010095BFA9 /* STPFile.m in Sources */, F1D3A24B1EB012010095BFA9 /* STPFile.m in Sources */,
C1C1B6E81EFC05F800BD1A36 /* StripeError+Private.m in Sources */,
049A3F9A1CC76A2400F57DE7 /* NSBundle+Stripe_AppName.m in Sources */, 049A3F9A1CC76A2400F57DE7 /* NSBundle+Stripe_AppName.m in Sources */,
049A3F7B1CC18D5300F57DE7 /* UIView+Stripe_FirstResponder.m in Sources */, 049A3F7B1CC18D5300F57DE7 /* UIView+Stripe_FirstResponder.m in Sources */,
04CDE5C51BC20AF800548833 /* STPBankAccountParams.m in Sources */, 04CDE5C51BC20AF800548833 /* STPBankAccountParams.m in Sources */,

View File

@ -94,6 +94,19 @@ typedef NS_ENUM(NSUInteger, STPBillingAddressFields) {
*/ */
@property (nonatomic, copy, nullable) NSString *email; @property (nonatomic, copy, nullable) NSString *email;
/**
* When creating a charge on your backend, you can attach shipping information
* to prevent fraud on a physical good. You can use this method to turn your user's
* shipping address and selected shipping method into a hash suitable for attaching
* to a charge. You should pass this to your backend, and use it as the `shipping`
* parameter when creating a charge.
* @see https://stripe.com/docs/api#create_charge-shipping
*
* @param address The user's shipping address. If nil, this method will return nil.
* @param method The user's selected shipping method. May be nil.
*/
+ (NSDictionary *)shippingInfoForChargeWithAddress:(nullable STPAddress *)address
shippingMethod:(nullable PKShippingMethod *)method;
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated" #pragma clang diagnostic ignored "-Wdeprecated"

View File

@ -10,6 +10,8 @@
#import "STPAPIResponseDecodable.h" #import "STPAPIResponseDecodable.h"
#import "STPSourceProtocol.h" #import "STPSourceProtocol.h"
@class STPAddress;
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
/** /**
@ -50,6 +52,11 @@ NS_ASSUME_NONNULL_BEGIN
*/ */
@property(nonatomic, readonly) NSArray<id<STPSourceProtocol>> *sources; @property(nonatomic, readonly) NSArray<id<STPSourceProtocol>> *sources;
/**
* The customer's shipping address.
*/
@property(nonatomic, readonly) STPAddress *shippingAddress;
@end @end
/** /**

View File

@ -46,10 +46,16 @@ NS_ASSUME_NONNULL_BEGIN
@property(nonatomic)STPBillingAddressFields requiredBillingAddressFields; @property(nonatomic)STPBillingAddressFields requiredBillingAddressFields;
/** /**
* The billing address fields the user must fill out when prompted for their shipping info. * The shipping address fields the user must fill out when prompted for their shipping info.
*/ */
@property(nonatomic)PKAddressField requiredShippingAddressFields; @property(nonatomic)PKAddressField requiredShippingAddressFields;
/**
* Whether the user should be prompted to verify prefilled shipping information.
* The default value is YES.
*/
@property(nonatomic)BOOL verifyPrefilledShippingAddress;
/** /**
* The type of shipping for this purchase. This property sets the labels displayed when the user is prompted for shipping info, and whether they should also be asked to select a shipping method. The default value is STPShippingTypeShipping. * The type of shipping for this purchase. This property sets the labels displayed when the user is prompted for shipping info, and whether they should also be asked to select a shipping method. The default value is STPShippingTypeShipping.
*/ */

View File

@ -148,6 +148,25 @@ NS_ASSUME_NONNULL_BEGIN
/** /**
* The user's shipping address. May be nil. * The user's shipping address. May be nil.
* If you've already collected a shipping address from your user, you may
* prefill it by setting a shippingAddress in PaymentContext's prefilledInformation.
* When your user enters a new shipping address, PaymentContext will save it to
* the current customer object. When PaymentContext loads, if you haven't
* manually set a prefilled value, any shipping information saved on the customer
* will be used to prefill the shipping address form. Note that because your
* customer's email may not be the same as the email provided with their shipping
* info, PaymentContext will not prefill the shipping form's email using your
* customer's email.
*
* You should not rely on the shipping information stored on the Stripe customer
* for order fulfillment, as your user may change this information if they make
* multiple purchases. We recommend adding shipping information when you create
* a charge (which can also help prevent fraud), or saving it to your own
* database. https://stripe.com/docs/api#create_charge-shipping
*
* Note: by default, your user will still be prompted to verify a prefilled
* shipping address. To change this behavior, you can set
* `verifyPrefilledShippingAddress` to NO in your `STPPaymentConfiguration`.
*/ */
@property(nonatomic, readonly, nullable)STPAddress *shippingAddress; @property(nonatomic, readonly, nullable)STPAddress *shippingAddress;

View File

@ -12,26 +12,27 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
/** /**
* You can use this class to specify information that you've already collected from your user. You can then set the `prefilledInformation` property on `STPPaymentContext`, `STPAddCardViewController`, etc and it will pre-fill this information whenever possible. * You can use this class to specify information that you've already collected
* from your user. You can then set the `prefilledInformation` property on
* `STPPaymentContext`, `STPAddCardViewController`, etc and it will pre-fill
* this information whenever possible.
*/ */
@interface STPUserInformation : NSObject<NSCopying> @interface STPUserInformation : NSObject<NSCopying>
/** /**
* The user's email address. * The user's billing address. When set, the add card form will be filled with
*/ * this address. The user will also have the option to fill their shipping address
@property(nonatomic, copy, nullable)NSString *email; * using this address.
/**
* The user's phone number. When set, this property will automatically strip out any non-numeric characters from the string you specify.
*/
@property(nonatomic, copy, nullable)NSString *phone;
/**
* The user's billing address. When set, the add card form will be filled with this address.
* The user will also have the option to fill their shipping address using this address.
*/ */
@property(nonatomic, strong, nullable)STPAddress *billingAddress; @property(nonatomic, strong, nullable)STPAddress *billingAddress;
/**
* The user's shipping address. When set, the shipping address form will be filled
* with this address. The user will also have the option to fill their billing
* address using this address.
*/
@property(nonatomic, strong, nullable)STPAddress *shippingAddress;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@ -135,10 +135,7 @@ typedef NS_ENUM(NSUInteger, STPPaymentCardSection) {
self.cardImageView = cardImageView; self.cardImageView = cardImageView;
self.tableView.tableHeaderView = cardImageView; self.tableView.tableHeaderView = cardImageView;
self.emailCell = [[STPRememberMeEmailCell alloc] initWithDelegate:self]; self.emailCell = [[STPRememberMeEmailCell alloc] initWithDelegate:self];
if ([STPEmailAddressValidator stringIsValidEmailAddress:self.prefilledInformation.email]) {
self.emailCell.contents = self.prefilledInformation.email;
}
STPRememberMePaymentCell *paymentCell = [[STPRememberMePaymentCell alloc] init]; STPRememberMePaymentCell *paymentCell = [[STPRememberMePaymentCell alloc] init];
paymentCell.paymentField.delegate = self; paymentCell.paymentField.delegate = self;
self.paymentCell = paymentCell; self.paymentCell = paymentCell;
@ -154,7 +151,6 @@ typedef NS_ENUM(NSUInteger, STPPaymentCardSection) {
self.rememberMePhoneCell = [[STPAddressFieldTableViewCell alloc] initWithType:STPAddressFieldTypePhone contents:nil lastInList:YES delegate:self]; self.rememberMePhoneCell = [[STPAddressFieldTableViewCell alloc] initWithType:STPAddressFieldTypePhone contents:nil lastInList:YES delegate:self];
self.rememberMePhoneCell.caption = STPLocalizedString(@"Phone", nil); self.rememberMePhoneCell.caption = STPLocalizedString(@"Phone", nil);
self.rememberMePhoneCell.contents = self.prefilledInformation.phone;
self.rememberMeTermsView = [STPRememberMeTermsView new]; self.rememberMeTermsView = [STPRememberMeTermsView new];
self.rememberMeTermsView.textView.alpha = 0; self.rememberMeTermsView.textView.alpha = 0;

View File

@ -29,6 +29,26 @@ NSString *stringIfHasContentsElseNil(NSString *string);
@implementation STPAddress @implementation STPAddress
+ (NSDictionary *)shippingInfoForChargeWithAddress:(nullable STPAddress *)address
shippingMethod:(nullable PKShippingMethod *)method {
if (!address) {
return nil;
}
NSMutableDictionary *params = [NSMutableDictionary new];
params[@"name"] = address.name;
params[@"phone"] = address.phone;
params[@"carrier"] = method.label;
NSMutableDictionary *addressDict = [NSMutableDictionary new];
addressDict[@"line1"] = address.line1;
addressDict[@"line2"] = address.line2;
addressDict[@"city"] = address.city;
addressDict[@"state"] = address.state;
addressDict[@"postal_code"] = address.postalCode;
addressDict[@"country"] = address.country;
params[@"address"] = [addressDict copy];
return [params copy];
}
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated" #pragma clang diagnostic ignored "-Wdeprecated"

View File

@ -8,15 +8,18 @@
#import "STPCustomer.h" #import "STPCustomer.h"
#import "STPAddress.h"
#import "STPCard.h" #import "STPCard.h"
#import "STPSource.h" #import "STPSource.h"
#import "StripeError.h" #import "StripeError.h"
#import "NSDictionary+Stripe.h"
@interface STPCustomer() @interface STPCustomer()
@property(nonatomic, copy)NSString *stripeID; @property(nonatomic, copy)NSString *stripeID;
@property(nonatomic) id<STPSourceProtocol> defaultSource; @property(nonatomic) id<STPSourceProtocol> defaultSource;
@property(nonatomic) NSArray<id<STPSourceProtocol>> *sources; @property(nonatomic) NSArray<id<STPSourceProtocol>> *sources;
@property(nonatomic) STPAddress *shippingAddress;
@property (nonatomic, readwrite, nonnull, copy) NSDictionary *allResponseFields; @property (nonatomic, readwrite, nonnull, copy) NSDictionary *allResponseFields;
@end @end
@ -57,16 +60,69 @@
return @[@"id"]; return @[@"id"];
} }
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
+ (instancetype)decodedObjectFromAPIResponse:(NSDictionary *)response { + (instancetype)decodedObjectFromAPIResponse:(NSDictionary *)response {
STPCustomerDeserializer *deserializer = [[STPCustomerDeserializer alloc] initWithJSONResponse:response]; NSDictionary *dict = [response stp_dictionaryByRemovingNullsValidatingRequiredFields:[self requiredFields]];
return deserializer.customer; if (!dict) {
return nil;
}
STPCustomer *customer = [STPCustomer new];
customer.stripeID = dict[@"id"];
NSString *defaultSourceId;
if ([dict[@"default_source"] isKindOfClass:[NSString class]]) {
defaultSourceId = dict[@"default_source"];
}
if ([dict[@"shipping"] isKindOfClass:[NSDictionary class]]) {
NSDictionary *shippingDict = dict[@"shipping"];
STPAddress *shipping = [STPAddress new];
shipping.name = shippingDict[@"name"];
shipping.phone = shippingDict[@"phone"];
shipping.line1 = shippingDict[@"address"][@"line1"];
shipping.line2 = shippingDict[@"address"][@"line2"];
shipping.city = shippingDict[@"address"][@"city"];
shipping.state = shippingDict[@"address"][@"state"];
shipping.postalCode = shippingDict[@"address"][@"postal_code"];
shipping.country = shippingDict[@"address"][@"country"];
customer.shippingAddress = shipping;
}
NSMutableArray *sources = [NSMutableArray array];
if ([dict[@"sources"] isKindOfClass:[NSDictionary class]] && [dict[@"sources"][@"data"] isKindOfClass:[NSArray class]]) {
for (id contents in dict[@"sources"][@"data"]) {
if ([contents isKindOfClass:[NSDictionary class]]) {
// eventually support other source types
if ([contents[@"object"] isEqualToString:@"card"]) {
STPCard *card = [STPCard decodedObjectFromAPIResponse:contents];
// ignore apple pay cards from the response
if (card && !card.isApplePayCard) {
[sources addObject:card];
if (defaultSourceId && [card.stripeID isEqualToString:defaultSourceId]) {
customer.defaultSource = card;
}
}
}
else if ([contents[@"object"] isEqualToString:@"source"]) {
STPSource *source = [STPSource decodedObjectFromAPIResponse:contents];
if (source) {
[sources addObject:source];
if (defaultSourceId && [source.stripeID isEqualToString:defaultSourceId]) {
customer.defaultSource = source;
}
}
}
}
}
customer.sources = sources;
}
customer.allResponseFields = dict;
return customer;
} }
#pragma clang diagnostic pop
@end @end
/**
NOTE: STPCustomerDeserializer has been deprecated. When we remove
STPBackendAPIAdapter, we should also remove STPCustomerDeserializer.
*/
@interface STPCustomerDeserializer() @interface STPCustomerDeserializer()
@property(nonatomic, nullable)STPCustomer *customer; @property(nonatomic, nullable)STPCustomer *customer;
@ -106,45 +162,12 @@
- (instancetype)initWithJSONResponse:(id)json { - (instancetype)initWithJSONResponse:(id)json {
self = [super init]; self = [super init];
if (self) { if (self) {
if (![json isKindOfClass:[NSDictionary class]] || ![json[@"id"] isKindOfClass:[NSString class]]) { STPCustomer *customer = [STPCustomer decodedObjectFromAPIResponse:json];
if (!customer) {
_error = [NSError stp_genericFailedToParseResponseError]; _error = [NSError stp_genericFailedToParseResponseError];
return self; } else {
_customer = customer;
} }
STPCustomer *customer = [STPCustomer new];
customer.stripeID = json[@"id"];
NSString *defaultSourceId;
if ([json[@"default_source"] isKindOfClass:[NSString class]]) {
defaultSourceId = json[@"default_source"];
}
NSMutableArray *sources = [NSMutableArray array];
if ([json[@"sources"] isKindOfClass:[NSDictionary class]] && [json[@"sources"][@"data"] isKindOfClass:[NSArray class]]) {
for (id contents in json[@"sources"][@"data"]) {
if ([contents isKindOfClass:[NSDictionary class]]) {
// eventually support other source types
if ([contents[@"object"] isEqualToString:@"card"]) {
STPCard *card = [STPCard decodedObjectFromAPIResponse:contents];
// ignore apple pay cards from the response
if (card && !card.isApplePayCard) {
[sources addObject:card];
if (defaultSourceId && [card.stripeID isEqualToString:defaultSourceId]) {
customer.defaultSource = card;
}
}
}
else if ([contents[@"object"] isEqualToString:@"source"]) {
STPSource *source = [STPSource decodedObjectFromAPIResponse:contents];
if (source) {
[sources addObject:source];
if (defaultSourceId && [source.stripeID isEqualToString:defaultSourceId]) {
customer.defaultSource = source;
}
}
}
}
}
customer.sources = sources;
}
_customer = customer;
} }
return self; return self;
} }

View File

@ -0,0 +1,23 @@
//
// STPCustomerContext+Private.h
// Stripe
//
// Created by Ben Guo on 6/9/17.
// Copyright © 2017 Stripe, Inc. All rights reserved.
//
#import <Stripe/Stripe.h>
#import "STPBlocks.h"
@class STPAddress;
NS_ASSUME_NONNULL_BEGIN
@interface STPCustomerContext (Private)
- (void)updateCustomerWithShippingAddress:(STPAddress *)shipping completion:(nullable STPErrorBlock)completion;
@end
NS_ASSUME_NONNULL_END

View File

@ -139,4 +139,32 @@ static NSTimeInterval const CachedCustomerMaxAge = 60;
}]; }];
} }
- (void)updateCustomerWithShippingAddress:(STPAddress *)shipping completion:(STPErrorBlock)completion {
[self.keyManager getCustomerKey:^(STPEphemeralKey *ephemeralKey, NSError *retrieveKeyError) {
if (retrieveKeyError) {
if (completion) {
stpDispatchToMainThreadIfNecessary(^{
completion(retrieveKeyError);
});
}
return;
}
NSMutableDictionary *params = [NSMutableDictionary new];
params[@"shipping"] = [STPAddress shippingInfoForChargeWithAddress:shipping
shippingMethod:nil];
[STPAPIClient updateCustomerWithParameters:[params copy]
usingKey:ephemeralKey
completion:^(STPCustomer *customer, NSError *error) {
if (customer) {
self.customer = customer;
}
if (completion) {
stpDispatchToMainThreadIfNecessary(^{
completion(error);
});
}
}];
}];
}
@end @end

View File

@ -38,6 +38,7 @@
_additionalPaymentMethods = STPPaymentMethodTypeAll; _additionalPaymentMethods = STPPaymentMethodTypeAll;
_requiredBillingAddressFields = STPBillingAddressFieldsNone; _requiredBillingAddressFields = STPBillingAddressFieldsNone;
_requiredShippingAddressFields = PKAddressFieldNone; _requiredShippingAddressFields = PKAddressFieldNone;
_verifyPrefilledShippingAddress = YES;
_companyName = [NSBundle stp_applicationName]; _companyName = [NSBundle stp_applicationName];
_smsAutofillDisabled = NO; _smsAutofillDisabled = NO;
_shippingType = STPShippingTypeShipping; _shippingType = STPShippingTypeShipping;
@ -51,6 +52,7 @@
copy.additionalPaymentMethods = self.additionalPaymentMethods; copy.additionalPaymentMethods = self.additionalPaymentMethods;
copy.requiredBillingAddressFields = self.requiredBillingAddressFields; copy.requiredBillingAddressFields = self.requiredBillingAddressFields;
copy.requiredShippingAddressFields = self.requiredShippingAddressFields; copy.requiredShippingAddressFields = self.requiredShippingAddressFields;
copy.verifyPrefilledShippingAddress = self.verifyPrefilledShippingAddress;
copy.shippingType = self.shippingType; copy.shippingType = self.shippingType;
copy.companyName = self.companyName; copy.companyName = self.companyName;
copy.appleMerchantIdentifier = self.appleMerchantIdentifier; copy.appleMerchantIdentifier = self.appleMerchantIdentifier;

View File

@ -12,6 +12,7 @@
#import "PKPaymentAuthorizationViewController+Stripe_Blocks.h" #import "PKPaymentAuthorizationViewController+Stripe_Blocks.h"
#import "STPAddCardViewController+Private.h" #import "STPAddCardViewController+Private.h"
#import "STPCardTuple.h" #import "STPCardTuple.h"
#import "STPCustomerContext+Private.h"
#import "STPDispatchFunctions.h" #import "STPDispatchFunctions.h"
#import "STPPaymentConfiguration+Private.h" #import "STPPaymentConfiguration+Private.h"
#import "STPPaymentContext+Private.h" #import "STPPaymentContext+Private.h"
@ -65,7 +66,7 @@ typedef NS_ENUM(NSUInteger, STPPaymentContextState) {
@property(nonatomic, assign) STPPaymentContextState state; @property(nonatomic, assign) STPPaymentContextState state;
@property(nonatomic)STPPaymentContextAmountModel *paymentAmountModel; @property(nonatomic)STPPaymentContextAmountModel *paymentAmountModel;
@property(nonatomic)BOOL shippingAddressNeedsVerification;
@end @end
@ -146,6 +147,10 @@ typedef NS_ENUM(NSUInteger, STPPaymentContextState) {
[self.loadingPromise fail:error]; [self.loadingPromise fail:error];
return; return;
} }
if (!self.shippingAddress && customer.shippingAddress) {
self.shippingAddress = customer.shippingAddress;
self.shippingAddressNeedsVerification = YES;
}
STPCard *selectedCard; STPCard *selectedCard;
NSMutableArray<STPCard *> *cards = [NSMutableArray array]; NSMutableArray<STPCard *> *cards = [NSMutableArray array];
for (id<STPSourceProtocol> source in customer.sources) { for (id<STPSourceProtocol> source in customer.sources) {
@ -196,6 +201,14 @@ typedef NS_ENUM(NSUInteger, STPPaymentContextState) {
}]; }];
} }
- (void)setPrefilledInformation:(STPUserInformation *)prefilledInformation {
_prefilledInformation = prefilledInformation;
if (prefilledInformation.shippingAddress && !self.shippingAddress) {
self.shippingAddress = prefilledInformation.shippingAddress;
self.shippingAddressNeedsVerification = YES;
}
}
- (void)setPaymentMethods:(NSArray<id<STPPaymentMethod>> *)paymentMethods { - (void)setPaymentMethods:(NSArray<id<STPPaymentMethod>> *)paymentMethods {
_paymentMethods = [paymentMethods sortedArrayUsingComparator:^NSComparisonResult(id<STPPaymentMethod> obj1, id<STPPaymentMethod> obj2) { _paymentMethods = [paymentMethods sortedArrayUsingComparator:^NSComparisonResult(id<STPPaymentMethod> obj1, id<STPPaymentMethod> obj2) {
Class applePayKlass = [STPApplePayPaymentMethod class]; Class applePayKlass = [STPApplePayPaymentMethod class];
@ -439,8 +452,13 @@ typedef NS_ENUM(NSUInteger, STPPaymentContextState) {
didFinishWithAddress:(STPAddress *)address didFinishWithAddress:(STPAddress *)address
shippingMethod:(PKShippingMethod *)method { shippingMethod:(PKShippingMethod *)method {
self.shippingAddress = address; self.shippingAddress = address;
self.shippingAddressNeedsVerification = NO;
self.selectedShippingMethod = method; self.selectedShippingMethod = method;
[self.delegate paymentContextDidChange:self]; [self.delegate paymentContextDidChange:self];
if ([self.apiAdapter isKindOfClass:[STPCustomerContext class]]) {
STPCustomerContext *customerContext = (STPCustomerContext *)self.apiAdapter;
[customerContext updateCustomerWithShippingAddress:self.shippingAddress completion:nil];
}
[self appropriatelyDismissViewController:addressViewController completion:^{ [self appropriatelyDismissViewController:addressViewController completion:^{
if (self.state == STPPaymentContextStateRequestingPayment) { if (self.state == STPPaymentContextStateRequestingPayment) {
self.state = STPPaymentContextStateNone; self.state = STPPaymentContextStateNone;
@ -472,6 +490,14 @@ typedef NS_ENUM(NSUInteger, STPPaymentContextState) {
#pragma mark - Request Payment #pragma mark - Request Payment
- (BOOL)requestPaymentShouldPresentShippingViewController {
BOOL shippingAddressRequired = self.configuration.requiredShippingAddressFields != STPBillingAddressFieldsNone;
BOOL shippingAddressIncomplete = ![self.shippingAddress containsRequiredShippingAddressFields:self.configuration.requiredShippingAddressFields];
BOOL shippingMethodRequired = ([self.delegate respondsToSelector:@selector(paymentContext:didUpdateShippingAddress:completion:)] && !self.selectedShippingMethod);
BOOL verificationRequired = self.configuration.verifyPrefilledShippingAddress && self.shippingAddressNeedsVerification;
return (shippingAddressRequired && (shippingAddressIncomplete || shippingMethodRequired || verificationRequired));
}
- (void)requestPayment { - (void)requestPayment {
FAUXPAS_IGNORED_IN_METHOD(APIAvailability); FAUXPAS_IGNORED_IN_METHOD(APIAvailability);
WEAK(self); WEAK(self);
@ -491,9 +517,7 @@ typedef NS_ENUM(NSUInteger, STPPaymentContextState) {
if (!self.selectedPaymentMethod) { if (!self.selectedPaymentMethod) {
[self presentPaymentMethodsViewControllerWithNewState:STPPaymentContextStateRequestingPayment]; [self presentPaymentMethodsViewControllerWithNewState:STPPaymentContextStateRequestingPayment];
} }
else if (self.configuration.requiredShippingAddressFields != STPBillingAddressFieldsNone && else if ([self requestPaymentShouldPresentShippingViewController]) {
!self.shippingAddress)
{
[self presentShippingViewControllerWithNewState:STPPaymentContextStateRequestingPayment]; [self presentShippingViewControllerWithNewState:STPPaymentContextStateRequestingPayment];
} }
else if ([self.selectedPaymentMethod isKindOfClass:[STPCard class]]) { else if ([self.selectedPaymentMethod isKindOfClass:[STPCard class]]) {
@ -532,7 +556,12 @@ typedef NS_ENUM(NSUInteger, STPPaymentContextState) {
STPPaymentAuthorizationBlock paymentHandler = ^(PKPayment *payment) { STPPaymentAuthorizationBlock paymentHandler = ^(PKPayment *payment) {
self.selectedShippingMethod = payment.shippingMethod; self.selectedShippingMethod = payment.shippingMethod;
self.shippingAddress = [[STPAddress alloc] initWithABRecord:payment.shippingAddress]; self.shippingAddress = [[STPAddress alloc] initWithABRecord:payment.shippingAddress];
self.shippingAddressNeedsVerification = NO;
[self.delegate paymentContextDidChange:self]; [self.delegate paymentContextDidChange:self];
if ([self.apiAdapter isKindOfClass:[STPCustomerContext class]]) {
STPCustomerContext *customerContext = (STPCustomerContext *)self.apiAdapter;
[customerContext updateCustomerWithShippingAddress:self.shippingAddress completion:nil];
}
}; };
STPApplePayTokenHandlerBlock applePayTokenHandler = ^(STPToken *token, STPErrorBlock tokenCompletion) { STPApplePayTokenHandlerBlock applePayTokenHandler = ^(STPToken *token, STPErrorBlock tokenCompletion) {
[self.apiAdapter attachSourceToCustomer:token completion:^(NSError *tokenError) { [self.apiAdapter attachSourceToCustomer:token completion:^(NSError *tokenError) {

View File

@ -89,16 +89,8 @@
if (shippingAddress != nil) { if (shippingAddress != nil) {
_addressViewModel.address = shippingAddress; _addressViewModel.address = shippingAddress;
} }
else if (prefilledInformation != nil) { else if (prefilledInformation.shippingAddress != nil) {
STPAddress *prefilledAddress = [STPAddress new]; _addressViewModel.address = prefilledInformation.shippingAddress;
prefilledAddress.country = _addressViewModel.address.country;
if (self.configuration.requiredShippingAddressFields & PKAddressFieldEmail) {
prefilledAddress.email = prefilledInformation.email;
}
if (self.configuration.requiredShippingAddressFields & PKAddressFieldPhone) {
prefilledAddress.phone = prefilledInformation.phone;
}
_addressViewModel.address = prefilledAddress;
} }
self.title = [self titleForShippingType:self.configuration.shippingType]; self.title = [self titleForShippingType:self.configuration.shippingType];
} }

View File

@ -12,15 +12,10 @@
@implementation STPUserInformation @implementation STPUserInformation
- (void)setPhone:(NSString *)phone {
_phone = [STPCardValidator sanitizedNumericStringForString:phone];
}
- (id)copyWithZone:(__unused NSZone *)zone { - (id)copyWithZone:(__unused NSZone *)zone {
STPUserInformation *copy = [self.class new]; STPUserInformation *copy = [self.class new];
copy.email = self.email;
copy.phone = self.phone;
copy.billingAddress = self.billingAddress; copy.billingAddress = self.billingAddress;
copy.shippingAddress = self.shippingAddress;
return copy; return copy;
} }

View File

@ -0,0 +1,17 @@
//
// StripeError+Private.h
// Stripe
//
// Created by Ben Guo on 5/22/17.
// Copyright © 2017 Stripe, Inc. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSError (StripePrivate)
+ (NSError *)stp_customerContextMissingKeyProviderError;
@end
void linkNSErrorPrivateCategory(void);

View File

@ -0,0 +1,24 @@
//
// StripeError+Private.m
// Stripe
//
// Created by Ben Guo on 6/22/17.
// Copyright © 2017 Stripe, Inc. All rights reserved.
//
#import "StripeError+Private.h"
#import "StripeError.h"
@implementation NSError (StripePrivate)
+ (NSError *)stp_customerContextMissingKeyProviderError {
NSDictionary *userInfo = @{
NSLocalizedDescriptionKey: [self stp_unexpectedErrorMessage],
STPErrorMessageKey: @"STPCustomerContext is missing a key provider. Did you forget to set the singleton instance's key provider?"
};
return [[self alloc] initWithDomain:StripeDomain code:STPCustomerContextMissingKeyProviderError userInfo:userInfo];
}
@end
void linkNSErrorPrivateCategory(void) {}

View File

@ -11,6 +11,7 @@
#import <PassKit/PassKit.h> #import <PassKit/PassKit.h>
#import <Contacts/Contacts.h> #import <Contacts/Contacts.h>
#import "STPAddress.h" #import "STPAddress.h"
#import "STPFixtures.h"
@interface STPAddressTests : XCTestCase @interface STPAddressTests : XCTestCase
@ -431,4 +432,26 @@
XCTAssertTrue([address containsRequiredShippingAddressFields:PKAddressFieldAll]); XCTAssertTrue([address containsRequiredShippingAddressFields:PKAddressFieldAll]);
} }
- (void)testShippingInfoForCharge {
STPAddress *address = [STPFixtures address];
PKShippingMethod *method = [[PKShippingMethod alloc] init];
method.label = @"UPS Ground";
NSDictionary *info = [STPAddress shippingInfoForChargeWithAddress:address
shippingMethod:method];
NSDictionary *expected = @{
@"address": @{
@"city": address.city,
@"country": address.country,
@"line1": address.line1,
@"line2": address.line2,
@"postal_code": address.postalCode,
@"state": address.state
},
@"name": address.name,
@"phone": address.phone,
@"carrier": method.label,
};
XCTAssertEqualObjects(expected, info);
}
@end @end

View File

@ -10,6 +10,7 @@
#import <OCMock/OCMock.h> #import <OCMock/OCMock.h>
#import <Stripe/Stripe.h> #import <Stripe/Stripe.h>
#import "STPAPIClient+Private.h" #import "STPAPIClient+Private.h"
#import "STPCustomerContext+Private.h"
#import "STPEphemeralKeyManager.h" #import "STPEphemeralKeyManager.h"
#import "STPFixtures.h" #import "STPFixtures.h"
@ -231,4 +232,43 @@
[self waitForExpectationsWithTimeout:2 handler:nil]; [self waitForExpectationsWithTimeout:2 handler:nil];
} }
- (void)testSetCustomerShippingCallsAPIClientCorrectly {
STPAddress *address = [STPFixtures address];
STPEphemeralKey *customerKey = [STPFixtures ephemeralKey];
id mockKeyManager = [self mockKeyManagerWithKey:customerKey];
id mockAPIClient = OCMClassMock([STPAPIClient class]);
XCTestExpectation *exp = [self expectationWithDescription:@"updateCustomer"];
NSDictionary *expectedParams = @{
@"shipping": @{
@"address": @{
@"city": address.city,
@"country": address.country,
@"line1": address.line1,
@"line2": address.line2,
@"postal_code": address.postalCode,
@"state": address.state
},
@"name": address.name,
@"phone": address.phone,
}
};
OCMStub([mockAPIClient updateCustomerWithParameters:[OCMArg isEqual:expectedParams]
usingKey:[OCMArg isEqual:customerKey]
completion:[OCMArg any]])
.andDo(^(NSInvocation *invocation) {
STPCustomerCompletionBlock completion;
[invocation getArgument:&completion atIndex:4];
completion([STPFixtures customerWithSingleCardTokenSource], nil);
[exp fulfill];
});
XCTestExpectation *exp2 = [self expectationWithDescription:@"updateCustomerWithShipping"];
STPCustomerContext *sut = [[STPCustomerContext alloc] initWithKeyManager:mockKeyManager];
[sut updateCustomerWithShippingAddress:address completion:^(NSError *error) {
XCTAssertNil(error);
[exp2 fulfill];
}];
[self waitForExpectationsWithTimeout:2 handler:nil];
}
@end @end

View File

@ -9,6 +9,7 @@
#import <XCTest/XCTest.h> #import <XCTest/XCTest.h>
#import "STPCustomer.h" #import "STPCustomer.h"
#import "StripeError.h" #import "StripeError.h"
#import "STPAddress.h"
#import "STPTestUtils.h" #import "STPTestUtils.h"
#import "STPSourceProtocol.h" #import "STPSourceProtocol.h"
@ -54,6 +55,15 @@
XCTAssertEqualObjects(sut.defaultSource.stripeID, card1[@"id"]); XCTAssertEqualObjects(sut.defaultSource.stripeID, card1[@"id"]);
XCTAssertEqualObjects(sut.sources[2].stripeID, cardSource[@"id"]); XCTAssertEqualObjects(sut.sources[2].stripeID, cardSource[@"id"]);
XCTAssertEqualObjects(sut.sources[3].stripeID, threeDSSource[@"id"]); XCTAssertEqualObjects(sut.sources[3].stripeID, threeDSSource[@"id"]);
XCTAssertEqualObjects(sut.shippingAddress.name, customer[@"shipping"][@"name"]);
XCTAssertEqualObjects(sut.shippingAddress.phone, customer[@"shipping"][@"phone"]);
XCTAssertEqualObjects(sut.shippingAddress.city, customer[@"shipping"][@"address"][@"city"]);
XCTAssertEqualObjects(sut.shippingAddress.country, customer[@"shipping"][@"address"][@"country"]);
XCTAssertEqualObjects(sut.shippingAddress.line1, customer[@"shipping"][@"address"][@"line1"]);
XCTAssertEqualObjects(sut.shippingAddress.line2, customer[@"shipping"][@"address"][@"line2"]);
XCTAssertEqualObjects(sut.shippingAddress.postalCode, customer[@"shipping"][@"address"][@"postal_code"]);
XCTAssertEqualObjects(sut.shippingAddress.state, customer[@"shipping"][@"address"][@"state"]);
} }
@end @end

View File

@ -13,6 +13,11 @@
@interface STPFixtures : NSObject @interface STPFixtures : NSObject
/**
An Address object with all fields filled.
*/
+ (STPAddress *)address;
/** /**
A PKPaymentObject with test payment data. A PKPaymentObject with test payment data.
*/ */

View File

@ -12,6 +12,20 @@
@implementation STPFixtures @implementation STPFixtures
+ (STPAddress *)address {
STPAddress *address = [STPAddress new];
address.name = @"Jenny Rosen";
address.phone = @"5555555555";
address.email = @"jrosen@example.com";
address.line1 = @"27 Smith St";
address.line2 = @"Apt 2";
address.postalCode = @"10001";
address.city = @"New York";
address.state = @"NY";
address.country = @"US";
return address;
}
+ (STPBankAccountParams *)bankAccountParams { + (STPBankAccountParams *)bankAccountParams {
STPBankAccountParams *bankParams = [STPBankAccountParams new]; STPBankAccountParams *bankParams = [STPBankAccountParams new];
// https://stripe.com/docs/testing#account-numbers // https://stripe.com/docs/testing#account-numbers