Reputation: 1320
Code:
Stripe::setApiKey($stripe_secret_key);
Stripe::setApiVersion('2017-02-14');
// Either create a new customer object or retrieve the existing customer object
$db->query(" SELECT StripeCustomerToken FROM stripe_ids WHERE UPPER(UserEmail) = UPPER(:email) ");
$db->bind(':email', $user_email);
$db->execute();
$customer_token = $db->fetchSingleColumn();
if(!$customer_token)
{
$customer = Stripe_Customer::create(
[
'card' => $stripe_card_token,
'description' => $user_email,
'email'=>$user_email
],
$stripe_access_token
);
$customer_token = $customer->id;
$db->query(" INSERT INTO stripe_ids (UserEmail, StripeCustomerToken) VALUES (:e, :custID) ");
$db->bind(':e', $user_email);
$db->bind(':custID', $customer_token);
$db->execute();
}
if(!$customer_token)
throw new Exception('No Stripe Customer Token detected. The payment could not be processed.');
$customer = Stripe_Customer::retrieve($customer_token);
// Using the token ID, retrieve the full token object and retrieve the fingerprint from the `card.fingerprint` attribute
$current_card = Stripe_Token::retrieve($stripe_card_token);
$current_card_fingerprint = $current_card->card->fingerprint;
$current_card_id = $current_card->card->id;
// Iterate through the list of the customer's saved sources and check if any has the same value for its `fingerprint`
$source_exists = false;
foreach($customer->sources->data as $i => $fingerprint)
{
if($current_card_fingerprint === $fingerprint)
$source_exists = true;
}
// If not, add the card to the customer object
if(!$source_exists)
$customer->sources->create(['source' => $stripe_card_token]);
$customer->default_source = $current_card_id;
$customer->save();
// Create the charge using the customer ID and the card ID
$charge = [
'customer' => $customer->id,
'source' => $current_card_id,
'amount' => $total_gross_received_cents,
'currency' => 'EUR',
'description' => $transaction_description_str,
'application_fee' => $databiz_fee
];
$charge_obj = Stripe_Charge::create($charge, $stripe_access_token);
if(empty($charge_obj))
throw new Exception('No Stripe Charge Object detected. The payment could not be processed.');
$charge_token = $charge_obj->id;
Issue:
Each time a customer uses the application, they must enter their CC details. Therefore I want to:
I was running into trouble whereby a returning customer's default card was not being updated to the currently used card (resulting in the funds coming from the default), so I added the card ID to the charge object, and saved the current card as the customer's default.
The issue now is that, on a live transaction, I am getting an InvalidRequest exception - I cannot use a token more than once.
Can someone point out to me where I am going wrong?
Upvotes: 0
Views: 347
Reputation: 17503
The token you're providing when adding the card to the customer (with $customer->sources->create(...)
) has likely already been consumed by another API request. You should check your integration to ensure that $stripe_card_token
contains a "fresh" token created by Checkout or Elements.
A few other things that I noticed:
you're using Connect with direct charges. This means the customer objects must exist on the connected account, not on the platform's account. When you retrieve the customer, you don't specify the connected account's API key, so the request is issued on your platform's account and will fail (since the customer ID doesn't exist on the platform's account)
you're using an old version (1.x) of Stripe's PHP library. You should consider upgrading to a more recent version. This will require updating your code to the new syntax (Stripe_Class
-> \Stripe\Class
)
when looking if the card already exists on the customer, you iterate over $customer->sources->data
. Note that when retrieving the customer object, this attribute will contain at most 10 sources. If the customer has more than 10 sources, you will need to use pagination to retrieve the full list.
Note that if you use a recent version of the Stripe's PHP library, you can use the auto-pagination feature to handle this easily.
Upvotes: 1