Rev 3561 | Rev 5527 | 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.catalog.Item;import in.shop2020.model.v1.catalog.InventoryService.Client;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.payments.Payment;import in.shop2020.payments.PaymentException;import in.shop2020.thrift.clients.CatalogClient;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;/*** 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, long sourceId){PaymentClient paymentServiceClient = null;try {paymentServiceClient = new PaymentClient();} catch (Exception e) {log.error("Error while getting payment client", e);return PAYMENT_NOT_CREATED;}try {amount = calculatePaymentAmount(currentCartId, sourceId);} catch (ShoppingCartException e1) {log.error("Unable to fetch payment amount from cart id.", e1);return PAYMENT_NOT_CREATED;} catch (TException e1) {log.error("Unable to fetch payment amount.", e1);return PAYMENT_NOT_CREATED;}try {paymentId = paymentServiceClient.getClient().createPayment(userId, amount, gatewayId, txnId);// 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 flaggedif(isFlagged){tStatus = TransactionStatus.FLAGGED;description = "Payment flagged for the order";}try {in.shop2020.model.v1.order.TransactionService.Client transactionClient = transactionServiceClient.getClient();transactionClient.changeTransactionStatus(txnId, tStatus, description);transaction = transactionClient.getTransaction(txnId);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);}trackCouponUsage(transaction);resetCart(transaction, userServiceClient);}/*** 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.");} 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){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");Transaction transaction = transactionClient.getTransaction(txnId);transactionClient.enqueueTransactionInfoEmail(txnId);UserClient userServiceClient = new UserClient();trackCouponUsage(transaction);resetCart(transaction, userServiceClient);} 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 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, long sourceId) throws ShoppingCartException, TException{double totalAmount = 0;Cart cart;UserClient userContextServiceClient = null;try {userContextServiceClient = new UserClient();} catch (Exception e) {log.error("Unable to initialize user context service client", e);throw new ShoppingCartException(100, "Unable to initialize the user service client");}in.shop2020.model.v1.user.UserContextService.Client userClient = userContextServiceClient.getClient();cart = userClient.getCart(cartId);if(cart.getCouponCode() == null) {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() * getItemPrice(productId, sourceId);}} else {totalAmount = cart.getDiscountedPrice();}return totalAmount;}private double getItemPrice(long itemId, long sourceId){CatalogClient catalogClient = null;Client client = null;Double itemPrice = 0.0;try {catalogClient = new CatalogClient();client = catalogClient.getClient();Item item = client.getItemForSource(itemId, sourceId);itemPrice = item.getSellingPrice();} catch(Exception e){log.error("Unable to get the item price because of:", e);}return itemPrice;}/*** 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 void resetCart(Transaction transaction, UserClient userServiceClient) {Map<Long, Double> items = new HashMap<Long, Double>();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);}}log.debug("Items to reset in cart are: " + items);try {//TODO Optimize the function to send less data over the wireuserServiceClient.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);}}/*** 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) {try {String couponCode = transaction.getCoupon_code();if (couponCode != null && !couponCode.isEmpty()) {PromotionClient promotionServiceClient = new PromotionClient();promotionServiceClient.getClient().trackCouponUsage(couponCode, transaction.getId(), transaction.getCustomer_id());}} 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);}}}