#darkknight-_code

1 messages · Page 1 of 1 (latest)

mortal flaxBOT
#

👋 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.

graceful umbraBOT
#

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.

opal fossil
mellow otter
#

Hi there, confirmHandler is handler for initPaymentSheet, and you need to handle it from front end, not backend.

opal fossil
#

sorry this is the thread id - 1229521630112387193

mellow otter
#

It'll be faster if you can directly tell me what problem that you are trying to resolve

opal fossil
#

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 ...

▶ Play video

Build an integration where you render the Payment Element before you create a PaymentIntent or SetupIntent, then confirm the Intent from your server.

mellow otter
#

Ok, you can check the payment method's fingerprint to determine if it's a duplicate card before saving it to your customer

opal fossil
#

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 });
}
mellow otter
#

What's the error trace?

opal fossil
# mellow otter 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

mellow otter
opal fossil
#

ok

mellow otter
#

How about running a adb logcat ?

opal fossil
#
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
mellow otter
#

Try to search anything that matches "PaymentSheet"

opal fossil
#
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}
mellow otter
#

Can do you a logcat filter by your package?

opal fossil
mellow otter
#

Yes

opal fossil
#
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

mellow otter
opal fossil
#
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);
  }
};
mellow otter
#

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

opal fossil
mellow otter
#

Ok, check the underlying Stripe Android SDK version that your react-native app is using

mellow otter
opal fossil
#

ig this issue is with

intentCreationCallback({ error });
#

maybe the type is different and it couldn't handle it

mellow otter
#

It's expecting a StripeError

opal fossil
#

so how to create StripeError<'Failed'>; on server side with the custom message

mellow otter
opal fossil
#

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) } };
  }
};
graceful umbraBOT
mellow otter
#

Cool!

opal fossil
#
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,
    });
  }
};
mellow otter
#

So the problem is with intentCreationCallback({ error }); ?

opal fossil
# mellow otter Cool!

however my message is not displaying it says - something went wrong instead of my custom message ie - "Only credit cards can be saved"

mellow otter
#

I see. I’m afraid that paymentsheet doesn’t support custom error message

opal fossil
past badge