package de.open4me.depot.abruf.impl; import java.io.IOException; import java.io.StringWriter; import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.Charset; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.commons.io.IOUtils; import com.gargoylesoftware.htmlunit.HttpMethod; import com.gargoylesoftware.htmlunit.UnexpectedPage; import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.WebRequest; import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.PathNotFoundException; import de.open4me.depot.abruf.utils.HtmlUtils; import de.open4me.depot.gui.dialogs.DebugDialogWithTextarea; import de.willuhn.jameica.hbci.gui.dialogs.DebugDialog; import de.willuhn.jameica.hbci.rmi.Konto; import de.willuhn.jameica.system.Application; import de.willuhn.jameica.system.OperationCanceledException; import de.willuhn.logging.Logger; import de.willuhn.util.ApplicationException; /*** * Genutzt wird hierbei die Schnittstelle, welche auch die Consorsbank-APP nutzt. * Leider gibt es keine Dokumentation für diese Schnittstelle. * Informationen muss man sich selber zusammenreihmen, indem man die Kommunikation zwischen der APP und dem Server belauscht. * * @author sven * */ public class CortalConsorsMitHBCI extends BasisHBCIDepotAbruf { // private final static I18N i18n = Application.getPluginLoader().getPlugin(DepotViewerPlugin.class).getResources().getI18N(); final static String PROP_KUNDENNUMMER = "Kontonummer / UserID (Webseite)"; final static String PROP_PASSWORD = "PIN / Passwort (Webseite)"; @Override public String getName() { return "HBCI CortalConsors"; } @Override public void run(Konto konto) throws ApplicationException { super.run(konto); // Bestand via HBCI Logger.info("Starte Screen Scraping für Cortal Consors"); runUmsaetze(konto); } @SuppressWarnings("unchecked") public void runUmsaetze(Konto konto) throws ApplicationException { List<String> fehlerhafteOrder = new ArrayList<String>(); String depotnummer = null; try { depotnummer = konto.getKontonummer(); } catch (RemoteException e2) { throw new ApplicationException("Kontonummer nicht gefunden", e2); } ArrayList<String> seiten = new ArrayList<String>(); try { String username = konto.getMeta(PROP_KUNDENNUMMER, null); if (username == null || username.length() == 0) { throw new ApplicationException("Bitte geben ihre Kundenummer in den Synchronisationsoptionen ein"); } String password = konto.getMeta(PROP_PASSWORD, null); try { if (password == null || password.length() == 0) { password = Application.getCallback().askPassword(getName()); } } catch (Exception e1) { e1.printStackTrace(); throw new ApplicationException("Password-Eingabe:" + e1.getMessage()); } final WebClient webClient = new WebClient(); HtmlUtils.setProxyCfg(webClient, "https://webservices.consorsbank.de/"); webClient.getOptions().setThrowExceptionOnFailingStatusCode(false); String url = "https://webservices.consorsbank.de/WebServicesDe/services/restful/login"; // Login und "sessionID" speichern // TODO Password und Username entsprechend escapen String request = "{\"1\":{\"2\":\"" + password + "\",\"3\":\"" + username + "\",\"0\":{\"5\":\"MOOTWINA\",\"6\":\"0\",\"2\":\"DE\",\"1\":\"DE\",\"0\":\"CCLogin\",\"3\":\"\",\"4\":\"1\"}}}"; String json = getRequest(webClient, url, request); seiten.add(json); // Prüfen, ob Login Fehlgeschlagen ist if (!"CCLogin".equals(jsonRead(json,"$.2.0.0"))) { Logger.debug(json); String msg = jsonRead(json,"$.20[0].1"); if (msg == null || msg.isEmpty()) { msg = "Login aus unbekannten Grund nicht möglich!"; } throw new ApplicationException(msg); } String sessionID = jsonRead(json,"$.2.0.3"); // Request: Alle Order String allordersRequest = "{\"6\":{\"1\":\"" + depotnummer + "\",\"600\":{\"2\":\"30\",\"3\":\"orderNo DESC\",\"1\":\"1\",\"0\":\"Order\"},\"601\":\"Q\",\"0\":{\"5\":\"MOOTWINA\",\"6\":\"0\",\"2\":\"DE\",\"1\":\"DE\",\"0\":\"CCOrderAllInquiry\",\"3\":\"" + sessionID + "\",\"4\":\"1\"}}}"; json = getRequest(webClient, "https://webservices.consorsbank.de/WebServicesDe/services/restful/getAllOrders", allordersRequest); seiten.add(json); // Für alle Order, die Detailinformationen requesten und Umsatz-Eintrag generieren try { Logger.debug("JSON für Order: " + json.replace(depotnummer, "000111222333")); Integer anzahlOrders = JsonPath.parse(json).read("$.7.602.2"); List<Map<String, Object>> orders = null; if (anzahlOrders == 1) { orders = new ArrayList<Map<String, Object>>(); orders.add(JsonPath.parse(json).read("$.7.2", Map.class)); } else if (anzahlOrders > 1){ orders = JsonPath.parse(json).read("$.7.2", List.class); } if (orders == null) { Logger.info("Es wurden keine Order gefunden!"); } else { Logger.info("Es wurden " + orders.size() + " Order gefunden!"); for (Map<String, Object> orderinfo : orders) { if (orderinfo.get("6").toString().equals("0")) { Logger.info("Offene Order übersprungen!"); continue; } String orderRequest = "{\"101\":{\"1\":\"" + depotnummer + "\",\"2\":\"" + orderinfo.get("4").toString() + "\",\"0\":{\"5\":\"MOOTWINA\",\"6\":\"0\",\"2\":\"DE\",\"1\":\"DE\",\"0\":\"CCOrderDetailInquiry\",\"3\":\"" + sessionID + "\",\"4\":\"1\"}}}"; String order = getRequest(webClient, "https://webservices.consorsbank.de/WebServicesDe/services/restful/getOrderDetail", orderRequest); seiten.add(order); parseOrder(depotnummer, konto, fehlerhafteOrder, orderinfo, order); } } } catch (PathNotFoundException pnfe) { Logger.error("Fehler bei der Verarbeitung der JSON", pnfe); fehlerhafteOrder.add(pnfe + json.replace(depotnummer, "000111222333")); } // Logout String logoutRequest = "{\"17\":{\"0\":{\"5\":\"MOOTWINA\",\"6\":\"0\",\"2\":\"DE\",\"1\":\"DE\",\"0\":\"CCLogout\",\"3\":\"" + sessionID + "\",\"4\":\"1\"}}}"; json = getRequest(webClient, "https://webservices.consorsbank.de/WebServicesDe/services/restful/logout", logoutRequest); seiten.add(json); // @TODO Anwort verifizieren } catch (IOException e) { throw new ApplicationException(e); } finally { try { debug(seiten, konto); } catch (RemoteException e) { throw new ApplicationException(e); } } try { if (fehlerhafteOrder.size() > 0) { DebugDialogWithTextarea dialog = new DebugDialogWithTextarea(DebugDialog.POSITION_CENTER, fehlerhafteOrder); dialog.open(); } } catch (OperationCanceledException oce) { // } catch (Exception e) { Logger.error("unable to display debug dialog",e); } } private void parseOrder(String depotnummer, Konto konto, List<String> fehlerhafteOrder, Map<String, Object> orderinfo, String order) throws RemoteException { Map<String, Object> detailInfo = null; try { detailInfo = JsonPath.parse(order).read("$.102.50", Map.class); CortalConsorsMitHBCIJSONWrapper wrapper = new CortalConsorsMitHBCIJSONWrapper(orderinfo, detailInfo); if (!wrapper.addUmsatz(konto.getID())) { fehlerhafteOrder.add(wrapper.getGrund() + System.lineSeparator() + CortalConsorsMitHBCIJSONWrapper.getAnnoymisierterBuchungstext(orderinfo, detailInfo)); } } catch (PathNotFoundException pnfe) { Logger.error("Fehler bei der Verarbeitung der JSON", pnfe); String out = CortalConsorsMitHBCIJSONWrapper.getAnnoymisierterBuchungstext(orderinfo, detailInfo).replace(depotnummer, "000111222333") + "\n" + order; Logger.info("Orderjson: " + out); fehlerhafteOrder.add(pnfe + out); } } @Override public List<String> getPROP(Konto konto) { List<String> result = super.getPROP(konto); result.add(0, PROP_PASSWORD); result.add(0, PROP_KUNDENNUMMER); return result; } @Override public List<String[]> getPropertiesChanges(int version) { List<String[]> liste = new ArrayList<String[]>(); if (version < 1) { liste.add(new String[]{ "Kundennummer (Webseite)", "Kontonummer / UserID (Webseite)", "1"}); } if (version < 2) { liste.add(new String[]{ "Passwort (Webseite)", "PIN / Passwort (Webseite)", "2"}); } if (version < 4) { liste.add(new String[] { "Nur Bestand via HBCI abholen?", "", "4" }); } return liste; } @Override public boolean isSupported(Konto konto) throws ApplicationException, RemoteException { return super.isSupported(konto) && (konto.getBLZ().equals("76030080") || konto.getBic().toUpperCase().equals("CSDBDE71XXX")); } /** * Führt ein JSON-Request auf * @param webClient htmlunit-client * @param url URL * @param request Request im JSON Format * @return Antwort des Server, auch in JSON Format * @throws MalformedURLException * @throws IOException */ private String getRequest(final WebClient webClient, String url, String request) throws MalformedURLException, IOException { WebRequest requestSettings = new WebRequest(new URL(url), HttpMethod.POST); requestSettings.setRequestBody(request); requestSettings.setAdditionalHeader("Content-Type", "application/json"); UnexpectedPage p = (UnexpectedPage) webClient.getPage(requestSettings); StringWriter writer = new StringWriter(); IOUtils.copy(p.getInputStream(), writer, Charset.forName("UTF-8")); String json = writer.toString(); return json; } private String jsonRead(String json, String xpath) { try { return JsonPath.parse(json).read(xpath); } catch (com.jayway.jsonpath.PathNotFoundException e) { return null; } } }