#alex_best-practices

1 messages ยท Page 1 of 1 (latest)

final nacelleBOT
#

๐Ÿ‘‹ 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/1397316976761638965

๐Ÿ“ 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.

spice raft
#

Hi, my understanding is that as long as the requests are made on your account, it should have the same fingerprints. Are you seeing otherwise? Can you share the two requests so I can take a further look?

final nacelleBOT
sterile rivet
#

Hi pgskc, the fingerprints are consistent yes.

#

The concern is the following, we are trying to leave as much data on Stripe's side as possible, as such we are trying to avoid storing an array of card fingerprints on a user document, unless that's unavoidable.

#

So I've been looking at ways to take a payment method (from Tap to Pay) and using the PM to then do a lookup and get a customer ID.

#

That would enable us to identify a return user by customer ID.

spice raft
#

Yeah, you can do that. We do recommend that you store the mapping on your end so you do not need to keep making GET calls as it won't scale since we have rate limits: https://docs.stripe.com/rate-limits

sterile rivet
#

ok, these calls are happening as the result of a user interaction, so they shouldn't be all that frequent, but I'll review the limits now quickly to see if that's a fail-point.

#

Based on my understanding here, we'd be making at most 2 calls to stripe.getPaymentMethod() per interaction, and certainly not expecting more than 1 interaction per second.

sly elk
#

Hi hi! Iโ€™m going to be taking over for my colleague here. What is your concern around storing these on your side?

sterile rivet
#

hi timebox, ok

#

the concern is maintaining a synchronized map of card fingerprints attached to a customer across our database and Stripe's. If we don't need to manage that mapping, I'd rather avoid it. I suppose I'm exploring the possibility of the stripe customer being the umbrella for all our stripe related data needs, so our application can store just that value and leave Stripe as the source of truth.

sly elk
sterile rivet
#

I suppose some added context here is we deal with various payment providers including campus credential systems, so we've tried to abstract away the payment concerns.

sly elk
#

Fair. What are you using the fingerprint for?

#

Oh, I see.

sterile rivet
#

I'll try to explain succinctly, and I think that will detail why metadata may not be sufficient in our case.

#

So we have two workflows, card present (Tap to Pay) and card not present (Stripe Elements).

sly elk
#

I got it, no worries.

Ok, here's what I'd do:

sterile rivet
#

ok

sly elk
#

Wait ๐Ÿ˜‚

#

Possible your customer would have more than one payment method in Stripe, or only ever one?

sterile rivet
#

yeah that's the kicker, via either workflow, the customer could use effectively any number of payment methods.

#

so we are trying to account for the case where a customer adds a second payment method via Elements, and we are receiving either of those payment methods via Tap to Pay.

sly elk
#

Ok. I think I have a solution. Gimme ... n seconds. ๐Ÿ˜‚

sterile rivet
#

lol ok no sweat

#

The direction I've been heading is to take the payment method contained in the setup-intent that we get back after Tap to Pay.
If that payment method has a customer, then I can lookup in our DB based on that customer ID, if it doesn't I create a new customer and attach the PM to the new customer.

But I got a bit hung up thinking about the top level payment method, the generated payment method, and how payment methods created from Elements would play into this.

As I'm talking that through, it may indeed be simpler to rely on the fingerprint as the ID that links them all.

sly elk
#

Boom. Ok. Assuming less than 50 cards ๐Ÿคช you can just add each card as metadata on the Customer wth the fingerprint as the key, and ~anything as the value. Then you can use the Search API to search for metadata[$FINGERPRINT]:null and you'll get back the Customer(s) with that fingerprint.

sterile rivet
#

Interesting, let me think about that for a minute.

sly elk
#

I just created a test customer with a couple cards, and when I run the following, I get that Customer back:

curl -G https://api.stripe.com/v1/customers/search \
  -u "sk_test_beepboopsecretsauce:" \
  --data-urlencode query="metadata[\"FINGERPRINT_\"]:null"

(With non-silly values, obviously)

#

(Assuming less than 50 metadata entries; if you're using metadata for other purposes, that would reduce that limit since you only get 50 of 'em)

sterile rivet
#

right, no we're not as of today using metadata nor are we expecting so many cards

#

but this solution still involves creating a distinct source of truth and the overhead of maintaining it

#

albeit it in this case the data lives on the customer itself

sly elk
#

Wait, how are you doing this for other processors? Do they have a 'fingerprint' analog?

sterile rivet
#

The other processors in question are campus credential systems, and they're all a little different. Primarily what we do is get the underlying user Id, which we can then use to effectuate transactions against the account, rather than any specific card. So I'm trying to achieve something similar here with Stripe.

sly elk
#

You can search for Fingerprint. Let me just test this too.

sterile rivet
#

interesting

#

that could be helpful

sly elk
#

Actually, that's for Charge search, not Customer search. ๐Ÿ˜ฆ

sterile rivet
#

Ah ok

sly elk
#

Oooo

#

But it includes the Customer ID (at least in the case where the Customer is known, I guess?)

#

But it would only work if there was a Charge.

#

How do you get the "underlying user Id" from the campus cred system(s)?

sterile rivet
#

There are two ways to get that User ID typically, either the students authenticates with SSO, or we read a card number from a physical terminal and can do a lookup to find the student that owns that card.

sly elk
#

Tell me more about that second part.

sterile rivet
#

For some of the card providers, the card number is considered authoritative enough to get a student account. It's essentially a key.

#

But the cards are fairly rudimentary, they just expose a string.

sly elk
#

LMAOHNO

#

The full PAN?

sterile rivet
#

Sorry what's a PAN?

sly elk
#

Credit card number.

sterile rivet
#

It's not a credit card, it's a campus card.

sly elk
#

Ohhhhhhhhh

sterile rivet
#

So the string on the card is basically just an ID.

sly elk
#

So like a "gift card" give or take?

sterile rivet
#

But it's assumed that the holder of the ID is the authenticated user, essentially. And an authorized partner can trade it for account info.

sly elk
#

Or more like a Student ID?

#

And you use that ID to look up the details in a third party system, or in your system?

sterile rivet
#

In the third party system. Of course we have to be certified and identify ourselves and so forth before we can get any info, but that's essentially how it works.

#

Now, we do also, in the same place as the stripe fingerprint, store that number for reference.

sly elk
#

For sure. Ok, so you don't have to store that ID at all, just capture on the terminal, query the third party system, and then use the results of that as your 'payment source', right?

sterile rivet
#

Yes effectively.

#

We do store it but technically don't have to, similarly to the fingerprint. The fingerprint has been handy for quickly recognizing repeat users with Tap to Pay.

#

And we could convert that field to an array and maintain a list of fingerprints, but yeah, we're back at the start and I'm looking for a more deterministic option.

sly elk
#

Right, which is part of its purpose.

What's wrong with the approach you're using exactly?

sterile rivet
#

So the product people now want to allow users to store X number of credit/debit/mobile wallet payment methods on their account.

#

In tandem, we want to recognize that user if they use the same PM with Tap to Pay.

sly elk
#

Right, ok. What server-side language and database do you use?

sterile rivet
#

nodejs/mongodb

sly elk
#

Ok awesome. So swapping from a single value to an array should be pretty straightforward, right?

sterile rivet
#

Yes, it's a viable option, for sure.

sly elk
#

It's likely the "best" way to solve this.

payment_methods: [
  "stripe:0Nef1NG3rprnt",
  "stripe:0th3er1NG3rprnt",
  "other_thingy:abc123studentId",
  "other_thingy:def456studentId",
]
#

...or similiar.

sterile rivet
#

yeah I mean, we already have different keys per payment provider thing, so we could go straight array with strings not a problem there.

#

I guess I'm really interested in a way to do this without relying on fingerprints, because it seems to me we go through the trouble of linking customers and payment methods, and it would be preferable if we could get our customer from a payment method.

#

Which I understand is possible.

sly elk
#

You can't get from a Fingerprint to a Payment Method, because it's not 1:1.

sterile rivet
#

No, but we should be able to get from a Payment Method to a customer?

sly elk
#

If I add the same card for my sixteen kids, you'll get sixteen results searching for that card's fingerprint.

#

...but when someone taps, there's only a fingerprint, not a Payment Method, right?

sterile rivet
#

When someone taps, we do get a payment method.

sly elk
#

Be aware you can get more than one if that card's been used by more than one Customer - like, fingerprint is not a unique key for ( fingerprint, payment_method )

sterile rivet
#

We create a setup intent (no customer), the user taps, we attach that payment method to the setup intent.
Then, (currently) if we match on the PM's fingerprint in our DB, we don't create a new customer. If we don't match, we create a new customer and attach the PM to that customer.

#

What I'd ideally like to do, is forget about the fingerprint, lookup against Stripe to see if that PM has a customer attached (meaning we have connected this PM previously). If none, create one and attach.

sly elk
#

That PM will never have a Customer attached unless you attach it to a Customer. Fingerprint (and therefore card) is not a unique key for ( fingerprint, payment_method ).

sterile rivet
#

What the docs tell us to do, is to use the PM.generated_card, since that is a card that can be charged off-session. so that's the one we link.
What I'm now trying to understand is what is the relationship between a generated card, and a regular card that might be inputted say via Stripe elements.

#

unless you attach it to a Customer.
Correct, we are explicitly attaching it to a customer, though.

sly elk
#

To which Customer?

sterile rivet
#

If we receive a card tap with a fingerprint that doesn't match an existing user in our DB, we create a new customer, attach the PM from the Tap to this new customer, and save the customer ID (and fingerprint) to our user.

sly elk
#

Right, ok.

You can't remove fingerprint from this flow, or it won't work. The PM ID that is created from today's tap will not be the same as the PM ID that you stored yesterday.

sterile rivet
#

Sure, but it's not the PM ID we store, it's the customer ID.

sly elk
#

Ok, I guess I'm confused what you're trying to accomplish, because I thought you were trying to stop storing the fingerprint.

sterile rivet
#

lol, yes, we are looking to not rely on the fingerprint, and rely on the customer for everything, essentially.

sly elk
#

You cannot get from a tap to one or more Stripe Customers without the fingerprint.

#

Fingerprint is effectively a ( card_number , stripe_account ) ID, and so could exist n times on m Customers in your Stripe Account.

sterile rivet
#

Hm, but it seems I can go from: SetupIntent -> LastAttempt -> PaymentMethod -> Customer

sly elk
#

If that SetupIntent is associated with a Customer, yes.

But unless you provided the Customer ID when creating the Tap to Pay Setup Intent (which makes no sense because you don't know the Customer yet), you would never have a Customer on that SetupIntent.

sterile rivet
#

Maybe I didn't clarify but we use SetupIntents with Tap to Pay to setup future usage, so not using a Payment Intent, if that changes anything.

#

Right, the customer is not on the Setup Intent. We create the customer after, and attach the payment method to the customer.

#

Then my understanding is when the same payment method is used to confirm a future setup intent, we can use the PM to get an attached customer.

#

But maybe like you're saying, that PaymentMethod ID will not be consistent across interactions?

sly elk
#

PhysicalCard + StripeAccount => Fingerprint

Fingerprint => [ PaymentMethod, PaymentMethod, ... ]

PaymentMethod => Customer

#

It will not.

sterile rivet
#

OK then that's the piece I was missing

#

So what is the purpose of attaching the Payment Method to the customer?

sly elk
#

The Customer is the thing that effectively allows reuse of the Payment Method. If you want to be able to reuse a Payment Method multiple times, it has to be owned by a Customer.

#

(if I remember correctly)

sterile rivet
#

OK

#

so attaching it is really just there for later charging the customer

sly elk
#

Correct.

sterile rivet
#

So put differently, a payment method ID is ephemeral.

sly elk
#

No, it's permanent - it just doesn't map 1:1 with a physical credit card.

#

Physical Card <> PaymentMethod is 1:*

#

No, actually it's *:*

#

No, I was right the first time. ๐Ÿ˜‚

sterile rivet
#

lol ok

sly elk
#

One physical card can have ~many Payment Method objects - even in your own Stripe Account.

sterile rivet
#

I see

sly elk
#

I.e., the 4242424242424242 Stripe test card always has the same fingerprint when attached to any Customer in your Stripe Account.

sterile rivet
#

right, but it can yield different payment method?

sly elk
#

Correct.

#

If you add the (same) card to a Customer twice, you get two Payment Methods with two different IDs, but with the same Fingerprint.

sterile rivet
#

ouf

#

ok, other mind bender for you since we're on a roll here. If I use Elements to input a card number, then use mobile wallet with the same underlying card, I'll get two fingerprints, correct? But those fingerprints will map to the ones I get during Tap to Pay?

sly elk
#

Apple Pay or Google Pay, correct.

#

(or ... other wallets that might exist ๐Ÿ˜‚)

sterile rivet
#

lol ok

#

are you saying apple will yield a fingerprint different from google for the same card info?

sly elk
#

They have something similar to the generated_card which is different from the underlying payment method - except Apple and Google don't surface the original details at all.

#

Correct.

sterile rivet
#

ok wow, so a single card can easily yield 3 fingerprints

sly elk
#

Apple, Google, "real card" => 3 different ... yes.

sterile rivet
#

but, critically, if use a wallet with Stripe elements, then the same wallet with Tap to Pay, fingerprint =

sly elk
#

In theory it should, yes.
(In practice, it most likely does but I've never tested it myself)

sterile rivet
#

fair enough

#

ok so I guess we're going array of fingerprints, thanks for your help!