Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unsure how to create a token #1059

Closed
gabriellend opened this issue Jul 27, 2022 · 10 comments
Closed

Unsure how to create a token #1059

gabriellend opened this issue Jul 27, 2022 · 10 comments

Comments

@gabriellend
Copy link

Hello!

I'm migrating over from tipsi-stripe so I think I'm mostly integrated with Stripe already but I cannot for the life of me figure out how to create a token in the way I was doing it before. I would appreciate anyone pointing me in the right direction. I scoured the docs, examples, and issues for the last two days but got very lost/confused and think I might've ended up on the wrong site? I tried running the example project to investigate further too but couldn't get it to work.

In the app I'm working on, we allow users to add cards to use later (it's a food delivery app so they shouldn't have to add their card every time, only once).
Before, I gathered the card info like this:

const params = {
        number: cardNumber,
        expMonth: Number(month),
        expYear: Number(year),
        cvc: security,
      };

and created a token (type object) like this:

const token = await stripe.createTokenWithCard(params);

I then sent the token to the backend where it talked to stripe which stored the card. The stripe-react-native function that seems to be analogous is createToken, which takes in an object where type is the only required field, and returns a token object and an error. Then what happens? How does createToken create a token that refers to a specific card with only the type (hypothetically) and not the number, cvc, etc? Is there another function that would let me take in my params and return an object with an id, or is this a much more complex problem?

I'm not using expo if that is important. I truly appreciate anyone's help. I'm still learning so some things aren't intuitive to me yet.

@charliecruzan-stripe
Copy link
Collaborator

charliecruzan-stripe commented Jul 27, 2022

createToken actually takes a couple different input options. But for cards, it relies on the CardField or CardForm components and pulls card data directly from there. This way, you aren't handling card data yourself and thus saves you a lot of time and effort around PCI compliance :) so I highly recommend going that route

Basically- render CardField and when the user fills that out, then you can create the token with createToken. See - https://github.com/stripe/stripe-react-native/blob/master/example/src/screens/CreateTokenScreen.tsx

@gabriellend
Copy link
Author

@charliecruzan-stripe
Thanks so much for the help! Does that mean I have to change my UI? I already have a component that takes in the card info, do I need to replace it with a CardField/CardForm component? And how does createToken know about CardField/CardForm? Is that just baked in?

@charliecruzan-stripe
Copy link
Collaborator

Totally baked in, you don't need to worry about it :)

And yes- you have to change your UI to incorporate CardField. Sorry for that, but it's definitely going to end up saving you lots of time in the long run since it makes your PCI approval process much easier: https://stripe.com/guides/pci-compliance. (Basically your requirements are far fewer)

@gabriellend
Copy link
Author

@charliecruzan-stripe I truly thank you for your time!

@idn-usmanmustafa
Copy link

@charliecruzan-stripe I'm not getting the token. I don't know what i'm doing wrong here. below is my code. I'm trying to integrate stripe in my react-native app.

let userDetails;

  function CardSection() {
    return (
      <>
        <CardField
          postalCodeEnabled={true}
          placeholders={{
            number: '4242 4242 4242 4242',
          }}
          cardStyle={{
            backgroundColor: '#FFFFFF',
            textColor: '#000000',
          }}
          style={{
            width: '100%',
            height: 50,
            marginVertical: 30,
          }}
          onCardChange={cardDetails => {
            userDetails = cardDetails;
            // console.log('mycardDetails=', cardDetails);
          }}
          onFocus={focusedField => {
            // console.log('focusField', focusedField);
          }}
        />
        <Button
          title={'Checkout'}
          onPress={() => {
            stripePaymentFunction();
          }}
        />
      </>
    );
  }

below function will run on press button

function stripePaymentFunction() {
    createToken({
      type: 'Card',
    })
      .then(token => {
        console.log('token= ', token);
      })
      .catch(error => {
        console.log('error= ', error);
      });
  }

return (
    <StripeProvider
      publishableKey={'publishable_key'}>
      <CardSection />
    </StripeProvider>
  );

I'm getting below error when i press checkout button:

{"error": {"code": "Failed", "declineCode": null, "localizedMessage": "Card details not complete", "message": "Card details not complete", "stripeErrorCode": null, "type": null}}

@charliecruzan-stripe
Copy link
Collaborator

Card details not complete

This means CardField isn't completely filled out. You should disable your Checkout button until the onCardChange callback fires with cardDetails.complete === true

@shashigit107
Copy link

Hello!

I'm migrating over from tipsi-stripe so I think I'm mostly integrated with Stripe already but I cannot for the life of me figure out how to create a token in the way I was doing it before. I would appreciate anyone pointing me in the right direction. I scoured the docs, examples, and issues for the last two days but got very lost/confused and think I might've ended up on the wrong site? I tried running the example project to investigate further too but couldn't get it to work.

In the app I'm working on, we allow users to add cards to use later (it's a food delivery app so they shouldn't have to add their card every time, only once). Before, I gathered the card info like this:

const params = {
        number: cardNumber,
        expMonth: Number(month),
        expYear: Number(year),
        cvc: security,
      };

and created a token (type object) like this:

const token = await stripe.createTokenWithCard(params);

I then sent the token to the backend where it talked to stripe which stored the card. The stripe-react-native function that seems to be analogous is createToken, which takes in an object where type is the only required field, and returns a token object and an error. Then what happens? How does createToken create a token that refers to a specific card with only the type (hypothetically) and not the number, cvc, etc? Is there another function that would let me take in my params and return an object with an id, or is this a much more complex problem?

I'm not using expo if that is important. I truly appreciate anyone's help. I'm still learning so some things aren't intuitive to me yet.

currently i am facing same issue you faced. can you please help , how you solved this.

@gabriellend
Copy link
Author

@shashigit107 I added the CardForm component like this:

<CardForm
    autofocus
    onFormComplete={(cardDetails) => {
      if (cardDetails.complete) {
        this.setState({ formValid: true });
      }
    }}
    placeholders={{ postalCode: 'Zip'/* Android only */ }}
    cardStyle={styles.cardStyle}
    style={styles.newCard}
/>

And then a button below that:

<Button
    onPress={this.getStripeToken}
    text="ADD CARD"
    success={success}
    loading={loading}
    disabled={!formValid}
    style={styles.addCardButton}
/>

I call createToken in getStripeToken like this:

getStripeToken = async () => {
      const {
        loading,
      } = this.state;
      const { STRIPE_KEY } = CONFIG || {};

      if (loading) return;

      this.setState({ loading: true });

      try {
        initStripe({
          publishableKey: STRIPE_KEY,
        });

        const token = await createToken({ type: 'Card' });
        if (typeof (token) === 'object' && typeof (token.token.id) === 'string' && token.token.id.length) {
          await this.postCard(token.token.id);
          return;
        }

        this.setState({ loading: false });

        Alert.alert('Error', 'Unable to add card.', [{ text: 'OK', onPress: () => { } }]);
        return;
      } catch (e) {
        Alert.alert(
          'Error',
          'Card is invalid.',
          [{ text: 'OK', onPress: () => { this.setState({ loading: false }); } }],
        );
      }
    }

CardForm and createToken are aware of each other and talk to each other under the hood so that's all you have to do! Hope that helps!

@hafizusman2
Copy link

@gabriellend @charliecruzan-stripe

not working for me, tried both approached

I tried multiple options as mentioned
No issue found in my code
I tried by using StripeProvider
also tried by using initStripe method
I am consistently facing the following error:
{"error": {"code": "Failed", "declineCode": null, "localizedMessage": "Card details not complete", "message": "Card details not complete", "stripeErrorCode": null, "type": null}}
blocked
@charliecruzan-stripe @gabriellend

here is my code

import {Box, KeyboardAvoidingView, Stack, Text, useTheme, View} from 'native-base';
import {CardField, initStripe, StripeProvider, useStripe} from '@stripe/stripe-react-native';
import {Config} from 'react-native-config';
import React, {useContext, useEffect, useState} from 'react';
import LoadingScreen from 'components/LoadingScreen';
import {AuthContext} from 'context/authContext';
import GoToNext from 'screens/GoalCreating/components/GoToNext';
import {TOAST_TYPE, ToastContext} from 'context/toastContext';
import {CREATED_ROUTES} from 'screens/GoalCreating/constants';
import {useNavigation} from '@react-navigation/native';
import {
  AmericanExpressIcon,
  DinnerClubIcon,
  DiscoverCardIcon,
  MastercardIcon,
  UnionPayIcon,
  VisaCardIcon
} from 'assets/icons/cards';
import {Platform} from 'react-native';
const {STRIPE_PUBLISHABLE_KEY} = Config;

console.log('STRIPE_PUBLISHABLE_KEY', STRIPE_PUBLISHABLE_KEY);

const PaymentToken = () => {
  const {createToken} = useStripe();
  const {me} = useContext(AuthContext);
  const {colors} = useTheme();
  const {addToast} = useContext(ToastContext);
  const {navigate} = useNavigation();

  const [loading, setLoading] = useState(false);
  const [loadingPay, setLoadingPay] = useState(false);
  const [paymentMethodData, setPaymentMethodData] = useState();
  const [cardData, setCardData] = useState();

  const initializeStripe = async () => {
    setLoading(true);
    try {
      await initStripe({
        publishableKey: 'pk_test_U9omQHnm44eocSCqocC4JUOp00tnnig3IV'
      });
      console.log('stripe initialized');
    } catch (e) {
      console.log('error in init stripe', e);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    initializeStripe();
  }, []);

  const handlePayPress = async () => {
    setLoadingPay(true);
    try {
      if (!cardData.complete) {
        console.log('card details not complete in creating stripe token', cardData);
        addToast({type: TOAST_TYPE.ERROR, text: 'Please fill the card details'});
        return;
      }

      await initializeStripe();

      console.log('cardData', cardData);
      const resToken = await createToken({
        type: 'Card'
      });
      if (resToken.error) {
        console.log('error in creating stripe token', resToken);
        addToast({type: TOAST_TYPE.ERROR, text: resToken.error.message});
        return;
      }

      addToast({type: TOAST_TYPE.SUCCESS, text: 'Card was added successfully'});
    } catch (e) {
      console.log('error in creating stripe token', e);
    } finally {
      setLoadingPay(false);
    }
  };

  const fetchCardDetails = cardDetail => {
    if (cardDetail?.complete) {
      return setCardData(cardDetail);
    }
    // setCardData(null);
  };

  const behavior = Platform.OS === 'ios' ? 'padding' : 'height';

  if (loading) return <LoadingScreen />;
  return (
    <View flex={1} px={4}>
      <KeyboardAvoidingView
        keyboardVerticalOffset={120}
        behavior={behavior}
        style={{
          flex: 1
        }}
      >
        <View flex={1}>
          <Text fontWeight={'600'}>Add payment method</Text>
          <Stack direction={'row'} justifyContent={'space-between'} mt={5}>
            <MastercardIcon />
            <VisaCardIcon />
            <UnionPayIcon />
            <AmericanExpressIcon />
            <DinnerClubIcon />
            <DiscoverCardIcon />
          </Stack>
          <StripeProvider publishableKey={STRIPE_PUBLISHABLE_KEY}>
            <CardField
              postalCodeEnabled={false}
              placeholders={{
                number: '4242 4242 4242 4242'
              }}
              cardStyle={{
                backgroundColor: '#FFFFFF',
                textColor: '#000000',
                placeholderColor: colors.secondary[200]
              }}
              style={{
                width: '100%',
                height: 50,
                marginVertical: 30
              }}
              onCardChange={cardDetails => {
                fetchCardDetails(cardDetails);
              }}
            />
          </StripeProvider>
          <View style={{flexGrow: 1}} />

          <Box mb={6}>
            <GoToNext handleSubmit={handlePayPress} isLoading={loadingPay} title={'save'} disabled={!cardData} />
          </Box>
        </View>
      </KeyboardAvoidingView>
    </View>
  );
};

export default PaymentToken;

#1594
https://github.com/stripe/stripe-react-native/issues/1594

@lukeirvin
Copy link

Is there an example of this for Swift/SwiftUI? I'm trying to create a token but I can only get the last four from STPPaymentMethod and it seems I need the full card number to create a token. cc @charliecruzan-stripe

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants