Android Open Source - 3DSView D3 S View






From Project

Back to project page 3DSView.

License

The source code is released under:

Apache License

If you think the Android project 3DSView listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package eu.livotov.labs.android.d3s;
//from www. j a v a 2 s .c o  m
import android.content.Context;
import android.graphics.Bitmap;
import android.net.http.SslError;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.webkit.SslErrorHandler;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * (c) Livotov Labs Ltd. 2013
 * Alex Askerov, Dmitri Livotov
 * <p/>
 * Date: 20/09/2013
 * <p/>
 * <h1>Intro</h1>
 * <p/>
 * <p>This is the 3DSecure WebView component. It can be used to perform 3D-Secure authorizations when processing internet
 * payments in apps. The technology is also named Verified By Visa and MasterCard Secure Code.</p>
 * <p/>
 * <p>The main idea is to route cardholder to the card issuer financial institution where cardholder will be required to
 * answer extra security question or enter one-time sms or token code in order to confirm the card transaction.</p>
 * <p/>
 * <h1>How to use</h1>
 * <p/>
 * <p>Add DDDSView to your layout, set custom (if required) postback url and authorization results listener via the
 * corresponding setters. Then invoke the <b>authorize(...)</b> method to start 3D-Secure authorization. You will need to
 * provide payment data, which will be issued by your card processor in attempt to make a transaction with the 3DS-capable
 * credit card.</p>
 * <p/>
 * <p>Once user completes the authorization, the authorization listener's onAuthorizationCompleted() method will be called
 * with the parameters, came from banking ACS server. Now you can use those parameters in your bckend processing server
 * to finalize the payment.</p>
 */
public class D3SView extends WebView
{

    /**
     * Namespace for JS bridge
     */
    private static String JavaScriptNS = "D3SJS";

    /**
     * Pattern to find the MD value in the ACS server post response
     */
    private static Pattern mdFinder = Pattern.compile(".*?(<input[^<>]* name=\\\"MD\\\"[^<>]*>).*?", 32);

    /**
     * Pattern to find the PaRes value in the ACS server post response
     */
    private static Pattern paresFinder = Pattern.compile(".*?(<input[^<>]* name=\\\"PaRes\\\"[^<>]*>).*?", 32);

    private static Pattern valuePattern = Pattern.compile(".*? value=\\\"(.*?)\\\"", 32);

    /**
     * Internal flag for tracking web page url changes in WebView
     */
    private boolean urlReturned = false;

    /**
     * When set to <b>true</b>, SSL certificate errors and self-signed certificates errors will be ignored.
     */
    private boolean debugMode = false;

    /**
     * Url that will be used by ACS server for posting result data on authorization completion. We will be monitoring
     * this URL in WebView handler to intercept its loading and grabbing the resulting data from POST message instead.
     */
    private String postbackUrl = "https://www.google.com";

    private boolean postbackHandled = false;

    /**
     * Callback to send authorization events to
     */
    private D3SSViewAuthorizationListener authorizationListener = null;


    public D3SView(final Context context)
    {
        super(context);
        initUI();
    }

    public D3SView(final Context context, final AttributeSet attrs)
    {
        super(context, attrs);
        initUI();
    }

    public D3SView(final Context context, final AttributeSet attrs, final int defStyle)
    {
        super(context, attrs, defStyle);
        initUI();
    }

    public D3SView(final Context context, final AttributeSet attrs, final int defStyle, final boolean privateBrowsing)
    {
        super(context, attrs, defStyle);
        initUI();
    }

    private void initUI()
    {
        getSettings().setJavaScriptEnabled(true);
        getSettings().setBuiltInZoomControls(true);
        addJavascriptInterface(new D3SJSInterface(), JavaScriptNS);

        setWebViewClient(new WebViewClient()
        {

            public void onPageStarted(WebView view, String url, Bitmap icon)
            {
                if (!urlReturned && !postbackHandled)
                {
                    if (url.toLowerCase().contains(postbackUrl.toLowerCase()))
                    {
                        postbackHandled = true;
                        view.loadUrl(String.format("javascript:window.%s.processHTML(document.getElementsByTagName('html')[0].innerHTML);", JavaScriptNS));
                        urlReturned = true;
                    } else
                    {
                        super.onPageStarted(view, url, icon);
                    }
                }
            }

            public boolean shouldOverrideUrlLoading(final WebView view, final String url)
            {
                if (!postbackHandled && url.toLowerCase().contains(postbackUrl.toLowerCase()))
                {
                    postbackHandled = true;
                    view.loadUrl(String.format("javascript:window.%s.processHTML(document.getElementsByTagName('html')[0].innerHTML);", JavaScriptNS));
                    return true;
                } else
                {
                    return super.shouldOverrideUrlLoading(view, url);
                }
            }

            public void onReceivedError(WebView view, int errorCode, String description, String failingUrl)
            {
                if (!failingUrl.startsWith(postbackUrl))
                {
                    authorizationListener.onAuthorizationWebPageLoadingError(errorCode, description, failingUrl);
                }
            }

            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error)
            {
                if (debugMode)
                {
                    handler.proceed();
                }
            }


        });

        setWebChromeClient(new WebChromeClient()
        {

            public void onProgressChanged(WebView view, int newProgress)
            {
                if (authorizationListener != null)
                {
                    authorizationListener.onAuthorizationWebPageLoadingProgressChanged(newProgress);
                }
            }
        });
    }

    private void completeAuthorization(String html)
    {
        String md = "";
        String pares = "";

        Matcher localMatcher1 = mdFinder.matcher(html);
        Matcher localMatcher2 = paresFinder.matcher(html);

        if (localMatcher1.find())
        {
            md = localMatcher1.group(1);
        }

        if (localMatcher2.find())
        {
            pares = localMatcher2.group(1);
        }

        if (!TextUtils.isEmpty(md))
        {
            Matcher valueMatcher = valuePattern.matcher(md);
            if (valueMatcher.find())
            {
                md = valueMatcher.group(1);
            }
        }

        if (!TextUtils.isEmpty(pares))
        {
            Matcher valueMatcher = valuePattern.matcher(pares);
            if (valueMatcher.find())
            {
                pares = valueMatcher.group(1);
            }
        }

        if (authorizationListener != null)
        {
            authorizationListener.onAuthorizationCompleted(md, pares);
        }
    }

    /**
     * Checks if debug mode is on. Note, that you must not turn debug mode for production app !
     *
     * @return
     */
    public boolean isDebugMode()
    {
        return debugMode;
    }

    /**
     * Sets the debug mode state. When set to <b>true</b>, ssl errors will be ignored. Do not turn debug mode ON
     * for production environment !
     *
     * @param debugMode
     */
    public void setDebugMode(final boolean debugMode)
    {
        this.debugMode = debugMode;
    }

    /**
     * Sets the callback to receive authorization events
     *
     * @param authorizationListener
     */
    public void setAuthorizationListener(final D3SSViewAuthorizationListener authorizationListener)
    {
        this.authorizationListener = authorizationListener;
    }

    /**
     * Starts 3DS authorization
     *
     * @param acsUrl ACS server url, returned by the credit card processing gateway
     * @param md     MD parameter, returned by the credit card processing gateway
     * @param paReq  PaReq parameter, returned by the credit card processing gateway
     */
    public void authorize(final String acsUrl, final String md, final String paReq)
    {
        authorize(acsUrl, md, paReq, null);
    }

    /**
     * Starts 3DS authorization
     *
     * @param acsUrl      ACS server url, returned by the credit card processing gateway
     * @param md          MD parameter, returned by the credit card processing gateway
     * @param paReq       PaReq parameter, returned by the credit card processing gateway
     * @param postbackUrl custom postback url for intercepting ACS server result posting. You may use any url you like
     *                    here, if you need, even non existing ones.
     */
    public void authorize(final String acsUrl, final String md, final String paReq, final String postbackUrl)
    {
        if (authorizationListener != null)
        {
            authorizationListener.onAuthorizationStarted(this);
        }

        if (!TextUtils.isEmpty(postbackUrl))
        {
            this.postbackUrl = postbackUrl;
        }

        urlReturned = false;

        List<NameValuePair> params = new LinkedList<NameValuePair>();

        params.add(new BasicNameValuePair("MD", md));
        params.add(new BasicNameValuePair("TermUrl", this.postbackUrl));
        params.add(new BasicNameValuePair("PaReq", paReq));

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try
        {
            new UrlEncodedFormEntity(params, HTTP.UTF_8).writeTo(bos);
        } catch (IOException e)
        {
        }

        postUrl(acsUrl, bos.toByteArray());
    }

    class D3SJSInterface
    {

        D3SJSInterface()
        {
        }

        @android.webkit.JavascriptInterface
        public void processHTML(final String paramString)
        {
            completeAuthorization(paramString);
        }
    }
}




Java Source Code List

eu.livotov.labs.android.d3s.D3SDialog.java
eu.livotov.labs.android.d3s.D3SSViewAuthorizationListener.java
eu.livotov.labs.android.d3s.D3SView.java