Tipsi-stripe: Can token be used to link debit card to Stripe-Connect account?

Created on 22 Dec 2017  Â·  23Comments  Â·  Source: tipsi/tipsi-stripe

Hey,

First of all thanks for creating this package!

My question is: I am currently implementing Stripe Connect in my RNapp. To link a users debit card to a stripe account (so that the user can receive payments from other users), I need to pass a token that is created with Stripe.js.

I thought the token returned by stripe.createTokenWithCard() would return said token that I can then send to the backend to make the actual connection. However, I get an error saying that the token I am providing is not from Stripe.js.

Is the token returned by stripe.createTokenWithCard() some other (maybe own created) token?

Thanks in advance!

Most helpful comment

@wmonecke @larryranches I have the same question

All 23 comments

@wmonecke I think you should not use stripe.js. Our module is a wrapper around native modules for iOS and Android.

@isnifer So it is NOT returning a Stripe.js token?

@wmonecke we do NOT use Stripe.js. We are using https://github.com/stripe/stripe-android and https://github.com/stripe/stripe-ios under the hood

@isnifer Thanks for the quick answer! I guess I will have to just send the card details to the backend and validate them over there.

@wmonecke no problem, you can do it with tipsi-stripe without Stripe.js

@isnifer Sorry for my noobish questions, I have never worked with tokens before.

In the Stripe docs it asks for

stripe.accounts.createExternalAccount( accountID, { external_account: tokenID // use tipsi-stripe for this }, function(err, bank_account) { // asynchronously called } );

The tokenID that is returned from lets say .createTokenWithBankAccount(params) contains the prop tokenId.

This is the one I am supposed to pass to the backend and it somehow contains all the information of the bank account in it (I guess encrypted)??

This is the one I am supposed to pass to the backend and it somehow contains all the information of the bank account in it (I guess encrypted)??

You may think about it in such way: token is the id of a row with your information on stripe server, but you cannot retrieve this information directly, but if you know secret key and token - you can ask stripe to make payment using this stored information.

@cybergrind Ahh I see! So to create a token Stripe needs to make a request first to its own server to provide us with the tokenId? I was always thinking the token was created locally.

All this is done to prevent errors with private info storage. When you don't store any private information on your server - you cannot leak it. You save confidential information in Stripe and it gives you token back.

stripe.accounts.createExternalAccount = send to stripe all private information where it will store it in a secure way: no one can see it and you can use it only when you know private key. And this has to be done on the client side (website or mobile application). You should never send/write financial credentials anywhere but Stripe.

@cybergrind Thank you for taking the time to explain it to me!!
I had a weird idea that the token was created locally and was using encryption of some sort.

@wmonecke Did you ever get stripe connect to work with this library?

@wmonecke @larryranches I have the same question

@isnifer So it can be done, but how? If you can point me in the right direction I'd appreciate it. I have setup an upwork job to set this up but nobody seems to have any idea of how to do the Stripe Connect integration: https://www.upwork.com/jobs/~01f63293e03129911a

Hey guys!

It is possible to make it work. You will need a backend however. I used firebase cloud functions to make it easy.

You cannot store your SECRET_KEY in the frontend because it is a major security issue.

Use tipsi-stripe in the front end then validate data in backend using stripe.js and you KEY.

Here is some code, it is a bit messy but I had to do it in a rush. If you need any help or clarification I will gladly help you!

FRONT END - the stripe class referenced here is coming from _tipsi-stripe_ and NOT _stripe.js_.
````
// first you create the customer account

export const createStripeCustomerAccount = (email, uid, callback) => {
firebase.auth().currentUser.getIdToken(true).then(idToken => {
// Send firebase idToken to your backend via HTTPS
axios({
method: 'post',
url: 'https://us-central1-lisdo-6bf0d.cloudfunctions.net/createStripeCustomerAccount',
headers: {
'Authorization': 'Bearer ' + idToken
},
data: {
email: email,
user_uid: uid,
user_type: 'serviceUsers'
}
})
.then(res => {
if (res.status === 200) {
// grab stripe account id and send it to the callback to be saved in firebase and/or your local storage
let stripeID = res.data.stripeID;

    return callback(stripeID);
  }
})
.catch(err => console.log('error while doing get req createStripeAccount:', err));

}).catch(error => {
// Handle error
console.log('Error retrieving IdToken!', err);
});
}

// then AFTER creating the user account you create a token of a card using tipsi-stripe

createTokenFromCard() {
const { number, cvc, expiry, name, type } = this.state.cardForm.values;
const expiryMonth = expiry.substr(0, expiry.indexOf('/'))1;
const expiryYear = expiry.substr(expiry.indexOf('/')+1)
1;
const sanitizedCardNumber = number.replace(/\s/g,'');

const params = {
  number: sanitizedCardNumber,
  expMonth: expiryMonth,
  expYear: expiryYear,
  cvc: cvc,
  name: name,
  object: 'card',
  currency: 'eur'
};

return this.setState({ makingCardRequest: true }, () => {
  return stripe.createTokenWithCard(params)
  .then(token   => this.props.addCardToStripeAccount(token.tokenId, this.props.user.stripeID))
  .catch(error  => console.log('error creating token', error));
});

}

// after getting the necessary data of the card and having the TOKEN using tipsi-stripe I called the following action to actually add a card to an existing account. The stripeID is the one we got by calling createStripeCustomerAccount()

export const addCardToStripeAccount = (tokenId, stripeID) => {
return (dispatch) => {

const { currentUser } = firebase.auth();

firebase.auth().currentUser.getIdToken(true).then(idToken => {

  axios({
    method: 'post',
    url: 'https://us-central1-lisdo-6bf0d.cloudfunctions.net/addCardToStripeAccount',
    headers: {
      'Authorization': 'Bearer ' + idToken
    },
    data: {
      tokenId: tokenId,
      stripeID: stripeID,
    }
  })
  .then(res  => {
    if (res.status === 200) {
      return dispatch({ type: ADD_CARD_TO_ACCOUNT_SUCCESS });
    }
  })
  .catch(err => console.log('error while doing get req addCardToStripeAccount:', err));

})
.catch(error => console.log('Error retrieving users ID token', error));

}
}
````

BACK END - the stripe class referenced here is coming from _stripe.js_ and NOT _tipsi-stripe_. That is the whole point of having the backend in the first place.

````
exports.addBankAccountToStripeAccount = functions.https.onRequest((req, res) => {
verifyUser(req, res, decodedIdToken => {

const user_type = req.body.user_type;
const tokenId   = req.body.tokenId;
const user_uid  = decodedIdToken.uid;
const stripeID  = req.body.stripeID;
const userData  = req.body.userData;

const updateStripeObject =
  userData.accountHolderType === 'individual'
  ?
  {
    legal_entity: {
      address: {
        city: userData.personal_city,
        postal_code: userData.personal_postal_code,
        line1: userData.personal_line1,
      },
      dob: {
        day: userData.dob_day,
        month: userData.dob_month,
        year: userData.dob_year,
      },
      first_name: userData.first_name,
      last_name: userData.last_name,
      type: userData.accountHolderType,
    },
    tos_acceptance: {
      date: Math.floor(Date.now() / 1000),
      ip: req.connection.remoteAddress
    }
  }
  :
  {
    legal_entity: {
      additional_owners: [],
      address: {
        city: userData.business_city,
        postal_code: userData.business_postal_code,
        line1: userData.business_line1,
      },
      personal_address: {
        city: userData.personal_city,
        postal_code: userData.personal_postal_code,
        line1: userData.personal_line1,
      },
      dob: {
        day: userData.dob_day,
        month: userData.dob_month,
        year: userData.dob_year,
      },
      first_name: userData.first_name,
      last_name: userData.last_name,
      type: userData.accountHolderType,
      business_name: userData.business_name,
      business_tax_id: userData.business_tax_id,
    },
    tos_acceptance: {
      date: Math.floor(Date.now() / 1000),
      ip: req.connection.remoteAddress
    }
  }

let promises = [];

promises.push(stripe.accounts.createExternalAccount(stripeID, { external_account: tokenId }));
promises.push(stripe.accounts.update(stripeID, updateStripeObject));

Promise.all(promises)
.then(response => {

  const bankAccount = response[0];

  let bankName = bankAccount.bank_name;
  let last4    = bankAccount.last4;
  let type     = bankAccount.object;
  let status   = bankAccount.status;

  let updates = {};
  updates[`/allUsers/${user_type}/${user_uid}/payoutDetails/type`]     = type;
  updates[`/allUsers/${user_type}/${user_uid}/payoutDetails/last4`]    = last4;
  updates[`/allUsers/${user_type}/${user_uid}/payoutDetails/status`]   = status;
  updates[`/allUsers/${user_type}/${user_uid}/payoutDetails/bankName`] = bankName;

  return admin.database().ref().update(updates)
  .then(() => res.status(200).send({ status: 'success' }))
  .catch(err => {
    console.log("error:", err)
    res.status(400).send({
      error: err,
      description: 'Error while updating firebase from cloud function in *addBankAccountToStripeAccount*'
    })
  });

})
.catch(err => {
  console.log('error', err)
  res.status(400).send({
    error: err,
    description: 'Error, not all Promises resolved in *addBankAccountToStripeAccount*'
  })
});

});
});

// The actual method to add the card your users connect account.

exports.addCardToStripeAccount = functions.https.onRequest((req, res) => {
verifyUser(req, res, (decodedIdToken) => {

const user_uid  = decodedIdToken.uid;
const tokenId   = req.body.tokenId;
const stripeID  = req.body.stripeID;

return stripe.customers.update(stripeID, { source: tokenId })
.then(customer => {

  const { last4, brand, id } = customer.sources.data[0];

  let updates = {};
  updates[`/allUsers/serviceUsers/${user_uid}/paymentDetails/last4`]  = last4;
  updates[`/allUsers/serviceUsers/${user_uid}/paymentDetails/cardID`] = id;
  updates[`/allUsers/serviceUsers/${user_uid}/paymentDetails/brand`]  = brand;

  return admin.database().ref().update(updates)
  .then(() => res.status(200).send({ status: 'success' }))
  .catch(err => res.status(400).send({
    error: err,
    description: 'Error while updating firebase from cloud function in *addCardToStripeAccount*'
  }));
})
.catch(error => console.log('Error while adding card to serviceUser', error));

});
});
````

@wmonecke Did you ever get stripe connect to work with this library?

So yes this can be done. Stripe connect is not dependent on a front-end library to work as far on-boarding. It's really the server side that makes this happen which you can create a backend server or use lambdas or functions.

Stripes example here https://github.com/stripe/stripe-connect-rocketrides in the server folder Rocket Rides app displays how this can be done from the Stripe Connect Express stand point.

The front-end library in this case tipsi-stripe for react-native help interact with the stripe api for on-boarding so the user can submit payments, etc. In this case, it can be used to process the payment transaction from buying customer (need a ride) to Stripe Connect producer (uber driver, etc.)

To summarize:

  • Client side: Use tipsi-stripe to onboard user and interact with stripe api from a purchase stand point to stripe connect users that are providing the service.

  • Server side: Must build server side Stripe Connect logic to onboard (drivers) and create Stripe Connect ID users. Recommend using Stripe Connect Express to start out.

Yes. Tipsi-Stripe can help with the individual API calls, but there's about
4 screens to onboard a user to Stripe Connect (enter phone number, enter
OTA code, enter personal info, enter bank / debit card data). In Stripe-ios
and Stripe-android sdks, the 4 screens are provided seamlessly by the SDK,
the native app itself will receive the final result of the onboarding flow
that happens via the Stripe SDK and talks directly to Stripe servers.
However, this certainly would not be the case when using tipsi-stripe would
it?
I just can't figure out what the correct flow would be to onboard a user to
Stripe Connect in React Native using tipsi-stripe.

On Fri, 27 Sep 2019 at 14:03, larryranches notifications@github.com wrote:

@wmonecke https://github.com/wmonecke Did you ever get stripe connect
to work with this library?

So yes this can be done. Stripe connect is not dependent on a front-end
library to work as far on-boarding. It's really the server side that makes
this happen which you can create a backend server or use lambdas or
functions.

Stripes example here https://github.com/stripe/stripe-connect-rocketrides
in the server folder Rocket Rides app displays how this can be done from
the Stripe Connect Express stand point.

The front-end library in this case tipsi-stripe for react-native help
interact with the stripe api for on-boarding so the user can submit
payments, etc. In this case, it can be used to process the payment
transaction from buying customer (need a ride) to Stripe Connect producer
(uber driver, etc.)

To summarize:

-

Client side: Use tipsi-stripe to onboard user and interact with stripe
api from a purchase stand point to stripe connect users that are providing
the service.
-

Server side: Must build server side Stripe Connect logic to onboard
(drivers) and create Stripe Connect ID users. Recommend using Stripe
Connect Express to start out.

—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/tipsi/tipsi-stripe/issues/214?email_source=notifications&email_token=AD2LRLZ6MPQZLKE7EPZA6EDQLZKHDA5CNFSM4EJLYYX2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7Z2JMQ#issuecomment-536061106,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AD2LRL7IXQPE3CZC3ETOZ5DQLZKHDANCNFSM4EJLYYXQ
.

Yes. Tipsi-Stripe can help with the individual API calls, but there's about 4 screens to onboard a user to Stripe Connect (enter phone number, enter OTA code, enter personal info, enter bank / debit card data). In Stripe-ios and Stripe-android sdks, the 4 screens are provided seamlessly by the SDK, the native app itself will receive the final result of the onboarding flow that happens via the Stripe SDK and talks directly to Stripe servers. However, this certainly would not be the case when using tipsi-stripe would it? I just can't figure out what the correct flow would be to onboard a user to Stripe Connect in React Native using tipsi-stripe.
…
On Fri, 27 Sep 2019 at 14:03, larryranches @.*> wrote: @wmonecke https://github.com/wmonecke Did you ever get stripe connect to work with this library? So yes this can be done. Stripe connect is not dependent on a front-end library to work as far on-boarding. It's really the server side that makes this happen which you can create a backend server or use lambdas or functions. Stripes example here https://github.com/stripe/stripe-connect-rocketrides in the server folder Rocket Rides app displays how this can be done from the Stripe Connect Express stand point. The front-end library in this case tipsi-stripe for react-native help interact with the stripe api for on-boarding so the user can submit payments, etc. In this case, it can be used to process the payment transaction from buying customer (need a ride) to Stripe Connect producer (uber driver, etc.) To summarize: - Client side: Use tipsi-stripe to onboard user and interact with stripe api from a purchase stand point to stripe connect users that are providing the service. - Server side: Must build server side Stripe Connect logic to onboard (drivers) and create Stripe Connect ID users. Recommend using Stripe Connect Express to start out. — You are receiving this because you commented. Reply to this email directly, view it on GitHub <#214?email_source=notifications&email_token=AD2LRLZ6MPQZLKE7EPZA6EDQLZKHDA5CNFSM4EJLYYX2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7Z2JMQ#issuecomment-536061106>, or mute the thread https://github.com/notifications/unsubscribe-auth/AD2LRL7IXQPE3CZC3ETOZ5DQLZKHDANCNFSM4EJLYYXQ .

@augustosamame Right, so for this case you don't use tipsi-stripe to onboard Stripe Connect users. You would use Stripe's service to start out like Express or https://stripe.com/docs/connect/custom-accounts to build custom React Native screens if you want a complete custom flow which is will be more difficult to do.

Either way you'll need to interact with a backend server and call Stripe Connects api e.g. https://connect.stripe.com/express/oauth/authorize?redirect_uri=https://connect.stripe.com/connect/default_new/oauth/test&client_id=ca_32D88BD1qLklliziD7gYQvctJIhWBSQ7&state={STATE_VALUE}&stripe_user[business_type]=company as stated in the docs.

If using Connect express, you'll have to redirect to a web browser to complete the onboarding flow, create a custom web page or something that will verify the token to complete onboarding (https://stripe.com/docs/connect/express-accounts) in Step 4 curl https://connect.stripe.com/oauth/token \ -d client_secret=sk_test_4eC39HqLyjWDarjtT1zdp7dc \ -d code="{AUTHORIZATION_CODE}" \ -d grant_type=authorization_code then figure a way to redirect to your app once verified.

Both Express or Custom will have a similar workflow.

Yes we finally decided to just use a webview and use Stripe express to
integrate with Stripe connect. That way we just let Stripe handle
everything. Will report back if we're successful

On Fri, Sep 27, 2019 at 14:25 larryranches notifications@github.com wrote:

Yes. Tipsi-Stripe can help with the individual API calls, but there's
about 4 screens to onboard a user to Stripe Connect (enter phone number,
enter OTA code, enter personal info, enter bank / debit card data). In
Stripe-ios and Stripe-android sdks, the 4 screens are provided seamlessly
by the SDK, the native app itself will receive the final result of the
onboarding flow that happens via the Stripe SDK and talks directly to
Stripe servers. However, this certainly would not be the case when using
tipsi-stripe would it? I just can't figure out what the correct flow would
be to onboard a user to Stripe Connect in React Native using tipsi-stripe.
… <#m_-3508308234964655727_>
On Fri, 27 Sep 2019 at 14:03, larryranches @.*> wrote: @wmonecke
https://github.com/wmonecke https://github.com/wmonecke Did you ever
get stripe connect to work with this library? So yes this can be done.
Stripe connect is not dependent on a front-end library to work as far
on-boarding. It's really the server side that makes this happen which you
can create a backend server or use lambdas or functions. Stripes example
here https://github.com/stripe/stripe-connect-rocketrides in the server
folder Rocket Rides app displays how this can be done from the Stripe
Connect Express stand point. The front-end library in this case
tipsi-stripe for react-native help interact with the stripe api for
on-boarding so the user can submit payments, etc. In this case, it can be
used to process the payment transaction from buying customer (need a ride)
to Stripe Connect producer (uber driver, etc.) To summarize: - Client side:
Use tipsi-stripe to onboard user and interact with stripe api from a
purchase stand point to stripe connect users that are providing the
service. - Server side: Must build server side Stripe Connect logic to
onboard (drivers) and create Stripe Connect ID users. Recommend using
Stripe Connect Express to start out. — You are receiving this because you
commented. Reply to this email directly, view it on GitHub <#214
https://github.com/tipsi/tipsi-stripe/issues/214?email_source=notifications&email_token=AD2LRLZ6MPQZLKE7EPZA6EDQLZKHDA5CNFSM4EJLYYX2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7Z2JMQ#issuecomment-536061106>,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AD2LRL7IXQPE3CZC3ETOZ5DQLZKHDANCNFSM4EJLYYXQ
.

@augustosamame https://github.com/augustosamame Right, so for this case
you don't use tipsi-stripe to onboard Stripe Connect users. You would use
Stripe's service to start out like Express or
https://stripe.com/docs/connect/custom-accounts to build custom React
Native screens if you want a complete custom flow which is will be more
difficult to do.

Either way you'll need to interact with a backend server and call Stripe
Connects api e.g.
https://connect.stripe.com/express/oauth/authorize?redirect_uri=https://connect.stripe.com/connect/default_new/oauth/test&client_id=ca_32D88BD1qLklliziD7gYQvctJIhWBSQ7&state={STATE_VALUE}&stripe_user[business_type]=company
as stated in the docs.

If using Connect express, you'll have to redirect to a web browser to
complete the onboarding flow, create a custom web page or something that
will verify the token to complete onboarding (
https://stripe.com/docs/connect/express-accounts) in Step 4 curl
https://connect.stripe.com/oauth/token \ -d
client_secret=sk_test_4eC39HqLyjWDarjtT1zdp7dc \ -d
code="{AUTHORIZATION_CODE}" \ -d grant_type=authorization_code then
figure a way to redirect to your app once verified.

Both Express or Custom will have a similar workflow.

—
You are receiving this because you were mentioned.

Reply to this email directly, view it on GitHub
https://github.com/tipsi/tipsi-stripe/issues/214?email_source=notifications&email_token=AD2LRLYSCUZTQIUZV6QBHN3QLZM4LA5CNFSM4EJLYYX2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7Z35ZA#issuecomment-536067812,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AD2LRL6YBNYOJGCRM5R4SITQLZM4LANCNFSM4EJLYYXQ
.

@augustosamame Yes, I think using Express is a fine choice to start out. Later, you can build a complete react native flow with custom in-app screens that reflect the Express web pages or browser screens that Stripe Express provides.

This would be ideal so that the user doesn't have to leave the app but much harder and I think can be done later especially if you're experimenting, but if a hard requirement, can be done initially as time allows :).

Hope this helps!

Honestly creating the onboarding screens wasn't as much of a pain as I thought it would be and it keeps the UX on point. The easiest way was using react-native-swiper with each screen asking for different info. Makes it intuitive and easy on the user.

Yes I would prefer this as well.
Can you point me in the right direction regarding the branch of
tipsi-stripe you used and which calls you used?

On Fri, Sep 27, 2019, 3:08 PM Walter Monecke notifications@github.com
wrote:

Honestly creating the onboarding screens wasn't as much of a pain as I
thought it would be and it keeps the UX on point. The easiest way was to
use react-native-swiper with each screen asking for different info. Makes
it intuitive and easy on the user.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/tipsi/tipsi-stripe/issues/214?email_source=notifications&email_token=AD2LRL4EGZZL7L7GYN7VN3TQLZR5NA5CNFSM4EJLYYX2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7Z65QA#issuecomment-536080064,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AD2LRL6FHXIIE536B36IQG3QLZR5NANCNFSM4EJLYYXQ
.

Honestly creating the onboarding screens wasn't as much of a pain as I thought it would be and it keeps the UX on point. The easiest way was using react-native-swiper with each screen asking for different info. Makes it intuitive and easy on the user.

Right, building the screens is not that hard but I was referring to in comparison to Express screens which is already built out of the box hence the name "Express" which is much much easier then custom :)

Was this page helpful?
0 / 5 - 0 ratings