Closed maxmijn closed 3 months ago
Hello @maxmijn, is there any other part of your code that could be causing this? This looks like interactive dismissal of the keyboard. Perhaps you're using .scrollDismissesKeyboard(.interactively)
somewhere?
Hi @yuki-stripe. We are not using that anywhere. I have confirmed that when running the SwiftUI payment sheet example, this does not occur, however, when I copy paste the example into my own SwiftUI app it does. This makes me think that it has something to do with the way SwiftUI handles views/scenes. There is no full SwiftUI example in your repository that I can test this with.
Also notice how I have to set configuration.apiClient.publishableKey = publishableKey
for it to work. Another thing I noticed is that handleURLCallback always returns false when I use a full SwiftUI implementation.
struct MainView: App {
@UIApplicationDelegateAdaptor(AppState.self) var appState
var body: some Scene {
WindowGroup {
ExampleSwiftUIPaymentSheet()
.onOpenURL { url in
let stripeHandled = StripeAPI.handleURLCallback(with: url)
print(stripeHandled)
if (stripeHandled) {
return
}
}
}
}
}
struct ExampleSwiftUIPaymentSheet: View {
@ObservedObject var model = MyBackendModel()
var body: some View {
VStack {
if let paymentSheet = model.paymentSheet {
PaymentSheet.PaymentButton(
paymentSheet: paymentSheet,
onCompletion: model.onCompletion
) {
Text("click")
}
} else {
Text("loading")
}
if let result = model.paymentResult {
Text("res")
}
}.onAppear { model.preparePaymentSheet() }
}
}
class MyBackendModel: ObservableObject {
let backendCheckoutUrl = URL(string: "https://stripe-mobile-payment-sheet.glitch.me/checkout")! // An example backend endpoint
@Published var paymentSheet: PaymentSheet?
@Published var paymentResult: PaymentSheetResult?
func preparePaymentSheet() {
// MARK: Fetch the PaymentIntent and Customer information from the backend
var request = URLRequest(url: backendCheckoutUrl)
request.httpMethod = "POST"
let task = URLSession.shared.dataTask(
with: request,
completionHandler: { (data, e, a) in
guard let data = data,
let json = try? JSONSerialization.jsonObject(with: data, options: [])
as? [String: Any],
let customerId = json["customer"] as? String,
let customerEphemeralKeySecret = json["ephemeralKey"] as? String,
let paymentIntentClientSecret = json["paymentIntent"] as? String,
let publishableKey = json["publishableKey"] as? String
else {
// Handle error
return
}
// MARK: Set your Stripe publishable key - this allows the SDK to make requests to Stripe for your account
STPAPIClient.shared.publishableKey = publishableKey
// MARK: Create a PaymentSheet instance
var configuration = PaymentSheet.Configuration()
configuration.apiClient.publishableKey = publishableKey
configuration.merchantDisplayName = "Example, Inc."
configuration.applePay = .init(
merchantId: "merchant.com.stripe.umbrella.test", // Be sure to use your own merchant ID here!
merchantCountryCode: "US"
)
configuration.customer = .init(
id: customerId, ephemeralKeySecret: customerEphemeralKeySecret)
configuration.returnURL = Config.url_scheme + "stripe-redirect"
// Set allowsDelayedPaymentMethods to true if your business can handle payment methods that complete payment after a delay, like SEPA Debit and Sofort.
configuration.allowsDelayedPaymentMethods = true
DispatchQueue.main.async {
self.paymentSheet = PaymentSheet(
paymentIntentClientSecret: paymentIntentClientSecret,
configuration: configuration)
}
})
task.resume()
}
func onCompletion(result: PaymentSheetResult) {
self.paymentResult = result
// MARK: Demo cleanup
if case .completed = result {
// A PaymentIntent can't be reused after a successful payment. Prepare a new one for the demo.
self.paymentSheet = nil
preparePaymentSheet()
}
}
}
Ah it was indeed a UIScrollView.appearance().keyboardDismissMode = .interactive
setting somewhere @yuki-stripe. Still looking into the returnUrl and configuration.apiClient.publishableKey = publishableKey
. Think it has something to do with the .shared modules not being shared correctly.
Ah great, glad to hear that issue is fixed!
On the publishable key issue - The default value of configuration.apiClient
is STPAPIClient.shared
(src), so both these things should do the same thing:
configuration.apiClient.publishableKey = publishableKey
STPAPIClient.shared.publishableKey = publishableKey
Regarding the returnURL
issue - to confirm my understanding, the . onOpenURL
closure is being called but StripeAPI.handleURLCallback(with: url)
is returning false
, is that right? Could you provide the value of the URL?
Yes exactly. This is the URL that is returned "bashappbeta://stripe-redirect?payment_intent=pi_3PLNhhI6FPXZGnXb1U3Az0hd&payment_intent_client_secret=pi_3PLNhhI6FPXZGnXb1U3Az0hd_secret_•••xNCM&redirect_status=succeeded"
Since calling STPAPIClient.shared.publishableKey = publishableKey
is not enough in my application (stripe returns and authentication error) I think the two issues might be related and that somehow the 'shared' environment is not picking up the returnUrl callback as well.
I deleted the Stripe package, re-added it and now it works!
https://drive.google.com/file/d/1egP1z9S4fBfmQ4bwqtdGAE_2cLk7qSVD/view?usp=drive_link https://github.com/stripe/stripe-ios/assets/16684728/4d0a4310-7626-4200-bdfe-e7e7e00bde8d
Summary
When trying to select a bank in the iDEAL selection, the selection sheets swipes away as you scroll, resulting in a terrible user experience. EDIT: also happens on the country selection when selecting credit card country.
I included a video of the behaviour.
Code to reproduce
iOS version
Seen on iOS 16 and 17
Installation method
Swift Package Manager
SDK version
23.27.2