#squidonomics
1 messages · Page 1 of 1 (latest)
Hello
Hmmm not really a good reason I can think of
What does "not show up" entail?
Like you are pressing the button to present the Payment Sheet but nothing happens?
Hrmmm have you hooked up the device to your Mac and run the debugger for it?
And you don't see anything in those logs?
no, the create payment intent, ephemeral key and customer are created on stripe servers
Have you added some logs to ensure all the proper config and client secret is present for Payment Sheet?
and it shows up in stripe logs as well
Okay so you are successfully hitting your server, that's good.
Is the server responding to your app successfully?
yes, the returned values all appear when debugging variables
and they match the values returned in stripe logs for that request
on both the emulator and the real phone?
No just the real phone
Yes, one second
Button(action: { model.preparePaymentSheet() }){ Text("Pay") .frame(width: 250, height: 50, alignment: .center) .background(Color(red: 15/255.0, green: 35/255.0, blue: 101/255)) .foregroundColor(Color.white) } VStack{ if let paymentSheet = model.paymentSheet { PaymentSheet.PaymentButton( paymentSheet: paymentSheet, onCompletion: model.onCompletion ) { Text("Buy") } } else { Text("Loading…") } if let result = model.paymentResult { switch result { case .completed: Text("Payment complete") case .failed(let error): Text("Payment failed: \(error.localizedDescription)") case .canceled: Text("Payment canceled.") } } } }.onAppear { model.preparePaymentSheet() }
pressing the button and onAppear are used to test the ability to get the sheet to appear
neither of which work for some reason
Thanks looking
While I look can you also take a quick screen cap of the emulator actually with the same flow
That would be helpful
So you set a breakpoint in model.paymentSheet, correct?
I set my breakpoints inside the backend function. I can test them on model.paymentSheet too
emulator
Oh yeah this isn't a backend issue I don't think
Wait wait wait
You are hitting a different button on your emulator
You are hitting "Buy" instead of "Pay"
buy auto loads because of the onAppear function
when it doesn't load I use the button "Pay" to preparePaymentIntent
Oooooh k that is different lol
So you aren't getting your "Buy" button to show, it isn't that PaymentSheet isn't presenting
yes, when buy shows the sheet presents but for some reason the real phone just doesn't pass the if let condition
Can you recreate the state on your emulator where you have "loading..." and hit "Pay"?
And to clarify, you said above, you already added logs to your iOS app here for the client secret?
I can't which is crazy. I've tried removing on appear and removing weak self but nothing
yes, debugging client secret returns the value stripe logs says they've sent
What is the code for the "Pay" button?
Like you are using that to hit your server, correct?
yes
Can you share that code?
Button(action: { model.preparePaymentSheet() }){ Text("Pay") .frame(width: 250, height: 50, alignment: .center) .background(Color(red: 15/255.0, green: 35/255.0, blue: 101/255)) .foregroundColor(Color.white) }
nothing fancy just preparing the sheet
And the full task code
Also just so you know, you can use three ` to share code more cleanly
Like this
Text("Pay")
.frame(width: 250, height: 50, alignment: .center)
.background(Color(red: 15/255.0, green: 35/255.0, blue: 101/255))
.foregroundColor(Color.white)
}```
👍
No probs. Can you dump all the relevant code to hitting your server here. Yep that function
// 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: { [weak self] (data, response, error) 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,
let self = self else {
return
}
STPAPIClient.shared.publishableKey = publishableKey
// MARK: Create a PaymentSheet instance
var configuration = PaymentSheet.Configuration()
configuration.merchantDisplayName = "Test Company"
configuration.customer = .init(id: customerId, ephemeralKeySecret: customerEphemeralKeySecret)
configuration.allowsDelayedPaymentMethods = true
DispatchQueue.main.async {
self.paymentSheet = PaymentSheet(paymentIntentClientSecret: paymentIntentClientSecret, configuration: configuration)
}
})
task.resume()
}
func onCompletion(result: PaymentSheetResult) {
self.paymentResult = result
if case .completed = result {
self.paymentSheet = nil
preparePaymentSheet()
}
}
}```
K let's add a bunch of logs just to double check. Can you add a print like:
print("ERROR HERE!")
return
}
And then let's log out your key:
print("pub key: ", publishableKey)```
And ``` DispatchQueue.main.async {
print("clientsecret: ", paymentIntentClientSecret)
self.paymentSheet = PaymentSheet(paymentIntentClientSecret: paymentIntentClientSecret, configuration: configuration)```
Then run a test and let's see what your XCode console shows
the error occurs on self
Okay that helps
I can show you the breakpoint print content if you need it
Oops
I forgot that should actually return an error!
Let's just print that error
print("ERROR HERE! ", error)
Err wait no
The error would before
on task
error will print nil there
Yeah
oh on task okay
Like we want the error from let task = URLSession.shared.dataTask(with: request, completionHandler: { [weak self] (data, response, error)
So I think you need a try/catch here
my thought was put a do right above the task then catch at the end?
but it looks like that won't be it
I'm not that familiar with iOS code tbh... can you just print the error in the guard?
Hmm no I really think it should be in the else block
Like in my sample I have: else { let message = error?.localizedDescription ?? "Failed to decode response from server." self?.displayAlert(title: "Error loading page", message: message) return }
So if you are getting null there then the issue is indeed how you are receiving the data from the server.
Can you show me your server code?
How are you sending the data back?
Sounds good. It likely will just show you "Failed to decode response from server."
yep
What device are you testing on?
app.post('/payment-sheet', async (req, res) => {
// Use an existing Customer ID if this is a returning customer.
const customer = await stripe.customers.create();
const ephemeralKey = await stripe.ephemeralKeys.create(
{customer: customer.id},
{apiVersion: '2022-08-01'}
);
const paymentIntent = await stripe.paymentIntents.create({
amount: 1099,
currency: 'eur',
customer: customer.id,
automatic_payment_methods: {
enabled: true,
},
});
res.json({
paymentIntent: paymentIntent.client_secret,
ephemeralKey: ephemeralKey.secret,
customer: customer.id,
publishableKey: 'key'
});
}); ```
I've tested on an iPhone 11 and iPhone 13
Feel like we need more detail to pinpoint. Can you try enabling CFNetwork diagnostic logging: https://developer.apple.com/documentation/network/debugging_https_problems_with_cfnetwork_diagnostic_logging
Ah actually
Maybe we should just try printing the response
That would be first step
Didn't realize we hadn't done that yet
In your else block let's print("response: ", response)
Then dump that payload
Connection = (
"keep-alive"
);
"Content-Length" = (
347
);
"Content-Type" = (
"application/json; charset=utf-8"
);
Date = (
"Fri, 14 Oct 2022 19:15:08 GMT"
);
Etag = (
"W/\"15b-FLxslPNdhSsyZtKI5uzNwix0FTY\""
);
"Keep-Alive" = (
"timeout=5"
);
"X-Powered-By" = (
Express
);
} })
Wat
That's super weird
Are you sure you are hitting the right endpoint?
I mean you said you are seeing the PI created in your Dashboard....
So it seems like you are....
yes I can send the stripe logs
I get all three created: create payment intent, create emphem key and create customer
If you print data in your else block to you get anything?
Oh snap
You are using HTTP
Instead of HTTPS. That might be the issue.
I can test https real quick
Yeah give that a try
I totally missed that the above does have Content-Length = 347
So the body is there
So it isn't HTTP that is the issue
Oh yeah because we didn't JSONSerialize yet
so do ``` let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String : Any],
And then print(json)
I'm so confused. It seems like everything is there and this is all working fine
So maybe I was wrong and this isn't the issue at all
Oh right because we are in the else block and then returning
So this does have to be the issue still.... self = self is not working
Okay so let's do the above and see what the response body looks like I guess
try and print the json?
putting print right after won't work because condition must be bool
Down in else?
json is out of scope
Ah okay that's what I wasn't sure about before
Let me go grab a colleague more familiar with iOS
We might just be missing something too
While I do that, instead of trying to log out json directly, can you just do JSONSerialization in the else block?
Oh wait no
Cause then data is out of scope too right
When you added your breakpoint you saw your customer/clientsecret/ephkey variables there, correct?
You added po in console to log those?
Oh yeah apparently you can just do po json
▿ 4 elements
▿ 0 : 2 elements
- key : "paymentIntent"
- value : pi_3LstroAwKsZ0hizW2xDmsBf8_secret_XDVYn1BAafCSCGeDKONuY3sdC
▿ 1 : 2 elements
- key : "ephemeralKey"
- value : ek_test_YWNjdF8xS2pxMUtBd0tzWjBoaXpXLHZsZDZ3T2NxYkVJVXBoaGlacVpVdDJTMTdPUW9zMGI_00iNmkmafK
▿ 2 : 2 elements
- key : "customer"
- value : cus_Mc88Np6026GWiP
▿ 3 : 2 elements
- key : "publishableKey"
- value : pk_test_51Kjq1KAwKsZ0hizWeuQL1sT3L1u9ZcsITh961jyj7uly6A6aYx2DSduWrZ6kiComgch3q4qsNAF4q0a80rmYCEhl00XBEFRLIx```
that's the breakpoint value of json
Thanks. Can you also put a breakpoint after self and let's look at what that gets assigned?
nil
sorry that was breakpoint on nil
let me try it again
(relsto.MyNewerBackendModel?) self = 0x0000000280dad980 {
backendCheckoutUrl = {}
_paymentSheet = {}
_paymentResult = {}
seconds = 7.7182398584174996E-313
}```
Okay yeah so that seems wrong. I think paymentSheet shouldn't be a null object here...
I can show you what is returned on the emulator
Yeah that is a good idea
Ah wait
self just might be handling the controller here
Still worth comparing
But not sure that paymentSheet won't be empty here
I just can't fathom why we are going into this else block
neither can I
I havent found this issue anywhere else on the internet
I've checked network traffic, memory usage like everything I can think of
Okay my colleague says that the self object should have some reference to your view controller so that likely is why then
Let's see what the emulator shows
Also have you rebuilt on your actual device at any point?
Probably worth ruling out the turn off/turn on approach in case somehow the device just got in a weird state on build
I'll reset my real device right now too
just cleaned the build folder to make sure this is how it runs
sorry this rebuild is taking forever
No worries
okay
emulator prints this
<MyNewerBackendModel: 0x6000018dc6e0>```
actually just hit an interesting bug
after hard reseting my real device
The build service has encountered an internal inconsistency error: unexpected incomplete target: <ConfiguredTarget target: FirebaseCoreInternal:8699adb1dd336b26511df848a716bd42020791fd2e7b7ddc8fb2658339c42e16> (started: 29, completed: 0)
it looks like its a total build error
I am out of build rows so I'm gonna clear it and try again
👍
alright it's completely rebuilding on the real iphone 11 now
still nothing unfortunately
cleaned all folders and rebuilt on both the xcode project and on the deivce
Yeah I don't know... try removing the return in your else block and see if it runs?
still nothin
well that sucks
I guess I'll keep messing around with it. Thanks for your help
One sec
Okay let's try one more thing
Instead of having two buttons here, let's just use the "Pay" button to present the sheet.
Can you hook your code up to that button and not mess with the "Loading/Buy" button at all?
Sounds good
I can't seem to put it into a button because it is its own button already
the text and how it's designed are the only things I can change
What do you mean by that exactly?
Payment Sheet really isn't designed for two buttons to begin with
paymentSheet: paymentSheet,
onCompletion: model.onCompletion
) ```
Ah maybe I mixed up the buttons I mentioned above. I mean just unhitch your "Pay" button.
I don't think I can force a payment button that is already designed into another button. I can get rid of the pay button and use only on appear
nothing on that either
I can't fathom why everything works until the response for a real device but not the emulator
You said you tested on two different real devices, yes?
What iOS SDK version are you on?
15.5
And Stripe iOS SDK?
oh for stripe my bad
Nah that is helpful too
The only real path forward at this point that I can think of is us tryin to repro this from our end
If you look at your Pods folder in the General tab and click on Stripe-Stripe for targets
Should show you your SDK version
22.8.1
Thanks
Going to try and repro with my basic sample. If you want us to dig further you could email our Support team and attach your project that we can use to attempt to repro/debug.
But yeah, I'm basically out of ideas at the moment
(https://support.stripe.com/contact/login) for contacting us over email