Subversion Repositories SmartDukaan

Rev

Rev 13521 | Blame | Compare with Previous | Last modification | View Log | RSS feed

package in.shop2020.serving.services;

import in.shop2020.logistics.PickUpType;
import in.shop2020.model.v1.order.EmiScheme;
import in.shop2020.model.v1.order.LineItem;
import in.shop2020.model.v1.order.Order;
import in.shop2020.model.v1.order.OrderSource;
import in.shop2020.model.v1.order.OrderType;
import in.shop2020.model.v1.order.RechargeOrder;
import in.shop2020.model.v1.order.Transaction;
import in.shop2020.model.v1.order.TransactionServiceException;
import in.shop2020.model.v1.order.TransactionStatus;
import in.shop2020.model.v1.user.PrivateDealUser;
import in.shop2020.model.v1.user.PromotionException;
import in.shop2020.model.v1.user.ShoppingCartException;
import in.shop2020.payments.Payment;
import in.shop2020.payments.PaymentException;
import in.shop2020.payments.PaymentStatus;
import in.shop2020.serving.controllers.ProceedToPayController;
import in.shop2020.thrift.clients.PaymentClient;
import in.shop2020.thrift.clients.PromotionClient;
import in.shop2020.thrift.clients.TransactionClient;
import in.shop2020.thrift.clients.UserClient;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.apache.thrift.TException;
import org.apache.thrift.transport.TTransportException;

/**
 * This class has methods to be used to process non-gateway-specific aspects of
 * payments and transactions.
 * 
 * @author Chandranshu
 * 
 */
public class CommonPaymentService {

        private static final boolean PAYMENT_NOT_CREATED = false;
        
        private static Logger log = Logger.getLogger(Class.class);
        private static long gvGatewayId = 9;
        
        private long paymentId;
        private double amount;
        private boolean isGv = false;
        private double gvAmount = 0;

        public long getPaymentId() {
                return paymentId;
        }

        public double getAmount() {
                return amount;
        }

        /**
         * Creates a payment for the given cart of the given user for the given
         * transaction. Stores the id of the newly created payment and the amount
         * for which this payment was created. They can be retrieved later on using
         * {@link #getPaymentId()}getPaymentId() and {@link #getAmount()}getAmount()
         * methods respectively later on.
         * 
         * @param userId
         *            The user for whom the payment has to be created.
         * @param txnId
         *            The transaction against which the payment has to be created.
         * @param gatewayId
         * @return True if the payment object is successfully created, False
         *         otherwise.
         */
        public boolean createPayment(long userId, long txnId, int gatewayId){
                PaymentClient paymentServiceClient = null;
                in.shop2020.payments.PaymentService.Client pclient;
                try {
                        paymentServiceClient = new PaymentClient();
                        pclient = paymentServiceClient.getClient();
                } catch (Exception e) {
                        log.error("Error while getting payment client", e);
                        return PAYMENT_NOT_CREATED;
                }
                
                try {
                        amount = calculatePaymentAmount(txnId);
                } catch (TransactionServiceException e1) {
                        log.error("Unable to fetch payment amount from txn id.", e1);
                        return PAYMENT_NOT_CREATED;
                } catch (TException e1) {
                        log.error("Unable to fetch payment amount.", e1);
                        return PAYMENT_NOT_CREATED;
                }
                
                try {
                        if(isGv){
                                paymentId = pclient.createPayment(userId, gvAmount, gvGatewayId, txnId, false);
                                pclient.updatePaymentDetails(paymentId, "", "", "SUCCESS", "Payment Received", "", "", "", "", PaymentStatus.SUCCESS, "", null);
                        }
                        
                        if(amount > 0) {
                        paymentId = paymentServiceClient.getClient().createPayment(userId, amount, gatewayId, txnId, false);
                        // This is being done to ensure that the amount which we pass on to
                        // the PGs is same as what we have in the database.
                        Payment payment = paymentServiceClient.getClient().getPayment(paymentId);
                        amount = payment.getAmount();
                        }
                } catch (PaymentException e1) {
                        log.error("Unable to create payment object.", e1);
                        return PAYMENT_NOT_CREATED;
                } catch (TException e) {
                        log.error("Not able to create payment object.", e);
                        return PAYMENT_NOT_CREATED;
                }
                
                return true;
        }

        // TODO: The service client parameters in the processSuccessfulTxn et al are
        // unnecessary but initializing them again when the caller has the necessary
        // references has a performance overhead. Need to think more about this.

        /**
         * Processes a successful transaction by:
         * <ol>
         * <li>Marking the given transaction as 'authorized'.</li>
         * <li>Removing the items in the cart for which the given transaction was
         * processed.</li>
         * <li>Marking the coupon associated with this transaction, if any, as used
         * for this user.</li>
         * <li>Queuing the transaction successful email, containing transaction
         * info, to be sent later by a batch job.</li>
         * </ol>
         * <br>
         * Please note that it's possible that a user has added items to the cart
         * and so it's not possible to simply wipe out their cart. Therefore, it's
         * important to ensure that we remove only as much quantity of items as for
         * which the order was processed.
         * 
         * @param txnId
         *            The transaction which should be marked as successful.
         * @param userServiceClient
         *            A user context service client to use.
         * @param transactionServiceClient
         *            A transaction service client to use.
         * @param isFlagged
         *                        If payment is flagged it will be true else false 
         */
        public static void processSuccessfulTxn(long txnId, UserClient userServiceClient, TransactionClient transactionServiceClient, boolean isFlagged) {
                Transaction transaction = null;
                TransactionStatus tStatus = TransactionStatus.AUTHORIZED;
        String description = "Payment authorized for the order";
                // if flag is set, status to be changed to flagged
                if(isFlagged){
                        tStatus = TransactionStatus.FLAGGED;
                        description = "Payment flagged for the order";
                }
                try {
                        in.shop2020.model.v1.order.TransactionService.Client transactionClient = transactionServiceClient.getClient();
                        transaction = transactionClient.getTransaction(txnId);
                        
                        transactionClient.changeTransactionStatus(txnId, tStatus, description, PickUpType.COURIER.getValue(), getOrderType(transaction.getCustomer_id()), OrderSource.WEBSITE);
                        transactionClient.enqueueTransactionInfoEmail(txnId);
                } catch (TException e1) {
                        log.error("Unable to update status of transaction. Thrift Exception:", e1);
                } catch (TransactionServiceException e) {
                        log.error("Unable to update status of transaction. Thrift Exception: ", e);
                }
                long sum = resetCart(transaction, userServiceClient);
                trackCouponUsage(transaction, sum);
        }

        /**
         * Marks a transaction as well as all its orders as failed.
         * 
         * @param txnId
         *            The id of the transaction which has to be marked as failed.
         * @param transactionServiceClient
         */
        public static void processFailedTxn(long txnId, TransactionClient transactionServiceClient) {
                try {
                        in.shop2020.model.v1.order.TransactionService.Client transactionClient = transactionServiceClient.getClient();
                        transactionClient.changeTransactionStatus(txnId, TransactionStatus.FAILED, "Payment failed for the transaction.", PickUpType.COURIER.getValue(), OrderType.B2C, OrderSource.WEBSITE);
                } catch(TException e){
                        log.error("Thrift exception while getting information from transaction service.", e);
                } catch (TransactionServiceException e) {
                        log.error("Error while updating status information in transaction database.", e);
                }
        }

    /**
     * Processes a COD transaction by:
     * <ol>
     * <li>Setting the COD flag of all the orders and moving them to the INIT
     * state.
     * <li>Marking the given transaction to be in COD_IN_PROCESS state
     * <li>Marking the coupon associated with this transaction, if any, as used
     * for this user.</li>
     * <li>Queuing the transaction successful email, containing transaction
     * info, to be sent later by a batch job.</li>
     * </ol>
     * <br>
     * Please note that it's possible that a user has added items to the cart
     * and so it's not possible to simply wipe out their cart. Therefore, it's
     * important to ensure that we remove only as much quantity of items as for
     * which the order was processed.
     * 
     * @param txnId
     *            The COD transaction which should be marked as verification
     *            pending.
     */
        public static void processCodTxn(long txnId, OrderType orderType){
        try {            
            TransactionClient transactionServiceClient = new TransactionClient();
            in.shop2020.model.v1.order.TransactionService.Client transactionClient = transactionServiceClient.getClient();
            transactionClient.changeTransactionStatus(txnId, TransactionStatus.COD_IN_PROCESS, "COD payment awaited", PickUpType.COURIER.getValue(), orderType, OrderSource.WEBSITE);
            Transaction transaction = transactionClient.getTransaction(txnId);
            transactionClient.enqueueTransactionInfoEmail(txnId);
            
            UserClient userServiceClient = new UserClient();
            long sum = resetCart(transaction, userServiceClient);
            trackCouponUsage(transaction, sum);
        } catch (TException e1) {
            log.error("Unable to update status of transaction. Thrift Exception:", e1);
        } catch (TransactionServiceException e) {
            log.error("Unable to update status of transaction. Thrift Exception: ", e);
        } catch (Exception e) {
            log.error("Unable to update status of transaction. Thrift Exception: ", e);
        }
        }
        
        public static void processCouponTxn(long txnId, OrderType orderType){
        try {            
            TransactionClient transactionServiceClient = new TransactionClient();
            in.shop2020.model.v1.order.TransactionService.Client transactionClient = transactionServiceClient.getClient();
            transactionClient.changeTransactionStatus(txnId, TransactionStatus.AUTHORIZED, "Payment by coupon successful", PickUpType.COURIER.getValue(), orderType, OrderSource.WEBSITE);
            Transaction transaction = transactionClient.getTransaction(txnId);
            UserClient userServiceClient = new UserClient();
            long sum = resetCart(transaction, userServiceClient);
            trackCouponUsage(transaction, sum);
        } catch (TException e1) {
            log.error("Unable to update status of transaction. Thrift Exception:", e1);
        } catch (TransactionServiceException e) {
            log.error("Unable to update status of transaction. Thrift Exception: ", e);
        } catch (Exception e) {
            log.error("Unable to update status of transaction. Thrift Exception: ", e);
        }
    }
        
        /**
         * Calculates the amount for the payment required for the given transaction.
         * 
         * @param txnId
         *            Id of the transaction for which this payment amount has to be
         *            calculated.
         * @return The total amount for which a payment should be created.
         * @throws TransactionServiceException 
         * @throws TException
         */
        private double calculatePaymentAmount(long txnId) throws TransactionServiceException, TException{
                double totalAmount = 0;
                TransactionClient transactionServiceClient = null;
                try {
                        transactionServiceClient = new TransactionClient();
                } catch (Exception e) {
                        log.error("Unable to initialize user context service client", e);
                        throw new TransactionServiceException(100, "Unable to initialize the user service client");
                }
                in.shop2020.model.v1.order.TransactionService.Client tClient = transactionServiceClient.getClient();
                Transaction transaction = tClient.getTransaction(txnId);
//              Map<Long, Double> miscCharges = tClient.getMiscCharges(txnId);
//              System.out.println(miscCharges);
//              if(miscCharges != null & !miscCharges.isEmpty()){
//                      totalAmount = totalAmount + miscCharges.get(1L);
//              }
                for(Order order: transaction.getOrders()){
                        totalAmount = totalAmount + order.getTotal_amount();
                        gvAmount = gvAmount + order.getGvAmount();
                }
                if(gvAmount>0){
                        isGv = true;    
                }
                return (totalAmount-gvAmount);
        }

        /**
         * Removes the items processed through the given transaction from the
         * shopping cart.
         * 
         * @param transaction
         *            The transaction whose items have to be removed from the
         *            shopping cart.
         * @param userServiceClient
         */
        private static long resetCart(Transaction transaction, UserClient userServiceClient) {
                long sum = 0;
                Map<Long, Double> items = new HashMap<Long, Double>();
                for(Order order: transaction.getOrders()){
                        sum += order.getGvAmount();
                        for(LineItem lineitem: order.getLineitems()){
                                Long itemId = lineitem.getItem_id();
                                Double quantity = items.get(itemId);
                                if(quantity==null){
                                        quantity = lineitem.getQuantity();
                                } else {
                                        quantity= quantity + lineitem.getQuantity();
                                }
                                items.put(itemId, quantity);
                        }
                }
                
                log.debug("Items to reset in cart are: " + items);
                
                try {
                        //TODO Optimize the function to send less data over the wire
            userServiceClient.getClient().resetCart(transaction.getShoppingCartid(), items);
                }catch (TException e) {
                        log.error("Error while updating information in payment database.", e);
                } catch (ShoppingCartException e) {
                        log.error("Error while reseting the cart in cart database.", e);
                }catch (Exception e) {
                        log.error("Unexpected exception", e);
        }
                
                return sum;
        }

        /**
         * Mark the coupon associated with the given transaction as used.
         * 
         * @param transaction
         *            The transaction to track coupon for.
         * 
         * @param userServiceClient
         *            The user service client instance to use.
         */
        private static void trackCouponUsage(Transaction transaction, long sum) {
        try {
            String couponCode = transaction.getCoupon_code();
            
            if (couponCode != null && !couponCode.isEmpty()) {
                PromotionClient promotionServiceClient = new PromotionClient();
                promotionServiceClient.getClient().trackCouponUsage(couponCode, transaction.getId(), transaction.getCustomer_id(), sum, false);
            }
        } catch (PromotionException e) {
            log.error("Promotion Exception: " + e);
        } catch (TException e)  {
            log.error("Transport from Promotion Service failed:", e);
        } catch (Exception e) {
            log.error("Unexpected exception:", e);
        }
        }

    public boolean createPayment(RechargeOrder rechargeOrder, int gatewayId) {
        PaymentClient paymentServiceClient = null;
        try {
            paymentServiceClient = new PaymentClient();
        } catch (Exception e) {
            log.error("Error while getting payment client", e);
            return PAYMENT_NOT_CREATED;
        }
        
        amount = rechargeOrder.getTotalAmount() - rechargeOrder.getWalletAmount() - rechargeOrder.getCouponAmount();
        
        try {
            paymentId = paymentServiceClient.getClient().createPayment(rechargeOrder.getUserId(), amount, gatewayId, rechargeOrder.getTransactionId(), true);
            // This is being done to ensure that the amount which we pass on to
            // the PGs is same as what we have in the database.
            Payment payment = paymentServiceClient.getClient().getPayment(paymentId);
            amount = payment.getAmount();
        } catch (PaymentException e1) {
            log.error("Unable to create payment object.", e1);
            return PAYMENT_NOT_CREATED;
        } catch (TException e) {
            log.error("Not able to create payment object.", e);
            return PAYMENT_NOT_CREATED;
        }
        
        return true;
    
    }
    
    public static double calculateEmiAmount(String payOption, double totalAmount){
        double emiAmount = 0.0;
        List<EmiScheme> schemes = ProceedToPayController.getEmiSchemes();
        for(EmiScheme scheme: schemes){
                if(scheme.getId() == Long.parseLong(payOption)){
                        double interestRate = scheme.getInterestRate()/12/100;
                        emiAmount = totalAmount*interestRate*Math.pow((1+interestRate), scheme.getTenure())/(Math.pow((1+interestRate), scheme.getTenure())-1);
                }
        }
        return emiAmount;
    }
    
    public static void main(String[] args) {
        Map<Long, Double> miscCharges = new HashMap<Long, Double>();
        miscCharges.put(1L, 3.988);
        System.out.println(miscCharges);
                if(miscCharges != null & !miscCharges.isEmpty()){
                        System.out.println( miscCharges.get(1L));;
                }
        }
    
        private static OrderType getOrderType(Long userId) throws TTransportException, TException{
                OrderType ot = OrderType.B2C;
                try {
                        
                        in.shop2020.model.v1.user.UserContextService.Client uc = new UserClient().getClient();
                        PrivateDealUser pdu = uc.getPrivateDealUser(userId);
                        if(pdu.getTin() != null && !pdu.getTin().trim().equals("")){
                                ot = OrderType.B2B;
                        }
                } catch (TTransportException e) {
                        log.error("Unable to get user service client.", e);     
                }
                return ot;
        }
}