#-cypher_api

1 messages ¡ Page 1 of 1 (latest)

wise stumpBOT
#

👋 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/1433802491283378227

📝 Have more to share? Add more details, code, screenshots, videos, etc. below.

rare junco
#

i am also on other pages using hosted checkout/redirecting them to stripe checkout

wise stumpBOT
rare junco
#

and this cant be right or

lament shale
#

Hi there! Looking into this now. Are there any failed payment errors associated with this?

rare junco
#

thank you very much, these are the last logs, that had some more events

#

should i send any event or id spcifically in

#

maybe this helps? that was on charge failed event ``` "calculated_statement_descriptor":
null,
"captured":
true,
"created":
1761896615
,
"currency":
"usd",
"customer":
"cus_TKrntH5IGF6Lyl"
,
"description":
"Subscription creation",
"destination":
null,
"dispute":
null,
"disputed":
false,
"failure_balance_transaction":
null,
"failure_code":
"payment_intent_payment_attempt_expired",
"failure_message":
"The latest payment attempt of this PaymentIntent has expired. You can provide payment_method_data or a new PaymentMethod to attempt to fulfill this PaymentIntent again.",
"fraud_details": {},
"livemode":
true,
"metadata": {},
"on_behalf_of":
null,
"order":
null,
"outcome": {
"advice_code":

lament shale
#

Yes, this helps! Can you paste the payment intent ID here (the one that starts with pi_)

rare junco
#

yes "object": { "id": "pi_3SOC3dD5euO8UD6n1g3Pa7az" , "object": "payment_intent", "amount": 356400, "amount_capturable": 0, "amount_details": { "tip": {},

lament shale
#

Sorry, juggling a couple of threads, looking into what would cause this expiration error

rare junco
#

all good appreciate it

#

if i can give you any more info / code snippet that would help please just say

lament shale
#

For additional context, is this the only Checkout Session where this is happening, or do you see it with others? Also, can you share your code you're using to make the Checkout Session?

rare junco
#

to be honest, im not getting payments through fro a few days now, neither on hosted nor custom ui, thats what i use


#

// Backend: Create Checkout Session (src/app/api/create-checkout-session/route.ts)

export async function POST(request: NextRequest) {
const session = await auth.api.getSession({ headers: request.headers });
if (!session?.user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

const body = await request.json();
const { tier, isYearly = true, uiMode = 'hosted' } = body;

const priceId = getStripePriceId(tier, isYearly);
const origin = request.headers.get('origin') || 'https://percify.io';
const user = session.user;

// Get or validate existing Stripe customer
let stripeCustomerId: string | null = null;
const userResult = await db.query(
'SELECT stripe_customer_id FROM "user" WHERE id = $1',
[user.id]
);

if (userResult.rows.length > 0 && userResult.rows[0].stripe_customer_id) {
try {
await stripe.customers.retrieve(userResult.rows[0].stripe_customer_id);
stripeCustomerId = userResult.rows[0].stripe_customer_id;
} catch (error) {
stripeCustomerId = null;
}
}

#

// Configure checkout session
const sessionConfig: Record<string, unknown> = {
mode: 'subscription',
line_items: [{ price: priceId, quantity: 1 }],
metadata: {
userId: user.id,
tier: tier,
isYearly: isYearly.toString(),
},
subscription_data: {
metadata: {
userId: user.id,
tier: tier,
isYearly: isYearly.toString(),
},
},
allow_promotion_codes: true,
automatic_tax: { enabled: true },
tax_id_collection: { enabled: true },
payment_method_collection: 'always',
};

// Customer configuration
if (stripeCustomerId) {
sessionConfig.customer = stripeCustomerId;
sessionConfig.customer_update = {
name: 'auto',
address: 'auto',
};
} else {
sessionConfig.customer_email = user.email;
}

// UI mode configuration
if (uiMode === 'custom') {
// Custom UI with embedded checkout
sessionConfig.ui_mode = 'custom';
sessionConfig.return_url = ${origin}/dashboard?checkout_return=true&session_id={CHECKOUT_SESSION_ID};
sessionConfig.cancel_url = ${origin}/dashboard?checkout_cancel=true&session_id={CHECKOUT_SESSION_ID};
} else {
// Hosted checkout
sessionConfig.success_url = ${origin}/dashboard?upgrade=success&tier=${tier};
sessionConfig.cancel_url = ${origin}/pricing;
sessionConfig.billing_address_collection = 'required';
}

// Create session with idempotency
const customerStrategy = stripeCustomerId ? 'existing' : 'new';
const idempotencyKey = cs_${user.id}_${tier}_${isYearly}_${customerStrategy}_${Math.floor(Date.now() / 60000)};

const checkoutSession = await stripe.checkout.sessions.create(
sessionConfig,
{ idempotencyKey }
);

#

if (uiMode === 'custom') {
return NextResponse.json({
clientSecret: checkoutSession.client_secret,
sessionId: checkoutSession.id,
});
} else {
return NextResponse.json({
url: checkoutSession.url,
sessionId: checkoutSession.id,
});
}
}

#

--------------------- and for frontend

#

// Frontend: Payment Confirmation (src/app/dashboard/components/payments/payment-form.tsx)

import { useCheckout } from '@stripe/react-stripe-js/checkout';
import { CheckoutProvider } from '@stripe/react-stripe-js/checkout';

export default function PaymentForm({ onPaymentError, setIsProcessing }) {
const checkoutState = useCheckout();

const handleSubmit = async () => {
if (checkoutState.type !== 'success') {
onPaymentError('Payment system not ready. Please wait...');
return;
}

const { checkout } = checkoutState;
setIsProcessing(true);

try {
  // Validate total amount (Stripe requirement)
  const totalAmount = checkout.total?.total?.amount;
  if (!totalAmount) {
    onPaymentError('Unable to retrieve payment amount. Please refresh.');
    return;
  }

  // Check all required data is present
  if (!checkout.canConfirm) {
    setIsProcessing(false);
    onPaymentError('Please complete all required fields before continuing.');
    return;
  }

  // Confirm payment with automatic redirect handling
  const result = await checkout.confirm({
    redirect: 'always'  // Handles 3DS, Amazon Pay, etc.
  });

  if (result && 'error' in result && result.error) {
    setIsProcessing(false);
    onPaymentError(result.error.message || 'Payment failed. Please try again.');
    return;
  }

  // Success - Stripe will redirect to return_url
  console.log('Payment confirmed, redirecting...');
} catch (err) {
  setIsProcessing(false);
  onPaymentError('An unexpected error occurred. Please try again.');
}

};

#

return (
<form onSubmit={(e) => { e.preventDefault(); handleSubmit(); }}>
<PaymentElement
options={{
fields: {
billingDetails: {
email: 'never',
name: 'auto',
address: {
country: 'auto',
postalCode: 'auto',
city: 'auto',
line1: 'auto',
line2: 'auto',
state: 'auto',
},
},
},
}}
/>
</form>
);
}

#

---------- and return handler

#

// Return URL Handler (src/app/dashboard/DashboardClient.tsx)

useEffect(() => {
const params = new URLSearchParams(window.location.search);
const checkoutReturn = params.get('checkout_return');
const sessionId = params.get('session_id');

if (checkoutReturn === 'true' && sessionId) {
window.history.replaceState({}, '', '/dashboard');

// Verify payment status
const verifyPayment = async () => {
  const response = await fetch(`/api/verify-checkout-session?session_id=${sessionId}`);
  const result = await response.json();
  
  if (result.status === 'complete' && result.paymentStatus === 'paid') {
    // Payment succeeded
    console.log('Payment confirmed');
  } else if (result.status === 'open' || result.paymentStatus === 'unpaid') {
    // Payment failed - create NEW session
    window.dispatchEvent(new CustomEvent('createNewCheckoutSession', {
      detail: { reason: 'payment_failed_or_cancelled' }
    }));
  }
};

verifyPayment();

}
}, []);

#

Using ui_mode: 'custom' with embedded CheckoutProvider
return_url and cancel_url are both configured
Using checkout.confirm({ redirect: 'always' }) to handle authentication
Checking checkout.canConfirm before confirmation
Not specifying payment_method_types (auto-enabled based on Dashboard settings)
Issue: Sessions timing out or PaymentIntents getting stuck in requires_action state ```
lament shale
#

Looking over the code, nothing stands out right away but still investigating, thanks for your patience. Do you have an example of a Payment Intent that failed from a 3DS issue (you mentioned there were different types of failures)?

rare junco
#

this is like requires_action

#

{
"object": {
"id":
"pi_3SOC3dD5euO8UD6n1g3Pa7az"
,
"object":
"payment_intent",
"amount":
356400,
"amount_capturable":
0,
"amount_details": {
"tip": {},
},
"amount_received":
0,
"application":
null,
"application_fee_amount":
null,
"automatic_payment_methods":
null,
"canceled_at":
null,
"cancellation_reason":
null,
"capture_method":
"automatic",
"client_secret":
"pi_3SOC3dD5euO8UD6n1g3Pa7az_secret_nQ5Bg23rclihp6zPElYe2aSid",
"confirmation_method":
"automatic",
"created":
1761893013
,
"currency":
"usd",
"customer":
"cus_TKrntH5IGF6Lyl"
,
"description":
"Subscription creation",
"excluded_payment_method_types":
null,
"last_payment_error":
null,
"latest_charge":
null,
"livemode":
true,
"metadata": {},
"next_action": {
"redirect_to_url": {
"return_url":
"https://checkout.stripe.com/c/pay/cs_live_b1fAT57bvsIwecSvNNduQOpjr6ea1sy6AAcv4MoOo5BBsv969mwsa8GlrF?returned_from_redirect=true&ui_mode=custom&return_url=https%3A%2F%2Fpercify.io%2Fdashboard%3Fcheckout_return%3Dtrue%26session_id%3Dcs_live_b1fAT57bvsIwecSvNNduQOpjr6ea1sy6AAcv4MoOo5BBsv969mwsa8GlrF#fid1d2BpamRhQ2prcSc%2FJ0xrcWB3JyknZ2p3YWB3VnF8aWAnPydhYGNkcGlxJykndnBndmZ3bHVxbGprUGtsdHBga2B2dkBrZGdpYGEnP2NkaXZgKSdkdWxOYHwnPyd1blppbHNgWjA0VkZTcDVBMGBwSj1QQTNrU3VAZFxgY3dRf0lzazFOM0xLPEEyUjNSQFxMYEBtSzJkTXJrSk5CQTFrVUxyUmROYndTQF9WRmlMfDxpQ0dtYF1tMVEyYkpwNTVuPUlMYUhgdycpJ2N3amhWYHdzYHcnP3F3cGApJ2dkZm5id2pwa2FGamlqdyc%2FJyY1NTU1NTUnKSdpZHxqcHFRfHVgJz8naHBpcWxabHFgaCcpJ2BrZGdpYFVpZGZgbWppYWB3dic%2FcXdwYHgl",
"url":
"https://pm-redirects.stripe.com/authorize/acct_1SCVu0D5euO8UD6n/pa_nonce_TKrnjA4lQYWD2plCikPDWCXTxIZ4lYZ",
},
"type":
"redirect_to_url",
},
"on_behalf_of":
null,
"payment_details": {
"customer_reference":
null,
"order_reference":
"cs_live_b1fAT57bvsIwecSvN",
},
"payment_method":
"pm_1SOC3bD5euO8UD6nlfWXt9et"
,

#

"payment_method_configuration_details":
null,
"payment_method_options": {
"amazon_pay": {
"express_checkout_element_session_id":
null,
"setup_future_usage":
"off_session",
},
},
"payment_method_types": [
"amazon_pay",
],
"processing":
null,
"receipt_email":
"edixreyz@gmail.com",
"review":
null,
"setup_future_usage":
null,
"shipping":
null,
"source":
null,
"statement_descriptor":
null,
"statement_descriptor_suffix":
null,
"status":
"requires_action",
"transfer_data":
null,
"transfer_group":
null,
},
"previous_attributes":
null,

#

----and after that its like an hour that goes by till payment intent failed

#

and charge failed

#

and for the pi failed, its ```

{
"object": {
"id":
"pi_3SOC3dD5euO8UD6n1g3Pa7az"
,
"object":
"payment_intent",
"amount":
356400,
"amount_capturable":
0,
"amount_details": {
"tip": {},
},
"amount_received":
0,
"application":
null,
"application_fee_amount":
null,
"automatic_payment_methods":
null,
"canceled_at":
null,
"cancellation_reason":
null,
"capture_method":
"automatic",
"client_secret":
"pi_3SOC3dD5euO8UD6n1g3Pa7az_secret_nQ5Bg23rclihp6zPElYe2aSid",
"confirmation_method":
"automatic",
"created":
1761893013
,
"currency":
"usd",
"customer":
"cus_TKrntH5IGF6Lyl"
,
"description":
"Subscription creation",
"excluded_payment_method_types":
null,
"last_payment_error": {
"code":
"payment_intent_payment_attempt_failed",
"decline_code":
"payment_intent_payment_attempt_expired",
"doc_url":
"https://stripe.com/docs/error-codes/payment-intent-payment-attempt-failed",
"message":
"The latest payment attempt of this PaymentIntent has expired. You can provide payment_method_data or a new PaymentMethod to attempt to fulfill this PaymentIntent again.",
"payment_method": {
"id":
"pm_1SOC3bD5euO8UD6nlfWXt9et"
,
"object":
"payment_method",
"allow_redisplay":
"limited",
"amazon_pay": {},
"billing_details": {
"address": {
"city":
null,
"country":
"UG",
"line1":
null,
"line2":
null,
"postal_code":
null,
"state":
null,
},
"email":
"edixreyz@gmail.com",
"name":
null,
"phone":
null,
"tax_id":
null,
},
"created":
1761893011
,
"customer":
null,
"livemode":
true,
"metadata": {},
"type":
"amazon_pay",
},
"type":
"card_error",
},
"latest_charge":
"py_3SOC3dD5euO8UD6n1Hui1deI"
,

#

"livemode": 
true,
"metadata": {},
"next_action": 
null,
"on_behalf_of": 
null,
"payment_details": {
"customer_reference": 
null,
"order_reference": 
"cs_live_b1fAT57bvsIwecSvN",
},
"payment_method": 
null,
"payment_method_configuration_details": 
null,
"payment_method_options": {
"amazon_pay": {
"express_checkout_element_session_id": 
null,
"setup_future_usage": 
"off_session",
},
},
"payment_method_types": [
"amazon_pay",
],
"processing": 
null,
"receipt_email": 
"edixreyz@gmail.com",
"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,
},
"previous_attributes": 
null,
}
#

i dont know if this might help

#

alright i removed cancel url from custom ui mode now

lament shale
#

Right, I was going to mention that I saw those errors. Still, it sounds like the issue you were describing was happening before this, correct?

rare junco
#

basically, it was either no event happend after "action_required"

#

or the session just immedialtey expired, without even any events before

#

it used to work with payments intents api, but i switched because of subscriptions i think

wise stumpBOT
rare junco
#

like still so many session expired, but there werent that many payment intent events, or customer created events

copper ether
#

Hi, taking over as my teammate needs to step away. Let me catch up.

rare junco
#

hey thank you very much

copper ether
#

This checkout session, cs_live_b1fAT57bvsIwecSvNNduQOpjr6ea1sy6AAcv4MoOo5BBsv969mwsa8GlrF still has not expired yet.

#

Can you share a checkout session id that expired please?

#

It's likely the authentication failed and we waited for 24 hours which expires then

rare junco
#

that was the last checkout.session.expired id "cs_live_b173fD1pVBkE754WvBNW7JibBtQsJmsdIAHIbE6MWbS8kfGLRfv7Is6dXt"

#

event id "evt_1SOJVhD5euO8UD6noPM6H48O"

copper ether
#

Looking

#

It looks like your integration is working as expected but your end users are not authenticated.

rare junco
#

meaning like it works, they are just not authenticated during the checkout session, or when they comeback from 3ds or amazon pay/paypal

copper ether
#

That is correct

rare junco
#

so what should i do next - should i just remove paypal and amazon pay, maybe that fixes it

copper ether
#

That is up to you, do you know why your customers are not going through with the payment? Like, are they coming back and choosing another payment method?

rare junco
#

i think they might be changing billing cycles, but it should be handled, that when they cancel 3ds, come back, choose a new billing cycle, that it doesnt pick up the old line item

#

like that was the last payment that went through, after that i just get failed

copper ether
#

What does 'but it should be handled' mean? They are existing out of the checkout or Cancelling them to go through the flow again.

#

You're creating the CheckoutSession each time

rare junco
#

we are doing "State resets on billing cycle change, Cache is cleared,
New idempotency key generated (includes billing cycle), New Stripe session created with correct line item, Old session cannot be reused"

copper ether
#

I do not follow, are you able to add more context please?

rare junco
#

im not wuite sure, because the last time the checkout worked successfuly, was , after that, im at idk 300 failed transactions due to the checkoutSesstions with subscriptions and custom ui, payment element

#

but youre saying everything looks fine, and its just a user not completing 3ds or paypal/amazon pay auth?

copper ether
#

Did you offer the same payment methods? If you offerred different payment methods, that is plausible. If not, you'd want to perform some analysis on your end.

rare junco
#

yes the successful payments

#

show only allowed payment method "card"

#

i wanted to add paypal, amazon pay

#

after that everything went south

#

now i reverted back to only 'card'

copper ether
#

Yeah, so your customers might not be used to how these payment methods work and how they need to authenticate.

#

This is how these payment methods work

rare junco
#

alright i will try it with only " sessionConfig.payment_method_types = ['card'];" again

#

hope that solves it

copper ether
#

I think the real solution is sending clear communications to your customers so they can choose between various payment methods. If offerring card works, sure.

rare junco
#

but they choose the payment methods themselves

#

in the stripe payment form element

#

tabs style

#

like im not hiding the payment methods, they initiate the choice themselves

copper ether
#

Yes, and they need to understand the authentication piece is important