From 29b50ac016cca6e5df0f0c270434aae53e24b3fd Mon Sep 17 00:00:00 2001 From: Yuki Date: Wed, 24 Mar 2021 14:56:39 -0700 Subject: [PATCH] Adds BLIK PaymentMethod bindings (#150) * Add STPPaymentMethodBLIK * Add STPPaymentMethodBLIKParams * Add STPPaymentMethodType, Params * Add confirm * Add new localized string * Fix tests * Fix up docstrings * Add PaymentHandler support --- Stripe.xcodeproj/project.pbxproj | 20 +++++-- .../en.lproj/Localizable.strings | 3 + Stripe/STPConfirmBLIKOptions.swift | 52 ++++++++++++++++++ Stripe/STPConfirmPaymentMethodOptions.swift | 5 ++ Stripe/STPIntentAction.swift | 11 ++++ Stripe/STPPaymentHandler.swift | 19 +++++-- Stripe/STPPaymentMethod.swift | 8 ++- Stripe/STPPaymentMethodBLIK.swift | 42 ++++++++++++++ Stripe/STPPaymentMethodBLIKParams.swift | 28 ++++++++++ Stripe/STPPaymentMethodEnums.swift | 5 ++ Stripe/STPPaymentMethodParams.swift | 42 +++++++++++++- .../STPConfirmPaymentMethodOptionsTest.m | 2 +- Tests/Tests/STPPaymentIntentFunctionalTest.m | 55 +++++++++++++++++++ Tests/Tests/STPPaymentMethodFunctionalTest.m | 18 ++++++ Tests/Tests/STPTestingAPIClient.m | 1 + 15 files changed, 296 insertions(+), 15 deletions(-) create mode 100644 Stripe/STPConfirmBLIKOptions.swift create mode 100644 Stripe/STPPaymentMethodBLIK.swift create mode 100644 Stripe/STPPaymentMethodBLIKParams.swift diff --git a/Stripe.xcodeproj/project.pbxproj b/Stripe.xcodeproj/project.pbxproj index 6dc104df24..6f00812d4b 100644 --- a/Stripe.xcodeproj/project.pbxproj +++ b/Stripe.xcodeproj/project.pbxproj @@ -445,6 +445,8 @@ B613DD3E22C54BA800C7603F /* SetupIntent.json in Resources */ = {isa = PBXBuildFile; fileRef = B613DD3D22C54BA700C7603F /* SetupIntent.json */; }; B632989F2295BDD90007D287 /* ApplePayPaymentMethod.json in Resources */ = {isa = PBXBuildFile; fileRef = B632989E2295BDD80007D287 /* ApplePayPaymentMethod.json */; }; B634497822A5BC91003881DC /* STPCardBrandTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B634497722A5BC91003881DC /* STPCardBrandTest.m */; }; + B63A414425F9759900929729 /* STPPaymentMethodBLIKParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = B63A414025F9742B00929729 /* STPPaymentMethodBLIKParams.swift */; }; + B63A414A25F97AD700929729 /* STPConfirmBLIKOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B63A414925F97AD700929729 /* STPConfirmBLIKOptions.swift */; }; B63E42792231F8FE007B5B95 /* STPPaymentMethodParamsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B63E42782231F8FE007B5B95 /* STPPaymentMethodParamsTest.m */; }; B640DB1A22C69C01003C8810 /* STPSetupIntentFunctionalTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B640DB1922C69C01003C8810 /* STPSetupIntentFunctionalTest.m */; }; B643470423173E5000754F11 /* WeChatPaySource.json in Resources */ = {isa = PBXBuildFile; fileRef = B643470323173E5000754F11 /* WeChatPaySource.json */; }; @@ -574,6 +576,7 @@ B6E40EC82542541200A5BABD /* BackwardsCompatibleColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E40EBB2542541100A5BABD /* BackwardsCompatibleColors.swift */; }; B6EC63CA22348D4600E4C0FB /* STPPaymentMethodiDEALTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B6EC63C922348D4600E4C0FB /* STPPaymentMethodiDEALTest.m */; }; B6ED573925784E1A007D74E0 /* STPIntentActionAlipayHandleRedirectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6ED573825784E19007D74E0 /* STPIntentActionAlipayHandleRedirectTest.swift */; }; + B6F0489925F972EB00060318 /* STPPaymentMethodBLIK.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6F0489825F972EB00060318 /* STPPaymentMethodBLIK.swift */; }; C1054F911FE197AE0033C87E /* STPPaymentContextSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C1054F901FE197AE0033C87E /* STPPaymentContextSnapshotTests.m */; }; C11B14971E8AE316000F760C /* OCMock.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = C11B14961E8AE316000F760C /* OCMock.xcframework */; }; C17D24EE1E37DBAC005CB188 /* STPSourceTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C17D24ED1E37DBAC005CB188 /* STPSourceTest.m */; }; @@ -1188,6 +1191,8 @@ B613DD3D22C54BA700C7603F /* SetupIntent.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = SetupIntent.json; sourceTree = ""; }; B632989E2295BDD80007D287 /* ApplePayPaymentMethod.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ApplePayPaymentMethod.json; sourceTree = ""; }; B634497722A5BC91003881DC /* STPCardBrandTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STPCardBrandTest.m; sourceTree = ""; }; + B63A414025F9742B00929729 /* STPPaymentMethodBLIKParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPPaymentMethodBLIKParams.swift; sourceTree = ""; }; + B63A414925F97AD700929729 /* STPConfirmBLIKOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPConfirmBLIKOptions.swift; sourceTree = ""; }; B63E42782231F8FE007B5B95 /* STPPaymentMethodParamsTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STPPaymentMethodParamsTest.m; sourceTree = ""; }; B640DB1922C69C01003C8810 /* STPSetupIntentFunctionalTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STPSetupIntentFunctionalTest.m; sourceTree = ""; }; B643470323173E5000754F11 /* WeChatPaySource.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = WeChatPaySource.json; sourceTree = ""; }; @@ -1249,6 +1254,7 @@ B6E40EBB2542541100A5BABD /* BackwardsCompatibleColors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackwardsCompatibleColors.swift; sourceTree = ""; }; B6EC63C922348D4600E4C0FB /* STPPaymentMethodiDEALTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STPPaymentMethodiDEALTest.m; sourceTree = ""; }; B6ED573825784E19007D74E0 /* STPIntentActionAlipayHandleRedirectTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPIntentActionAlipayHandleRedirectTest.swift; sourceTree = ""; }; + B6F0489825F972EB00060318 /* STPPaymentMethodBLIK.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPPaymentMethodBLIK.swift; sourceTree = ""; }; C1054F901FE197AE0033C87E /* STPPaymentContextSnapshotTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STPPaymentContextSnapshotTests.m; sourceTree = ""; }; C1080F4B1CBED48A007B2D89 /* STPAddressTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPAddressTests.m; sourceTree = ""; }; C11B14961E8AE316000F760C /* OCMock.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = OCMock.xcframework; path = Carthage/Build/OCMock.xcframework; sourceTree = ""; }; @@ -1562,6 +1568,7 @@ isa = PBXGroup; children = ( 317ABD7125117C9700CC59EF /* STPConfirmAlipayOptions.swift */, + B63A414925F97AD700929729 /* STPConfirmBLIKOptions.swift */, 317ABD8F25117C9900CC59EF /* STPConfirmCardOptions.swift */, 317ABD9225117C9900CC59EF /* STPConfirmPaymentMethodOptions.swift */, 317ABDF425117C9E00CC59EF /* STPPaymentIntent.swift */, @@ -1772,8 +1779,9 @@ 3144E134253E818400FE2605 /* Types */ = { isa = PBXGroup; children = ( - 317ABDE525117C9E00CC59EF /* STPPaymentMethodAlipay.swift */, 0FC7C93F25AE758B00E99D3E /* STPPaymentMethodAfterpayClearpay.swift */, + 0FC7C94225AE7BEE00E99D3E /* STPPaymentMethodAfterpayClearpayParams.swift */, + 317ABDE525117C9E00CC59EF /* STPPaymentMethodAlipay.swift */, 317ABDE425117C9E00CC59EF /* STPPaymentMethodAlipayParams.swift */, 317ABD4C25117C9500CC59EF /* STPPaymentMethodAUBECSDebit.swift */, 317ABDC325117C9C00CC59EF /* STPPaymentMethodAUBECSDebitParams.swift */, @@ -1781,7 +1789,8 @@ 317ABDA625117C9A00CC59EF /* STPPaymentMethodBacsDebitParams.swift */, 317ABD7825117C9700CC59EF /* STPPaymentMethodBancontact.swift */, 317ABDD425117C9D00CC59EF /* STPPaymentMethodBancontactParams.swift */, - 0FC7C94225AE7BEE00E99D3E /* STPPaymentMethodAfterpayClearpayParams.swift */, + B6F0489825F972EB00060318 /* STPPaymentMethodBLIK.swift */, + B63A414025F9742B00929729 /* STPPaymentMethodBLIKParams.swift */, 317ABE0925117CA000CC59EF /* STPPaymentMethodCard.swift */, 317ABDDC25117C9D00CC59EF /* STPPaymentMethodCardChecks.swift */, 317ABDB125117C9B00CC59EF /* STPPaymentMethodCardNetworks.swift */, @@ -1800,6 +1809,8 @@ 317ABD3025117C9300CC59EF /* STPPaymentMethodGrabPayParams.swift */, 317ABE1725117CA100CC59EF /* STPPaymentMethodiDEAL.swift */, 317ABD2925117C9200CC59EF /* STPPaymentMethodiDEALParams.swift */, + 8FF52D3825668499000C15EF /* STPPaymentMethodNetBanking.swift */, + 8FF52D3A25668601000C15EF /* STPPaymentMethodNetBankingParams.swift */, 316F811525410B0E000A80B5 /* STPPaymentMethodOXXO.swift */, 316F811925410B12000A80B5 /* STPPaymentMethodOXXOParams.swift */, 316F813525411644000A80B5 /* STPPaymentMethodPayPal.swift */, @@ -1811,8 +1822,6 @@ 317ABD9E25117C9900CC59EF /* STPPaymentMethodSofort.swift */, 317ABDD325117C9D00CC59EF /* STPPaymentMethodSofortParams.swift */, 317ABD2B25117C9300CC59EF /* STPPaymentMethodThreeDSecureUsage.swift */, - 8FF52D3825668499000C15EF /* STPPaymentMethodNetBanking.swift */, - 8FF52D3A25668601000C15EF /* STPPaymentMethodNetBankingParams.swift */, 8F5ED78225552296003BE002 /* STPPaymentMethodUPI.swift */, 8F5ED77825551F2D003BE002 /* STPPaymentMethodUPIParams.swift */, ); @@ -2961,6 +2970,7 @@ 31D1A518252FAB8A00913BD5 /* STPBlocks.swift in Sources */, 3111C559252BDAA800207E32 /* NSArray+Stripe.swift in Sources */, B67243472524F502002E1AAF /* STPSourceKlarnaDetails.swift in Sources */, + B63A414A25F97AD700929729 /* STPConfirmBLIKOptions.swift in Sources */, 31D4D6962512EE0100809066 /* UIViewController+Stripe_ParentViewController.swift in Sources */, 31281BC325254F8E00591A95 /* STPPaymentCardTextFieldCell.swift in Sources */, 31F2E875252403AD004D4B5E /* STPRedirectContext.swift in Sources */, @@ -3151,6 +3161,7 @@ B67243032524172E002E1AAF /* STPPaymentMethodGiropay.swift in Sources */, B64519C6251410EA006BF25E /* STPPaymentMethodAUBECSDebitParams.swift in Sources */, 31CC108725B76482002F898D /* UISpringTimingParameters+Stripe.swift in Sources */, + B6F0489925F972EB00060318 /* STPPaymentMethodBLIK.swift in Sources */, 31D4D6762512E73700809066 /* UIImage+Stripe.swift in Sources */, B672432B2524E6CE002E1AAF /* STPPaymentMethodSofort.swift in Sources */, 8F5ED77925551F2D003BE002 /* STPPaymentMethodUPIParams.swift in Sources */, @@ -3212,6 +3223,7 @@ B6D9CEBC2515243900AAD424 /* STPPaymentMethodCardChecks.swift in Sources */, 3111C32F2526BE8600207E32 /* NSDecimalNumber+Stripe_Currency.swift in Sources */, 31D49B23251D75BA003FDB84 /* STPToken.swift in Sources */, + B63A414425F9759900929729 /* STPPaymentMethodBLIKParams.swift in Sources */, 3194906C25140C0B00AD8F0B /* STPCoreScrollViewController.swift in Sources */, E69552EB25D1E4F000753FDA /* STPGenericInputPickerField.swift in Sources */, 3111C3152526A53400207E32 /* STPSourceEnums.swift in Sources */, diff --git a/Stripe/Resources/Localizations/en.lproj/Localizable.strings b/Stripe/Resources/Localizations/en.lproj/Localizable.strings index fc5c00c60e..fd0f8ab879 100644 --- a/Stripe/Resources/Localizations/en.lproj/Localizable.strings +++ b/Stripe/Resources/Localizations/en.lproj/Localizable.strings @@ -75,6 +75,9 @@ /* Title for billing address entry section */ "Billing Address" = "Billing Address"; +/* Payment Method type brand name */ +"BLIK" = "BLIK"; + /* Placeholder text for BSB Number entry field for BECS Debit. */ "BSB" = "BSB"; diff --git a/Stripe/STPConfirmBLIKOptions.swift b/Stripe/STPConfirmBLIKOptions.swift new file mode 100644 index 0000000000..17fbe0b271 --- /dev/null +++ b/Stripe/STPConfirmBLIKOptions.swift @@ -0,0 +1,52 @@ +// +// STPConfirmBLIKOptions.swift +// StripeiOS +// +// Created by Yuki Tokuhiro on 3/10/21. +// Copyright © 2021 Stripe, Inc. All rights reserved. +// + +import Foundation + +/// BLIK options to pass to `STPConfirmPaymentMethodOptions`` +/// - seealso: https://site-admin.stripe.com/docs/api/payment_intents/confirm#confirm_payment_intent-payment_method_options-blik +public class STPConfirmBLIKOptions: NSObject { + + /// The 6-digit BLIK code that a customer has generated using their banking application. + @objc public var code: String + + @objc public var additionalAPIParameters: [AnyHashable: Any] = [:] + + /// :nodoc: + @objc public override var description: String { + let props = [ + // Object + String(format: "%@: %p", NSStringFromClass(type(of: self)), self), + "code = \(code)", + ] + + return "<\(props.joined(separator: "; "))>" + } + + /// Initializes STPConfirmBLIKOptions + /// - parameter code: The 6-digit BLIK code that a customer has generated using their banking application. + @objc public required init(code: String) { + self.code = code + super.init() + } +} + +// MARK: - STPFormEncodable +extension STPConfirmBLIKOptions: STPFormEncodable { + @objc + public class func propertyNamesToFormFieldNamesMapping() -> [String: String] { + return [ + NSStringFromSelector(#selector(getter:code)): "code", + ] + } + + @objc + public class func rootObjectName() -> String? { + return "blik" + } +} diff --git a/Stripe/STPConfirmPaymentMethodOptions.swift b/Stripe/STPConfirmPaymentMethodOptions.swift index 6b48c9c7a1..f3e2b2cb64 100644 --- a/Stripe/STPConfirmPaymentMethodOptions.swift +++ b/Stripe/STPConfirmPaymentMethodOptions.swift @@ -19,6 +19,9 @@ public class STPConfirmPaymentMethodOptions: NSObject { /// Options for an Alipay Payment Method. @objc public var alipayOptions: STPConfirmAlipayOptions? + /// Options for a BLIK Payment Method. + @objc public var blikOptions: STPConfirmBLIKOptions? + /// :nodoc: @objc public var additionalAPIParameters: [AnyHashable: Any] = [:] @@ -29,6 +32,7 @@ public class STPConfirmPaymentMethodOptions: NSObject { String(format: "%@: %p", NSStringFromClass(type(of: self)), self), "alipay = \(String(describing: alipayOptions))", "card = \(String(describing: cardOptions))", + "blik = \(String(describing: blikOptions))", ] return "<\(props.joined(separator: "; "))>" } @@ -41,6 +45,7 @@ extension STPConfirmPaymentMethodOptions: STPFormEncodable { return [ NSStringFromSelector(#selector(getter:alipayOptions)): "alipay", NSStringFromSelector(#selector(getter:cardOptions)): "card", + NSStringFromSelector(#selector(getter:blikOptions)): "blik", ] } diff --git a/Stripe/STPIntentAction.swift b/Stripe/STPIntentAction.swift index 538ba13eaf..dd3fdc0f03 100644 --- a/Stripe/STPIntentAction.swift +++ b/Stripe/STPIntentAction.swift @@ -40,6 +40,9 @@ import Foundation /// Contains instructions for authenticating a payment by redirecting your customer to Alipay App or website. case alipayHandleRedirect + /// The action type for BLIK payment methods. The customer must authorize the transaction in their banking app within 1 minute. + case BLIKAuthorize + /// Parse the string and return the correct `STPIntentActionType`, /// or `STPIntentActionTypeUnknown` if it's unrecognized by this version of the SDK. /// - Parameter string: the NSString with the `next_action.type` @@ -53,6 +56,8 @@ import Foundation self = .OXXODisplayDetails case "alipay_handle_redirect": self = .alipayHandleRedirect + case "blik_authorize": + self = .BLIKAuthorize default: self = .unknown } @@ -71,6 +76,8 @@ import Foundation return "oxxo_display_details" case .alipayHandleRedirect: return "alipay_handle_redirect" + case .BLIKAuthorize: + return "blik_authorize" case .unknown: break } @@ -132,6 +139,8 @@ public class STPIntentAction: NSObject { if let alipayHandleRedirect = alipayHandleRedirect { props.append("alipayHandleRedirect = \(alipayHandleRedirect)") } + case .BLIKAuthorize: + break // no additional details case .unknown: // unrecognized type, just show the original dictionary for debugging help props.append("allResponseFields = \(allResponseFields)") @@ -205,6 +214,8 @@ extension STPIntentAction: STPAPIResponseDecodable { if alipayHandleRedirect == nil { type = .unknown } + case .BLIKAuthorize: + break // no additional details } return STPIntentAction( diff --git a/Stripe/STPPaymentHandler.swift b/Stripe/STPPaymentHandler.swift index 1af9f13c8f..d82652c07a 100644 --- a/Stripe/STPPaymentHandler.swift +++ b/Stripe/STPPaymentHandler.swift @@ -146,7 +146,7 @@ public class STPPaymentHandler: NSObject, SFSafariViewControllerDelegate, STPURL /// - Parameters: /// - paymentParams: The params used to confirm the PaymentIntent. Note that this method overrides the value of `paymentParams.useStripeSDK` to `@YES`. /// - authenticationContext: The authentication context used to authenticate the payment. - /// - completion: The completion block. If the status returned is `STPPaymentHandlerActionStatusSucceeded`, the PaymentIntent status will always be either STPPaymentIntentStatusSucceeded or STPPaymentIntentStatusRequiresCapture if you are using manual capture. In the latter case, capture the PaymentIntent to complete the payment. + /// - completion: The completion block. If the status returned is `STPPaymentHandlerActionStatusSucceeded`, the PaymentIntent status is not necessarily STPPaymentIntentStatusSucceeded (e.g. some bank payment methods take days before the PaymentIntent succeeds). @objc(confirmPayment:withAuthenticationContext:completion:) public func confirmPayment( _ paymentParams: STPPaymentIntentParams, @@ -171,9 +171,7 @@ public class STPPaymentHandler: NSObject, SFSafariViewControllerDelegate, STPURL // Reset our internal state strongSelf.inProgress = false // Ensure the .succeeded case returns a PaymentIntent in the expected state. - if let paymentIntent = paymentIntent, - status == .succeeded - { + if let paymentIntent = paymentIntent, status == .succeeded { let successIntentState = paymentIntent.status == .succeeded || paymentIntent.status == .requiresCapture || (paymentIntent.status == .processing @@ -542,7 +540,8 @@ public class STPPaymentHandler: NSObject, SFSafariViewControllerDelegate, STPURL .netBanking, .OXXO, .grabPay, - .afterpayClearpay: + .afterpayClearpay, + .blik: return false case .unknown: @@ -1051,6 +1050,14 @@ public class STPPaymentHandler: NSObject, SFSafariViewControllerDelegate, STPURL ])) } + case .BLIKAuthorize: + // The customer must authorize the transaction in their banking app within 1 minute + // The merchant integration should spin and poll their backend or Stripe to determine success + guard let currentAction = self.currentAction as? STPPaymentHandlerPaymentIntentActionParams else { + fatalError() + } + currentAction.complete(with: .succeeded, error: nil) + @unknown default: fatalError() } @@ -1346,7 +1353,7 @@ public class STPPaymentHandler: NSObject, SFSafariViewControllerDelegate, STPURL threeDSSourceID = nextAction.redirectToURL?.threeDSSourceID case .useStripeSDK: threeDSSourceID = nextAction.useStripeSDK?.threeDSSourceID - case .OXXODisplayDetails, .alipayHandleRedirect, .unknown: + case .OXXODisplayDetails, .alipayHandleRedirect, .unknown, .BLIKAuthorize: break @unknown default: fatalError() diff --git a/Stripe/STPPaymentMethod.swift b/Stripe/STPPaymentMethod.swift index a2ded89f41..d7d4d8fb53 100644 --- a/Stripe/STPPaymentMethod.swift +++ b/Stripe/STPPaymentMethod.swift @@ -61,6 +61,8 @@ public class STPPaymentMethod: NSObject, STPAPIResponseDecodable, STPPaymentOpti @objc private(set) public var payPal: STPPaymentMethodPayPal? /// If this is an AfterpayClearpay PaymentMethod (i.e. `self.type == STPPaymentMethodTypeAfterpayClearpay`), this contains additional details. :nodoc: @objc private(set) public var afterpayClearpay: STPPaymentMethodAfterpayClearpay? + /// If this is a BLIK PaymentMethod (i.e. `self.type == STPPaymentMethodTypeBLIK`), this contains additional details. :nodoc: + @objc private(set) public var blik: STPPaymentMethodBLIK? /// The ID of the Customer to which this PaymentMethod is saved. Nil when the PaymentMethod has not been saved to a Customer. @objc private(set) public var customerId: String? // MARK: - Deprecated @@ -108,6 +110,7 @@ public class STPPaymentMethod: NSObject, STPAPIResponseDecodable, STPPaymentOpti "sofort = \(String(describing: sofort))", "upi = \(String(describing: upi))", "afterpay_clearpay = \(String(describing: afterpayClearpay))", + "blik = \(String(describing: blik))", "liveMode = \(liveMode ? "YES" : "NO")", "type = \(allResponseFields["type"] as? String ?? "")", ] @@ -136,6 +139,7 @@ public class STPPaymentMethod: NSObject, STPAPIResponseDecodable, STPPaymentOpti "alipay": NSNumber(value: STPPaymentMethodType.alipay.rawValue), "paypal": NSNumber(value: STPPaymentMethodType.payPal.rawValue), "afterpay_clearpay": NSNumber(value: STPPaymentMethodType.afterpayClearpay.rawValue), + "blik": NSNumber(value: STPPaymentMethodType.blik.rawValue), ] } @@ -243,6 +247,8 @@ public class STPPaymentMethod: NSObject, STPAPIResponseDecodable, STPPaymentOpti fromAPIResponse: dict.stp_dictionary(forKey: "paypal")) paymentMethod.afterpayClearpay = STPPaymentMethodAfterpayClearpay.decodedObject( fromAPIResponse: dict.stp_dictionary(forKey: "afterpay_clearpay")) + paymentMethod.blik = STPPaymentMethodBLIK.decodedObject( + fromAPIResponse: dict.stp_dictionary(forKey: "blik")) return paymentMethod } @@ -290,7 +296,7 @@ public class STPPaymentMethod: NSObject, STPAPIResponseDecodable, STPPaymentOpti case .alipay /* Careful! Revisit this if/when we support recurring Alipay */, .AUBECSDebit, .bacsDebit, .SEPADebit, .iDEAL, .FPX, .cardPresent, .giropay, .EPS, .payPal, .przelewy24, .bancontact, - .OXXO, .sofort, .grabPay, .netBanking, .UPI, .afterpayClearpay, // fall through + .OXXO, .sofort, .grabPay, .netBanking, .UPI, .afterpayClearpay, .blik, // fall through .unknown: return false @unknown default: diff --git a/Stripe/STPPaymentMethodBLIK.swift b/Stripe/STPPaymentMethodBLIK.swift new file mode 100644 index 0000000000..5f2cfc7b98 --- /dev/null +++ b/Stripe/STPPaymentMethodBLIK.swift @@ -0,0 +1,42 @@ +// +// STPPaymentMethodBLIK.swift +// StripeiOS +// +// Created by Yuki Tokuhiro on 3/10/21. +// Copyright © 2021 Stripe, Inc. All rights reserved. +// + +import Foundation + +/// Contains details for a BLIK Payment Method. +/// - seealso: https://stripe.com/docs/api/payment_methods/object#payment_method_object-blik +public class STPPaymentMethodBLIK: NSObject, STPAPIResponseDecodable { + /// :nodoc: + private(set) public var allResponseFields: [AnyHashable: Any] = [:] + + // MARK: - Description + /// :nodoc: + @objc public override var description: String { + let props = [ + // Object + String(format: "%@: %p", NSStringFromClass(STPPaymentMethodBLIK.self), self) + ] + + return "<\(props.joined(separator: "; "))>" + } + + // MARK: - STPAPIResponseDecodable + /// :nodoc: + public class func decodedObject(fromAPIResponse response: [AnyHashable: Any]?) -> Self? { + guard let dict = (response as NSDictionary?)?.stp_dictionaryByRemovingNulls() else { + return nil + } + return self.init(dictionary: dict) + + } + + required init(dictionary dict: [AnyHashable: Any]) { + super.init() + allResponseFields = dict + } +} diff --git a/Stripe/STPPaymentMethodBLIKParams.swift b/Stripe/STPPaymentMethodBLIKParams.swift new file mode 100644 index 0000000000..6af3781c30 --- /dev/null +++ b/Stripe/STPPaymentMethodBLIKParams.swift @@ -0,0 +1,28 @@ +// +// STPPaymentMethodBLIKParams.swift +// StripeiOS Tests +// +// Created by Yuki Tokuhiro on 3/10/21. +// Copyright © 2021 Stripe, Inc. All rights reserved. +// + +import Foundation + +/// An object representing parameters used to create a BLIK Payment Method +/// There are currently no parameters to pass. +/// - seealso: https://site-admin.stripe.com/docs/api/payment_methods/create#create_payment_method-blik +@objc +public class STPPaymentMethodBLIKParams: NSObject, STPFormEncodable { + @objc public var additionalAPIParameters: [AnyHashable: Any] = [:] + + + @objc + public class func rootObjectName() -> String? { + return "blik" + } + + @objc + public class func propertyNamesToFormFieldNamesMapping() -> [String: String] { + return [:] + } +} diff --git a/Stripe/STPPaymentMethodEnums.swift b/Stripe/STPPaymentMethodEnums.swift index ba394abd61..d352d3afa1 100644 --- a/Stripe/STPPaymentMethodEnums.swift +++ b/Stripe/STPPaymentMethodEnums.swift @@ -48,6 +48,9 @@ import Foundation case payPal /// An AfterpayClearpay payment method case afterpayClearpay + /// A BLIK payment method + @objc(STPPaymentMethodTypeBLIK) + case blik /// An unknown type. case unknown @@ -88,6 +91,8 @@ import Foundation return STPLocalizedString("PayPal", "Payment Method type brand name") case .afterpayClearpay: return STPLocalizedString("AfterpayClearpay", "Payment Method type brand name") + case .blik: + return STPLocalizedString("BLIK", "Payment Method type brand name") case .bacsDebit, .cardPresent, .unknown: diff --git a/Stripe/STPPaymentMethodParams.swift b/Stripe/STPPaymentMethodParams.swift index a134ce7ae2..7d0251597b 100644 --- a/Stripe/STPPaymentMethodParams.swift +++ b/Stripe/STPPaymentMethodParams.swift @@ -72,6 +72,8 @@ public class STPPaymentMethodParams: NSObject, STPFormEncodable, STPPaymentOptio @objc public var grabPay: STPPaymentMethodGrabPayParams? /// If this is a Afterpay PaymentMethod, this contains additional details. @objc public var afterpayClearpay: STPPaymentMethodAfterpayClearpayParams? + /// If this is a BLIK PaymentMethod, this contains additional details. + @objc public var blik: STPPaymentMethodBLIKParams? /// Set of key-value pairs that you can attach to the PaymentMethod. This can be useful for storing additional information about the PaymentMethod in a structured format. @objc public var metadata: [String: String]? @@ -392,6 +394,23 @@ public class STPPaymentMethodParams: NSObject, STPFormEncodable, STPPaymentOptio self.metadata = metadata } + /// Creates params for a BLIK PaymentMethod. + /// - Parameters: + /// - blik: An object containing additional BLIK details. + /// - billingDetails: An object containing the user's billing details. + /// - metadata: Additional information to attach to the PaymentMethod. + @objc + public convenience init( + blik: STPPaymentMethodBLIKParams, + billingDetails: STPPaymentMethodBillingDetails?, + metadata: [String: String]? + ) { + self.init() + self.type = .blik + self.blik = blik + self.billingDetails = billingDetails + self.metadata = metadata + } /// Creates params from a single-use PaymentMethod. This is useful for recreating a new payment method /// with similar settings. It will return nil if used with a reusable PaymentMethod. /// - Parameter paymentMethod: An object containing the original single-use PaymentMethod. @@ -572,6 +591,8 @@ public class STPPaymentMethodParams: NSObject, STPFormEncodable, STPPaymentOptio return "PayPal" case .afterpayClearpay: return "Afterpay Clearpay" + case .blik: + return "BLIK" case .cardPresent, .unknown: return STPLocalizedString("Unknown", "Default missing source type label") @unknown default: @@ -585,7 +606,7 @@ public class STPPaymentMethodParams: NSObject, STPFormEncodable, STPPaymentOptio return true case .alipay, .AUBECSDebit, .bacsDebit, .SEPADebit, .iDEAL, .FPX, .cardPresent, .giropay, .grabPay, .EPS, .przelewy24, .bancontact, .netBanking, .OXXO, .payPal, .sofort, .UPI, - .afterpayClearpay, // fall through + .afterpayClearpay, .blik, // fall through .unknown: return false @unknown default: @@ -832,7 +853,7 @@ extension STPPaymentMethodParams { alipay: alipay, billingDetails: billingDetails, metadata: metadata) } - /// Creates params for a PayPal PaymentMethod. :nodoc: + /// Creates params for a PayPal PaymentMethod. /// - Parameters: /// - payPal: An object containing additional PayPal details. /// - billingDetails: An object containing the user's billing details. @@ -847,7 +868,7 @@ extension STPPaymentMethodParams { payPal: payPal, billingDetails: billingDetails, metadata: metadata) } - /// Creates params for an AfterpayClearpay PaymentMethod. :nodoc: + /// Creates params for an AfterpayClearpay PaymentMethod. /// - Parameters: /// - afterpayClearpay: An object containing additional AfterpayClearpay details. /// - billingDetails: An object containing the user's billing details. @@ -861,6 +882,21 @@ extension STPPaymentMethodParams { return STPPaymentMethodParams( afterpayClearpay: afterpayClearpay, billingDetails: billingDetails, metadata: metadata) } + + /// Creates params for a BLIK PaymentMethod. + /// - Parameters: + /// - blik: An object containing additional BLIK details. + /// - billingDetails: An object containing the user's billing details. + /// - metadata: Additional information to attach to the PaymentMethod. + @objc(paramsWithBLIK:billingDetails:metadata:) + public class func paramsWith( + blik: STPPaymentMethodBLIKParams, + billingDetails: STPPaymentMethodBillingDetails?, + metadata: [String: String]? + ) -> STPPaymentMethodParams { + return STPPaymentMethodParams( + blik: blik, billingDetails: billingDetails, metadata: metadata) + } } extension STPPaymentMethodParams { diff --git a/Tests/Tests/STPConfirmPaymentMethodOptionsTest.m b/Tests/Tests/STPConfirmPaymentMethodOptionsTest.m index 4c0924bb1d..4e47f8593c 100644 --- a/Tests/Tests/STPConfirmPaymentMethodOptionsTest.m +++ b/Tests/Tests/STPConfirmPaymentMethodOptionsTest.m @@ -27,7 +27,7 @@ - (void)testFormEncoding { NSDictionary *propertyToFieldMap = [STPConfirmPaymentMethodOptions propertyNamesToFormFieldNamesMapping]; - NSDictionary *expected = @{@"cardOptions": @"card", @"alipayOptions": @"alipay"}; + NSDictionary *expected = @{@"cardOptions": @"card", @"alipayOptions": @"alipay", @"blikOptions": @"blik"}; XCTAssertEqualObjects(propertyToFieldMap, expected, @"Unexpected property to field name mapping."); } diff --git a/Tests/Tests/STPPaymentIntentFunctionalTest.m b/Tests/Tests/STPPaymentIntentFunctionalTest.m index 47f2546616..649b3e94cb 100644 --- a/Tests/Tests/STPPaymentIntentFunctionalTest.m +++ b/Tests/Tests/STPPaymentIntentFunctionalTest.m @@ -917,6 +917,61 @@ [self waitForExpectationsWithTimeout:STPTestingNetworkRequestTimeout handler:nil]; } +#pragma mark - BLIK + +- (void)testConfirmPaymentIntentWithBLIK { + __block NSString *clientSecret = nil; + XCTestExpectation *createExpectation = [self expectationWithDescription:@"Create PaymentIntent."]; + [[STPTestingAPIClient sharedClient] + createPaymentIntentWithParams: @{ + @"payment_method_types": @[@"blik"], + @"currency": @"pln", + @"amount": @1000, + } + completion:^(NSString * _Nullable createdClientSecret, NSError * _Nullable creationError) { + XCTAssertNotNil(createdClientSecret); + XCTAssertNil(creationError); + [createExpectation fulfill]; + clientSecret = [createdClientSecret copy]; + }]; + [self waitForExpectationsWithTimeout:STPTestingNetworkRequestTimeout handler:nil]; + XCTAssertNotNil(clientSecret); + + STPAPIClient *client = [[STPAPIClient alloc] initWithPublishableKey:STPTestingDefaultPublishableKey]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Payment Intent confirm"]; + + STPPaymentIntentParams *paymentIntentParams = [[STPPaymentIntentParams alloc] initWithClientSecret:clientSecret]; + STPPaymentMethodBLIKParams *blik = [STPPaymentMethodBLIKParams new]; + + STPPaymentMethodBillingDetails *billingDetails = [STPPaymentMethodBillingDetails new]; + billingDetails.name = @"Jane Doe"; + + paymentIntentParams.paymentMethodParams = [STPPaymentMethodParams paramsWithBLIK:blik + billingDetails:billingDetails + metadata:@{@"test_key": @"test_value"}]; + STPConfirmPaymentMethodOptions *options = [STPConfirmPaymentMethodOptions new]; + options.blikOptions = [[STPConfirmBLIKOptions alloc] initWithCode:@"123456"]; + paymentIntentParams.paymentMethodOptions = options; + paymentIntentParams.returnURL = @"example-app-scheme://unused"; + [client confirmPaymentIntentWithParams:paymentIntentParams + completion:^(STPPaymentIntent * _Nullable paymentIntent, NSError * _Nullable error) { + XCTAssertNil(error, @"With valid key + secret, should be able to confirm the intent"); + + XCTAssertNotNil(paymentIntent); + XCTAssertEqualObjects(paymentIntent.stripeId, paymentIntentParams.stripeId); + XCTAssertFalse(paymentIntent.livemode); + XCTAssertNotNil(paymentIntent.paymentMethodId); + + // Blik transitions to requires_action until the customer authorizes the transaction or 1 minute passes + XCTAssertEqual(paymentIntent.status, STPPaymentIntentStatusRequiresAction); + XCTAssertEqual(paymentIntent.nextAction.type, STPIntentActionTypeBLIKAuthorize); + + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:STPTestingNetworkRequestTimeout handler:nil]; +} + #pragma mark - Test Objective-C setupFutureUsage - (void)testObjectiveCSetupFutureUsage { diff --git a/Tests/Tests/STPPaymentMethodFunctionalTest.m b/Tests/Tests/STPPaymentMethodFunctionalTest.m index b3ccfe8708..ccdf8ba269 100644 --- a/Tests/Tests/STPPaymentMethodFunctionalTest.m +++ b/Tests/Tests/STPPaymentMethodFunctionalTest.m @@ -145,4 +145,22 @@ [self waitForExpectationsWithTimeout:5 handler:nil]; } +- (void)testCreateBLIKPaymentMethod { + STPAPIClient *client = [[STPAPIClient alloc] initWithPublishableKey:STPTestingDefaultPublishableKey]; + + STPPaymentMethodParams *params = [STPPaymentMethodParams paramsWithBLIK:[STPPaymentMethodBLIKParams new] billingDetails:nil metadata:nil]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"Payment Method create"]; + [client createPaymentMethodWithParams:params + completion:^(STPPaymentMethod *paymentMethod, NSError *error) { + XCTAssertNil(error); + XCTAssertNotNil(paymentMethod); + XCTAssertEqual(paymentMethod.type, STPPaymentMethodTypeBLIK); + XCTAssertNotNil(paymentMethod.blik); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:5 handler:nil]; +} + @end diff --git a/Tests/Tests/STPTestingAPIClient.m b/Tests/Tests/STPTestingAPIClient.m index 6946136e2e..5678faef87 100644 --- a/Tests/Tests/STPTestingAPIClient.m +++ b/Tests/Tests/STPTestingAPIClient.m @@ -59,6 +59,7 @@ NS_ASSUME_NONNULL_BEGIN STPError.errorMessageKey: [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding], }; NSError *apiError = [NSError errorWithDomain:STPError.stripeDomain code:STPAPIError userInfo:userInfo]; + NSLog(@"%@", apiError); completion(nil, apiError); }); } else {