3
\$\begingroup\$

I am trying to determine if there is a better way to handle reversals for this function.

This is how it works:

A user inputs in amount, bank, and account number this would be used to debit his virtual wallet like so:

models.sequelize.transaction(function(t){
      Q.fcall(function(){
        console.log("this is amount: ",params.amount);
        var debit_wallet = models.wallet.findOne({
          attributes: ['id', 'balance'],
          transaction: t,
          lock: t.LOCK.UPDATE,
          where: {
            userId: req.session.passport.user,
            balance: {
              $gt: params.amount
            }
          }
        });

          return [debit_wallet, params];
      })
      .spread(function(debit_wallet, params){
        if (!debit_wallet) throw(new Error("insufficient balance"));

        originalBalance = debit_wallet.balance;

        debit_wallet.set('balance', parseFloat(debit_wallet.balance) - parseFloat(params.amount));

        var withdrawLog = models.withdrawal.build({
          userId: req.session.passport.user,
          //provider_ref: response.data.data.ref,
          account_number: params.accountNumber,
          bank_code: params.bankcode,
          amount: params.amount
        });

        return [ withdrawLog.save({transaction: t}), debit_wallet.save({transaction: t}), params];
      })
      .spread(function(withdrawLog, debit_wallet, params){
        var bankLog = models.bankaccount.findOrCreate({
          where: {
            'userId': req.session.passport.user,
            'bank_account': params.accountNumber,
            'bank_code': params.bankcode
          }
        });

        var walletLog = models.wallethistory.build({
          withdrawalId: withdrawLog.id,
          walletId: debit_wallet.id,
          payments_direction: 'debit',
          amount: params.amount
        });

        return [withdrawLog, debit_wallet, bankLog, walletLog.save({transaction: t}), params];
      })
      .spread(function(withdrawLog, debit_wallet, bankLog, walletLog, params){
        txd.resolve(withdrawLog);
      })
      .catch(function(err){
        console.log("Error Occurred: ", err);
        txd.reject(err);
      })
      return txd.promise;
    })
    .then(function(response){
      res.json({success: true, response: response});// A response is sent back for successful debit.
      d.resolve(response);

Only after that is the call to the external API made to actually send the money to the user's account, that call is made inside the then call of the transaction so as not to block/slowdown db operations.

Sample:

  .then(function(response){
      res.json({success: true, response: response});
      d.resolve(response);
     // Request is fired to external call
      request(requestObject).then(function(response){
     // Response is checked here if it fails I want to reverse the db action of `debit` that took place.
        if (response.data.data && response.data.data.responsecode == '00') {
          statusObject.payload = response.data.data.ref;
          request(statusObject).then(function(result){
            if (result.data.flutterResponseCode != '00' || result.data.flutterResponseCode != '0') {
          // I try to reverse debit here and update status
              Q.fcall(function(){
                var debitReversal = models.wallet.findOne({
                  attributes: ['id', 'balance'],
                  where: {
                    userId: req.session.passport.user,
                  }
                });

                var withdrawLog = models.withdrawal.findOne({
                  attributes: ['id','provider_status', 'provider_ref'],
                  where: {
                    'userId': req.session.passport.user
                  }
                });

                  return [debitReversal, withdrawLog];
              })
              .spread(function(debitReversal, withdrawLog){
                debitReversal.set('balance', parseFloat(debitReversal.balance) + parseFloat(params.amount));

                withdrawLog.set('provider_status', 'failed');
                withdrawLog.set('provider_ref', result.data.flutterReference);

                var walletLog = models.wallethistory.findOne({
                  where: {
                    'withdrawalId': withdrawLog.id
                  }
                });

                return [debitReversal.save(), withdrawLog.save(), walletLog];
              })
              .spread(function(debitReversal, withdrawLog, walletLog){
                walletLog.set('payment_state', 'failed');

                return [debitReversal, withdrawLog, walletLog.save()];
              })
              .spread(function(debitReversal, withdrawLog, walletLog){
                  txd.resolve(debitReversal);
              })
              .catch(function(err){
                console.log("Error ocurred: ", err);
                txd.reject(err);
              })
            } else {
              Q.fcall(function(){
                var withdrawLog = models.withdrawal.findOne({
                  attributes: ['id','provider_status', 'provider_ref'],
                  where: {
                    'userId': req.session.passport.user
                  }
                });

                return [withdrawLog];
              })
              .spread(function(withdrawLog){
                withdrawLog.set('provider_status', 'successful');
                withdrawLog.set('provider_ref', result.data.flutterReference);

                var walletLog = models.wallethistory.findOne({
                  where: {
                    'withdrawalId': withdrawLog.id
                  }
                });

                return [withdrawLog.save(), walletLog];
              })
              .spread(function(withdrawLog, walletLog){
                walletLog.set('payment_state', 'successful');

                return [withdrawLog, walletLog.save()];
              })
              .spread(function(withdrawLog, walletLog){
                txd.resolve(walletLog);
              })
              .catch(function(err){
                console.log("Error Occured: ", err);
                txd.reject(err);
              })
            }
          }) /* end of disburse status call */
          .catch(function(err){
            console.log("Error occured checking disburse status: ",err);
          })
        }
      }) /* end of disburse call*/
      .catch(function(err){
        console.log("Error occured disbursing: ", err);
      })
    })
    .catch(function(err){
      res.json({success: false, response: err.message});
      d.reject(err);
    })
    return d.promise;

  }

What I am doing inside the then is checking if the call failed and reversing the debit on the virtual wallet.

I need help on review to know if:

  1. I am following the right process for a reversal
  2. How the code can be much cleaner
  3. What areas I am employing bad practices
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.