#darkknight-_code
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/1229644496417849387
📝 Have more to share? Add more details, code, screenshots, videos, etc. below.
Below are links to other discussions we've had with you in the past week in case you want to review that information. If your question is related to one of these previous discussions, please provide a comprehensive summary of the current state and what you need help with now. We help many users simultaneously, so a summary allows us to resolve your issue as soon as possible.
- darkknight-_code, 6 hours ago, 38 messages
- darkknight-_unexpected, 12 hours ago, 26 messages
here is the context - #dev-help message
Hi there, confirmHandler is handler for initPaymentSheet, and you need to handle it from front end, not backend.
yup I'm aware of that
check out this convo - #dev-help message
sorry this is the thread id - 1229521630112387193
this is thread link from #dev-help
It'll be faster if you can directly tell me what problem that you are trying to resolve
ok
I'm basically trying to save only the credit cards those were not saved already. Initially I was saving the cards like shown in the yt tutorial - https://youtu.be/ooFuMx6xsJY?list=PLy1nL-pvL2M6AMi4m_vqIz5SrTTz9L3co but it turns out to be saving duplicate cards (also I was not able to check if the card was debit or credit). So @balmy mesa told me referred me to this - https://docs.stripe.com/payments/finalize-payments-on-the-server?platform=react-native
Depending on your business needs, you may require your customers provide options up front for you to charge them for your services. This may be the case if you support trial periods, pay per usage or even subscriptions.
In this video, Charles Cruzan and Cecil Phillip get today to showcase how save payment methods for later use in your app with ...
Ok, you can check the payment method's fingerprint to determine if it's a duplicate card before saving it to your customer
Yes exactly, this the API endpoint that does the checking of fingerprint and type of the card is credit:
server/src/index.ts:
app.post('/create-confirm-intent', async (req, res) => {
try {
const { customerId, paymentMethodId } = req.body;
const stripe = new Stripe(STRIPE_SECRET_KEY, {
apiVersion: '2024-04-10',
typescript: true
});
const paymentMethod = await stripe.paymentMethods.retrieve(paymentMethodId);
if (paymentMethod.type !== "card") {
return res.status(400).json("Only credit cards can be saved");
}
if (paymentMethod.card?.funding !== "credit") {
return res.status(400).json("Only credit cards can be saved");
}
if (paymentMethod.card.fingerprint === "vxOxUrmdfHx3dY7M") {
return res.status(400).json("The card has already been saved");
}
const setupCreateParams: Stripe.SetupIntentCreateParams = {
customer: customerId,
payment_method: paymentMethod.id,
confirm: true,
mandate_data: {
customer_acceptance: {
type: "online",
online: {
ip_address: req.ip || "",
user_agent: req.get("user-agent") || "",
}
}
},
};
const { client_secret } = await stripe.setupIntents.create(setupCreateParams);
return res.status(200).json({ client_secret });
} catch (err) {
return res.status(500).json({ error: err.message });
}
});
the confirmHandler function definition:
const confirmHandler = async (
paymentMethod: PaymentMethod.Result,
_shouldSavePaymentMethod: boolean,
intentCreationCallback: (
result: IntentCreationCallbackParams,
) => void,
) => {
try {
const { client_secret, error } = await createConfirmIntent(
stripeCustomerId,
paymentMethod.id,
);
if (client_secret) {
intentCreationCallback({ clientSecret: client_secret });
} else {
intentCreationCallback({ error });
}
} catch (err) {
console.log({ err }, 'from handler');
}
};
the success scenario works fine, it only crashes when I return these errors from the server:
if (paymentMethod.type !== "card") {
return res.status(400).json("Only credit cards can be saved");
}
if (paymentMethod.card?.funding !== "credit") {
return res.status(400).json("Only credit cards can be saved");
}
if (paymentMethod.card.fingerprint === "vxOxUrmdfHx3dY7M") {
return res.status(400).json("The card has already been saved");
}
ie the app crahes when setting the error:
else {
intentCreationCallback({ error });
}
What's the error trace?
it is clean, crashes just like that. No logs on the metro, didn't get caught when I tried to catch it like this:
try {
const { client_secret, error } = await createConfirmIntent(
stripeCustomerId,
paymentMethod.id,
);
if (client_secret) {
intentCreationCallback({ clientSecret: client_secret });
} else {
intentCreationCallback({ error });
}
} catch (err) {
console.log({ err });
}
api caller code:
export const createConfirmIntent = async (
customerId: string,
paymentMethodId: string,
): Promise<ConfirmIntentData> => {
try {
const { data } = await axios.post(
`${CUSTOM_STRIPE_SERVER_URL}/create-confirm-intent`,
{ customerId, paymentMethodId },
{
headers: {
'Content-Type': 'application/json',
},
},
);
return data as ConfirmIntentData;
} catch (err) {
return { error: err } as ConfirmIntentData;
}
};
bismarck also told he/she will spin up an repo for this, but I went to sleep and the thread was closed
https://reactnative.dev/docs/native-debugging use native debugging to see if you can get the stacktrace
couldn't find any trace
How about running a adb logcat ?
it logs a lot, couldn't stop. I'll try ctrl + c when it crashes
04-16 10:21:53.275 527 952 I Process : Sending signal. PID: 4900 SIG: 9
04-16 10:21:53.275 527 952 W WindowManager: Exception thrown during dispatchAppVisibility Window{a2d13e4 u0 com.passenger/com.passenger.MainActivity EXITING}
04-16 10:21:53.275 527 952 W WindowManager: android.os.DeadObjectException
Try to search anything that matches "PaymentSheet"
04-16 10:21:53.273 527 551 V WindowManager: Finish Transition #254: created at 04-16 10:21:52.603 collect-started=0.056ms request-sent=0.319ms started=9.466ms ready=109.107ms sent=313.355ms finished=669.186ms
04-16 10:21:53.275 527 952 W WindowManager: Exception thrown during dispatchAppVisibility Window{7f4e2b3 u0 com.passenger/com.stripe.android.paymentsheet.PaymentSheetActivity EXITING}
04-16 10:21:52.963 527 555 W WindowManager: Failed to deliver inset control state change to w=Window{7f4e2b3 u0 com.passenger/com.stripe.android.paymentsheet.PaymentSheetActivity EXITING}
04-16 10:21:51.835 527 954 D CoreBackPreview: Window{7f4e2b3 u0 com.passenger/com.stripe.android.paymentsheet.PaymentSheetActivity}: Setting back callback OnBackInvokedCallbackInfo{mCallback=android.window.IOnBackInvokedCallback$Stub$Proxy@fe996ea, mPriority=0, mIsAnimationCallback=false}
Can do you a logcat filter by your package?
u mean by my app name - which is com.passenger?
Yes
04-16 10:21:51.834 4900 4900 W WindowOnBackDispatcher: sendCancelIfRunning: isInProgress=falsecallback=ImeCallback=ImeOnBackInvokedCallback@236905609 Callback=android.window.IOnBackInvokedCallback$Stub$Proxy@f135f3a
04-16 10:21:51.835 527 954 D CoreBackPreview: Window{7f4e2b3 u0 com.passenger/com.stripe.android.paymentsheet.PaymentSheetActivity}: Setting back callback OnBackInvokedCallbackInfo{mCallback=android.window.IOnBackInvokedCallback$Stub$Proxy@fe996ea, mPriority=0, mIsAnimationCallback=false}
04-16 10:21:51.835 4900 4900 I ImeTracker: com.passenger:114b4410: onCancelled at PHASE_CLIENT_APPLY_ANIMATION
04-16 10:21:51.846 405 476 D audioserver: FGS Logger Transaction failed
04-16 10:21:52.713 527 952 I WindowManager: WIN DEATH: Window{7f4e2b3 u0 com.passenger/com.stripe.android.paymentsheet.PaymentSheetActivity}
04-16 10:21:52.713 527 952 W InputManager-JNI: Input channel object '7f4e2b3 com.passenger/com.stripe.android.paymentsheet.PaymentSheetActivity (client)' was disposed without first being removed with the input manager!
04-16 10:21:52.963 527 555 W WindowManager: Failed to deliver inset control state change to w=Window{7f4e2b3 u0 com.passenger/com.stripe.android.paymentsheet.PaymentSheetActivity EXITING}
04-16 10:21:52.963 527 555 W WindowManager: android.os.DeadObjectException
04-16 10:21:52.963 527 555 W WindowManager: at android.os.BinderProxy.transactNative(Native Method)
04-16 10:21:53.275 527 952 W WindowManager: Exception thrown during dispatchAppVisibility Window{7f4e2b3 u0 com.passenger/com.stripe.android.paymentsheet.PaymentSheetActivity EXITING}
04-16 10:21:53.275 527 952 W WindowManager: android.os.DeadObjectException
04-16 10:21:53.275 527 952 W WindowManager: at android.os.BinderProxy.transactNative(Native Method)
04-16 10:21:53.275 527 952 W WindowManager: at android.os.BinderProxy.transact(BinderProxy.java:584)
couldn't find aything more
https://docs.stripe.com/payments/accept-a-payment-deferred?platform=react-native&type=payment#initialize-paymentsheet did you follow this guide to initalize paymentsheet?
yes
const initialisePaymentSheet = async () => {
const confirmHandler = async (
paymentMethod: PaymentMethod.Result,
_shouldSavePaymentMethod: boolean,
intentCreationCallback: (
result: IntentCreationCallbackParams,
) => void,
) => {
try {
const { client_secret, error } = await createConfirmIntent(
stripeCustomerId,
paymentMethod.id,
);
if (client_secret) {
intentCreationCallback({ clientSecret: client_secret });
} else {
intentCreationCallback({ error });
}
} catch (err) {
console.debug({ err });
console.log({ err });
}
};
const { error } = await initPaymentSheet({
merchantDisplayName: 'Destt',
style: 'automatic',
intentConfiguration: {
mode: {
currencyCode: 'USD',
setupFutureUsage: 'OffSession',
},
confirmHandler,
},
});
if (error) {
Alert.alert(`Error code: ${error.code}`, error.message);
} else {
setReady(true);
}
};
Can you share with me a simplified react-native project that I can run and reproduce?
If you are not comfortable sharing here, you can also send it to support https://support.stripe.com/contact/email
Find help and support for Stripe. Our support site provides answers on all types of situations, including account information, charges and refunds, and subscriptions information. Get your questions answered and find international support for Stripe.
its just that I don't have the time, only 7 days left before the YC application closes, before that I'm looking forward to setup the saving of card feature and update the product demo. Its only this stripe part that's stopping me from updating the demo
Ok, check the underlying Stripe Android SDK version that your react-native app is using
yeah working on it
make sure it's 20.38.0 and above https://github.com/stripe/stripe-android/blob/master/CHANGELOG.md#20380---2024-02-26, because that's the version that start supporting deferred intention creation flow
ig this issue is with
intentCreationCallback({ error });
maybe the type is different and it couldn't handle it
how to create this error object from server side - https://stripe.dev/stripe-react-native/api-reference/modules/PaymentSheet.html#IntentCreationError
Documentation for @stripe/stripe-react-native
It's expecting a StripeError
so how to create StripeError<'Failed'>; on server side with the custom message
https://stripe.dev/stripe-react-native/api-reference/interfaces/StripeError.html here you go, you can create a StripeError at client side and pass it to intentCreationCallback
Documentation for @stripe/stripe-react-native
now it works like a charm. this is the updated code:
// server/index.ts
app.post('/create-confirm-intent', async (req, res) => {
try {
const { customerId, paymentMethodId } = req.body;
const stripe = new Stripe(STRIPE_SECRET_KEY, {
apiVersion: '2024-04-10',
typescript: true
});
const paymentMethod = await stripe.paymentMethods.retrieve(paymentMethodId);
if (paymentMethod.type !== "card") {
return res.status(400).json({
error: {
code: "Failed",
message: "Only credit cards can be saved"
}
});
}
if (paymentMethod.card?.funding !== "credit") {
return res.status(400).json({
error: {
code: "Failed",
message: "Only credit cards can be saved"
}
});
}
if (paymentMethod.card.fingerprint === "vxOxUrmdfHx3dY7M") {
return res.status(400).json({
error: {
code: "Failed",
message: "The card has already been saved"
}
});
}
const setupCreateParams: Stripe.SetupIntentCreateParams = {
customer: customerId,
payment_method: paymentMethod.id,
return_url: "https://destt-website.vercel.app/",
confirm: true,
mandate_data: {
customer_acceptance: {
type: "online",
online: {
ip_address: req.ip || "",
user_agent: req.get("user-agent") || "",
}
}
},
};
const { client_secret } = await stripe.setupIntents.create(setupCreateParams);
return res.status(200).json({ clientSecret: client_secret });
} catch (err) {
return res.status(500).json({
error: {
code: "Failed",
message: JSON.stringify(err),
}
});
}
});
// api caller function
const createConfirmIntent = async (
customerId: string,
paymentMethodId: string,
): Promise<IntentCreationCallbackParams> => {
try {
const { data } = await axios.post(
`${CUSTOM_STRIPE_SERVER_URL}/create-confirm-intent`,
{ customerId, paymentMethodId },
);
return data;
} catch (err) {
if (err.response.data) {
return err.response.data;
}
return { error: { code: 'Failed', message: JSON.stringify(err) } };
}
};
Cool!
const confirmHandler = async (
paymentMethod: PaymentMethod.Result,
_shouldSavePaymentMethod: boolean,
intentCreationCallback: (
result: IntentCreationCallbackParams,
) => void,
) => {
const { clientSecret, error } = await createConfirmIntent(
stripeCustomerId,
paymentMethod.id,
);
if (clientSecret) {
intentCreationCallback({ clientSecret });
} else if (error) {
intentCreationCallback({
error,
});
}
};
So the problem is with intentCreationCallback({ error }); ?
however my message is not displaying it says - something went wrong instead of my custom message ie - "Only credit cards can be saved"
I see. I’m afraid that paymentsheet doesn’t support custom error message
how about localization of the text's such as Add your payment information, Card information, Card number, and etc..
Currently, localization is not supported in React Native SDK: https://github.com/stripe/stripe-react-native/issues/243
Sorry, this statement is incorrect. It should be possible to add locales in the plist file: https://github.com/stripe/stripe-react-native/issues/243#issuecomment-860081339