Rev 13518 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
package in.shop2020.payment.service.handler;import in.shop2020.model.v1.order.RechargeOrder;import in.shop2020.payment.domain.Refund;import in.shop2020.payment.handler.PaymentGatewayHandler;import in.shop2020.payment.handler.PaymentHandler;import in.shop2020.payment.handler.PaymentRequiringExtraProcessingHandler;import in.shop2020.payment.handler.RefundHandler;import in.shop2020.payment.service.handler.IPaymentHandler.Errors;import in.shop2020.payments.Attribute;import in.shop2020.payments.ExtraPaymentProcessingType;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.TransactionClient;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Arrays;import java.util.Date;import java.util.HashMap;import java.util.List;import java.util.Map;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.apache.thrift.TException;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class PaymentServiceHandler implements Iface {private static final Log logger = LogFactory.getLog(PaymentServiceHandler.class);/*** Enum of all statuses that can be returned by the HDFC gateway** @author Chandranshu**/public 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"),SUCCESS("SUCCESS"),FAILURE("FAILURE");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 PAYU_GATEWAY_ID = 19;private static final long EBAY_GATEWAY_ID = 16;private static final long SNAPDEAL_GATEWAY_ID = 18;private static final List<Long> HDFC_EMI_GATEWAY_IDS = Arrays.asList(5L,10L,11L,12L,14L);ApplicationContext context = new ClassPathXmlApplicationContext("context.xml");PaymentHandler paymentHandler = (PaymentHandler) context.getBean("paymentHandler");PaymentRequiringExtraProcessingHandler paymentRequiringExtraProcessingHandler =(PaymentRequiringExtraProcessingHandler) context.getBean("paymentRequiringExtraProcessingHandler");PaymentGatewayHandler paymentGatewayHandler = (PaymentGatewayHandler) context.getBean("paymentGatewayHandler");RefundHandler refundHandler = (RefundHandler) context.getBean("refundHandler");public void setDataSourceUrl(String dbHost){org.apache.commons.dbcp.BasicDataSource ds = (org.apache.commons.dbcp.BasicDataSource)context.getBean("dataSource");ds.setUrl(dbHost);}public String getDataSourceUrl(){org.apache.commons.dbcp.BasicDataSource ds = (org.apache.commons.dbcp.BasicDataSource)context.getBean("dataSource");return ds.getUrl();}@Overridepublic long createPayment(long userId, double amount, long gatewayId, long txnId, boolean isDigital) 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());payment.setDigital(isDigital);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 List<Payment> getPaymentsByCapturedDate(long fromTime, long toTime, long gatewayId) throws PaymentException, TException {logger.info("Getting payments from " + fromTime + " to " + toTime + " for " + gatewayId);return getThriftPayments(paymentHandler.getPaymentsByCapturedDate(fromTime, toTime, gatewayId));}@Overridepublic PaymentGateway getPaymentGateway(long id) throws PaymentException, TException {logger.info("Getting payment gateway with id:" + id);return paymentGatewayHandler.getPaymentGateway(id).getThriftPaymentGateway();}@Overridepublic List<PaymentGateway> getActivePaymentGateways() throws PaymentException, TException {logger.info("Getting all active payment gateways");return getThriftPaymentGateways(paymentGatewayHandler.getActivePaymentGateways());}@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 List<Payment> getPaymentForRechargeTxnId(long txnId) throws PaymentException, TException {logger.info("Getting payment for the txn id: " + txnId);return getThriftPayments(paymentHandler.getPaymentForRechargeTxn(txnId));}@Overridepublic Payment getSuccessfulPaymentForTxnId(long txnId) throws PaymentException, TException {for (Payment payment: getPaymentForTxnId(txnId)) {if (payment.getStatus() == PaymentStatus.SUCCESS || payment.getStatus() == PaymentStatus.PARTIALLY_CAPTURED) {return payment;}}return null;}@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());persistPaymentRequiringExtraProcessing(payment, ExtraPaymentProcessingType.FAILED_PAYMENTS);}else if (status.equals(PaymentStatus.PROVISIONALLY_CAPTURED)) {// FIXME different column to note provisional capture timestamp// FIXME Requires extra processing at Crm end for actual manual capturepayment.setProvisionalCaptureTimestamp(new Date());persistPaymentRequiringExtraProcessing(payment, ExtraPaymentProcessingType.PENDING_CAPTURE);}}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;}/*** Persists a given payment Id in another table for future processing by CRM etc.* Failed payments generally require a follow-up to help customers through our* CRM-Outbound team.** @param payment the payment object that failed.* @param type TODO*/private void persistPaymentRequiringExtraProcessing(in.shop2020.payment.domain.Payment payment, ExtraPaymentProcessingType type) {try {paymentRequiringExtraProcessingHandler.insert(payment.getId(), type);} catch (Exception e) {logger.error("Could not persist payment: " + 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, boolean isMobile) 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, isMobile);} catch (Exception e) {throw new PaymentException(102, "Error while initiliazing payment. Check service log for more details.");}return redirectURL;}@Overridepublic String doHdfcPaymentForDigitalOrder(long merchantPaymentId, long rechargeOrderId, String phone, boolean isMobile) throws PaymentException, TException {logger.info("Initializing HDFC payment with id: " + merchantPaymentId);logger.info("doHdfcPaymentForDigitalOrder phone---- " + phone);in.shop2020.payment.domain.Payment payment = paymentHandler.getPayment(merchantPaymentId);TransactionClient tc = new TransactionClient();String redirectURL;RechargeOrder rechargeOrder;try {rechargeOrder = tc.getClient().getRechargeOrder(rechargeOrderId);redirectURL = HdfcPaymentHandler.initializeHdfcPayment(payment, rechargeOrder, phone, this, isMobile);} catch (Exception e) {throw new PaymentException(102, "Error while initiliazing payment. Check service log for more details.");}return redirectURL;}@Overridepublic String initializeHdfcEmiPayment(long merchantPaymentId, boolean isMobile) 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, isMobile);} catch (Exception e) {throw new PaymentException(102, "Error while initiliazing payment. Check service log for more details.");}return redirectURL;}@Overridepublic synchronized boolean refundPayment(long merchantTxnId, double amount, boolean isDigital) throws PaymentException, TException {logger.info("Attempting to refund payment of amount " + amount + " corresponding to our transaction " + merchantTxnId);List<in.shop2020.payment.domain.Payment> payments;if(isDigital){payments = paymentHandler.getPaymentForRechargeTxn(merchantTxnId);}else{payments = paymentHandler.getPaymentForTxn(merchantTxnId);}if(payments ==null || payments.isEmpty())throw new PaymentException(104, "No payments found corresponding to the merchant txn " + 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.getAmount() < amount){logger.warn("Refund amount is more than payment amount.");return false;}switch(PaymentStatus.findByValue(payment.getStatus())){case PENDING:logger.error("Attempt to refund a non-authorized payment");return false;case INIT:logger.warn("Attempt to refund a non-authorized payment");return false;case AUTHORIZED:logger.warn("Attempt to refund a non-captured payment");return false;case CAPTURE_IN_PROCESS:logger.warn("Attempt to refund a non-captured payment");return false;case PROVISIONALLY_CAPTURED:logger.warn("Attempt to refund a non-captured payment");return false;case REFUNDED:logger.warn("Attempt to refund a refunded payment");return false;case SUCCESS:break;case FAILED:logger.error("Attempting to capture a failed payment");return false;}long gatewayId = payment.getGatewayId();if(gatewayId == HDFC_GATEWAY_ID){//Refund the HDFC paymentreturn refundHdfcPayment(payment, amount);}else if (gatewayId == EBS_GATEWAY_ID){//Refund the EBS paymentreturn refundEbsPayment(payment, amount);}else if (HDFC_EMI_GATEWAY_IDS.contains(gatewayId)){//Capture and update the HDFC EMI paymentreturn refundHdfcEmiPayment(payment, amount);}else if (gatewayId == PAYU_GATEWAY_ID) {//Refund PayUreturn refundPayUPayment(payment, amount);}logger.error("We have an captured payment from unknown gateway: " + gatewayId);return false;}private boolean refundPayUPayment (in.shop2020.payment.domain.Payment payment, double amount) throws PaymentException{Map<String, String> captureResult = PayuPaymentHandler.refundPayment(payment, amount);if(captureResult.containsKey(IPaymentHandler.ERROR)){payment.setDescription(captureResult.get(IPaymentHandler.ERROR));payment.setErrorCode(captureResult.get(IPaymentHandler.ERR_CODE));payment.setErrorTimestamp(new Date());if(captureResult.get(IPaymentHandler.ERR_CODE).equals(Errors.CAPTURE_FAILURE)) {paymentHandler.updatePayment(payment, captureResult);persistPaymentRequiringExtraProcessing(payment, ExtraPaymentProcessingType.FAILED_PAYMENTS);} else {logger.error("Refund attempt failed for Payu payment with id: " + payment.getId());/*payment.setStatus(PaymentStatus.CAPTURE_IN_PROCESS.getValue());paymentHandler.updatePayment(payment, captureResult);throw new PaymentException(106, captureResult.get(IPaymentHandler.ERROR));*/}return false;} else {payment.setGatewayTxnStatus("Refund initiated");payment.setStatus(PaymentStatus.REFUNDED.getValue());payment.setRefundAmount(amount);SimpleDateFormat captureTimeDateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");captureResult.put(IPaymentHandler.REFUND_TXN_ID, captureResult.get(IPaymentHandler.REFUND_TXN_ID));captureResult.put(IPaymentHandler.REFUND_TIME, captureTimeDateFormatter.format(new Date()));paymentHandler.updatePayment(payment, captureResult);return true;}}@Overridepublic long createRefund(long orderId, long merchantTxnId, double amount) throws PaymentException, TException{logger.info("Attempting to create a refund for order: " + orderId);// if(!refundPayment(merchantTxnId, amount, false)){// logger.warn("Not able to refund corresponding to the merchant txn " + 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);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 synchronized boolean capturePayment(long merchantTxnId, boolean isDigital) throws PaymentException, TException {logger.info("Attempting to capture payment corresponding to our transaction " + merchantTxnId + " and Is Digital is:" + isDigital);List<in.shop2020.payment.domain.Payment> payments;if(isDigital){payments = paymentHandler.getPaymentForRechargeTxn(merchantTxnId);}else{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:case CAPTURE_IN_PROCESS://Actual work to be done in this case. Let the call proceed.break;case PROVISIONALLY_CAPTURED:logger.info("Attempting to capture a payment that is provisonally captured but we can let the client proceed.");return true;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 (HDFC_EMI_GATEWAY_IDS.contains(gatewayId)){//Capture and update the HDFC EMI paymentreturn captureAndUpdateHdfcEmiPayment(payment);} else if (gatewayId == EBAY_GATEWAY_ID || gatewayId == SNAPDEAL_GATEWAY_ID) {return true;} else if (gatewayId == PAYU_GATEWAY_ID) {return captureAndUpdatePayuPayment(payment);}logger.error("We have an authorized payment from unknown gateway: " + gatewayId);return false;}private boolean captureAndUpdatePayuPayment(in.shop2020.payment.domain.Payment payment) throws PaymentException{long merchantPaymentId = payment.getId();logger.info("Capturing Payu payment with id: " + merchantPaymentId);Map<String, String> attrMap = new HashMap<String, String>();Map<String, String> captureResult = PayuPaymentHandler.captureTransaction(merchantPaymentId + "", payment.getGatewayTxnId());if(captureResult.containsKey(IPaymentHandler.ERROR)){payment.setDescription(captureResult.get(IPaymentHandler.ERROR));payment.setErrorCode(captureResult.get(IPaymentHandler.ERR_CODE));payment.setErrorTimestamp(new Date());if(captureResult.get(IPaymentHandler.ERR_CODE).equals(Errors.CAPTURE_FAILURE)) {payment.setStatus(PaymentStatus.FAILED.getValue());paymentHandler.updatePayment(payment, attrMap);persistPaymentRequiringExtraProcessing(payment, ExtraPaymentProcessingType.FAILED_PAYMENTS);} else {logger.error("Capture attempt failed for Payu payment with id: " + merchantPaymentId);payment.setStatus(PaymentStatus.CAPTURE_IN_PROCESS.getValue());paymentHandler.updatePayment(payment, attrMap);throw new PaymentException(106, captureResult.get(IPaymentHandler.ERROR));}return false;} else {logger.info("Capture attempt successful for HDFC payment with id: " + merchantPaymentId);payment.setDescription("Payment Captured");payment.setGatewayTxnStatus("captured");payment.setStatus(PaymentStatus.SUCCESS.getValue());payment.setSuccessTimestamp(new Date());payment.setReferenceCode(captureResult.get(PayuPaymentHandler.REF_NO));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;}}@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.* @throws PaymentException*/private boolean captureAndUpdateHdfcPayment(in.shop2020.payment.domain.Payment payment) throws PaymentException {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.setErrorTimestamp(new Date());if (IPaymentHandler.Errors.CONN_FAILURE.code.equals(errorCode)) {payment.setStatus(PaymentStatus.CAPTURE_IN_PROCESS.getValue());paymentHandler.updatePayment(payment, attrMap);throw new PaymentException(106, "Could not capture due to connection issue");}else {payment.setStatus(PaymentStatus.FAILED.getValue());paymentHandler.updatePayment(payment, attrMap);persistPaymentRequiringExtraProcessing(payment, ExtraPaymentProcessingType.FAILED_PAYMENTS);}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.* @throws PaymentException*/private boolean captureAndUpdateHdfcEmiPayment(in.shop2020.payment.domain.Payment payment) throws PaymentException{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.setErrorTimestamp(new Date());// Not marking payments as failed in case of connection issuesif (IPaymentHandler.Errors.CONN_FAILURE.code.equals(errorCode)) {payment.setStatus(PaymentStatus.CAPTURE_IN_PROCESS.getValue());paymentHandler.updatePayment(payment, attrMap);throw new PaymentException(106, "Could not capture due to connection issue");}else {payment.setStatus(PaymentStatus.FAILED.getValue());paymentHandler.updatePayment(payment, attrMap);persistPaymentRequiringExtraProcessing(payment, ExtraPaymentProcessingType.FAILED_PAYMENTS);}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.* @throws PaymentException*/private boolean captureAndUpdateEbsPayment(in.shop2020.payment.domain.Payment payment) throws PaymentException{Map<String, String> captureResult = EbsPaymentHandler.capturePayment(payment);String captureStatus = captureResult.get(EbsPaymentHandler.STATUS);for(String key : captureResult.keySet()) {logger.info("key : " + key + " value : " + captureResult.get(key));}logger.info("capture status : " + captureStatus);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.setErrorTimestamp(new Date());if (IPaymentHandler.Errors.CONN_FAILURE.code.equals(errorCode)) {payment.setStatus(PaymentStatus.CAPTURE_IN_PROCESS.getValue());paymentHandler.updatePayment(payment, attrMap);throw new PaymentException(106, "Could not capture due to connection issue");}else {payment.setStatus(PaymentStatus.FAILED.getValue());paymentHandler.updatePayment(payment, attrMap);persistPaymentRequiringExtraProcessing(payment, ExtraPaymentProcessingType.FAILED_PAYMENTS);}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;}}/*** Refund the HDFC payment represented by the given payment object. If the* refund attempt is not successful, will not any action.** @param payment The payment which has to be captured.* @amount amount to be refunded* @return True if the payment attempt is successful, false if not.* @throws PaymentException*/private boolean refundHdfcPayment(in.shop2020.payment.domain.Payment payment, double amount) throws PaymentException {long merchantPaymentId = payment.getId();logger.info("Refunding HDFC payment with id: " + merchantPaymentId);Map<String, String> refundResult = HdfcPaymentHandler.refundPayment(payment, amount);String refundStatus = refundResult.get(IPaymentHandler.STATUS);String gatewayStatus = refundResult.get(IPaymentHandler.GATEWAY_STATUS);Map<String, String> attrMap = new HashMap<String, String>();if (!refundStatus.trim().equals("0")|| !HdfcPaymentReturnStatus.CAPTURED.value().equals(gatewayStatus)) {logger.error("Refund attempt failed for HDFC payment with id: " + merchantPaymentId);String description = refundResult.get(IPaymentHandler.ERROR);String errorCode = refundResult.get(IPaymentHandler.ERR_CODE);payment.setDescription(description);payment.setErrorCode(errorCode);payment.setErrorTimestamp(new Date());if (IPaymentHandler.Errors.CONN_FAILURE.code.equals(errorCode)) {//payment.setStatus(PaymentStatus.CAPTURE_IN_PROCESS.getValue());//paymentHandler.updatePayment(payment, attrMap);throw new PaymentException(106, "Could not capture due to connection issue. Try Later");}else {// payment.setStatus(PaymentStatus.FAILED.getValue());// paymentHandler.updatePayment(payment, attrMap);persistPaymentRequiringExtraProcessing(payment, ExtraPaymentProcessingType.FAILED_PAYMENTS);}return false;} else {// Successlogger.info("Refund attempt successful for HDFC payment with id: " + merchantPaymentId);payment.setDescription("Payment Refunded");payment.setGatewayTxnStatus(gatewayStatus);payment.setStatus(PaymentStatus.REFUNDED.getValue());payment.setRefundAmount(amount);attrMap.put(IPaymentHandler.REFUND_TXN_ID, refundResult.get(IPaymentHandler.REFUND_TXN_ID));attrMap.put(IPaymentHandler.REFUND_REF_ID, refundResult.get(IPaymentHandler.REFUND_REF_ID));attrMap.put(IPaymentHandler.REFUND_AUTH_ID, refundResult.get(IPaymentHandler.REFUND_AUTH_ID));attrMap.put(IPaymentHandler.REFUND_AMNT, refundResult.get(IPaymentHandler.REFUND_AMNT));SimpleDateFormat captureTimeDateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");attrMap.put(HdfcPaymentHandler.REFUND_TIME, captureTimeDateFormatter.format(new Date()));paymentHandler.updatePayment(payment, attrMap);return true;}}/*** Refund the HDFC EMI payment represented by the given payment object. If* the capture attempt is not successful, we will not do anything.** @param payment* The payment which has to be captured.* @return True if the payment attempt is successful, false if not.* @throws PaymentException*/private boolean refundHdfcEmiPayment(in.shop2020.payment.domain.Payment payment, double amount) throws PaymentException{long merchantPaymentId = payment.getId();logger.info("Refunding HDFC payment with id: " + merchantPaymentId);Map<String, String> refundResult = HdfcEmiPaymentHandler.refundPayment(payment, amount);String refundStatus = refundResult.get(IPaymentHandler.STATUS);String gatewayStatus = refundResult.get(IPaymentHandler.GATEWAY_STATUS);Map<String, String> attrMap = new HashMap<String, String>();if (!refundStatus.trim().equals("0")|| !HdfcPaymentReturnStatus.CAPTURED.value().equals(gatewayStatus)) {// Failurelogger.error("Refund attempt failed for HDFC payment with id: " + merchantPaymentId);String description = refundResult.get(IPaymentHandler.ERROR);String errorCode = refundResult.get(IPaymentHandler.ERR_CODE);payment.setDescription(description);payment.setErrorCode(errorCode);payment.setErrorTimestamp(new Date());// Not marking payments as failed in case of connection issuesif (IPaymentHandler.Errors.CONN_FAILURE.code.equals(errorCode)) {// payment.setStatus(PaymentStatus.CAPTURE_IN_PROCESS.getValue());// paymentHandler.updatePayment(payment, attrMap);throw new PaymentException(106, "Could not capture due to connection issue");}else {// payment.setStatus(PaymentStatus.FAILED.getValue());// paymentHandler.updatePayment(payment, attrMap);persistPaymentRequiringExtraProcessing(payment, ExtraPaymentProcessingType.FAILED_PAYMENTS);}return false;} else {// Successlogger.info("Refund attempt successful for HDFC payment with id: " + merchantPaymentId);payment.setDescription("Payment Refunded");payment.setGatewayTxnStatus(gatewayStatus);payment.setStatus(PaymentStatus.REFUNDED.getValue());payment.setRefundAmount(amount);attrMap.put(IPaymentHandler.REFUND_TXN_ID, refundResult.get(IPaymentHandler.REFUND_TXN_ID));attrMap.put(IPaymentHandler.REFUND_REF_ID, refundResult.get(IPaymentHandler.REFUND_REF_ID));attrMap.put(IPaymentHandler.REFUND_AUTH_ID, refundResult.get(IPaymentHandler.REFUND_AUTH_ID));attrMap.put(IPaymentHandler.REFUND_AMNT, refundResult.get(IPaymentHandler.REFUND_AMNT));SimpleDateFormat captureTimeDateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");attrMap.put(HdfcPaymentHandler.REFUND_TIME, captureTimeDateFormatter.format(new Date()));paymentHandler.updatePayment(payment, attrMap);return true;}}/*** Refund the EBS payment represented by the given payment object. If the* capture attempt is not successful, we will ignore. 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.* @amount Amount to be refunded* @return True if the payment attempt is successful, false if not.* @throws PaymentException*/private boolean refundEbsPayment(in.shop2020.payment.domain.Payment payment, double amount) throws PaymentException{Map<String, String> refundResult = EbsPaymentHandler.refundPayment(payment, amount);String refundStatus = refundResult.get(EbsPaymentHandler.STATUS);Map<String, String> attrMap = new HashMap<String, String>();if("".equals(refundStatus)){//Failurelogger.error("Refund attempt failed for EBS payment with id: " + payment.getId());String description = refundResult.get(EbsPaymentHandler.ERROR);String errorCode = refundResult.get(EbsPaymentHandler.ERR_CODE);payment.setDescription(description);payment.setErrorCode(errorCode);payment.setErrorTimestamp(new Date());if (IPaymentHandler.Errors.CONN_FAILURE.code.equals(errorCode)) {// payment.setStatus(PaymentStatus.CAPTURE_IN_PROCESS.getValue());// paymentHandler.updatePayment(payment, attrMap);throw new PaymentException(106, "Could not capture due to connection issue");}else {// payment.setStatus(PaymentStatus.FAILED.getValue());// paymentHandler.updatePayment(payment, attrMap);persistPaymentRequiringExtraProcessing(payment, ExtraPaymentProcessingType.FAILED_PAYMENTS);}return false;}else{//Successlogger.info("Refund attempt successful for EBS payment with id: " + payment.getId());payment.setGatewayTxnStatus(refundStatus);payment.setStatus(PaymentStatus.REFUNDED.getValue());payment.setRefundAmount(amount);attrMap.put(IPaymentHandler.REFUND_TXN_ID, refundResult.get(IPaymentHandler.REFUND_TXN_ID));attrMap.put(IPaymentHandler.REFUND_TIME, refundResult.get(IPaymentHandler.REFUND_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 capture amount higher than payment amount by more than 50 paisa,// there is some issue and we should raise exception.if(captureAmount - payment.getAmount() > 0.5){throw new PaymentException(105, "We've got a settlement request for an amount which is more than the transaction value.");}// If capture amount differs from payment amount by less than 50 paisa, lets mark the payment as successful.// Else we can safely assume there will be some more orders for the payment, leading to make the payment as partially captured.if(Math.abs(captureAmount - payment.getAmount()) < 0.5){payment.setStatus(PaymentStatus.SUCCESS.getValue());}else {payment.setStatus(PaymentStatus.PARTIALLY_CAPTURED.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;}/*** Creates a list of thrift payment gateway objects corresponding to a list of* payment gateway data objects.** @param daoPaymentGateways* A list of payment gateway DAO.* @return A list of Thrift payment gateway objects.*/private List<PaymentGateway> getThriftPaymentGateways(List<in.shop2020.payment.domain.PaymentGateway> daoPaymentGateways){List<PaymentGateway> paymentGateways = new ArrayList<PaymentGateway>();for(in.shop2020.payment.domain.PaymentGateway paymentGateway : daoPaymentGateways){paymentGateways.add(paymentGateway.getThriftPaymentGateway());}return paymentGateways;}@Overridepublic boolean isAlive() {try {return !paymentGatewayHandler.getActivePaymentGateways().isEmpty();} catch (Exception e) {logger.error("Could not fetch payment gateways", e);}return false;}@Overridepublic void closeSession() throws TException {// TODO Auto-generated method stub}@Overridepublic List<Long> getPaymentsRequiringExtraProcessing (ExtraPaymentProcessingType category) throws TException {return paymentRequiringExtraProcessingHandler.getPaymentIds(category);}@Overridepublic void markPaymentAsProcessed(long paymentId,ExtraPaymentProcessingType category) throws TException {paymentRequiringExtraProcessingHandler.delete(paymentId, category);}@Overridepublic PaymentStatus getPaymentStatusAtGateway(long merchantTxnId, double amount, boolean isDigital) throws PaymentException, TException {logger.info("Attempting to get status of payment of amount " + amount + " corresponding to our transaction " + merchantTxnId);List<in.shop2020.payment.domain.Payment> payments;if(isDigital){payments = paymentHandler.getPaymentForRechargeTxn(merchantTxnId);}else{payments = paymentHandler.getPaymentForTxn(merchantTxnId);}if(payments ==null || payments.isEmpty())throw new PaymentException(104, "No payments found corresponding to the merchant txn " + 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);long gatewayId = payment.getGatewayId();if(gatewayId == HDFC_GATEWAY_ID){return HdfcPaymentHandler.validateHdfcPayment(payment, amount);}else if (gatewayId == EBS_GATEWAY_ID){//return validateEbsPayment(payment, amount);}else if (HDFC_EMI_GATEWAY_IDS.contains(gatewayId)){//return validateHdfcEmiPayment(payment, amount);}logger.error("We have an payment from unknown gateway: " + gatewayId);return PaymentStatus.INIT;}}