$30 off During Our Annual Pro Sale. View Details »

Streamlining Payments on Mobile

Streamlining Payments on Mobile

Payments on Mobile is not an easy task but it is certainly one of the most important in a commerce application. At Capitaine Train it was a very important part of our mission to help people purchase their train tickets easily and we improved the paying experience multiple times since the first release of the application.

In this talk, we will see how to build a payment process from scratch: from the simplest payment form to the more evolved one, handling 3DSecure, how to use fingerprint to streamline even more the process, handling multiple payment methods.

Video: https://www.youtube.com/watch?v=kwruiDw31T0

Mathieu Calba

April 11, 2017
Tweet

More Decks by Mathieu Calba

Other Decks in Programming

Transcript

  1. Streamlining Payments on Mobile
    Android Makers 2017

    View Slide

  2. @Mathieu_Calba

    View Slide

  3. @Mathieu_Calba

    View Slide

  4. Payment is not Easy

    View Slide

  5. Payment is not Easy
    Amazon

    View Slide

  6. Payment is not Easy
    Amazon
    No Data Validation
    No Formatting
    No Limitation

    View Slide

  7. Payment is not Easy
    Lydia

    View Slide

  8. Payment is not Easy
    Lydia
    Pre-filling

    View Slide

  9. Payment is not Easy
    Lydia
    No Formatting
    Simple data filling

    View Slide

  10. Payment is not Easy
    Stripe

    View Slide

  11. Payment is not Easy
    Stripe
    Format hints
    Card type recognition
    Formatting
    Error highlight

    View Slide

  12. View Slide

  13. Compatible card types

    View Slide

  14. Keep It Stupid and Simple

    View Slide

  15. Keep It Stupid and Simple

    View Slide

  16. Keep It Stupid and Simple
    Payment Account Number
    4111 1111 1111 1111

    View Slide

  17. Keep It Stupid and Simple
    Payment Account Number
    Expiration Date
    4111 1111 1111 1111
    04/17

    View Slide

  18. Keep It Stupid and Simple
    Payment Account Number
    Expiration Date
    CVV
    4111 1111 1111 1111
    04/17
    111

    View Slide

  19. Keep It Stupid and Simple
    Payment Account Number
    Expiration Date
    CVV
    Holder Name
    4111 1111 1111 1111
    04/17
    111
    Bill Murray

    View Slide

  20. Keep It Stupid and Simple
    Payment Account Number
    Expiration Date
    CVV
    Holder Name
    Terms of Sales
    4111 1111 1111 1111
    04/17
    111
    Bill Murray
    I agree with the Terms of
    Services of Android Makers

    View Slide

  21. Keep It Stupid and Simple
    Payment Account Number
    Expiration Date
    CVV
    Holder Name
    Terms of Sales
    Pay Button
    4111 1111 1111 1111
    04/17
    111
    Bill Murray
    I agree with the Terms of
    Services of Android Makers
    PAY 123.00 €

    View Slide

  22. Keep It Stupid and Simple
    Payment Account Number
    Expiration Date
    CVV
    Holder Name
    Terms of Sales
    Pay Button
    4111 1111 1111 1111
    04/17
    111
    Bill Murray
    I agree with the Terms of
    Services of Android Makers
    PAY 123.00 €

    View Slide

  23. Testing it

    View Slide

  24. Step 1 - Simplest Form
    ... >
    android:id="@+id/edit_card_number"
    android:hint="Card Number"
    ... />
    android:id="@+id/edit_expiration_month"
    android:hint="Expiration Month"
    ... />
    ...
    android:id="@+id/check_box_terms"
    android:text="I accept the terms & conditions
    of AndroidMakers"
    ... />
    android:id="@+id/bt_pay"
    android:text="Pay"
    ... />

    View Slide

  25. Step 1 - Simplest Form
    ... >
    android:id="@+id/edit_card_number"
    android:hint="Card Number"
    ... />
    android:id="@+id/edit_expiration_month"
    android:hint="Expiration Month”
    ... />
    ...
    android:id="@+id/check_box_terms"
    android:text="I accept the terms & conditions
    of AndroidMakers"
    ... />
    android:id="@+id/bt_pay"
    android:text="Pay"
    ... />

    View Slide

  26. Step 1 - Simplest Form
    Loose sense of what are we filling

    View Slide

  27. Step 1 - Simplest Form
    Loose sense of what are we filling
    We can input whatever we want

    View Slide

  28. Step 1 - Simplest Form
    Loose sense of what are we filling
    We can input whatever we want
    We have to manually select the next field

    View Slide

  29. Step 1 - Simplest Form
    Loose sense of what are we filling
    We can input whatever we want
    We have to manually select the next field
    Keyboard not showing up automatically

    View Slide

  30. Step 1 - Simplest Form
    Loose sense of what are we filling
    We can input whatever we want
    We have to manually select the next field
    Keyboard not showing up automatically

    View Slide

  31. Taking care of these first form
    problems

    View Slide

  32. Step 2 - Minimal Form
    android:id="@+id/edit_card_number"
    android:hint="Card Number"
    ... />
    Keep sense of what are we filling

    View Slide

  33. Step 2 - Minimal Form
    android:id="@+id/card_number"
    android:hint="Card Number"
    ... >
    android:id="@+id/edit_card_number"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

    Material Components for Android
    compile 'com.android.support:design:25.3.0'
    Keep sense of what are we filling

    View Slide

  34. Step 2 - Minimal Form
    InputType
    Bit masking defining the basic content type of a text in an Editable object.
    Used by IME to determine which keyboard to display.
    Limit the input

    View Slide

  35. Step 2 - Minimal Form
    4 classes of InputType:
    TYPE_CLASS_DATETIME
    TYPE_CLASS_NUMBER
    TYPE_CLASS_PHONE
    TYPE_CLASS_TEXT
    Limit the input

    View Slide

  36. Step 2 - Minimal Form
    Many flags & variations:
    TYPE_DATETIME_VARIATION_DATE
    TYPE_DATETIME_VARIATION_NORMAL
    TYPE_DATETIME_VARIATION_TIME
    TYPE_NUMBER_FLAG_DECIMAL
    TYPE_NUMBER_FLAG_SIGNED
    TYPE_NUMBER_VARIATION_NORMAL
    TYPE_TEXT_FLAG_AUTO_COMPLETE
    TYPE_TEXT_FLAG_AUTO_CORRECT
    TYPE_TEXT_FLAG_CAP_CHARACTERS
    Limit the input
    TYPE_TEXT_FLAG_CAP_SENTENCES
    TYPE_TEXT_FLAG_CAP_WORDS
    TYPE_TEXT_FLAG_MULTI_LINE
    TYPE_TEXT_FLAG_NO_SUGGESTIONS
    TYPE_TEXT_VARIATION_PASSWORD
    TYPE_TEXT_VARIATION_PERSON_NAME
    TYPE_TEXT_VARIATION_PHONETIC
    TYPE_TEXT_VARIATION_URI
    ...

    View Slide

  37. Step 2 - Minimal Form
    TextView.setInputType(int type)
    Only change the input capabilities (keyboard, pasting, etc). Does NOT validate
    the input.
    Limit the input

    View Slide

  38. Step 2 - Minimal Form
    android:inputType
    XML attribute
    Limit the input

    View Slide

  39. Step 2 - Minimal Form
    Many flags & variations:
    date
    datetime
    none
    number
    numberDecimal
    numberSigned
    textAutoComplete
    textAutoCorrect
    textCapCharacters
    textCapSentences
    Limit the input
    textCapWords
    textImeMultiLine
    textMultiLine
    textNoSuggestions
    textPassword
    textPersonName
    textPhonetic
    textVisiblePassword
    time
    ...

    View Slide

  40. Step 2 - Minimal Form
    Expiration Date
    TYPE_CLASS_DATETIME |
    TYPE_DATETIME_VARIATION_DATE or date
    Payment Account Number & CVV
    TYPE_CLASS_NUMBER |
    TYPE_NUMBER_VARIATION_NORMAL or
    number
    Card Holder
    TYPE_CLASS_TEXT |
    TYPE_TEXT_VARIATION_PERSON_NAME or
    textPersonName
    Limit the input

    View Slide

  41. Step 2 - Minimal Form
    Expiration Date
    TYPE_CLASS_DATETIME |
    TYPE_DATETIME_VARIATION_DATE or date
    Payment Account Number & CVV
    TYPE_CLASS_NUMBER |
    TYPE_NUMBER_VARIATION_NORMAL or
    number
    Card Holder
    TYPE_CLASS_TEXT |
    TYPE_TEXT_VARIATION_PERSON_NAME or
    textPersonName
    Limit the input

    View Slide

  42. Step 2 - Minimal Form
    Expiration Date
    TYPE_CLASS_DATETIME |
    TYPE_DATETIME_VARIATION_DATE or date
    Payment Account Number & CVV
    TYPE_CLASS_NUMBER |
    TYPE_NUMBER_VARIATION_NORMAL or
    number
    Card Holder
    TYPE_CLASS_TEXT |
    TYPE_TEXT_VARIATION_PERSON_NAME or
    textPersonName
    Limit the input

    View Slide

  43. Step 2 - Minimal Form
    Expiration Date
    TYPE_CLASS_DATETIME |
    TYPE_DATETIME_VARIATION_DATE or date
    Payment Account Number & CVV
    TYPE_CLASS_NUMBER |
    TYPE_NUMBER_VARIATION_NORMAL or
    number
    Card Holder
    TYPE_CLASS_TEXT |
    TYPE_TEXT_VARIATION_PERSON_NAME or
    textPersonName
    Limit the input

    View Slide

  44. Step 2 - Minimal Form
    TextView.setImeOptions
    (int imeOptions)
    Extend the type information for an Editor to
    improve IME integration with the application.
    Moving between inputs

    View Slide

  45. Step 2 - Minimal Form
    Many actions & flags:
    IME_ACTION_DONE
    IME_ACTION_GO
    IME_ACTION_NEXT
    IME_ACTION_NONE
    IME_ACTION_PREVIOUS
    IME_ACTION_SEARCH
    IME_ACTION_SEND
    IME_ACTION_UNSPECIFIED
    Moving between inputs
    IME_FLAG_FORCE_ASCII
    IME_FLAG_NAVIGATE_NEXT
    IME_FLAG_NAVIGATE_PREVIOUS
    IME_FLAG_NO_ACCESSORY_ACTION
    IME_FLAG_NO_ENTER_ACTION
    IME_FLAG_NO_EXTRACT_UI
    IME_FLAG_NO_FULLSCREEN

    View Slide

  46. Step 2 - Minimal Form
    android:imeOptions
    XML attribute
    Moving between inputs

    View Slide

  47. Step 2 - Minimal Form
    Many actions & flags:
    actionDone
    actionGo
    actionNext
    actionNone
    actionPrevious
    actionSearch
    actionSend
    actionUnspecified
    Moving between inputs
    flagForceAscii
    flagNavigateNext
    flagNavigatePrevious
    flagNoAccessoryAction
    flagNoEnterAction
    flagNoExtractUi
    flagNoFullscreen
    normal

    View Slide

  48. Step 2 - Minimal Form
    TextView.setOnEditorActionListener
    (OnEditorActionListener l)
    Help you react on the Keyboard action key press
    Moving between inputs

    View Slide

  49. Step 2 - Minimal Form
    Going Next (all except Card Holder)
    IME_ACTION_NEXT or actionNext
    Done (Card Holder)
    IME_ACTION_DONE or actionDone
    Moving between inputs

    View Slide

  50. Step 2 - Minimal Form
    When Activity starts
    Showing/Hiding Keyboard

    View Slide

  51. Step 2 - Minimal Form
    android:windowSoftInputMode
    Activity attribute defining how it interacts with the window containing the
    keyboard.
    Showing/Hiding Keyboard

    View Slide

  52. Step 2 - Minimal Form
    Can be a state and/or adjust.
    state: state of the keyboard when
    Activity becomes visible
    stateUnspecified (default)
    stateUnchanged
    stateHidden
    stateAlwaysHidden
    stateVisible
    stateAlwaysVisible
    Showing/Hiding Keyboard
    adjust: adjustments made to the
    Activity’s main Window
    adjustUnspecified (default)
    adjustResize
    adjustPan

    View Slide

  53. Step 2 - Minimal Form
    Can be a state and/or adjust.
    state: state of the keyboard when
    Activity becomes visible
    stateUnspecified (default)
    stateUnchanged
    stateHidden
    stateAlwaysHidden
    stateVisible
    stateAlwaysVisible
    Showing/Hiding Keyboard
    adjust: adjustments made to the
    Activity’s main Window
    adjustUnspecified (default)
    adjustResize
    adjustPan

    View Slide

  54. Step 2 - Minimal Form
    Hide the keyboard if shown when the user tap
    the terms and conditions checkbox
    Showing/Hiding Keyboard

    View Slide

  55. Step 2 - Minimal Form
    On demand
    InputMethodManager.showSoftInput(View view, int flags)
    InputMethodManager.hideSoftInputFromWindow
    (IBinder windowToken, int flags)
    Showing/Hiding Keyboard

    View Slide

  56. Step 2 - Minimal Form
    On demand
    public static void hideSoftKeyboard(Context context, View view) {
    InputMethodManager imm = (InputMethodManager) context.
    getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
    }
    public static void showSoftKeyboard(Context context, View view) {
    InputMethodManager imm = (InputMethodManager) context.
    getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.showSoftInput(view, 0);
    }
    Showing/Hiding Keyboard

    View Slide

  57. Step 2 - Minimal Form
    On demand
    public static void hideSoftKeyboard(Context context, View view) {
    InputMethodManager imm = (InputMethodManager) context.
    getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
    }
    public static void showSoftKeyboard(Context context, View view) {
    InputMethodManager imm = (InputMethodManager) context.
    getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.showSoftInput(view, 0);
    }
    Showing/Hiding Keyboard

    View Slide

  58. Step 2 - Minimal Form
    On demand
    public static void hideSoftKeyboard(Context context, View view) {
    InputMethodManager imm = (InputMethodManager) context.
    getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
    }
    public static void showSoftKeyboard(Context context, View view) {
    InputMethodManager imm = (InputMethodManager) context.
    getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.showSoftInput(view, 0);
    }
    Showing/Hiding Keyboard

    View Slide

  59. Adapting each Fields
    to its Kind of Content

    View Slide

  60. First up:
    Expiration Date fields

    View Slide

  61. Step 3 - Improving Expiration Date
    No data validation
    No error display

    View Slide

  62. Step 3 - Improving Expiration Date
    Spinner are not suitable for month/year
    selection on mobile due to the large amount of
    possibilities
    Data Validation

    View Slide

  63. Step 3 - Improving Expiration Date
    TextView.addTextChangedListener
    (TextWatcher watcher)
    Listener on user inputs
    Data Validation

    View Slide

  64. Step 3 - Improving Expiration Date
    public interface TextWatcher extends NoCopySpan {
    public void beforeTextChanged(CharSequence text,
    int start, int count,
    int after);
    public void onTextChanged(CharSequence text,
    int start, int before,
    int count);
    public void afterTextChanged(Editable text);
    }
    Data Validation

    View Slide

  65. Step 3 - Improving Expiration Date
    public interface TextWatcher extends NoCopySpan {
    public void beforeTextChanged(CharSequence text,
    int start, int count,
    int after);
    public void onTextChanged(CharSequence text,
    int start, int before,
    int count);
    public void afterTextChanged(Editable text);
    }
    Data Validation

    View Slide

  66. Step 3 - Improving Expiration Date
    public interface TextWatcher extends NoCopySpan {
    public void beforeTextChanged(CharSequence text,
    int start, int count,
    int after);
    public void onTextChanged(CharSequence text,
    int start, int before,
    int count);
    public void afterTextChanged(Editable text);
    }
    Data Validation

    View Slide

  67. Step 3 - Improving Expiration Date
    public interface TextWatcher extends NoCopySpan {
    public void beforeTextChanged(CharSequence text,
    int start, int count,
    int after);
    public void onTextChanged(CharSequence text,
    int start, int before,
    int count);
    public void afterTextChanged(Editable text);
    }
    Data Validation

    View Slide

  68. Step 3 - Improving Expiration Date
    private ForegroundColorSpan mErrorSpan = new ForegroundColorSpan(Color.RED));
    private boolean mIsEditing;
    @Override
    public void afterTextChanged(Editable text) {
    if (mIsEditing) {
    return;
    }
    mIsEditing = true;
    text.removeSpan(mErrorSpan);
    String expirationMonth = text.toString();
    if (isExpirationMonthLengthValid(expirationMonth)) {
    if (!isExpirationMonthValid(expirationMonth)) {
    text.setSpan(mErrorSpan, 0, LENGTH_EXPIRATION_MONTH, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    mEditView.setContentDescription("incorrect card expiration month");
    mEditView.announceForAccessibility(mEditView.getContentDescription());
    }
    }
    mIsEditing = false;
    }
    Data Validation

    View Slide

  69. Step 3 - Improving Expiration Date
    private ForegroundColorSpan mErrorSpan = new ForegroundColorSpan(Color.RED));
    private boolean mIsEditing;
    @Override
    public void afterTextChanged(Editable text) {
    if (mIsEditing) {
    return;
    }
    mIsEditing = true;
    text.removeSpan(mErrorSpan);
    String expirationMonth = text.toString();
    if (isExpirationMonthLengthValid(expirationMonth)) {
    if (!isExpirationMonthValid(expirationMonth)) {
    text.setSpan(mErrorSpan, 0, LENGTH_EXPIRATION_MONTH, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    mEditView.setContentDescription("incorrect card expiration month");
    mEditView.announceForAccessibility(mEditView.getContentDescription());
    }
    }
    mIsEditing = false;
    }
    Data Validation

    View Slide

  70. Step 3 - Improving Expiration Date
    private ForegroundColorSpan mErrorSpan = new ForegroundColorSpan(Color.RED));
    private boolean mIsEditing;
    @Override
    public void afterTextChanged(Editable text) {
    if (mIsEditing) {
    return;
    }
    mIsEditing = true;
    text.removeSpan(mErrorSpan);
    String expirationMonth = text.toString();
    if (isExpirationMonthLengthValid(expirationMonth)) {
    if (!isExpirationMonthValid(expirationMonth)) {
    text.setSpan(mErrorSpan, 0, LENGTH_EXPIRATION_MONTH, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    mEditView.setContentDescription("incorrect card expiration month");
    mEditView.announceForAccessibility(mEditView.getContentDescription());
    }
    }
    mIsEditing = false;
    }
    Data Validation

    View Slide

  71. Step 3 - Improving Expiration Date
    private ForegroundColorSpan mErrorSpan = new ForegroundColorSpan(Color.RED));
    private boolean mIsEditing;
    @Override
    public void afterTextChanged(Editable text) {
    if (mIsEditing) {
    return;
    }
    mIsEditing = true;
    text.removeSpan(mErrorSpan);
    String expirationMonth = text.toString();
    if (isExpirationMonthLengthValid(expirationMonth)) {
    if (!isExpirationMonthValid(expirationMonth)) {
    text.setSpan(mErrorSpan, 0, LENGTH_EXPIRATION_MONTH, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    mEditView.setContentDescription("incorrect card expiration month");
    mEditView.announceForAccessibility(mEditView.getContentDescription());
    }
    }
    mIsEditing = false;
    }
    Data Validation

    View Slide

  72. Step 3 - Improving Expiration Date
    private ForegroundColorSpan mErrorSpan = new ForegroundColorSpan(Color.RED));
    private boolean mIsEditing;
    @Override
    public void afterTextChanged(Editable text) {
    if (mIsEditing) {
    return;
    }
    mIsEditing = true;
    text.removeSpan(mErrorSpan);
    String expirationMonth = text.toString();
    if (isExpirationMonthLengthValid(expirationMonth)) {
    if (!isExpirationMonthValid(expirationMonth)) {
    text.setSpan(mErrorSpan, 0, LENGTH_EXPIRATION_MONTH, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    mEditView.setContentDescription("incorrect card expiration month");
    mEditView.announceForAccessibility(mEditView.getContentDescription());
    }
    }
    mIsEditing = false;
    }
    Data Validation

    View Slide

  73. Step 3 - Improving Expiration Date
    Data Validation

    View Slide

  74. Step 3 - Improving Expiration Date
    Lot of space wasted, maybe we could merge
    the expiration month and year fields into one
    ‣ Consolidate same kind of data together
    ‣ Allows us to validate the month with the
    year
    ‣ Make room for CVV on the right and Pay
    button moves up, making it always visible

    View Slide

  75. Step 3 - Improving Expiration Date
    Lot of space wasted, maybe we could merge
    the expiration month and year fields into one
    ‣ Consolidate same kind of data together
    ‣ Allows us to validate the month with the
    year
    ‣ Make room for CVV on the right and Pay
    button moves up, making it always visible

    View Slide

  76. Step 3 - Improving Expiration Date
    Lot of space wasted, maybe we could merge
    the expiration month and year fields into one
    ‣ Consolidate same kind of data together
    ‣ Allows us to validate the month with the
    year
    ‣ Make room for CVV on the right and Pay
    button moves up, making it always visible

    View Slide

  77. Step 3 - Improving Expiration Date
    Lot of space wasted, maybe we could merge
    the expiration month and year fields into one
    ‣ Consolidate same kind of data together
    ‣ Allows us to validate the month with the
    year
    ‣ Make room for CVV on the right and Pay
    button moves up, making it always visible

    View Slide

  78. Step 3 - Improving Expiration Date
    Using formatting pattern MM/YY
    Without letting the user enter /
    Optimising fields distribution

    View Slide

  79. Step 3 - Improving Expiration Date
    private boolean mIsEditing;
    @Override
    public void afterTextChanged(Editable text) {
    if (mIsEditing) {
    return;
    }
    mIsEditing = true;
    // Remove filters because they prevent us from inserting
    // non-digit characters when inputType is set to "number"
    InputFilter[] inputFilters = text.getFilters();
    text.setFilters(InputFilterUtils.EMPTY_FILTERS);
    [...]
    mIsEditing = false;
    }
    Optimising fields distribution

    View Slide

  80. Step 3 - Improving Expiration Date
    @Override
    public void afterTextChanged(Editable text) {
    [...]
    // 1. We extract the expirationMonth and expirationYear
    String noSpaceText = getDigitOnlyText(text);
    int length = noSpaceText.length();
    String expirationMonth = noSpaceText.
    substring(0, Math.min(length, LENGTH_EXPIRATION_PARTIAL));
    String expirationYear = "";
    if (noSpaceText.length() > LENGTH_EXPIRATION_PARTIAL) {
    expirationYear = noSpaceText.substring(
    LENGTH_EXPIRATION_PARTIAL, Math.min(length, LENGTH_EXPIRATION));
    }
    [...]
    }
    Optimising fields distribution

    View Slide

  81. Step 3 - Improving Expiration Date
    @Override
    public void afterTextChanged(Editable text) {
    [...]
    // 2. We try to format the newly entered text here
    CharSequence base = getFormattedExpirationDate(
    expirationMonth, expirationYear);
    text.removeSpan(mErrorSpan);
    text.replace(0, text.length(), base);
    // 3. We handle the error in this field like in previous slide
    // 4. Re-install filters
    text.setFilters(inputFilters);
    mIsEditing = false;
    }
    Optimising fields distribution

    View Slide

  82. Step 3 - Improving Expiration Date
    @Override
    public void afterTextChanged(Editable text) {
    [...]
    // 2. We try to format the newly entered text here
    CharSequence base = getFormattedExpirationDate(
    expirationMonth, expirationYear);
    text.removeSpan(mErrorSpan);
    text.replace(0, text.length(), base);
    // 3. We handle the error in this field like in previous slide
    // 4. Re-install filters
    text.setFilters(inputFilters);
    mIsEditing = false;
    }
    Optimising fields distribution

    View Slide

  83. Step 3 - Improving Expiration Date
    @Override
    public void afterTextChanged(Editable text) {
    [...]
    // 2. We try to format the newly entered text here
    CharSequence base = getFormattedExpirationDate(
    expirationMonth, expirationYear);
    text.removeSpan(mErrorSpan);
    text.replace(0, text.length(), base);
    // 3. We handle the error in this field like in previous slide
    // 4. Re-install filters
    text.setFilters(inputFilters);
    mIsEditing = false;
    }
    Optimising fields distribution

    View Slide

  84. Step 3 - Improving Expiration Date
    Auto-advance to the next field!

    View Slide

  85. Step 3 - Improving Expiration Date
    @Override
    public void afterTextChanged(Editable text) {
    [...]
    if (isMonthValid && isYearValid) {
    if (isExpirationDateValid(month, year)) {
    mCvvView.requestFocus();
    CharSequence text = mCvvEditView.getText();
    mCvvEditView.setSelection(text.length());
    }
    }
    mIsEditing = false;
    }
    Auto-advance to the next field

    View Slide

  86. Spreading these improvements

    View Slide

  87. Step 4 - Improving PAN
    No formatting

    View Slide

  88. Step 4 - Improving PAN
    No formatting
    No data validation

    View Slide

  89. Step 4 - Improving PAN
    No formatting
    No data validation
    No error display

    View Slide

  90. Step 4 - Improving PAN
    No formatting
    No data validation
    No error display

    View Slide

  91. Step 4 - Improving PAN
    Many types of Cards, multiple formats
    Visa - 4xxx xxxx xxxx xxxx
    Formatting

    View Slide

  92. Step 4 - Improving PAN
    Many types of Cards, multiple formats
    Visa - 4xxx xxxx xxxx xxxx
    Mastercard - 51xx xxxx xxxx xxxx
    -> 55xx xxxx xxxx xxxx
    Formatting

    View Slide

  93. Step 4 - Improving PAN
    Many types of Cards, multiple formats
    Visa - 4xxx xxxx xxxx xxxx
    Mastercard - 51xx xxxx xxxx xxxx
    to 55xx xxxx xxxx xxxx
    American Express - 34xx xxxxx xxxxx
    and 37xx xxxxx xxxxx
    Formatting

    View Slide

  94. Step 4 - Improving PAN
    Many types of Cards, multiple formats
    Visa - 4xxx xxxx xxxx xxxx
    Mastercard - 51xx xxxx xxxx xxxx
    to 55xx xxxx xxxx xxxx
    American Express - 34xx xxxxx xxxxx
    and 37xx xxxxx xxxxx
    and many others: https://en.wikipedia.org/wiki/Payment_card_number
    Formatting

    View Slide

  95. Step 4 - Improving PAN
    Each type must validate the Luhn Algorithm:
    public static boolean checkLuhnValidity(String number) {
    int sum = 0;
    boolean isOdd = true;
    for (int i = number.length() - 1; i >= 0; i--) {
    String digitString = number.substring(i, i + 1);
    int digit = Integer.parseInt(digitString);
    sum += isOdd ? digit : digit / 5 + (digit << 1) % 10;
    isOdd = !isOdd;
    }
    return sum % 10 == 0;
    }
    Data Validation

    View Slide

  96. Step 4 - Improving PAN
    Using same technics as with the Expiration
    Date field

    View Slide

  97. Step 4 - Improving PAN
    Detect type of card

    View Slide

  98. Step 4 - Improving PAN
    Detect type of card
    Validate it

    View Slide

  99. Step 4 - Improving PAN
    Detect type of card
    Validate it
    Display type of card

    View Slide

  100. Step 4 - Improving PAN
    Detect type of card
    Validate it
    Display type of card
    Handle CVV length

    View Slide

  101. Step 4 - Improving PAN
    Detect type of card
    Validate it
    Display type of card
    Handle CVV length
    Display icon for each fields

    View Slide

  102. Step 4 - Improving PAN
    Detect type of card
    Validate it
    Display type of card
    Handle CVV length
    Display icon for each fields

    View Slide

  103. Keep it Safe for your Users
    (and for you)

    View Slide

  104. Keep it Safe for your Users (and for you)
    WindowManager.LayoutParams.FLAG_SECURE
    Allows to configure a Window as secure
    Blocking screen capture

    View Slide

  105. Keep it Safe for your Users (and for you)
    Before calling Activity.setContentView(), add:
    getWindow().setFlags(
    LayoutParams.FLAG_SECURE,
    LayoutParams.FLAG_SECURE)
    Blocking screen capture

    View Slide

  106. Keep it Safe for your Users (and for you)
    Blocking screen capture

    View Slide

  107. Keep it Safe for your Users (and for you)
    A webpage hosted by the bank to validate the
    identity of the person using the credit card
    Totally outside of our control and the control of
    your provider of payment
    3D Secure

    View Slide

  108. Keep it Safe for your Users (and for you)
    Webpage can take forever to load or can fail to load without any message
    Need to inform the user what’s happening
    Adding a permanent SnackBar or other in layout message indicating that the
    process is in progress, and what to do if nothing happens after some time
    3D Secure

    View Slide

  109. Keep it Contextual

    View Slide

  110. Keep it Contextual
    Let the User know what he is paying
    Display the price directly on the Pay button

    View Slide

  111. Adapt to your Users

    View Slide

  112. Adapt to your Users
    Be frictionless on the payment
    Accept the most payment methods
    Adapt to your marketed countries (PayPal in Italy, SOFORT in Germany, etc)
    Use Android Pay if it’s available in your country (No France, thanks Google )
    Remember their preferred payment method
    Multiple Payment Methods

    View Slide

  113. Adapt to your Users
    How to adapt the UI with multiple payment methods and keeping it clean?
    No universal good way
    Depends on your business and what you want to optimise for
    Multiple Payment Methods

    View Slide

  114. Adapt to your Users
    Expanding list with one entry per payment
    method
    Multiple Payment Methods

    View Slide

  115. Adapt to your Users
    List with your preferred way of paying or the
    mostly used at the top promoted at the top
    Multiple Payment Methods

    View Slide

  116. Adapt to your Users
    Directly select the preferred way of paying (or
    the mostly used)
    And add an access to other payment methods
    Multiple Payment Methods

    View Slide

  117. View Slide

  118. Remember for Them

    View Slide

  119. –Me, right now
    “The better way to make a user fill in a form
    is to fill it for him”

    View Slide

  120. Remember for Them
    Suggest saving payment cards on your server
    for recurring users, but do not force them to do
    so
    The perfect moment to suggest it is just after a
    payment succeed

    View Slide

  121. Remember for Them
    Do it on the server, not locally, as some public organisations do not allow you to
    do so
    Server needs to be certified with PCI-DSS at a very high level, not an easy and
    quick thing to obtain

    View Slide

  122. Remember for Them
    A saved payment card is
    probably the preferred way
    of paying, so make it the
    most accessible one

    View Slide

  123. Remember for Them
    Autofill Framework
    Coming to Android O

    View Slide

  124. Stay One Touch Away
    aka le Paiement Digital

    View Slide

  125. Stay One Touch Away
    Do not force the user to use
    his fingerprint
    Fingerprint API

    View Slide

  126. Stay One Touch Away
    Allow to use the CVV
    Fingerprint API

    View Slide

  127. Stay One Touch Away
    API 23 min, but compatibility version available in support library
    Requires android.permission.USE_FINGERPRINT (not dangerous)
    Fingerprint API

    View Slide

  128. Stay One Touch Away
    FingerprintManager
    Detect presence of Fingerprint hardware
    CTS enforce the presence of an hardware backed KeyStore for it to be
    compatible with the Android API
    Detect if any fingerprint is enrolled
    Authenticate a FingerprintManager.CryptoObject
    Fingerprint API

    View Slide

  129. Stay One Touch Away
    FingerprintManager.CryptoObject
    Wrapper for using crypto objects with FingerprintManager
    Works on Cipher, Mac, and Signature objects
    Fingerprint API

    View Slide

  130. Stay One Touch Away
    Mac
    Checks integrity of information transmitted over or stored in an unreliable
    medium, using a secret key known by each entities that want to read/write the
    information
    Fingerprint API

    View Slide

  131. Stay One Touch Away
    Signature
    Provides digital signature algorithm
    Used for authentication and integrity assurance of digital data
    Fingerprint API

    View Slide

  132. Stay One Touch Away
    Fingerprint API with Signature - enrolment
    My Application
    Server
    Payment
    Cards
    Fingerprint
    Manager
    create Signature
    for a PrivateKey
    1
    authenticate
    Signature
    2
    register
    Signature
    3
    save
    Signature
    4
    KeyStore

    View Slide

  133. Stay One Touch Away
    Fingerprint API with Signature - payment
    My Application
    Server
    Payment
    Cards
    Fingerprint
    Manager
    create Signature
    for a PrivateKey
    1
    authenticate
    Signature
    2
    pay with
    Signature
    3
    validate
    Signature
    4
    5
    access
    Payment Card
    KeyStore

    View Slide

  134. Stay One Touch Away
    Cipher
    Cryptographic cipher for encryption and decryption
    Needs a transformation, aka the operations you want to apply to your input to
    get the output
    Fingerprint API

    View Slide

  135. Stay One Touch Away
    Fingerprint API with Cipher - encryption
    My Application
    KeyStore
    Fingerprint
    Manager
    prepare Cipher
    with a SecretKey
    in encrypt mode
    1
    authenticate
    Cipher
    2
    Database
    save Cipher.getIV()
    and encrypted Data
    4
    encrypt Data with
    authenticated Cipher
    3

    View Slide

  136. Stay One Touch Away
    Fingerprint API with Cipher - encryption
    My Application
    KeyStore
    Server
    Payment
    Cards
    Fingerprint
    Manager
    prepare Cipher
    with a SecretKey
    and the IV
    in decrypt mode
    2
    authenticate
    Cipher
    3
    pay with
    decrypted Data
    6
    Database
    get the IV for
    this Data
    1
    decrypt Data
    with Cipher
    5
    get the
    encrypted Data
    4

    View Slide

  137. Stay One Touch Away
    Consult a security expert to know exactly
    what you can do with storing credit card data!
    Fingerprint API

    View Slide

  138. Stay One Touch Away
    FingerprintManager.isHardwareDetected()
    Fingerprint API

    View Slide

  139. Stay One Touch Away
    FingerprintManager.hasEnrolledFingerprints()
    Fingerprint API

    View Slide

  140. Stay One Touch Away
    FingerprintManager.authenticate(
    CryptoObject crypto,
    int flags,
    CancellationSignal cancel,
    AuthenticationCallback callback,
    Handler handler)
    Fingerprint API

    View Slide

  141. Stay One Touch Away
    public static abstract class AuthenticationCallback {
    public void onAuthenticationError(int errorCode, CharSequence errString) { }
    public void onAuthenticationHelp(int helpCode, CharSequence helpString) { }
    public void onAuthenticationSucceeded(AuthenticationResult result) { }
    public void onAuthenticationFailed() { }
    public void onAuthenticationAcquired(int acquireInfo) { }
    };
    Fingerprint API

    View Slide

  142. Questions?

    View Slide

  143. Credits
    • Google Pixel device frames: Anjo Cerdeña https://material.uplabs.com/
    posts/flat-google-pixel-svgs
    • Fonts: Yanone Kaffeesatz & Signika

    View Slide