Skip to content

Commit

Permalink
- fixed 3ds auth bug with some Russian banks
Browse files Browse the repository at this point in the history
- added IDEA artifact
  • Loading branch information
livotov committed Oct 22, 2013
1 parent 4dd961d commit a019a67
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 34 deletions.
10 changes: 10 additions & 0 deletions .idea/artifacts/3dsview_jar.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Manifest-Version: 1.0

106 changes: 72 additions & 34 deletions src/eu/livotov/labs/android/d3s/D3SView.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.net.http.SslError;
import android.os.AsyncTask;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.webkit.*;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
Expand All @@ -21,40 +23,47 @@
/**
* (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 Pattern mdFinder = Pattern.compile(".*?<input.*name=\\\"MD\\\" value=\\\"(.*?)\\\".*?>", Pattern.DOTALL);
private static Pattern mdFinder = Pattern.compile(".*?(<input[^<>]* name=\\\"MD\\\"[^<>]*>).*?", 32);

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

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

/**
* Internal flag for trasking web page url changes in WebView
Expand Down Expand Up @@ -105,6 +114,10 @@ public D3SView(final Context context, final AttributeSet attrs, final int defSty
private void initUI()
{
getSettings().setJavaScriptEnabled(true);
getSettings().setBuiltInZoomControls(true);
addJavascriptInterface(new D3SJSInterface(), JavaScriptNS);
getSettings().setSavePassword(false);

setWebViewClient(new WebViewClient()
{
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl)
Expand All @@ -118,7 +131,7 @@ public void onPageStarted(WebView view, String url, Bitmap icon)
{
if (url.startsWith(postbackUrl))
{
view.loadUrl("javascript:console.log('3DSEC'+document.getElementsByTagName('html')[0].innerHTML);");
view.loadUrl(String.format("javascript:window.%s.processHTML(document.getElementsByTagName('html')[0].innerHTML);", JavaScriptNS));
urlReturned = true;
} else
{
Expand All @@ -140,17 +153,6 @@ public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError e

setWebChromeClient(new WebChromeClient()
{
public boolean onConsoleMessage(ConsoleMessage cmsg)
{
if (cmsg.message().startsWith("3DSEC"))
{
String msg = cmsg.message().substring(4);
completeAuthorization(msg);
return true;
}

return false;
}

public void onProgressChanged(WebView view, int newProgress)
{
Expand All @@ -167,17 +169,35 @@ private void completeAuthorization(String html)
String md = "";
String pares = "";

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

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

if (parseMatcher.find())
if (localMatcher2.find())
{
pares = parseMatcher.group(1);
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)
Expand All @@ -188,6 +208,7 @@ private void completeAuthorization(String html)

/**
* Checks if debug mode is on. Note, that you must not turn debug mode for production app !
*
* @return
*/
public boolean isDebugMode()
Expand All @@ -198,6 +219,7 @@ public boolean isDebugMode()
/**
* 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)
Expand All @@ -207,6 +229,7 @@ public void setDebugMode(final boolean debugMode)

/**
* Sets the callback to receive auhtorization events
*
* @param authorizationListener
*/
public void setAuthorizationListener(final D3SSViewAuthorizationListener authorizationListener)
Expand All @@ -216,9 +239,10 @@ public void setAuthorizationListener(final D3SSViewAuthorizationListener authori

/**
* 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 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)
{
Expand All @@ -227,11 +251,12 @@ public void authorize(final String acsUrl, final String md, final String paReq)

/**
* 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.
*
* @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)
{
Expand Down Expand Up @@ -264,4 +289,17 @@ public void authorize(final String acsUrl, final String md, final String paReq,
postUrl(acsUrl, bos.toByteArray());
}

class D3SJSInterface
{

D3SJSInterface()
{
}

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

0 comments on commit a019a67

Please sign in to comment.