Rev 3649 | Rev 4008 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
package in.shop2020.payment.service.handler;import in.shop2020.crm.CRMService.Client;import in.shop2020.payment.domain.Refund;import in.shop2020.payment.handler.PaymentGatewayHandler;import in.shop2020.payment.handler.PaymentHandler;import in.shop2020.payment.handler.RefundHandler;import in.shop2020.payments.Attribute;import in.shop2020.payments.Payment;import in.shop2020.payments.PaymentException;import in.shop2020.payments.PaymentGateway;import in.shop2020.payments.PaymentService.Iface;import in.shop2020.payments.PaymentStatus;import in.shop2020.thrift.clients.CRMClient;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Date;import java.util.HashMap;import java.util.List;import java.util.Map;import org.apache.thrift.TException;import org.apache.thrift.transport.TTransportException;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class PaymentServiceHandler implements Iface {private static Logger logger = LoggerFactory.getLogger(PaymentServiceHandler.class);/*** Enum of all statuses that can be returned by the HDFC gateway** @author Chandranshu**/private enum HdfcPaymentReturnStatus{APPROVED("APPROVED"),NOT_APPROVED("NOT APPROVED"),CAPTURED("CAPTURED"),NOT_CAPTURED ("NOT CAPTURED"),CANCELLED ("CANCELLED"),DENIED_BY_RISK("DENIED BY RISK"),HOST_TIMEOUT("HOST TIMEOUT");private String value;HdfcPaymentReturnStatus(String value) {this.value = value;}public String value(){return this.value;}}public static final long PAYMENT_NOT_CREATED = -1;private static final long HDFC_GATEWAY_ID = 1;private static final long EBS_GATEWAY_ID = 2;private static final long HDFC_EMI_GATEWAY_ID = 5;ApplicationContext context = new ClassPathXmlApplicationContext("context.xml");PaymentHandler paymentHandler = (PaymentHandler) context.getBean("paymentHandler");PaymentGatewayHandler paymentGatewayHandler = (PaymentGatewayHandler) context.getBean("paymentGatewayHandler");RefundHandler refundHandler = (RefundHandler) context.getBean("refundHandler");@Overridepublic long createPayment(long userId, double amount, long gatewayId, long txnId) throws PaymentException, TException {logger.info("Creating payment corresponding to our txn id:" + txnId);in.shop2020.payment.domain.Payment payment = new in.shop2020.payment.domain.Payment();payment.setUserId(userId);payment.setAmount(amount);payment.setGatewayId(gatewayId);payment.setMerchantTxnId(txnId);payment.setStatus(PaymentStatus.INIT.getValue());return paymentHandler.insertPayment(payment);}@Overridepublic List<Payment> getPaymentsForUser(long userId, long fromTime, long toTime, PaymentStatus status, long gatewayId) throws PaymentException, TException {logger.info("Getting payments from " + fromTime + " to " + toTime + " for user: " + userId);int statusValue = -1;if(status != null)statusValue = status.getValue();elsestatusValue = -1;return getThriftPayments(paymentHandler.getPaymentsForUser(userId, fromTime, toTime, statusValue, gatewayId));}@Overridepublic List<Payment> getPayments(long fromTime, long toTime, PaymentStatus status, long gatewayId) throws PaymentException, TException {logger.info("Getting payments from " + fromTime + " to " + toTime);int statusValue = -1;if(status != null)statusValue = status.getValue();elsestatusValue = -1;return getThriftPayments(paymentHandler.getPayments(fromTime, toTime, statusValue, gatewayId));}@Overridepublic PaymentGateway getPaymentGateway(long id) throws PaymentException, TException {logger.info("Getting payment gateway with id:" + id);return paymentGatewayHandler.getPaymentGateway(id).getThriftPaymentGateway();}@Overridepublic Payment getPayment(long id) throws PaymentException, TException {logger.info("Getting payment with id: " + id);return paymentHandler.getPayment(id).getThriftPayment();}@Overridepublic List<Payment> getPaymentForTxnId(long txnId) throws PaymentException, TException {logger.info("Getting payment for the txn id: " + txnId);return getThriftPayments(paymentHandler.getPaymentForTxn(txnId));}@Overridepublic boolean updatePaymentDetails(long id, String gatewayPaymentId,String sessionId, String gatewayTxnStatus, String description,String gatewayTxnId, String authCode, String referenceCode,String errorCode, PaymentStatus status, String gatewayTxnDate,List<Attribute> attributes) throws PaymentException, TException {logger.info("Updating details of payment id: " + id);in.shop2020.payment.domain.Payment payment = paymentHandler.getPayment(id);payment.setGatewayPaymentId(gatewayPaymentId);payment.setSessionId(sessionId);payment.setGatewayTxnStatus(gatewayTxnStatus);payment.setDescription(description);payment.setGatewayTxnId(gatewayTxnId);payment.setAuthCode(authCode);payment.setReferenceCode(referenceCode);payment.setErrorCode(errorCode);if(status!=null){payment.setStatus(status.getValue());if(status.equals(PaymentStatus.SUCCESS))payment.setSuccessTimestamp(new Date());else if(status.equals(PaymentStatus.FAILED)) {payment.setErrorTimestamp(new Date());createTicketForFailedPayment(payment);}}payment.setGatewayTxnDate(gatewayTxnDate);Map<String, String> attrMap = new HashMap<String, String>();if(attributes != null){for(Attribute attribute : attributes){attrMap.put(attribute.getName(), attribute.getValue());}}paymentHandler.updatePayment(payment, attrMap);return true;}/*** Creates a ticket in CRM tool for each customer whose payment got failed.* Later, CRM agents follow-up with the users and try making a sale.** We swallow the exceptions raised and just log them at error level. This is* done to not make Website unaffected by any CRM related issues.** @param payment the payment object that failed.*/private void createTicketForFailedPayment(in.shop2020.payment.domain.Payment payment) {try {Client crmClient = new CRMClient().getClient();// This call is aync (oneway). This ensures that website response// is sent in time.crmClient.processPaymentFailure(payment.getUserId());} catch (TTransportException e) {logger.error("Could not create CRM client", e);} catch (TException e) {logger.error("Could not process paymentId: " + payment.getId(), e);}}@Overridepublic List<Double> getSuccessfulPaymentsAmountRange() throws TException {logger.info("Getting the range of successful payments.");List<Double> minMaxAmounts = new ArrayList<Double>();Map<String, Float> minMax = paymentHandler.getMinMaxPaymentAmount();minMaxAmounts.add(Double.parseDouble(Float.toString(minMax.get("MIN"))));minMaxAmounts.add(Double.parseDouble(Float.toString(minMax.get("MAX"))));return minMaxAmounts;}@Overridepublic String initializeHdfcPayment(long merchantPaymentId) throws PaymentException, TException {logger.info("Initializing HDFC payment with id: " + merchantPaymentId);in.shop2020.payment.domain.Payment payment = paymentHandler.getPayment(merchantPaymentId);String redirectURL;try {redirectURL = HdfcPaymentHandler.initializeHdfcPayment(payment, this);} catch (Exception e) {throw new PaymentException(102, "Error while initiliazing payment. Check service log for more details.");}return redirectURL;}@Overridepublic String initializeHdfcEmiPayment(long merchantPaymentId) throws PaymentException, TException {logger.info("Initializing HDFC payment with id: " + merchantPaymentId);in.shop2020.payment.domain.Payment payment = paymentHandler.getPayment(merchantPaymentId);String redirectURL;try {redirectURL = HdfcEmiPaymentHandler.initializeHdfcPayment(payment, this);} catch (Exception e) {throw new PaymentException(102, "Error while initiliazing payment. Check service log for more details.");}return redirectURL;}@Overridepublic long createRefund(long orderId, long merchantTxnId, double amount) throws PaymentException, TException{logger.info("Attempting to create a refund for order: " + orderId);List<in.shop2020.payment.domain.Payment> payments = paymentHandler.getPaymentForTxn(merchantTxnId);if(payments ==null || payments.isEmpty())throw new PaymentException(104, "No payments found corresponding to the merchant txn " + merchantTxnId);in.shop2020.payment.domain.Payment payment = payments.get(0);if(payment.getStatus() != PaymentStatus.SUCCESS.getValue())throw new PaymentException(104, "No successful payments found corresponding to the merchant txn " + merchantTxnId);Refund refund = new Refund();refund.setOrderId(orderId);refund.setPaymentId(payment.getId());refund.setGatewayId(payment.getGatewayId());refund.setAmount(amount);refund.setAttempts(0);return refundHandler.createRefund(refund);}@Overridepublic boolean capturePayment(long merchantTxnId) throws PaymentException, TException {logger.info("Attempting to capture payment corresponding to our transaction " + merchantTxnId);List<in.shop2020.payment.domain.Payment> payments = paymentHandler.getPaymentForTxn(merchantTxnId);if(payments ==null || payments.isEmpty())throw new PaymentException(104, "No payments found corresponding to the merchant txn " + merchantTxnId);in.shop2020.payment.domain.Payment payment = payments.get(0);switch(PaymentStatus.findByValue(payment.getStatus())){case PENDING:logger.error("Attempt to capture a non-authorized payment");return false;case INIT:logger.warn("Attempt to capture a non-authorized payment");return false;case AUTHORIZED://Actual work to be done in this case. Let the call proceed.break;case SUCCESS:logger.warn("Attempting to capture an already captured payment but we can let the client proceed.");return true;case FAILED:logger.error("Attempting to capture a failed payment");return false;}long gatewayId = payment.getGatewayId();if(gatewayId == HDFC_GATEWAY_ID){//Capture and update the HDFC paymentreturn captureAndUpdateHdfcPayment(payment);} else if (gatewayId == EBS_GATEWAY_ID){//Capture and update the EBS paymentreturn captureAndUpdateEbsPayment(payment);} else if (gatewayId == HDFC_EMI_GATEWAY_ID){//Capture and update the HDFC EMI paymentreturn captureAndUpdateHdfcEmiPayment(payment);}logger.error("We have an authorized payment from unknown gateway: " + gatewayId);return false;}@Overridepublic boolean partiallyCapturePayment(long merchantTxnId, double amount, String xferBy, String xferTxnId, long xferDate) throws PaymentException, TException {logger.info("Attempting to partially capture payment corresponding to our transaction " + merchantTxnId);List<in.shop2020.payment.domain.Payment> payments = paymentHandler.getPaymentForTxn(merchantTxnId);if(payments ==null || payments.isEmpty())throw new PaymentException(104, "No payments found corresponding to the merchant txn " + merchantTxnId);in.shop2020.payment.domain.Payment payment = payments.get(0);switch(PaymentStatus.findByValue(payment.getStatus())){case PENDING:// COD payments lie in this state before settlement.case INIT:case PARTIALLY_CAPTURED:case AUTHORIZED:// COD payments would not be in this state but we are processing// payments in this state as well for forward compatibility since// someday we'd want to be able to capture authorized CC payments// partially.break;case SUCCESS:logger.warn("Attempting to capture an already captured payment but we can let the client proceed.");return true;case FAILED:logger.error("Attempting to capture a failed payment");return false;}SimpleDateFormat mysqlDateFormatter = new SimpleDateFormat("yyyy-MM-dd 00:00:00");String xferDateStr = mysqlDateFormatter.format(new Date(xferDate));return settleAndUpdateCodPayment(payment, amount, xferBy, xferTxnId, xferDateStr);}/*** Capture the HDFC payment represented by the given payment object. If the* capture attempt is not successful, we mark this payment as failed. We* don't retry or anything. We'll add the support of multiple attempts later* on.** @param payment The payment which has to be captured.* @return True if the payment attempt is successful, false if not.*/private boolean captureAndUpdateHdfcPayment(in.shop2020.payment.domain.Payment payment){long merchantPaymentId = payment.getId();logger.info("Capturing HDFC payment with id: " + merchantPaymentId);Map<String, String> captureResult = HdfcPaymentHandler.capturePayment(payment);String captureStatus = captureResult.get(IPaymentHandler.STATUS);String gatewayStatus = captureResult.get(IPaymentHandler.GATEWAY_STATUS);Map<String, String> attrMap = new HashMap<String, String>();if (!captureStatus.trim().equals("0")|| !HdfcPaymentReturnStatus.CAPTURED.value().equals(gatewayStatus)) {// Failurelogger.error("Capture attempt failed for HDFC payment with id: " + merchantPaymentId);String description = captureResult.get(IPaymentHandler.ERROR);String errorCode = captureResult.get(IPaymentHandler.ERR_CODE);payment.setDescription(description);payment.setErrorCode(errorCode);payment.setStatus(PaymentStatus.FAILED.getValue());payment.setErrorTimestamp(new Date());paymentHandler.updatePayment(payment, attrMap);createTicketForFailedPayment(payment);return false;} else {// Successlogger.info("Capture attempt successful for HDFC payment with id: " + merchantPaymentId);payment.setDescription("Payment Captured");payment.setGatewayTxnStatus(gatewayStatus);payment.setStatus(PaymentStatus.SUCCESS.getValue());payment.setSuccessTimestamp(new Date());attrMap.put(IPaymentHandler.CAPTURE_TXN_ID, captureResult.get(IPaymentHandler.CAPTURE_TXN_ID));attrMap.put(IPaymentHandler.CAPTURE_REF_ID, captureResult.get(IPaymentHandler.CAPTURE_REF_ID));attrMap.put(IPaymentHandler.CAPTURE_AUTH_ID, captureResult.get(IPaymentHandler.CAPTURE_AUTH_ID));SimpleDateFormat captureTimeDateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");attrMap.put(HdfcPaymentHandler.CAPTURE_TIME, captureTimeDateFormatter.format(new Date()));paymentHandler.updatePayment(payment, attrMap);return true;}}/*** Capture the HDFC EMI payment represented by the given payment object. If* the capture attempt is not successful, we mark this payment as failed. We* don't retry or anything. We'll add the support of multiple attempts later* on.** @param payment* The payment which has to be captured.* @return True if the payment attempt is successful, false if not.*/private boolean captureAndUpdateHdfcEmiPayment(in.shop2020.payment.domain.Payment payment){long merchantPaymentId = payment.getId();logger.info("Capturing HDFC payment with id: " + merchantPaymentId);Map<String, String> captureResult = HdfcEmiPaymentHandler.capturePayment(payment);String captureStatus = captureResult.get(IPaymentHandler.STATUS);String gatewayStatus = captureResult.get(IPaymentHandler.GATEWAY_STATUS);Map<String, String> attrMap = new HashMap<String, String>();if (!captureStatus.trim().equals("0")|| !HdfcPaymentReturnStatus.CAPTURED.value().equals(gatewayStatus)) {// Failurelogger.error("Capture attempt failed for HDFC payment with id: " + merchantPaymentId);String description = captureResult.get(IPaymentHandler.ERROR);String errorCode = captureResult.get(IPaymentHandler.ERR_CODE);payment.setDescription(description);payment.setErrorCode(errorCode);payment.setStatus(PaymentStatus.FAILED.getValue());payment.setErrorTimestamp(new Date());paymentHandler.updatePayment(payment, attrMap);createTicketForFailedPayment(payment);return false;} else {// Successlogger.info("Capture attempt successful for HDFC payment with id: " + merchantPaymentId);payment.setDescription("Payment Captured");payment.setGatewayTxnStatus(gatewayStatus);payment.setStatus(PaymentStatus.SUCCESS.getValue());payment.setSuccessTimestamp(new Date());attrMap.put(IPaymentHandler.CAPTURE_TXN_ID, captureResult.get(IPaymentHandler.CAPTURE_TXN_ID));attrMap.put(IPaymentHandler.CAPTURE_REF_ID, captureResult.get(IPaymentHandler.CAPTURE_REF_ID));attrMap.put(IPaymentHandler.CAPTURE_AUTH_ID, captureResult.get(IPaymentHandler.CAPTURE_AUTH_ID));SimpleDateFormat captureTimeDateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");attrMap.put(HdfcPaymentHandler.CAPTURE_TIME, captureTimeDateFormatter.format(new Date()));paymentHandler.updatePayment(payment, attrMap);return true;}}/*** Capture the EBS payment represented by the given payment object. If the* capture attempt is not successful, we mark this payment as failed. We* don't retry or anything. We'll add the support of multiple attempts later* on.** @param payment The payment which has to be captured.* @return True if the payment attempt is successful, false if not.*/private boolean captureAndUpdateEbsPayment(in.shop2020.payment.domain.Payment payment){Map<String, String> captureResult = EbsPaymentHandler.capturePayment(payment);String captureStatus = captureResult.get(EbsPaymentHandler.STATUS);Map<String, String> attrMap = new HashMap<String, String>();if("".equals(captureStatus)){//Failurelogger.error("Capture attempt failed for EBS payment with id: " + payment.getId());String description = captureResult.get(EbsPaymentHandler.ERROR);String errorCode = captureResult.get(EbsPaymentHandler.ERR_CODE);payment.setDescription(description);payment.setErrorCode(errorCode);payment.setStatus(PaymentStatus.FAILED.getValue());payment.setErrorTimestamp(new Date());paymentHandler.updatePayment(payment, attrMap);createTicketForFailedPayment(payment);return false;}else{//Successlogger.info("Capture attempt successful for EBS payment with id: " + payment.getId());payment.setGatewayTxnStatus(captureStatus);payment.setStatus(PaymentStatus.SUCCESS.getValue());payment.setSuccessTimestamp(new Date());attrMap.put(IPaymentHandler.CAPTURE_TXN_ID, captureResult.get(IPaymentHandler.CAPTURE_TXN_ID));attrMap.put(IPaymentHandler.CAPTURE_TIME, captureResult.get(IPaymentHandler.CAPTURE_TIME));paymentHandler.updatePayment(payment, attrMap);return true;}}/*** Updates the settlement details of COD payments. Sets payment status as* either PARTIALLY CAPTURED or SUCCESS depending on whether the complete* amount has been captured. Other parameters are set as attributes.** @param payment* The payment which needs to be updated.* @param amount* Amount that has been captured.* @param xferBy* Entity which transferred the money.* @param xferTxnId* Transaction Id of the transfer.* @param xferDateStr* Date on which the transfer took place.* @return true if the payment details were updated successfully.* @throws PaymentException* if the captured amount will become more than the actual* amount after this update.*/private boolean settleAndUpdateCodPayment(in.shop2020.payment.domain.Payment payment, double amount, String xferBy, String xferTxnId, String xferDateStr) throws PaymentException{Map<String, String> attrMap = payment.getAttributeMap();double captureAmount = 0;String captureAmntStr = attrMap.get(IPaymentHandler.CAPTURE_AMNT);if(captureAmntStr != null)captureAmount = Double.parseDouble(captureAmntStr);captureAmount += amount;if(captureAmount > payment.getAmount())throw new PaymentException(105, "We've got a settlement request for an amount which is more than the transaction value.");if(captureAmount < payment.getAmount()){payment.setStatus(PaymentStatus.PARTIALLY_CAPTURED.getValue());} else {payment.setStatus(PaymentStatus.SUCCESS.getValue());}payment.setSuccessTimestamp(new Date());attrMap.put(IPaymentHandler.CAPTURE_AMNT, captureAmount + "");attrMap.put(IPaymentHandler.XFER_TXN_ID, xferTxnId);attrMap.put(IPaymentHandler.XFER_TXN_DATE, xferDateStr);attrMap.put(IPaymentHandler.XFER_BY, xferBy);paymentHandler.updatePayment(payment, attrMap);return true;}/*** Creates a list of thrift payment objects corresponding to a list of* payment data objects.** @param daoPayments* A list of payment DAO.* @return A list of Thrift payment objects.*/private List<Payment> getThriftPayments(List<in.shop2020.payment.domain.Payment> daoPayments){List<Payment> payments = new ArrayList<Payment>();for(in.shop2020.payment.domain.Payment payment : daoPayments){payments.add(payment.getThriftPayment());}return payments;}@Overridepublic boolean isAlive() throws TException {// TODO Auto-generated method stubreturn true;}@Overridepublic void closeSession() throws TException {// TODO Auto-generated method stub}}