#ortega_paymentsheet-group
1 messages ยท Page 1 of 1 (latest)
๐ Welcome to your new thread!
โฒ๏ธ We'll be here soon! Typically we respond in a few minutes, but sometimes we might take a bit longer if the server is busy or if you have a particularly tricky question.
โฑ๏ธ We close idle threads, which makes them read-only. Once a thread is closed it won't be reopened, but you can always start a new thread if you have another question.
๐ This thread will always be available, even after it's closed. You can find it again using Discord's search, or you can save this link: https://discord.com/channels/841573134531821608/1278842355352469515
๐ Have more to share? Add more details, code, screenshots, videos, etc. below.
(give me a few minutes)
Thank you koopajah
To add to this, the connection with the backend and the application works perfectly. The problem is that I want a payment to be processed and the user to be able to store payment methods for future purchases.
Can you share your client-side code (the Swift part) where you initialize the PaymentSheet?
Yes
It's an extractSubview():
// MARK: Implementation New PaymentSheet
if let paymentSheet = model.paymentSheet {
PaymentSheet.PaymentButton(
paymentSheet: paymentSheet,
onCompletion: model.onPaymentCompletion
) {
Text("Buy")
}
} else {
Text("Loading...")
}
if let result = model.paymentResult {
switch result {
case .completed:
// print("Payment completed successfully!")
Text("Payment successfully!")
case .failed(let error):
Text("Payment failed: \(error.localizedDescription)")
case .canceled:
Text("Payment canceled.")
}
}
In the main view I call the function in the onAppear state:
let total = totalAmount(subtotal: subtotal, taxes: salesTax)
model.preparePaymentSheet(total: Int(total)) { success in
if success {
print("El monto total a enviar al backend es: \(total)")
} else {
print("No se pudo obtener el total a enviar al backend")
}
} onError: { error in
print("Error al preparar la hoja de pago: \(error.localizedDescription)")
}
I ask for the total purchase.
yeah none of those are configuring the PaymentSheet. I need to see where you configure it, pass the API key, client_secret, etc.
ortega_paymentsheet-group
Well, the model that communicates with the backend is:
What I did is that when a user registers in my application, it automatically creates a new customer in the Stripe dashboard. Therefore I can retrieve the customerId and also the ephemeralKey to be able to make a paymentIntent.
yeah the reason I'm asking is that 99% of the time you pass the wrong ids to that part of the code
let ephemeralKey = json["ephemeralKey"] as? String,
let publishableKey = json["publishableKey"] as? String {```
can you log those and then show me a redacted version of each? I especially want the prefix
paymentIntentClientSecret should look like pi_123123213_secret_xxxx
ephemeralKey should look like ek_123456
publishableKey should look like pk_1234
Ah also you use customerId elsewhere so it should be cus_1234
Give me a few minutes to try.
I have already made a new registration:
Dates send to backend: newpaymentintent@gmail.com, 1234567890, New Payment
CustomerId: cus_QkmyYDYLtlBjZv
Stripe customer created successfully
Received data: {"paymentIntent":{"id":"pi_3PtHOrAZJHGoU7Pr1ta9Zqrd","object":"payment_intent","amount":1400,"amount_capturable":0,"amount_details":{"tip":{}},"amount_received":0,"application":null,"application_fee_amount":null,"automatic_payment_methods":{"allow_redirects":"always","enabled":true},"canceled_at":null,"cancellation_reason":null,"capture_method":"automatic_async","client_secret":"pi_3PtHOrAZJHGoU7Pr1ta9Zqrd_secret_BYPtIte5wcDhi9CRjSxH3yyOM","confirmation_method":"automatic","created":1724972709,"currency":"usd","customer":"cus_QkmyYDYLtlBjZv","description":null,"invoice":null,"last_payment_error":null,"latest_charge":null,"livemode":false,"metadata":{},"next_action":null,"on_behalf_of":null,"payment_method":null,"payment_method_configuration_details":{"id":"pmc_1Pn3zOAZJHGoU7Prat5MbHE1","parent":null},"payment_method_options":{"card":{"installments":null,"mandate_options":null,"network":null,"request_three_d_secure":"automatic"}},"payment_method_types":["card"],"processing":null,"receipt_email":null,"review":null,"setup_future_usage":null,"shipping":null,"source":null,"statement_descriptor":null,"statement_descriptor_suffix":null,"status":"requires_payment_method","transfer_data":null,"transfer_group":null},"ephemeralKey":{"id":"ephkey_1PtHOrAZJHGoU7PrskycQhE4","object":"ephemeral_key","associated_objects":[{"id":"cus_QkmyYDYLtlBjZv","type":"customer"}],"created":1724972709,"expires":1724976309,"livemode":false,"secret":"ek_test_YWNjdF8xUGx4d3BBWkpIR29VN1ByLHg1SjVrcXp5Vk1ZY3RCdFpnbE1IQUtHWGV1eTBLU3Y_00EfC24Brk"},"customer":"cus_QkmyYDYLtlBjZv","publishableKey":"pk_test_51Pe15fHKD95FdBqCHsvpnGg53byBPt4gmaaFBuyMFqErmwy1NOAyYxyXGnzl58XLyp1SVAd7IbKnEG5Mx8WPHlpT00rLI1Qqdd"}
Error preparing payment sheet: Invalid response structure
yeah look at the PaymentIntent
it's supposed to be the secret but your returned the whole object in JSON
fix your server-side code to only return the client_secret
The logs of backend is:
PaymentIntent creado: {
id: 'pi_3PtHOrAZJHGoU7Pr1ta9Zqrd',
object: 'payment_intent',
amount: 1400,
amount_capturable: 0,
amount_details: { tip: {} },
amount_received: 0,
application: null,
application_fee_amount: null,
automatic_payment_methods: { allow_redirects: 'always', enabled: true },
canceled_at: null,
cancellation_reason: null,
capture_method: 'automatic_async',
client_secret: 'pi_3PtHOrAZJHGoU7Pr1ta9Zqrd_secret_BYPtIte5wcDhi9CRjSxH3yyOM',
confirmation_method: 'automatic',
created: 1724972709,
currency: 'usd',
customer: 'cus_QkmyYDYLtlBjZv',
description: null,
invoice: null,
last_payment_error: null,
latest_charge: null,
livemode: false,
metadata: {},
next_action: null,
on_behalf_of: null,
payment_method: null,
payment_method_configuration_details: { id: 'pmc_1Pn3zOAZJHGoU7Prat5MbHE1', parent: null },
payment_method_options: {
card: {
installments: null,
mandate_options: null,
network: null,
request_three_d_secure: 'automatic'
}
},
payment_method_types: [ 'card' ],
processing: null,
receipt_email: null,
review: null,
setup_future_usage: null,
shipping: null,
source: null,
statement_descriptor: null,
statement_descriptor_suffix: null,
status: 'requires_payment_method',
transfer_data: null,
transfer_group: null
}
EphemeralKey creado: {
id: 'ephkey_1PtHOrAZJHGoU7PrskycQhE4',
object: 'ephemeral_key',
associated_objects: [ { id: 'cus_QkmyYDYLtlBjZv', type: 'customer' } ],
created: 1724972709,
expires: 1724976309,
livemode: false,
secret: 'ek_test_YWNjdF8xUGx4d3BBWkpIR29VN1ByLHg1SjVrcXp5Vk1ZY3RCdFpnbE1IQUtHWGV1eTBLU3Y_00EfC24Brk'
}
(see what I said above)
app.post('/payment-sheet-new', async (req, res) => {
// Use an existing Customer ID if this is a returning customer.
console.log('Cuerpo de payment sheet: ', req.body);
const { amount, currency, customerId } = req.body;
if (!customerId) {
res.status(400).send("Customer ID invalid")
}
const ephemeralKey = await stripe.ephemeralKeys.create(
{customer: customerId},
{apiVersion: '2024-06-20'}
);
const paymentIntent = await stripe.paymentIntents.create({
amount: Math.round(amount*100),
currency: currency,
customer: customerId,
// In the latest version of the API, specifying the automatic_payment_methods parameter
// is optional because Stripe enables its functionality by default.
automatic_payment_methods: {
enabled: true,
},
});
console.log('PaymentIntent creado:', paymentIntent);
console.log('EphemeralKey creado:', ephemeralKey);
res.json({
paymentIntent: paymentIntent,
ephemeralKey: ephemeralKey,
customer: customerId,
publishableKey: 'if i have'
});
});
Actually, This is how the endpoint is implemented.
Are you there?
yes
I told you the issue above twice sorry
you just dumped a ton of text and didn't notice
yeah look at the PaymentIntent
it's supposed to be the secret but your returned the whole object in JSON
fix your server-side code to only return the client_secret
Change either your server-side code to only return the PaymentIntent's client_secret or change your Swift code to parse the JSON and extract client_secret. Right now you are passing the full JSON object
Sorry, only client_secret of paymentIntent's or ephemeralKey.secret too?
both
all values should be a string of the exact client_secret/secret/id. Not the whole JSON
Okey, thank you very much sr!
of course! Let me know if that doesn't fix it!