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

feat: Expose ApplePay Shipping Options and align them with Web SDK #1049

Merged
merged 9 commits into from
Dec 16, 2024
2 changes: 1 addition & 1 deletion Debug App/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@ SPEC CHECKSUMS:

PODFILE CHECKSUM: fa17ead44d40b0b09abc2f30a5cc3d8aefe389e1

COCOAPODS: 1.15.2
COCOAPODS: 1.16.2
127 changes: 100 additions & 27 deletions Debug App/Resources/Localized Views/Base.lproj/Main.storyboard

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,19 @@ class MerchantSessionAndSettingsViewController: UIViewController {
@IBOutlet weak var applePayCaptureBillingAddressSwitch: UISwitch!
@IBOutlet weak var applePayCheckProvidedNetworksSwitch: UISwitch!

@IBOutlet weak var applePayBillingControlStackView: UIStackView!
@IBOutlet weak var applePayBillingContactNameSwitch: UISwitch!
@IBOutlet weak var applePayBillingContactEmailSwitch: UISwitch!
@IBOutlet weak var applePayBillingContactPhoneSwitch: UISwitch!
@IBOutlet weak var applePayBillingContactPostalAddressSwitch: UISwitch!

@IBOutlet weak var applePayShippingControlStackView: UIStackView!
@IBOutlet weak var applePayShippingDetailsSwitch: UISwitch!
@IBOutlet weak var applePayCaptureShippingAddressEnabledSwitch: UISwitch!
@IBOutlet weak var applePayRequireShippingMethodSwitch: UISwitch!
@IBOutlet weak var applePayShippingContactNameSwitch: UISwitch!
@IBOutlet weak var applePayShippingContactEmailSwitch: UISwitch!
@IBOutlet weak var applePayShippingContactPhoneSwitch: UISwitch!
@IBOutlet weak var applePayShippingContactPostalAddressSwitch: UISwitch!

// MARK: Order Inputs

Expand Down Expand Up @@ -159,14 +165,12 @@ class MerchantSessionAndSettingsViewController: UIViewController {
var payAfterVaultSuccess: Bool = false

var applePayCaptureBillingAddress = false
var applePayBillingAdditionalContactFields: [PrimerApplePayOptions.RequiredContactField]? = []
var applePayCaptureShippingDetails = false
var applePayRequireShippingMethod = false
var applePayShippingAdditionalContactFields: [PrimerApplePayOptions.RequiredContactField]? = []
var applePayCheckProvidedNetworks = false

//Below are gated by applePayCaptureShippingDetails, default to on when above is true
var applePayCaptureShippingAddress = true
var applePayRequireShippingMethod = true
// var applePayAdditionalContactFields: [PrimerApplePayOptions.ShippingOptions.AdditionalShippingContactField]? = [.name, .emailAddress, .phoneNumber]

func setAccessibilityIds() {
self.view.accessibilityIdentifier = "Background View"
self.testingModeSegmentedControl.accessibilityIdentifier = "Testing Mode Segmented Control"
Expand Down Expand Up @@ -417,66 +421,139 @@ class MerchantSessionAndSettingsViewController: UIViewController {
}

@IBAction func applePayCaptureBillingAddressSwitchValueChanged(_ sender: UISwitch) {
applePayBillingControlStackView.isHidden = !sender.isOn
applePayCaptureBillingAddress = sender.isOn
}


@IBAction func applePayBillingContactNameSwitchChanged(_ sender: UISwitch) {
if sender.isOn {
var fields = applePayBillingAdditionalContactFields ?? []
if !fields.contains(.name) {
fields.append(.name)
}
applePayBillingAdditionalContactFields = fields
} else {
applePayBillingAdditionalContactFields?.removeAll(where: { $0 == .name })
if applePayBillingAdditionalContactFields?.isEmpty == true {
applePayBillingAdditionalContactFields = nil
}
}
}


@IBAction func applePayBillingContactEmailField(_ sender: UISwitch) {
if sender.isOn {
var fields = applePayBillingAdditionalContactFields ?? []
if !fields.contains(.emailAddress) {
fields.append(.emailAddress)
}
applePayBillingAdditionalContactFields = fields
} else {
applePayBillingAdditionalContactFields?.removeAll(where: { $0 == .emailAddress })
if applePayBillingAdditionalContactFields?.isEmpty == true {
applePayBillingAdditionalContactFields = nil
}
}
}

@IBAction func applePayBillingContactPhoneSwitchChanged(_ sender: UISwitch) {
if sender.isOn {
var fields = applePayBillingAdditionalContactFields ?? []
if !fields.contains(.phoneNumber) {
fields.append(.phoneNumber)
}
applePayBillingAdditionalContactFields = fields
} else {
applePayBillingAdditionalContactFields?.removeAll(where: { $0 == .phoneNumber })
if applePayBillingAdditionalContactFields?.isEmpty == true {
applePayBillingAdditionalContactFields = nil
}
}
}

@IBAction func applePayBillingContactPostalAddressSwitchChanged(_ sender: UISwitch) {
if sender.isOn {
var fields = applePayBillingAdditionalContactFields ?? []
if !fields.contains(.postalAddress) {
fields.append(.postalAddress)
}
applePayBillingAdditionalContactFields = fields
} else {
applePayBillingAdditionalContactFields?.removeAll(where: { $0 == .postalAddress })
if applePayBillingAdditionalContactFields?.isEmpty == true {
applePayBillingAdditionalContactFields = nil
}
}
}

@IBAction func applePayCaptureShippingDetailsSwitchChanged(_ sender: UISwitch) {
applePayShippingControlStackView.isHidden = !sender.isOn
applePayCaptureShippingDetails = sender.isOn
}

@IBAction func captureShippingAddressSwitchChanged(_ sender: UISwitch) {
applePayCaptureShippingAddress = sender.isOn
}

@IBAction func applePayRequireShippingMethodSwitchChanged(_ sender: UISwitch) {
applePayRequireShippingMethod = sender.isOn
}

@IBAction func applePayShippingContactNameSwitchChanged(_ sender: UISwitch) {
// if sender.isOn {
// var fields = applePayAdditionalContactFields ?? []
// if !fields.contains(.name) {
// fields.append(.name)
// }
// applePayAdditionalContactFields = fields
// } else {
// applePayAdditionalContactFields?.removeAll(where: { $0 == .name })
// if applePayAdditionalContactFields?.isEmpty == true {
// applePayAdditionalContactFields = nil
// }
// }
if sender.isOn {
var fields = applePayShippingAdditionalContactFields ?? []
if !fields.contains(.name) {
fields.append(.name)
}
applePayShippingAdditionalContactFields = fields
} else {
applePayShippingAdditionalContactFields?.removeAll(where: { $0 == .name })
if applePayShippingAdditionalContactFields?.isEmpty == true {
applePayShippingAdditionalContactFields = nil
}
}
}


@IBAction func applePayShippingContactEmailField(_ sender: UISwitch) {
// if sender.isOn {
// var fields = applePayAdditionalContactFields ?? []
// if !fields.contains(.emailAddress) {
// fields.append(.emailAddress)
// }
// applePayAdditionalContactFields = fields
// } else {
// applePayAdditionalContactFields?.removeAll(where: { $0 == .emailAddress })
// if applePayAdditionalContactFields?.isEmpty == true {
// applePayAdditionalContactFields = nil
// }
// }
if sender.isOn {
var fields = applePayShippingAdditionalContactFields ?? []
if !fields.contains(.emailAddress) {
fields.append(.emailAddress)
}
applePayShippingAdditionalContactFields = fields
} else {
applePayShippingAdditionalContactFields?.removeAll(where: { $0 == .emailAddress })
if applePayShippingAdditionalContactFields?.isEmpty == true {
applePayShippingAdditionalContactFields = nil
}
}
}

@IBAction func applePayShippingContactPhoneSwitchChanged(_ sender: UISwitch) {
// if sender.isOn {
// var fields = applePayAdditionalContactFields ?? []
// if !fields.contains(.phoneNumber) {
// fields.append(.phoneNumber)
// }
// applePayAdditionalContactFields = fields
// } else {
// applePayAdditionalContactFields?.removeAll(where: { $0 == .phoneNumber })
// if applePayAdditionalContactFields?.isEmpty == true {
// applePayAdditionalContactFields = nil
// }
// }
if sender.isOn {
var fields = applePayShippingAdditionalContactFields ?? []
if !fields.contains(.phoneNumber) {
fields.append(.phoneNumber)
}
applePayShippingAdditionalContactFields = fields
} else {
applePayShippingAdditionalContactFields?.removeAll(where: { $0 == .phoneNumber })
if applePayShippingAdditionalContactFields?.isEmpty == true {
applePayShippingAdditionalContactFields = nil
}
}
}

@IBAction func applePayShippingContactPostalAddressSwitchChanged(_ sender: UISwitch) {
if sender.isOn {
var fields = applePayShippingAdditionalContactFields ?? []
if !fields.contains(.postalAddress) {
fields.append(.postalAddress)
}
applePayShippingAdditionalContactFields = fields
} else {
applePayShippingAdditionalContactFields?.removeAll(where: { $0 == .postalAddress })
if applePayShippingAdditionalContactFields?.isEmpty == true {
applePayShippingAdditionalContactFields = nil
}
}
}

@IBAction func applePayCheckProvidedNetworksSwitchValueChanged(_ sender: UISwitch) {
Expand Down Expand Up @@ -646,11 +723,12 @@ class MerchantSessionAndSettingsViewController: UIViewController {

let mandateData = PrimerStripeOptions.MandateData.templateMandate(merchantName: "Primer Inc.")

// For Express Checkout Beta
// let shippingOptions = applePayCaptureShippingDetails ?
// PrimerApplePayOptions.ShippingOptions(isCaptureShippingAddressEnabled: true,
// additionalShippingContactFields: [.name, .emailAddress, .phoneNumber],
// requireShippingMethod: true) : nil
let shippingOptions = applePayCaptureShippingDetails ?
PrimerApplePayOptions.ShippingOptions(shippingContactFields: applePayShippingAdditionalContactFields,
requireShippingMethod: applePayRequireShippingMethod) : nil

let billingOptions = applePayCaptureBillingAddress ?
PrimerApplePayOptions.BillingOptions(requiredBillingContactFields: applePayBillingAdditionalContactFields) : nil

let stripePublishableKey = SecretsManager.shared.value(forKey: .stripePublishableKey)

Expand All @@ -663,7 +741,9 @@ class MerchantSessionAndSettingsViewController: UIViewController {
merchantName: merchantNameTextField.text ?? "Primer Merchant",
isCaptureBillingAddressEnabled: applePayCaptureBillingAddress,
showApplePayForUnsupportedDevice: false,
checkProvidedNetworks: applePayCheckProvidedNetworks),
checkProvidedNetworks: applePayCheckProvidedNetworks,
shippingOptions: shippingOptions,
billingOptions: billingOptions),
stripeOptions: stripePublishableKey == nil ? nil : PrimerStripeOptions(publishableKey: stripePublishableKey!, mandateData: mandateData)),
uiOptions: uiOptions,
debugOptions: PrimerDebugOptions(is3DSSanityCheckEnabled: false)
Expand All @@ -687,10 +767,12 @@ class MerchantSessionAndSettingsViewController: UIViewController {
@IBAction func primerHeadlessButtonTapped(_ sender: Any) {
customDefinedApiKey = (apiKeyTextField.text ?? "").isEmpty ? nil : apiKeyTextField.text

// let shippingOptions = applePayCaptureShippingDetails ?
// PrimerApplePayOptions.ShippingOptions(isCaptureShippingAddressEnabled: true,
// additionalShippingContactFields: [.name, .emailAddress, .phoneNumber],
// requireShippingMethod: true) : nil
let shippingOptions = applePayCaptureShippingDetails ?
PrimerApplePayOptions.ShippingOptions(shippingContactFields: applePayShippingAdditionalContactFields,
requireShippingMethod: applePayRequireShippingMethod) : nil

let billingOptions = applePayCaptureBillingAddress ?
PrimerApplePayOptions.BillingOptions(requiredBillingContactFields: applePayBillingAdditionalContactFields) : nil

let stripePublishableKey = SecretsManager.shared.value(forKey: .stripePublishableKey)

Expand All @@ -703,7 +785,9 @@ class MerchantSessionAndSettingsViewController: UIViewController {
merchantName: merchantNameTextField.text ?? "Primer Merchant",
isCaptureBillingAddressEnabled: applePayCaptureBillingAddress,
showApplePayForUnsupportedDevice: false,
checkProvidedNetworks: applePayCheckProvidedNetworks),
checkProvidedNetworks: applePayCheckProvidedNetworks,
shippingOptions: shippingOptions,
billingOptions: billingOptions),
stripeOptions: stripePublishableKey == nil ? nil : PrimerStripeOptions(publishableKey: stripePublishableKey!)),
uiOptions: nil,
debugOptions: PrimerDebugOptions(is3DSSanityCheckEnabled: false)
Expand Down
47 changes: 28 additions & 19 deletions Sources/PrimerSDK/Classes/Data Models/PrimerSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ public class PrimerApplePayOptions: Codable {
let merchantIdentifier: String
@available(*, deprecated, message: "Use Client Session API to provide merchant name value: https://primer.io/docs/payment-methods/apple-pay/direct-integration#prepare-the-client-session")
let merchantName: String?
@available(*, deprecated, message: "Use BillingOptions to configure required billing fields.")
let isCaptureBillingAddressEnabled: Bool
/// If in some cases you dont want to present ApplePay option if the device is not supporting it set this to `false`.
/// Default value is `true`.
Expand All @@ -151,6 +152,7 @@ public class PrimerApplePayOptions: Codable {
/// we introduced this flag to continue supporting the old behaviour. Default value is `true`.
let checkProvidedNetworks: Bool
let shippingOptions: ShippingOptions?
let billingOptions: BillingOptions?

public init(merchantIdentifier: String,
merchantName: String?,
Expand All @@ -160,43 +162,50 @@ public class PrimerApplePayOptions: Codable {
self.merchantIdentifier = merchantIdentifier
self.merchantName = merchantName
self.isCaptureBillingAddressEnabled = isCaptureBillingAddressEnabled
self.shippingOptions = nil
self.showApplePayForUnsupportedDevice = showApplePayForUnsupportedDevice
self.checkProvidedNetworks = checkProvidedNetworks
self.shippingOptions = nil
self.billingOptions = nil
}

private init(merchantIdentifier: String,
merchantName: String?,
isCaptureBillingAddressEnabled: Bool = false,
showApplePayForUnsupportedDevice: Bool = true,
checkProvidedNetworks: Bool = true,
shippingOptions: ShippingOptions? = nil) {
public init(merchantIdentifier: String,
merchantName: String?,
isCaptureBillingAddressEnabled: Bool = false,
showApplePayForUnsupportedDevice: Bool = true,
checkProvidedNetworks: Bool = true,
shippingOptions: ShippingOptions? = nil,
billingOptions: BillingOptions? = nil) {
self.merchantIdentifier = merchantIdentifier
self.merchantName = merchantName
self.isCaptureBillingAddressEnabled = isCaptureBillingAddressEnabled
self.shippingOptions = shippingOptions
self.showApplePayForUnsupportedDevice = showApplePayForUnsupportedDevice
self.checkProvidedNetworks = checkProvidedNetworks
self.shippingOptions = shippingOptions
self.billingOptions = billingOptions
}

internal struct ShippingOptions: Codable {
let isCaptureShippingAddressEnabled: Bool
let additionalShippingContactFields: [AdditionalShippingContactField]?
let requireShippingMethod: Bool
public struct ShippingOptions: Codable {
public let shippingContactFields: [RequiredContactField]?
public let requireShippingMethod: Bool

public init(isCaptureShippingAddressEnabled: Bool,
additionalShippingContactFields: [AdditionalShippingContactField]? = nil,
public init(shippingContactFields: [RequiredContactField]? = nil,
requireShippingMethod: Bool) {
self.isCaptureShippingAddressEnabled = isCaptureShippingAddressEnabled
self.additionalShippingContactFields = additionalShippingContactFields
self.shippingContactFields = shippingContactFields
self.requireShippingMethod = requireShippingMethod
}
}

// swiftlint:disable:next nesting
internal enum AdditionalShippingContactField: Codable {
case name, emailAddress, phoneNumber
public struct BillingOptions: Codable {
public let requiredBillingContactFields: [RequiredContactField]?

public init(requiredBillingContactFields: [RequiredContactField]? = nil) {
self.requiredBillingContactFields = requiredBillingContactFields
}
}

public enum RequiredContactField: Codable {
case name, emailAddress, phoneNumber, postalAddress
}
}

// MARK: Klarna
Expand Down
Loading
Loading