Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Cardinal UI Type and Render Types #1134

Merged
merged 13 commits into from
Nov 15, 2023
8 changes: 8 additions & 0 deletions Braintree.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,8 @@
BEBC6F3329380B82004E25A0 /* BTExceptionCatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = BEBC6F3129380B82004E25A0 /* BTExceptionCatcher.m */; };
BEBD05222A1FE8BE0003C15C /* BTWebAuthenticationSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEBD05212A1FE8BE0003C15C /* BTWebAuthenticationSession.swift */; };
BEBD05242A1FEE150003C15C /* MockWebAuthenticationSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEBD05232A1FEE150003C15C /* MockWebAuthenticationSession.swift */; };
BEBF0C202B02793B0079DA74 /* BTThreeDSecureUIType.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEBF0C1F2B02793B0079DA74 /* BTThreeDSecureUIType.swift */; };
BEBF0C222B0279F10079DA74 /* BTThreeDSecureRenderTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEBF0C212B0279F10079DA74 /* BTThreeDSecureRenderTypes.swift */; };
BEC3F11328A4401E0074DF0F /* BTHTTPError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEC3F11228A4401E0074DF0F /* BTHTTPError.swift */; };
BEC66DBE2901CC9E0030B6B2 /* BraintreeCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 570B93AC285397520041BAFE /* BraintreeCore.framework */; };
BECBA0E62AEABC99002518AC /* BTCardClient_IntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BECBA0E52AEABC99002518AC /* BTCardClient_IntegrationTests.swift */; };
Expand Down Expand Up @@ -858,6 +860,8 @@
BEBC6F3429380BA6004E25A0 /* BTExceptionCatcher.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BTExceptionCatcher.h; sourceTree = "<group>"; };
BEBD05212A1FE8BE0003C15C /* BTWebAuthenticationSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTWebAuthenticationSession.swift; sourceTree = "<group>"; };
BEBD05232A1FEE150003C15C /* MockWebAuthenticationSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockWebAuthenticationSession.swift; sourceTree = "<group>"; };
BEBF0C1F2B02793B0079DA74 /* BTThreeDSecureUIType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTThreeDSecureUIType.swift; sourceTree = "<group>"; };
BEBF0C212B0279F10079DA74 /* BTThreeDSecureRenderTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTThreeDSecureRenderTypes.swift; sourceTree = "<group>"; };
BEC3F11228A4401E0074DF0F /* BTHTTPError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTHTTPError.swift; sourceTree = "<group>"; };
BECA56C929C3F0A80098EC3C /* BTThreeDSecureV2UICustomization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTThreeDSecureV2UICustomization.swift; sourceTree = "<group>"; };
BECBA0E52AEABC99002518AC /* BTCardClient_IntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTCardClient_IntegrationTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1527,11 +1531,13 @@
BE01A84229D32EA9000DFA24 /* BTThreeDSecureError.swift */,
BE01A82C29CCCDE9000DFA24 /* BTThreeDSecureLookup.swift */,
BE01A83429D1F7B9000DFA24 /* BTThreeDSecurePostalAddress.swift */,
BEBF0C212B0279F10079DA74 /* BTThreeDSecureRenderTypes.swift */,
80482F7F29D39A1D007E5F50 /* BTThreeDSecureRequest.swift */,
80482F8129D39BF5007E5F50 /* BTThreeDSecureRequestDelegate.swift */,
80482F8729D3A571007E5F50 /* BTThreeDSecureRequestedExemptionType.swift */,
BE80C00229C549BE00793A6C /* BTThreeDSecureResult.swift */,
80482F8529D3A498007E5F50 /* BTThreeDSecureShippingMethod.swift */,
BEBF0C1F2B02793B0079DA74 /* BTThreeDSecureUIType.swift */,
BE01A83E29D32CA0000DFA24 /* BTThreeDSecureV2Provider.swift */,
80CD33FF2A6042FC009545F5 /* CardinalSessionTestable.swift */,
801A0A1725890DF200DAF851 /* V2UICustomization */,
Expand Down Expand Up @@ -2884,11 +2890,13 @@
buildActionMask = 2147483647;
files = (
BE48CE4829D5DDA600F0825C /* BTThreeDSecureV2TextBoxCustomization.swift in Sources */,
BEBF0C222B0279F10079DA74 /* BTThreeDSecureRenderTypes.swift in Sources */,
80D1638729E75CF1001D880E /* BTThreeDSecureClient.swift in Sources */,
BEEBC3C129D5DD68009C7F77 /* BTThreeDSecureV2LabelCustomization.swift in Sources */,
BEEBC3C029D5DD16009C7F77 /* BTThreeDSecureV2UICustomization.swift in Sources */,
80482F8029D39A1D007E5F50 /* BTThreeDSecureRequest.swift in Sources */,
BE01A83D29D23833000DFA24 /* BTThreeDSecureAdditionalInformation.swift in Sources */,
BEBF0C202B02793B0079DA74 /* BTThreeDSecureUIType.swift in Sources */,
BE01A83F29D32CA0000DFA24 /* BTThreeDSecureV2Provider.swift in Sources */,
BE82E73F29C4A06B0059FE97 /* BTThreeDSecureV2ToolbarCustomization.swift in Sources */,
BE01A84129D32CE1000DFA24 /* BTThreeDSecureAuthenticateJWT.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## unreleased
* BraintreeThreeDSecure
* Add `cardAddChallengeRequested` to `BTThreeDSecureRequest`
* Add `cardAddChallengeRequested`, `uiType`, and `renderTypes` to `BTThreeDSecureRequest`
* Deprecate `BTThreeDSecureRequest.cardAddChallenge`
* Fix bug where defaults for `BTThreeDSecureRequest.accountType`, `BTThreeDSecureRequest.requestedExemptionType`, and `BTThreeDSecureRequest.dfReferenceID` were improperly returning an error if not passed into the request
* BraintreeCard
Expand Down
8 changes: 8 additions & 0 deletions Demo/Application/Features/ThreeDSecureViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,14 @@ class ThreeDSecureViewController: PaymentButtonBaseViewController {
request.requestedExemptionType = .lowValue
request.email = "[email protected]"
request.shippingMethod = .sameDay
request.uiType = .both
request.renderTypes = [
BTThreeDSecureRenderType.otp,
BTThreeDSecureRenderType.singleSelect,
BTThreeDSecureRenderType.multiSelect,
BTThreeDSecureRenderType.oob,
BTThreeDSecureRenderType.html
]

let billingAddress = BTThreeDSecurePostalAddress()
billingAddress.givenName = "Jill"
Expand Down
22 changes: 22 additions & 0 deletions Sources/BraintreeThreeDSecure/BTThreeDSecureRenderTypes.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Foundation

/// Render types that the device supports for displaying specific challenge user interfaces within the 3D Secure challenge.
@objcMembers public class BTThreeDSecureRenderType: NSObject {

public typealias StringValue = String

/// OTP
public static let otp: StringValue = "CardinalSessionRenderTypeOTP"

/// HTML
public static let html: StringValue = "CardinalSessionRenderTypeHTML"

/// Single select
public static let singleSelect: StringValue = "CardinalSessionRenderTypeSingleSelect"

/// Multi Select
public static let multiSelect: StringValue = "CardinalSessionRenderTypeMultiSelect"

/// OOB
public static let oob: StringValue = "CardinalSessionRenderTypeOOB"
Copy link
Contributor

Choose a reason for hiding this comment

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

Would the @objc annotation work for us (stack post)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That works for the enum but that's not the issue, the issue is that arrays of Int enums don't exist in Obj-C and enums can only be bridged to Int types to be interoperable with Obj-C. A Swift array is bridged to Obj-C's NSArray, and an Obj-C array should contain only pointers, but an enum of Int doesn't contain pointers so cannot be bridged.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah got it yeah. That makes sense. Swift does have OptionSet as an NS_OPTIONS equivalent, but I can't tell if option sets are accessible from Obj-C.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤔 we may be able to make that work - it'll need to be a class instead of a struct since structs don't exist in Obj-c, but I think that may be okay. I'll poke around at it a bit.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done: 5911c55. Great suggestion, that's way cleaner 🔥

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also if anyone is interested this is how the implementation will look for Obj-C merchants:

BTThreeDSecureRequest *request = [[BTThreeDSecureRequest alloc] init];
request.renderTypes = @[BTThreeDSecureRenderType.oob, BTThreeDSecureRenderType.html, BTThreeDSecureRenderType.singleSelect, BTThreeDSecureRenderType.multiSelect, BTThreeDSecureRenderType.otp];

}
11 changes: 11 additions & 0 deletions Sources/BraintreeThreeDSecure/BTThreeDSecureRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,17 @@ import BraintreeCore
/// Optional. UI Customization for 3DS2 challenge views.
public var v2UICustomization: BTThreeDSecureV2UICustomization?

/// Optional. Sets all UI types that the device supports for displaying specific challenge user interfaces in the 3D Secure challenge.
///
/// Defaults to `.both`
public var uiType: BTThreeDSecureUIType = .both

/// Optional. List of all the render types that the device supports for displaying specific challenge user interfaces within the 3D Secure challenge.
///
/// - Note: When using `BTThreeDSecureUIType.both` or `BTThreeDSecureUIType.html`, all `BTThreeDSecureRenderType` options must be set.
/// When using `BTThreeDSecureUIType.native`, all `BTThreeDSecureRenderType` options except `BTThreeDSecureRenderType.html` must be set.
public var renderTypes: [BTThreeDSecureRenderType.StringValue]?
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Arrays of Int enums do not exist in Obj-C so we cannot use the cleaner version (array of enums) here. 😢 This seems to be the consensus on the best practice in these cases. Open to other ideas if folks find something that works better as this is certainly not the most Swift-y approach. But working in the confines of Obj-C it seems to be our best bet.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't have any better ideas for this but curious if we are planning to remove obj-c support in the next major version?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Currently there isn't a plan to remove Obj-C support in the next major version but we could consider discussing it further as an option! Or if Apple announces an official deprecation policy at some point it'd be a much easier choice.


/// A delegate for receiving information about the ThreeDSecure payment flow.
public weak var threeDSecureRequestDelegate: BTThreeDSecureRequestDelegate?

Expand Down
26 changes: 26 additions & 0 deletions Sources/BraintreeThreeDSecure/BTThreeDSecureUIType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import Foundation
import CardinalMobile

/// The interface types that the device supports for displaying specific challenge user interfaces within the 3D Secure challenge.
@objc public enum BTThreeDSecureUIType: Int {

/// Native
case native

/// HTML
case html

/// Both
case both

var cardinalValue: CardinalSessionUIType {
switch self {
case .native:
return .native
case .html:
return .HTML
case .both:
return .both
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ class BTThreeDSecureV2Provider {
cardinalEnvironment = .production
}

cardinalConfiguration.uiType = request.uiType.cardinalValue

if let renderTypes = request.renderTypes {
cardinalConfiguration.renderType = renderTypes
}

guard let cardinalAuthenticationJWT = configuration.cardinalAuthenticationJWT else {
completion(nil)
return
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import XCTest
import CardinalMobile
@testable import BraintreeTestShared
@testable import BraintreeThreeDSecure

Expand Down Expand Up @@ -114,4 +115,48 @@ class BTThreeDSecureRequest_Tests: XCTestCase {
let request = BTThreeDSecureRequest()
XCTAssertEqual(request.requestedExemptionType.stringValue, nil)
}

// MARK: - UIType

func testUIType_whenUITypeNative_setsCardinalUITypeNative() {
let request = BTThreeDSecureRequest()
request.uiType = .native
XCTAssertEqual(request.uiType.cardinalValue, CardinalSessionUIType.native)
}

func testUIType_whenUITypeHTML_setsCardinalUITypeHTML() {
let request = BTThreeDSecureRequest()
request.uiType = .html
XCTAssertEqual(request.uiType.cardinalValue, CardinalSessionUIType.HTML)
}

func testUIType_whenUITypeBoth_setsCardinalUITypeBoth() {
let request = BTThreeDSecureRequest()
request.uiType = .native
XCTAssertEqual(request.uiType.cardinalValue, CardinalSessionUIType.both)
}

// MARK: RenderTypes

func testRenderTypes_whenAllRenderTypesAreSet_setsAllCardinalRenderTypes() {
let request = BTThreeDSecureRequest()
request.renderTypes = [
BTThreeDSecureRenderType.otp,
BTThreeDSecureRenderType.singleSelect,
BTThreeDSecureRenderType.multiSelect,
BTThreeDSecureRenderType.oob,
BTThreeDSecureRenderType.html
]

XCTAssertEqual(
request.renderTypes,
[
"CardinalSessionRenderTypeOTP",
"CardinalSessionRenderTypeSingleSelect",
"CardinalSessionRenderTypeMultiSelect",
"CardinalSessionRenderTypeOOB",
"CardinalSessionRenderTypeHTML"
]
)
}
}
Loading