Subversion Repositories SmartDukaan

Rev

Rev 1981 | Rev 2141 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

package in.shop2020.serving.services;

import in.shop2020.model.v1.order.LineItem;
import in.shop2020.model.v1.order.Order;
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.Cart;
import in.shop2020.model.v1.user.Line;
import in.shop2020.model.v1.user.PromotionException;
import in.shop2020.model.v1.user.ShoppingCartException;
import in.shop2020.model.v1.user.UserContextService;
import in.shop2020.payments.PaymentException;
import in.shop2020.payments.PaymentService.Client;
import in.shop2020.serving.utils.Utils;
import in.shop2020.thrift.clients.PaymentServiceClient;
import in.shop2020.thrift.clients.PromotionServiceClient;
import in.shop2020.thrift.clients.TransactionServiceClient;
import in.shop2020.thrift.clients.UserContextServiceClient;

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

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

/**
 * 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 long paymentId;
        private double amount;

        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 currentCartId
         *            The cart for which the payment object has to be created.
         * @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 currentCartId, long userId, long txnId, int gatewayId){
                PaymentServiceClient paymentServiceClient = null;
                try {
                        paymentServiceClient = new PaymentServiceClient();
                } catch (Exception e) {
                        log.error("Error while getting payment client");
                        e.printStackTrace();
                        return PAYMENT_NOT_CREATED;
                }
                
                try {
                        amount = calculatePaymentAmount(currentCartId);
                } catch (ShoppingCartException e1) {
                        log.error("Not able to fetch payment amount from cart id." + e1.getId() + e1.getMessage());
                        e1.printStackTrace();
                        return PAYMENT_NOT_CREATED;
                } catch (TException e1) {
                        log.error("Not able to fetch payment amount." + e1.getMessage());
                        e1.printStackTrace();
                        return PAYMENT_NOT_CREATED;
                }
                
                Client paymentClient = paymentServiceClient.getClient();
                try {
                        paymentId = paymentClient.createPayment(userId, amount, gatewayId, txnId);
                } catch (PaymentException e1) {
                        log.error("Not able to create payment object." + e1.getError_code() + e1.getMessage());
                        e1.printStackTrace();
                        return PAYMENT_NOT_CREATED;
                } catch (TException e) {
                        log.error("Not able to create payment object." + e.getMessage());
                        e.printStackTrace();
                        return PAYMENT_NOT_CREATED;
                }
                
                return true;
        }

        /**
         * Marks the given transaction as in process. Once the transaction is marked
         * as successful, the items in the cart for which this transaction was
         * processed are removed from the cart.<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.<br>
         * Again, 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.
         */
        public static void processSuccessfulTxn(long txnId, UserContextServiceClient userServiceClient, TransactionServiceClient transactionServiceClient) {
                Transaction transaction = null;
                Map<Long,Double> items = new HashMap<Long, Double>();
                try {
                        in.shop2020.model.v1.order.TransactionService.Client transactionClient = transactionServiceClient.getClient();
                        transactionClient.changeTransactionStatus(txnId, TransactionStatus.IN_PROCESS, "Payment received for the order");
                        transaction = transactionClient.getTransaction(txnId);
                        for(Order order: transaction.getOrders()){
                                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);
                                }
                        }
                } catch (TException e1) {
                        log.error("Unable to update status of transaction. Thrift Exception" + e1.getMessage());
                        e1.printStackTrace();
                } catch (TransactionServiceException e) {
                        log.error("Unable to update status of transaction. Thrift Exception" + e.getErrorCode() + e.getMessage());
                        e.printStackTrace();
                }
        
        try   {
            // Tracking Coupon Usage 
            UserContextService.Client userClient = userServiceClient.getClient();
            Cart cart = userClient.getCart(transaction.getShoppingCartid());
            
            PromotionServiceClient promotionServiceClient = new PromotionServiceClient();
            String couponCode = cart.getCouponCode();

            if (couponCode != null && !couponCode.isEmpty()) {
                promotionServiceClient.getClient().trackCouponUsage(couponCode, txnId, transaction.getCustomer_id());
            }
        } catch (ShoppingCartException e) {
            log.error("Error occurred in reading CardId for transaction");
            e.printStackTrace();
        } catch (PromotionException e) {
            log.error("Promotion Exception: " + e.getMessage());
            e.printStackTrace();
        } catch (TException e)  {
            log.error("Transport from Promotion Service failed");
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        
                try {
                        //TODO Optimize the function to send less amount of data over network
                        log.info("Transaction shopping cart id is: " + transaction.getShoppingCartid());
                        log.info("Items to restr in cart are: " + items );
                                   
            userServiceClient.getClient().resetCart(transaction.getShoppingCartid(), items);
                        
                }catch (TException e) {
                        log.error("Error while updating information in payment database.");
                        e.printStackTrace();
                        //FIXME Even we get exception we should sent url back to payment gateway. And some thing back channel should be done
                        
                } catch (ShoppingCartException e) {
                        log.error("Error while reseting the cart in cart database.");
                        e.printStackTrace();
                }catch (Exception e) {
            // TODO: handle exception
        }
        }
        
        /**
         * Enqueues the transaction successful email, containing transaction info,
         * to be sent later by batch job.
         * 
         * @param transactionClient
         */
        public static void sendTxnEmail(long txnId, TransactionServiceClient transactionServiceClient) {
                in.shop2020.model.v1.order.TransactionService.Client transactionClient = transactionServiceClient.getClient();
                try {
                        transactionClient.enqueueTransactionInfoEmail(txnId);
                } catch (Exception e) {
                        log.error("Error while adding email to dispatch queue.");
                        e.printStackTrace();
                }
        }

        /**
         * 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, TransactionServiceClient transactionServiceClient) {
                try {
                        in.shop2020.model.v1.order.TransactionService.Client transactionClient = transactionServiceClient.getClient();
                        transactionClient.changeTransactionStatus(txnId, TransactionStatus.FAILED, "Payment failed for the transaction.");
                } catch(TException e){
                        log.error("Thrift exception while getting information from transaction service.");
                        e.printStackTrace();
                } catch (TransactionServiceException e) {
                        log.error("Error while updating status information in transaction database.");
                        e.printStackTrace();
                }
        }

        /**
         * Calculates the amount for the payment required for the given cart.
         * 
         * @param cartId
         *            Id of the cart for which this payment amount has to be
         *            calculated.
         * @return The total amount for which a payment should be created.
         * @throws ShoppingCartException
         *             If no cart can be found for the given id.
         * @throws TException
         */
        private double calculatePaymentAmount(long cartId) throws ShoppingCartException, TException{
                double totalAmount = 0;
                Cart cart;
                UserContextServiceClient userContextServiceClient = null;
                try {
                        userContextServiceClient = new UserContextServiceClient();
                } catch (Exception e) {
                        e.printStackTrace();
                }
                in.shop2020.model.v1.user.UserContextService.Client userClient = userContextServiceClient.getClient();
                cart = userClient.getCart(cartId);
                
                if(cart.getCouponCode() == null || cart.getDiscountedPrice() == 0.0)  {
                    List<Line> lineItems = cart.getLines(); 
                    
                for (Line line : lineItems) {
                    long productId = line.getItemId();
                    // FIXME: It's expensive to get the price of each item from the
                    // catalog service. We maintain the pricing info in the line items
                    // themselves now.
                    totalAmount =  totalAmount + line.getQuantity() * Utils.getItemPrice(productId);
                }
                } else    {
                    totalAmount = cart.getDiscountedPrice();
                }
                
                return totalAmount;
        }

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