0
votes

TLDR; I want to allow customers to place orders using the previously used/saved card of their choice in my app. This requires the ability to specify to Stripe "which" card (previously associated to a customer) to charge. This should be possible in theory according to the docs, but I'm having no luck in practice.

Alright... talk is cheap, here are the codes:

// pseudo code of a customer placing an order for the first time
var stripe = require("stripe")(".....");

(async function(userId, cardToken) {
    // Create a Customer AND save the payment method
    const customer = await stripe.customers.create({
        source: cardToken.id, // e.g. 'tok_mastercard'  (retrieved from stripe.js)
        email: '[email protected]',
    });

    // Charge the Customer instead of the card:
    const charge = await stripe.charges.create({
        amount: 1000,
        currency: 'usd',
        customer: customer.id
    });

    // save customer id to DB
    await setCustomerIdToDatabase(userId, customer.id);

    // save Card ID (not the card token ID) to DB
    await addCustomerCardToDatabase(userId, cardToken.card.id); // <= note cardToken".card".id  e.g. card_kldlkns...

})(userId, cardToken /* <= provided by app*/);

So far so good. Stripe saved the card from the specified token as the default payment method for the newly created customer then charges it successfully. But then my pain begins...

// pseudo code of a customer placing subsequent orders (WITH NEW CARDS)
var stripe = require("stripe")(".....");

(async function(userId, cardToken) {

    const customerId = await getCustomerIdFromDatabase(userId);

    // Create a Customer AND save the payment method
    await stripe.customers.createSource(
        customerId,
        { 
            source: cardToken.id, // e.g. 'tok_mastercard'  (retrieved from stripe.js)
        }
    );

    // Attempt to charge the newly added card instead the Customer:
    const charge = await stripe.charges.create({
        amount: 1000,
        currency: 'usd',
        customer: customerId,
        source: cardToken.card.id // <= note cardToken".card".id  e.g. card_kldlkns...
    });

    /////////ERROR TRHOWN: No such token: card_kldlkns...
    ///////// what? I just saved it with .customers.createSource and you said it was OK???

    /// unreachable code

    // save Card ID (not the card token ID) to DB
    await addCustomerCardToDatabase(userId, cardToken.card.id); // <= note cardToken".card".id  e.g. card_kldlkns...

})(userId, cardToken /* <= provided by app*/);

Fails miserably... and so does this

// pseudo code of a customer placing subsequent orders (WITH SAVED CARDS)
var stripe = require("stripe")(".....");

(async function(userId, savedCardId) {

    const customerId = await getCustomerIdFromDatabase(userId);

    // Attempt to charge the newly added card instead the Customer:
    const charge = await stripe.charges.create({
        amount: 1000,
        currency: 'usd',
        customer: customerId,
        source: savedCardId  //  e.g card_kldlkns...
    });

    /////////ERROR TRHOWN: No such token: card_kldlkns...
    ///////// what??????

})(userId, savedCardId /* <= provided by app*/);

What am I not getting here? The docs clearly says Stripe wants the card.id and the customer.id back if I want to charge a specific card.

Any help is appreciated

1
Making a call to stripe.charges.create with both the customer and the source equal to a card id should charge a specific card on a given customer. If you're getting no such token errors, try listing the sources on a given customer first to ensure a card with the specific id card_dddyyyxxx is really there, stripe.com/docs/api/cards/list - duck
@duck true, but as you can see, the card was just added to the customer with the stripe.customers.createSource call, then charged immediately after the promise resolved successfully (no error thrown). In this case, checking whether the card is really there before charging help wouldn't help much because the situation is not remediable. All I could do is throw an error to the client myself instead of let stripe throw it. Maybe that's not the way to do this at all, hence this post - rookienumberone
Yes, I realize this isn't what you'd implement in production, I am purely speaking in terms of debugging this... if you're getting errors that a card is not there ("no such token"), it can be helpful to debug by seeing what card(s), if any, ARE actually saved to the customer at the time you call stripe.charges.create - duck

1 Answers

0
votes

Turns out this was not an issue at all.

There was a faulty condition is the codes that was calling the wrong stripe API method