-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Async init for embedded and initial selection #4068
Merged
Merged
Changes from 14 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
3443723
Rename helper function
porter-stripe 7707959
Add async load function
porter-stripe 0d645a6
Update playground for embedded
porter-stripe 2d359fd
Implement load
porter-stripe df0e5b4
Merge branch 'master' into porter/MOBILESDK-2525
porter-stripe 00fd4f5
Fix build after merge
porter-stripe fbb8df5
Implement default selection
porter-stripe 94429a3
Update snapshot
porter-stripe bb0cf70
More snapshots
porter-stripe f7acdd6
Document future config protocol
porter-stripe cd37ab5
Fix possible retain cycle
porter-stripe 979f6ec
Create EmbeddedConfig in PlaygroundController
porter-stripe 486fcb6
Trigger Build
porter-stripe a8ca11d
Merge branch 'master' into porter/MOBILESDK-2525
porter-stripe 7db1816
Assert selection
porter-stripe File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,7 +14,7 @@ import Contacts | |
import PassKit | ||
@_spi(STP) import StripeCore | ||
@_spi(STP) import StripePayments | ||
@_spi(CustomerSessionBetaAccess) @_spi(STP) @_spi(PaymentSheetSkipConfirmation) @_spi(ExperimentalAllowsRemovalOfLastSavedPaymentMethodAPI) @_spi(ExperimentalPaymentMethodLayoutAPI) import StripePaymentSheet | ||
@_spi(CustomerSessionBetaAccess) @_spi(STP) @_spi(PaymentSheetSkipConfirmation) @_spi(ExperimentalAllowsRemovalOfLastSavedPaymentMethodAPI) @_spi(ExperimentalPaymentMethodLayoutAPI) @_spi(EmbeddedPaymentElementPrivateBeta) import StripePaymentSheet | ||
import SwiftUI | ||
import UIKit | ||
|
||
|
@@ -177,6 +177,75 @@ class PlaygroundController: ObservableObject { | |
return configuration | ||
} | ||
|
||
var embeddedConfiguration: EmbeddedPaymentElement.Configuration { | ||
var configuration = EmbeddedPaymentElement.Configuration(formSheetAction: .confirm(completion: { [weak self] result in | ||
// TODO(porter) Handle two step confirm | ||
self?.lastPaymentResult = result | ||
})) | ||
configuration.externalPaymentMethodConfiguration = externalPaymentMethodConfiguration | ||
switch settings.externalPaymentMethods { | ||
case .paypal: | ||
configuration.paymentMethodOrder = ["card", "external_paypal"] | ||
case .off, .all: // When using all EPMs, alphabetize the order by not setting `paymentMethodOrder`. | ||
break | ||
} | ||
configuration.merchantDisplayName = "Example, Inc." | ||
configuration.applePay = applePayConfiguration | ||
configuration.customer = customerConfiguration | ||
configuration.appearance = appearance | ||
if settings.userOverrideCountry != .off { | ||
configuration.userOverrideCountry = settings.userOverrideCountry.rawValue | ||
} | ||
configuration.returnURL = "payments-example://stripe-redirect" | ||
|
||
if settings.defaultBillingAddress != .off { | ||
configuration.defaultBillingDetails.name = "Jane Doe" | ||
configuration.defaultBillingDetails.address = .init( | ||
city: "San Francisco", | ||
country: "US", | ||
line1: "510 Townsend St.", | ||
postalCode: "94102", | ||
state: "California" | ||
) | ||
} | ||
switch settings.defaultBillingAddress { | ||
case .on: | ||
configuration.defaultBillingDetails.email = "[email protected]" | ||
configuration.defaultBillingDetails.phone = "+13105551234" | ||
case .randomEmail: | ||
configuration.defaultBillingDetails.email = "test-\(UUID().uuidString)@stripe.com" | ||
configuration.defaultBillingDetails.phone = "+13105551234" | ||
case .randomEmailNoPhone: | ||
configuration.defaultBillingDetails.email = "test-\(UUID().uuidString)@stripe.com" | ||
case .customEmail: | ||
configuration.defaultBillingDetails.email = settings.customEmail | ||
case .off: | ||
break | ||
} | ||
|
||
if settings.allowsDelayedPMs == .on { | ||
configuration.allowsDelayedPaymentMethods = true | ||
} | ||
|
||
if settings.shippingInfo != .off { | ||
configuration.allowsPaymentMethodsRequiringShippingAddress = true | ||
configuration.shippingDetails = { [weak self] in | ||
return self?.addressDetails | ||
} | ||
} | ||
configuration.primaryButtonLabel = settings.customCtaLabel | ||
|
||
configuration.billingDetailsCollectionConfiguration.name = .init(rawValue: settings.collectName.rawValue)! | ||
configuration.billingDetailsCollectionConfiguration.phone = .init(rawValue: settings.collectPhone.rawValue)! | ||
configuration.billingDetailsCollectionConfiguration.email = .init(rawValue: settings.collectEmail.rawValue)! | ||
configuration.billingDetailsCollectionConfiguration.address = .init(rawValue: settings.collectAddress.rawValue)! | ||
configuration.billingDetailsCollectionConfiguration.attachDefaultsToPaymentMethod = settings.attachDefaults == .on | ||
configuration.preferredNetworks = settings.preferredNetworksEnabled == .on ? [.visa, .cartesBancaires] : nil | ||
configuration.allowsRemovalOfLastSavedPaymentMethod = settings.allowsRemovalOfLastSavedPaymentMethod == .on | ||
|
||
return configuration | ||
} | ||
|
||
var addressConfiguration: AddressViewController.Configuration { | ||
var configuration = AddressViewController.Configuration(additionalFields: .init(phone: .optional), appearance: configuration.appearance) | ||
if case .onWithDefaults = settings.shippingInfo { | ||
|
@@ -599,7 +668,7 @@ extension PlaygroundController { | |
) | ||
} | ||
} else if self.settings.uiStyle == .embedded { | ||
self.embeddedPaymentElement() | ||
self.makeEmbeddedPaymentElement() | ||
self.isLoading = false | ||
self.currentlyRenderedSettings = self.settings | ||
} | ||
|
@@ -806,14 +875,14 @@ class AnalyticsLogObserver: ObservableObject { | |
@Published var analyticsLog: [[String: Any]] = [] | ||
} | ||
|
||
|
||
// MARK: Embedded helpers | ||
extension PlaygroundController: EmbeddedPlaygroundViewControllerDelegate { | ||
func embeddedPaymentElement() { | ||
embeddedPlaygroundController = EmbeddedPlaygroundViewController(settings: settings, appearance: appearance) | ||
embeddedPlaygroundController?.delegate = self | ||
extension PlaygroundController { | ||
func makeEmbeddedPaymentElement() { | ||
embeddedPlaygroundController = EmbeddedPlaygroundViewController(configuration: embeddedConfiguration, | ||
intentConfig: intentConfig, | ||
appearance: appearance) | ||
} | ||
|
||
func presentEmbedded() { | ||
guard let embeddedPlaygroundController else { return } | ||
let closeButton = UIBarButtonItem(barButtonSystemItem: .close, target: self, action: #selector(dismissEmbedded)) | ||
|
@@ -826,8 +895,4 @@ extension PlaygroundController: EmbeddedPlaygroundViewControllerDelegate { | |
@objc func dismissEmbedded() { | ||
embeddedPlaygroundController?.dismiss(animated: true, completion: nil) | ||
} | ||
|
||
func didComplete(with result: StripePaymentSheet.PaymentSheetResult) { | ||
lastPaymentResult = result | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,19 +55,60 @@ public class EmbeddedPaymentElement { | |
intentConfiguration: IntentConfiguration, | ||
configuration: Configuration | ||
) async throws -> EmbeddedPaymentElement { | ||
// TODO(https://jira.corp.stripe.com/browse/MOBILESDK-2525) | ||
let dummyView = await EmbeddedPaymentMethodsView( | ||
savedPaymentMethod: nil, | ||
appearance: .default, | ||
shouldShowApplePay: true, | ||
shouldShowLink: true | ||
// TODO(porter) MOBILESDK-2533 Make a protocol for our configurations | ||
let paymentSheetConfiguration = configuration.makePaymentSheetConfiguration() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a temporary hack right? If so, let's document it |
||
|
||
// TODO(porter) When we do analytics decide how to handle `isCustom` | ||
let analyticsHelper = PaymentSheetAnalyticsHelper(isCustom: true, configuration: paymentSheetConfiguration) | ||
AnalyticsHelper.shared.generateSessionID() | ||
|
||
let loadResult = try await PaymentSheetLoader.load(mode: .deferredIntent(intentConfiguration), | ||
configuration: paymentSheetConfiguration, | ||
analyticsHelper: analyticsHelper, | ||
integrationShape: .embedded) | ||
|
||
let paymentMethodTypes = PaymentSheet.PaymentMethodType.filteredPaymentMethodTypes(from: .deferredIntent(intentConfig: intentConfiguration), | ||
elementsSession: loadResult.elementsSession, | ||
configuration: paymentSheetConfiguration, | ||
logAvailability: true) | ||
let shouldShowApplePay = PaymentSheet.isApplePayEnabled(elementsSession: loadResult.elementsSession, configuration: paymentSheetConfiguration) | ||
let shouldShowLink = PaymentSheet.isLinkEnabled(elementsSession: loadResult.elementsSession, configuration: paymentSheetConfiguration) | ||
let savedPaymentMethodAccessoryType = await RowButton.RightAccessoryButton.getAccessoryButtonType( | ||
savedPaymentMethodsCount: loadResult.savedPaymentMethods.count, | ||
isFirstCardCoBranded: loadResult.savedPaymentMethods.first?.isCoBrandedCard ?? false, | ||
isCBCEligible: loadResult.elementsSession.isCardBrandChoiceEligible, | ||
allowsRemovalOfLastSavedPaymentMethod: configuration.allowsRemovalOfLastSavedPaymentMethod, | ||
allowsPaymentMethodRemoval: loadResult.elementsSession.allowsRemovalOfPaymentMethodsForPaymentSheet() | ||
) | ||
|
||
let initialSelection: EmbeddedPaymentMethodsView.Selection? = { | ||
// Default to the customer's default or the first saved payment method, if any | ||
let customerDefault = CustomerPaymentOption.defaultPaymentMethod(for: configuration.customer?.id) | ||
switch customerDefault { | ||
case .applePay: | ||
return .applePay | ||
case .link: | ||
return .link | ||
case .stripeId, nil: | ||
return loadResult.savedPaymentMethods.first.map { .saved(paymentMethod: $0) } | ||
} | ||
}() | ||
|
||
let embeddedPaymentMethodsView = await EmbeddedPaymentMethodsView( | ||
initialSelection: initialSelection, | ||
paymentMethodTypes: paymentMethodTypes, | ||
savedPaymentMethod: loadResult.savedPaymentMethods.first, | ||
appearance: configuration.appearance, | ||
shouldShowApplePay: shouldShowApplePay, | ||
shouldShowLink: shouldShowLink, | ||
savedPaymentMethodAccessoryType: savedPaymentMethodAccessoryType | ||
) | ||
return .init(view: dummyView, configuration: configuration) | ||
return .init(view: embeddedPaymentMethodsView, configuration: configuration) | ||
} | ||
|
||
/// The result of an `update` call | ||
@frozen public enum UpdateResult { | ||
/// The update succeded | ||
/// The update succeeded | ||
case succeeded | ||
/// The update was canceled. This is only returned when a subsequent `update` call cancels previous ones. | ||
case canceled | ||
|
@@ -180,3 +221,41 @@ extension EmbeddedPaymentElement { | |
public typealias BillingDetailsCollectionConfiguration = PaymentSheet.BillingDetailsCollectionConfiguration | ||
public typealias ExternalPaymentMethodConfiguration = PaymentSheet.ExternalPaymentMethodConfiguration | ||
} | ||
|
||
// TODO(porter) MOBILESDK-2533 Create a protocol for the commonalities between PaymentSheet.Configuration <> EmbeddedPaymentElement.Configuration | ||
extension EmbeddedPaymentElement.Configuration { | ||
func makePaymentSheetConfiguration() -> PaymentSheet.Configuration { | ||
var paymentConfig = PaymentSheet.Configuration() | ||
|
||
paymentConfig.allowsDelayedPaymentMethods = allowsDelayedPaymentMethods | ||
paymentConfig.allowsPaymentMethodsRequiringShippingAddress = allowsPaymentMethodsRequiringShippingAddress | ||
paymentConfig.apiClient = apiClient | ||
paymentConfig.applePay = applePay | ||
paymentConfig.primaryButtonColor = primaryButtonColor | ||
paymentConfig.primaryButtonLabel = primaryButtonLabel | ||
paymentConfig.style = style | ||
paymentConfig.customer = customer | ||
paymentConfig.merchantDisplayName = merchantDisplayName | ||
paymentConfig.returnURL = returnURL | ||
paymentConfig.defaultBillingDetails = defaultBillingDetails | ||
paymentConfig.savePaymentMethodOptInBehavior = savePaymentMethodOptInBehavior | ||
paymentConfig.appearance = appearance | ||
paymentConfig.shippingDetails = shippingDetails | ||
paymentConfig.preferredNetworks = preferredNetworks | ||
paymentConfig.userOverrideCountry = userOverrideCountry | ||
paymentConfig.billingDetailsCollectionConfiguration = billingDetailsCollectionConfiguration | ||
paymentConfig.removeSavedPaymentMethodMessage = removeSavedPaymentMethodMessage | ||
paymentConfig.externalPaymentMethodConfiguration = externalPaymentMethodConfiguration | ||
paymentConfig.paymentMethodOrder = paymentMethodOrder | ||
paymentConfig.allowsRemovalOfLastSavedPaymentMethod = allowsRemovalOfLastSavedPaymentMethod | ||
|
||
/* Note: | ||
There are 3 properties that differ today: | ||
hidesMandateText | ||
formSheetAction | ||
paymentMethodLayout | ||
*/ | ||
|
||
return paymentConfig | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ticket or immediate followup? We should probably make an embedded-only
formSheetAction
toggle in the playgroundThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can make a ticket, in my head I was thinking the week of Oct. 7 it would be added but I can add sooner
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll make a follow up right now and add the hide mandate API too.