Subversion Repositories SmartDukaan

Rev

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

package in.shop2020.serving.services;

import in.shop2020.config.ConfigException;
import in.shop2020.payments.Attribute;
import in.shop2020.payments.PaymentStatus;
import in.shop2020.thrift.clients.PaymentServiceClient;
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.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
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 EbsPaymentService implements IPaymentService{
        
        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.");
                }
        }
        
        private static int gatewayId=2;
        private long paymentId;
        
        @Override
        public long createPayment(long currentCartId, long userId, long txnId, String paymentOption){
                log.info("Creating payment for the txn#: " + txnId + " for the user: " + userId + " for processing through EBS");
                CommonPaymentService cps = new CommonPaymentService();
                if(!cps.createPayment(currentCartId, userId, txnId, gatewayId)){
                        log.error("Error while creating the basic payment");
                        return PAYMENT_NOT_CREATED;
                }
                paymentId = cps.getPaymentId();
                
                if(paymentOption != null){
                        List<Attribute> attributes = new ArrayList<Attribute>();
                        attributes.add(new Attribute(IPaymentService.PAYMENT_METHOD, paymentOption));
                        
                        try {
                                PaymentServiceClient paymentServiceClient = new PaymentServiceClient();
                                paymentServiceClient.getClient().updatePaymentDetails(paymentId, null, null, null, null, null, null, null, null, PaymentStatus.INIT, null, attributes);
                        } catch (Exception e) {
                                log.error("Error while saving payment option attribute", e);
                                // TODO: We've already created the payment. We could allow the
                                // payment to go through. The customer will be a little
                                // annoyed to have to select from a host of options again but
                                // will be better than completely disallowing him.
                                return PAYMENT_NOT_CREATED;
                        }
                }

                return paymentId;
        }

        /**
         * 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(double amount, String paymentId){
                log.info("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("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol");
            
            try {
                    // URL of CGI-Bin script.
                        url = new URL("https://secure.ebs.in/api/1_0");
                    // URL connection channel.
                    urlConn = url.openConnection();
                    //urlConn.setRequestMethod("POST");
            } catch (MalformedURLException e1) {
                        log.error(Errors.CONN_FAILURE.message, e1);
                        resultMap.put(ERR_CODE, Errors.CONN_FAILURE.code);
                        resultMap.put(ERROR, Errors.CONN_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 (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;
        }
        
        public static void main(String[] args){
                capturePayment(30450.00, "2412653");
                
//              <output  transactionId="4793507"  paymentId="2411078"  amount="25005"  dateTime="2011-05-16 09:03:15"  mode="TEST"  referenceNo="4"  transactionType="Captured"  status="Processing"  />";

//              <output  errorCode="2"  error="Invalid Account ID/Secret Key"  />
//              <output  errorCode="12"  error="This payment is failed"  />
//              <output  errorCode="13"  error="This payment is captured already"  />           
        }
}