Subversion Repositories SmartDukaan

Rev

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

package in.shop2020.payment.service.handler;

import in.shop2020.config.ConfigException;
import in.shop2020.payment.domain.Payment;
import in.shop2020.thrift.clients.config.ConfigClient;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.ConnectException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.log4j.Logger;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

public class EbsPaymentHandler implements IPaymentHandler {
        
        private static Logger log = Logger.getLogger(Class.class);
        
        public static final String TXN_ID = "transactionId";
        public static final String PAYMENT_ID = "paymentId";
        public static final String AMOUNT = "amount";
        public static final String DATE_TIME = "dateTime";
        public static final String MODE = "mode";
        public static final String REF_NO = "referenceNo";
        public static final String TXN_TYPE = "transactionType";
        
    private static String accountId;
    private static String secretKey;
    
        static{
                try {
                        accountId = ConfigClient.getClient().get("ebs_account_id");
                        secretKey = ConfigClient.getClient().get("ebs_secret_key");
                } catch (ConfigException e) {
                        log.error("Unable to get EBS payment configuration.", e);
                }
        }
        
        /**
         * Capture the amount which was authorized for this payment Id. Makes
         * requests over the network and so internet connectivity is must for it to
         * succeed.
         * 
         * @param amount
         *            The amount to be captured. Must be the same value which was
         *            authorized for this payment.
         * @param paymentId
         *            The payment ID generated by the gateway.
         * @return A Map. The Map will definitely have status and will have error
         *         information in case of error and txn information in case of
         *         success. In case there is an error in processing, the returned
         *         result map will be empty.
         */
        public static Map<String, String> capturePayment(Payment payment){
            double amount = payment.getAmount();
            String paymentId = payment.getGatewayPaymentId();
                System.out.println("Capturing amount: Rs " + amount + " for payment Id: " + paymentId);
                URL url = null;
            URLConnection urlConn = null;
            DataOutputStream printout;
            BufferedReader reader;
            Map<String, String> resultMap = new HashMap<String, String>();

            //Prepare resultMap to elicit failure behaviour in case anything goes wrong.
            resultMap.put(STATUS, "");
            
                /*
                 * HDFC Insanity
                 * 
                 * If we don't set this property, all payments through HDFC will fail if
                 * the first payment is made through EBS.
                 * 
                 * The JAR file provided by HDFC requires that the instances of
                 * HttpsURLConnection returned by a call to URL.openConnection be an
                 * instance of com.sun.net.ssl.HttpsURLConnection. Java versions before
                 * 1.4 used to return an instance of this class which goes against the
                 * current policy of using instances of undocumented internal classes of
                 * JDK since they can change at any time. This behaviour was changed in
                 * JAVA 1.4 to return instances of javax.net.ssl.HttpsURLConnection.
                 * However, it appears, that to allow clients with JDK 1.4 and earlier,
                 * HDFC favours the use of the deprecated class.
                 */
            System.setProperty("https.protocols", "TLSv1");
            
            try {
                    // URL of CGI-Bin script.
                        url = new URL("https://api.secure.ebs.in/api/1_0");
                    // URL connection channel.
                    urlConn = url.openConnection();
                    //urlConn.setRequestMethod("POST");
            } catch (MalformedURLException e1) {
                log.error(Errors.CAPTURE_FAILURE.message, e1);
                        resultMap.put(ERR_CODE, Errors.CAPTURE_FAILURE.code);
                        resultMap.put(ERROR, Errors.CAPTURE_FAILURE.message);
                        return resultMap;
                } catch (IOException e) {
                        log.error("Unable to initialize connection to EBS API server", e);
                        resultMap.put(ERR_CODE, Errors.CONN_FAILURE.code);
                        resultMap.put(ERROR, Errors.CONN_FAILURE.message);
                        return resultMap;
                }

            // Let the run-time system (RTS) know that we want input.
            urlConn.setDoInput (true);
            // Let the RTS know that we want to do output.
            urlConn.setDoOutput (true);
            // No caching, we want the real thing.
            urlConn.setUseCaches (false);
            // Specify the content type.
            urlConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            // Send POST output.
            try {
                        printout = new DataOutputStream (urlConn.getOutputStream ());
                    String content =
                            "Action=" + URLEncoder.encode("capture", "UTF-8") +
                            "&AccountID=" + URLEncoder.encode(accountId, "UTF-8") +
                            "&SecretKey=" + URLEncoder.encode(secretKey, "UTF-8") +
                            "&Amount=" + URLEncoder.encode(""+amount, "UTF-8") +
                            "&PaymentID=" + URLEncoder.encode(paymentId, "UTF-8") +
                            "&submitted=Submit";
                    printout.writeBytes (content);
                    printout.flush ();
                    printout.close ();

                    // Get response data.
                    reader = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
                    resultMap = parseCaptureOutput(reader);
                    reader.close ();
            }
            catch(ConnectException e) {
            log.error("Unable to initialize connection to EBS API server", e);
            resultMap.put(ERR_CODE, Errors.CONN_FAILURE.code);
            resultMap.put(ERROR, Errors.CONN_FAILURE.message);
            return resultMap;
        } 
            catch (IOException e) {
                log.error(Errors.CAPTURE_FAILURE.message, e);
                        resultMap.clear();
                        resultMap.put(STATUS, "");
                        resultMap.put(ERR_CODE, Errors.CAPTURE_FAILURE.code);
                        resultMap.put(ERROR, Errors.CAPTURE_FAILURE.message);
                }
            
            return resultMap;
        }

        /**
         * Parses the given input stream and returns a map containing the
         * transaction details
         * 
         * @param reader The reader containing the response of the capture request.
         * @return A Map. The Map will definitely have status and will have error
         *         information in case of error and txn information in case of
         *         success.
         */
        private static Map<String, String> parseCaptureOutput(BufferedReader reader){
                Map<String, String> resultMap = new HashMap<String, String>();
                resultMap.put(STATUS, "");
                
                String str = null;
                try {
                        str = reader.readLine();
                        log.info("Capture response: " + str);
                } catch (IOException e) {
                        log.error("Error reading the capture response:", e);
                        return resultMap;
                }
                
                InputSource inputSource = new InputSource(new StringReader(str));
                XPath xpath = XPathFactory.newInstance().newXPath();
                String expression = "/output";
                NodeList nodes = null;
                try {
                        nodes = (NodeList) xpath.evaluate(expression, inputSource,      XPathConstants.NODESET);
                } catch(XPathExpressionException xpee) {
                        log.error("Input couldn't be parsed. See capture response for more info.");
                        return resultMap;
                }
                
                Element elem = (Element) nodes.item(0);
                String status = elem.getAttribute(STATUS);
                resultMap.put(STATUS, status);
                if("".equals(status) || !"Processing".equals(status)){
                        //We've received an error. Retrieve the error values
                        resultMap.put(ERR_CODE, elem.getAttribute(ERR_CODE));
                        resultMap.put(ERROR, elem.getAttribute(ERROR));
                }else{
                        resultMap.put(CAPTURE_TXN_ID, elem.getAttribute(TXN_ID));
                        resultMap.put(PAYMENT_ID, elem.getAttribute(PAYMENT_ID));
                        resultMap.put(AMOUNT, elem.getAttribute(AMOUNT));
                        resultMap.put(CAPTURE_TIME, elem.getAttribute(DATE_TIME));
                        resultMap.put(MODE, elem.getAttribute(MODE));
                        resultMap.put(REF_NO, elem.getAttribute(REF_NO));
                        resultMap.put(TXN_TYPE, elem.getAttribute(TXN_TYPE));
                }

                log.info("Parsed capture response:");
                for(Entry<String, String> entry : resultMap.entrySet()){
                        log.info("Key: " + entry.getKey() + ", Value: " + entry.getValue());
                }
                
                return resultMap;
        }

        /**
         * Refund the amount which was captured for this payment Id. Makes
         * requests over the network and so internet connectivity is must for it to
         * succeed.
         * 
         * @param amount
         *            The amount to be refunded.
         * @param paymentId
         *            The payment ID generated by the gateway.
         * @return A Map. The Map will definitely have status and will have error
         *         information in case of error and txn information in case of
         *         success. In case there is an error in processing, the returned
         *         result map will be empty.
         */
        public static Map<String, String> refundPayment(Payment payment, double amount){
            String paymentId = payment.getGatewayPaymentId();
                System.out.println("Refunding amount: Rs " + amount + " for payment Id: " + paymentId);
                URL url = null;
            URLConnection urlConn = null;
            DataOutputStream printout;
            BufferedReader reader;
            Map<String, String> resultMap = new HashMap<String, String>();

            //Prepare resultMap to elicit failure behaviour in case anything goes wrong.
            resultMap.put(STATUS, "");
            
                /*
                 * HDFC Insanity
                 * 
                 * If we don't set this property, all payments through HDFC will fail if
                 * the first payment is made through EBS.
                 * 
                 * The JAR file provided by HDFC requires that the instances of
                 * HttpsURLConnection returned by a call to URL.openConnection be an
                 * instance of com.sun.net.ssl.HttpsURLConnection. Java versions before
                 * 1.4 used to return an instance of this class which goes against the
                 * current policy of using instances of undocumented internal classes of
                 * JDK since they can change at any time. This behaviour was changed in
                 * JAVA 1.4 to return instances of javax.net.ssl.HttpsURLConnection.
                 * However, it appears, that to allow clients with JDK 1.4 and earlier,
                 * HDFC favours the use of the deprecated class.
                 */
            System.setProperty("https.protocols", "TLSv1");
            
            try {
                    // URL of CGI-Bin script.
                        url = new URL("https://api.secure.ebs.in/api/1_0");
                    // URL connection channel.
                    urlConn = url.openConnection();
                    //urlConn.setRequestMethod("POST");
            } catch (MalformedURLException e1) {
                log.error(Errors.CAPTURE_FAILURE.message, e1);
                        resultMap.put(ERR_CODE, Errors.CAPTURE_FAILURE.code);
                        resultMap.put(ERROR, Errors.CAPTURE_FAILURE.message);
                        return resultMap;
                } catch (IOException e) {
                        log.error("Unable to initialize connection to EBS API server", e);
                        resultMap.put(ERR_CODE, Errors.CONN_FAILURE.code);
                        resultMap.put(ERROR, Errors.CONN_FAILURE.message);
                        return resultMap;
                }

            // Let the run-time system (RTS) know that we want input.
            urlConn.setDoInput (true);
            // Let the RTS know that we want to do output.
            urlConn.setDoOutput (true);
            // No caching, we want the real thing.
            urlConn.setUseCaches (false);
            // Specify the content type.
            urlConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            // Send POST output.
            try {
                        printout = new DataOutputStream (urlConn.getOutputStream ());
                    String content =
                            "Action=" + URLEncoder.encode("refund", "UTF-8") +
                            "&AccountID=" + URLEncoder.encode(accountId, "UTF-8") +
                            "&SecretKey=" + URLEncoder.encode(secretKey, "UTF-8") +
                            "&Amount=" + URLEncoder.encode(""+amount, "UTF-8") +
                            "&PaymentID=" + URLEncoder.encode(paymentId, "UTF-8") +
                            "&submitted=Submit";
                    printout.writeBytes (content);
                    printout.flush ();
                    printout.close ();

                    // Get response data.
                    reader = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
                    resultMap = parseCaptureOutput(reader);
                    resultMap.put(REFUND_TXN_ID, resultMap.get(CAPTURE_TXN_ID));
                    resultMap.put(REFUND_TIME, resultMap.get(CAPTURE_TIME));
                    resultMap.remove(CAPTURE_TXN_ID);
                    resultMap.remove(CAPTURE_TIME);
                    reader.close ();
            }
            catch(ConnectException e) {
            log.error("Unable to initialize connection to EBS API server", e);
            resultMap.put(ERR_CODE, Errors.CONN_FAILURE.code);
            resultMap.put(ERROR, Errors.CONN_FAILURE.message);
            return resultMap;
        } 
            catch (IOException e) {
                log.error(Errors.CAPTURE_FAILURE.message, e);
                        resultMap.clear();
                        resultMap.put(STATUS, "");
                        resultMap.put(ERR_CODE, Errors.CAPTURE_FAILURE.code);
                        resultMap.put(ERROR, Errors.CAPTURE_FAILURE.message);
                }
            
            return resultMap;
        }

        public static void main(String[] args){
            Payment p = new Payment();
            p.setGatewayPaymentId("30573297");
            p.setAmount(20);
            
            capturePayment(p);
        }
}