Ensure that item is only purchased/(doc updated) once

I wrote an app where people can purchase credits with real money.
They can then use these credits to purchase items (news articles) in the app.
Each article should only be purchased once by a user.
The problem that occurs sometimes is, that people purchase the same item twice. This should not happen. I tried to enforce this in the code I wrote (see below).
I am not a very experienced developer, so probably I am missing some fundamental design flaw.
Could someone please take a look? :slight_smile:
(purchased articles are stored in an array of documents)

Meteor.methods({
  purchaseArticle: function(id) {
    check(id, String);

    if (!Meteor.user().emails[0].verified) {
      throw new Meteor.Error('not verified');
    }

    var user = InfoUser.findOne(this.userId);
    var article = Articles.findOne(id);

    if (article) { // does article exist?
      var array = user.purchased;
      var hasArticle = _.some(array, function(el) {
          return el._id == id;
      });
      if (!hasArticle) { // has user already purchased the article? TODO this does not seem to work
        if (user.credit >= article.price) { // does user have enough credits?
          var price = (article.price * -1);
          InfoUser.update(this.userId , {$inc: {credit: price}, $push: {'purchased.articles': {_id: id, time: new Date}}}, function(err, res) {
            if (err) {throw new Meteor.Error('error');}
          });
        } else {
          throw new Meteor.Error('not enough credits');
        }
      } else {
        throw new Meteor.Error('you already purchased this article');
      }
    } else {
      throw new Meteor.Error('article does not exist');
    }
  }
});

Hm, the thing that catches my eye is the use of _.some ā€“ Iā€™d go with:

var purchasedArray = user.purchased; // Better description, also 'array' can be confused with 'Array'
var hasArticle = _.contains(purchasedArray, id);

// Basic debug
console.log(id, purchasedArray, hasArticle);
1 Like