0

I am working on a website with a server in nodeJS.

I implemented stripe API to make people pay every month.

I need to get their email / custumer_ID in my database (along with other informations) but I can't manage to get those informations.

here is my code :

app.post("/charge", (req, res) => {
    stripe.customers.create({
        email: req.body.stripeEmail,
        source: req.body.stripeToken
    })
        .then(customer =>
            stripe.subscriptions.create({
                plan: 'plan_EQoygL3d1jnHM2',
                customer: customer.id
                }))
        .then(charge => res.render("charge.pug"));
    var sql = "INSERT INTO authentification (customerID, email, discord_key, datePayement) values ?";
    var datenow = new Date();
    var values = [
        [customer.id, email, generateKey(), datenow]
    ];
    DB.query(sql, values, function (err, result) {
        if (err) throw err;
        console.log(result);
    });
});

This is the error I get :

ReferenceError: customer is not defined.
Customer not defined on this part : 
var values = [
            [customer.id, email, generateKey(), datenow]
        ];

I also wanted to know if the way I did it was secured or if there were other ways to do it?

Thank you very much for your help !

I am new to node JS.

lucky simon
  • 241
  • 1
  • 6
  • 22
  • customer.id only exists inside the `.then(customer => ...)` block; this is a [promise continuation](https://stackoverflow.com/questions/3884281/what-does-the-function-then-mean-in-javascript). Assuming the customer details are also in the charge object (I don't know the API), and you only want to write the customer to the database if you set the charge up successfully, you probably want to put your DB code in the `.then(charge => res.render("charge.pug"))` block before the res.render. You probably also need more graceful handling of Stripe errors throughout. – Rup Jan 31 '19 at 14:56
  • i am new to stripe so trying to figure out how to handle all the errors. And yes i am trying to figure out how to see if the payment was a success – lucky simon Jan 31 '19 at 15:06

1 Answers1

3

The customer variable only exists within the scope of this function

customer => stripe.subscriptions.create({
    plan: 'plan_EQoygL3d1jnHM2',
    customer: customer.id
})

it is the short hand form of writing

function(customer) {
    stripe.subscriptions.create({
        plan: 'plan_EQoygL3d1jnHM2',
        customer: customer.id
    })
}

and it is called when stripe.customers.create finishes running. It is asynchronous, and I'm not going to go into much detail about it, but it simply means it doesn't block the execution thread but instead it all moves on to the next line of code, and calls the above function whenever the Stripe API replies back.

Accounting for this, it means that what happens right now is that

var values = [
   [customer.id, email, generateKey(), datenow]
];

MUST be throwing an error along the lines of ReferenceError: customer is not defined

You have multiple options to solve this.

The easiest to understand and read is, provided that you're using a node version higher than 7.6 (type node -v in your terminal/cmd), using async/await to deal with the asynchronous call as such

app.post("/charge", async (req, res) => {
    try {
        var customer = await stripe.customers.create({
            email: req.body.stripeEmail,
            source: req.body.stripeToken
        })

        await stripe.subscriptions.create({ // no point in awaiting here
            plan: 'plan_EQoygL3d1jnHM2',
            customer: customer.id
        }))
        res.render("charge.pug")
        var sql = "INSERT INTO authentification (customerID, email, discord_key, datePayement) values ?";
        var datenow = new Date();
        var values = [
            [customer.id, customer.email, generateKey(), datenow]
        ];
        DB.query(sql, values, function (err, result) {
            if (err) throw err;
            console.log(result);
        });
    } catch (ex) {
        console.error('/charge encountered exception', exception) // the try/catch block is optional, but should help you figure out further problems along the way
        res.sendStatus(503)
    }
});

If, however, you're constrained to a lower version of Node, you can keep using Promises (briefly, the .then pattern you see) as such

app.post("/charge", (req, res) => {
    stripe.customers.create({
        email: req.body.stripeEmail,
        source: req.body.stripeToken
    }).then(customer => {
        var sql = "INSERT INTO authentification (customerID, email, discord_key, datePayement) values ?";
        var datenow = new Date();
        var values = [
            [customer.id, customer.email, generateKey(), datenow]
        ];
        DB.query(sql, values, function (err, result) {
            if (err) throw err;
            console.log(result);
        });
        return stripe.subscriptions.create({ // returning a Promise here means the next .then will wait for it to solve, before rendering 'charge.pug'
            plan: 'plan_EQoygL3d1jnHM2',
            customer: customer.id
        })
    })
    .then(charge => res.render("charge.pug"));
    .catch(exception => {
        console.error('/charge encountered exception', exception) // the .catch is optional, but should help you figure out further problems along the way
        res.sendStatus(503)
    })
});

Hope this helps!

  • Looks good, but in the second case I think the DB stuff belongs after the subscription has been created. – Rup Jan 31 '19 at 15:04
  • Thank you very much. this helps a lot. I didn't know how to get rid of the ".then" because I didn't trully understand the meaning. I will go from this and continu. – lucky simon Jan 31 '19 at 15:05
  • @Rup not really, as there's no code using whatever `stripe.subscriptions.create`. As I've written in the first example, too, there's no point awaiting there, it can be left to run in the background. Although, @lucky_dandu , I suggest you create a second table where to store Stripe subscription details (customerID, subscriptionID, amount, and createdAt should be quite enough to start). It helps you if you want users to be able to cancel/upgrade/downgrade their subscriptions within your UI. – Gabriel Bîra Jan 31 '19 at 15:13
  • So I should create 2 tables, 1 to check subsciptions with the discord and 1 that saves stripes infos? Do you know how to get the email of the customer? you can't get the email like we did. – lucky simon Jan 31 '19 at 15:20
  • 1
    @lucky_dandu the 2 tables thing really depends on your circumstances, but generally a good idea. About the email, you can do `async function getCustomer(customerID) { var customer = await stripe.customers.get({ customer: customerID }); console.log('email', customer.email) }` – Gabriel Bîra Jan 31 '19 at 15:24
  • @lucky_dandu I'm not sure if this was the original question and I forgot answering it or if you edited it, but let me know if you want me to edit my answer to include the bit about customer email. – Gabriel Bîra Jan 31 '19 at 15:29
  • hello @GabrielBîra yes, that was the question thank you. Thank you very much for everyone's help – lucky simon Jan 31 '19 at 15:32
  • @lucky_dandu you're welcome. I've editted the code to use the already fetched customer object to get the email. – Gabriel Bîra Jan 31 '19 at 15:33
  • At first glance [the Stripe API throws exceptions on failure](https://stripe.com/docs/api/errors/handling), so even if the subscribe result is being thrown away (and I suspect it shouldn't and the subscription ID should be saved in the DB too), if you await the create or include it in a chain of promises then page will fail if the subscription create fails. As it probably should, and it probably shouldn't write to the DB on failure. – Rup Jan 31 '19 at 16:31