17

I'm sure I'm missing something obvious here, but I can't get my head around how to check for an existing card against a customer.

I'm using the stripe connect api within an laravel app to manage payments on behalf of others, and the basic process is as follows:

  • a stripe token is created via stripe.js and submitted with the payment form
  • if the customer exists in the local database, I grab their stripe_id, otherwise a new customer is created using the token as the source/card
  • a charge is then created using the retrieved or new customer stripe_id

Currently, if the customer returns and uses a different card, as the charge only includes a customer, not source, it'll be charged against their default card regardless.

What I'd like to do is:

  • create a stripe token
  • check customer against local database etc
  • check card fingerprint against customer's cards
  • if necessary, create new card on customer's record
  • create charge using both customer and card ids

Simply put: I can't see where in the process a persistent card_id is generated; both those used in the stripe.js response, and when created in the stripe dashboard, appear to be unique, meaning every charge creates a brand-new card object in stripe.

I know I can retrieve a list of cards stored against a customer's account - but where do I get the initial card_id from to search against?

I've seen a question that touches on this here - Can I check whether stripe a card is already existed before going to create new one? - but I don't know Ruby, so can't make head nor tail of it.

EDIT:

Simpler version - is there a way to get the fingerprint as described in the stripe docs here - https://stripe.com/docs/api/php#card_object - without having to first create a card object ?

Community
  • 1
  • 1
taekni
  • 1,308
  • 3
  • 12
  • 20

2 Answers2

25

So the idea here would be to use the fingerprint on the Card object or the Token object and not the id itself as those would be different if you add the same card multiple times.

When you get a new card token you can retrieve it through the Retrieve Token API and look for the fingerprint in the card hash.

You would keep a list of known fingerprints in your database associated with a specific customer and/or card so that you can detect duplicate cards.

NOTE: make sure you are using the secret keys to get those information. otherwise if you are using the publishable key, you might not get the fingerprint value.

Mahesh Agrawal
  • 3,348
  • 20
  • 34
koopajah
  • 23,792
  • 9
  • 78
  • 104
  • Your idea is perfect but don't know why stripe is not returning `fingerprint` in Retrieve Token API. if any idea why stripe not returning, please share. – Mahesh Agrawal Oct 12 '16 at 11:22
  • @M.K. Stripe definitely returns that fingerprint on that API. The only case where you don't see the fingerprint is if you use your publishable key for this and it's documented explicitly here: https://stripe.com/docs/upgrades#2014-10-07 – koopajah Oct 12 '16 at 12:21
  • ooh....i got it..now am able to get the fingerprint. thank you a lot. am editing your answer with this note. so that no one will be confused. thanks a lot again. – Mahesh Agrawal Oct 12 '16 at 12:37
  • 1
    Keep in mind that if you do that, and the Customer tries to submit the same card, but with different exp date, they will not be allowed. – jonathancardoso Mar 14 '17 at 20:41
  • @JCM and rightly so, they shouldn't be allowed. If a card has expired you always get a new card which has a new card number and exp date. – user3574492 Jun 14 '18 at 14:36
  • 1
    @user3574492 you don't always get a new card, it's probably even safe to say that the majority of issuers are going to give you a card with the same number, with only the 3 digit code being different – jonathancardoso Jun 15 '18 at 22:51
  • @JCM Every issuer must give you a new card with a new card number once it expires. the account number stays the same but the long card number is always different aswell as the cvc for renewed cards. – user3574492 Jun 16 '18 at 01:47
  • 1
    @user3574492 You are wrong. I just received a new VISA credit card in the mail (when my old one expired) with the same number, new expiration date, and new CVC. – xinthose Sep 26 '19 at 13:45
  • Is it legal (In US) to store stripe card fingerprint in application database ? – yaswant singh Oct 04 '19 at 11:29
  • @yaswantsingh Yes it is, and Stripe explicitly mentions you should store this value in your database along with `customer.id`, `paymentMethod.id`, `last4`, `exp_month`, `exp_year`, `brand (as in "Visa", "Mastercard" etc.)` to maintain good site performance and reduced Stripe API calls. – A Friend Sep 03 '20 at 21:35
2

I created a function to do this:

  • $customer is the stripe customer object
  • $stripe_account is either your account's stripe ID or the connected account's stripe ID
  • $token comes from stripe.js elements
  • $check_exp allows you to decide if you want to check the card's expiration date as well, because the fingerprint does not change if the card's number is the same
  • stripe PHP API 7.0.0

    function check_duplicate_card($customer, $stripe_account, $token, $check_exp) {
    $loc = "check_duplicate_card >> ";
    $debug = true;
    
    if ($debug) {
        // see here for an explanation for logging: http://php.net/set_error_handler >> Examples
        trigger_error("$loc started", E_USER_NOTICE);
    }
    
    try
    {
        // get token data
        $response = \Stripe\Token::retrieve(
            $token,
            ["stripe_account" => $stripe_account]
        );
        $token_fingerprint = $response->card->fingerprint;
        $token_exp_month = $response->card->exp_month;
        $token_exp_year = $response->card->exp_year;
        if ($debug) {
            trigger_error("$loc token_fingerprint = $token_fingerprint; token_exp_month = $token_exp_month; token_exp_year = $token_exp_year", E_USER_NOTICE);
        }
    
        // check for duplicate source
        if ($debug) {
            trigger_error("$loc customer sources = " . json_encode($customer->sources), E_USER_NOTICE);
        }
        $duplicate_found = false;
        foreach ($customer->sources->data as &$value) {
            // get data
            $fingerprint = $value->fingerprint;
            $exp_month = $value->exp_month;
            $exp_year = $value->exp_year;
    
            if ($fingerprint == $token_fingerprint) {
                if ($check_exp) {
                    if (($exp_month == $token_exp_month) && ($exp_year == $token_exp_year)) {
                        $duplicate_found = true;
                        break;
                    }
                } else {
                    $duplicate_found = true;
                    break;    
                }
            }
        }
        if ($debug) {
            trigger_error("$loc duplicate_found = " . json_encode($duplicate_found), E_USER_NOTICE);
        }
    } catch (Exception $e) {
        if ($e instanceof \Stripe\Exception\ApiErrorException) {
            $return_array = [
                "status" => $e->getHttpStatus(),
                "type" => $e->getError()->type,
                "code" => $e->getError()->code,
                "param" => $e->getError()->param,
                "message" => $e->getError()->message,
            ];
            $return_str = json_encode($return_array);
            trigger_error("$loc $return_str", E_USER_WARNING);
            http_response_code($e->getHttpStatus());
            echo $return_str;
        } else {
            $return_array = [
                "message" => $e->getMessage(),
            ];
            $return_str = json_encode($return_array);
            trigger_error("$loc $return_str", E_USER_ERROR);
            http_response_code(500); // Internal Server Error
            echo $return_str;
        }
    }
    
    if ($debug) {
        trigger_error("$loc ended", E_USER_NOTICE);
    }
    
    return $duplicate_found;
    }
    
xinthose
  • 3,213
  • 3
  • 40
  • 59