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

9949dfe0542bd0fd32676d63c97a625f?s=128

Mathieu Calba

April 11, 2017
Tweet

Transcript

  1. Streamlining Payments on Mobile Android Makers 2017

  2. @Mathieu_Calba

  3. @Mathieu_Calba

  4. Payment is not Easy

  5. Payment is not Easy Amazon

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

    No Limitation
  7. Payment is not Easy Lydia

  8. Payment is not Easy Lydia Pre-filling

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

  10. Payment is not Easy Stripe

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

    Formatting Error highlight
  12. None
  13. Compatible card types

  14. Keep It Stupid and Simple

  15. Keep It Stupid and Simple

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

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

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

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

    CVV Holder Name 4111 1111 1111 1111 04/17 111 Bill Murray
  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
  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 €
  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 €
  23. Testing it

  24. Step 1 - Simplest Form <android.support.constraint.ConstraintLayout ... > <EditText android:id="@+id/edit_card_number"

    android:hint="Card Number" ... /> <EditText android:id="@+id/edit_expiration_month" android:hint="Expiration Month" ... /> ... <CheckBox android:id="@+id/check_box_terms" android:text="I accept the terms &amp; conditions of AndroidMakers" ... /> <Button android:id="@+id/bt_pay" android:text="Pay" ... /> </android.support.constraint.ConstraintLayout>
  25. Step 1 - Simplest Form <android.support.constraint.ConstraintLayout ... > <EditText android:id="@+id/edit_card_number"

    android:hint="Card Number" ... /> <EditText android:id="@+id/edit_expiration_month" android:hint="Expiration Month” ... /> ... <CheckBox android:id="@+id/check_box_terms" android:text="I accept the terms &amp; conditions of AndroidMakers" ... /> <Button android:id="@+id/bt_pay" android:text="Pay" ... /> </android.support.constraint.ConstraintLayout>
  26. Step 1 - Simplest Form Loose sense of what are

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

    we filling We can input whatever we want
  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
  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
  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
  31. Taking care of these first form problems

  32. Step 2 - Minimal Form <EditText android:id="@+id/edit_card_number" android:hint="Card Number" ...

    /> Keep sense of what are we filling
  33. Step 2 - Minimal Form <android.support.design.widget.TextInputLayout android:id="@+id/card_number" android:hint="Card Number" ...

    > <android.support.design.widget.TextInputEditText android:id="@+id/edit_card_number" android:layout_width="match_parent" android:layout_height="wrap_content" /> </android.support.design.widget.TextInputLayout> Material Components for Android compile 'com.android.support:design:25.3.0' Keep sense of what are we filling
  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
  35. Step 2 - Minimal Form 4 classes of InputType: TYPE_CLASS_DATETIME

    TYPE_CLASS_NUMBER TYPE_CLASS_PHONE TYPE_CLASS_TEXT Limit the input
  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 ...
  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
  38. Step 2 - Minimal Form android:inputType XML attribute Limit the

    input
  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 ...
  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
  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
  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
  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
  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
  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
  46. Step 2 - Minimal Form android:imeOptions XML attribute Moving between

    inputs
  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
  48. Step 2 - Minimal Form TextView.setOnEditorActionListener (OnEditorActionListener l) Help you

    react on the Keyboard action key press Moving between inputs
  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
  50. Step 2 - Minimal Form When Activity starts Showing/Hiding Keyboard

  51. Step 2 - Minimal Form android:windowSoftInputMode Activity attribute defining how

    it interacts with the window containing the keyboard. Showing/Hiding Keyboard
  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
  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
  54. Step 2 - Minimal Form Hide the keyboard if shown

    when the user tap the terms and conditions checkbox Showing/Hiding Keyboard
  55. Step 2 - Minimal Form On demand InputMethodManager.showSoftInput(View view, int

    flags) InputMethodManager.hideSoftInputFromWindow (IBinder windowToken, int flags) Showing/Hiding Keyboard
  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
  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
  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
  59. Adapting each Fields to its Kind of Content

  60. First up: Expiration Date fields

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

    error display
  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
  63. Step 3 - Improving Expiration Date TextView.addTextChangedListener (TextWatcher watcher) Listener

    on user inputs Data Validation
  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
  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
  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
  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
  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
  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
  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
  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
  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
  73. Step 3 - Improving Expiration Date Data Validation

  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
  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
  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
  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
  78. Step 3 - Improving Expiration Date Using formatting pattern MM/YY

    Without letting the user enter / Optimising fields distribution
  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
  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
  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
  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
  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
  84. Step 3 - Improving Expiration Date Auto-advance to the next

    field!
  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
  86. Spreading these improvements

  87. Step 4 - Improving PAN No formatting

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

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

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

    No error display
  91. Step 4 - Improving PAN Many types of Cards, multiple

    formats Visa - 4xxx xxxx xxxx xxxx Formatting
  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
  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
  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
  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
  96. Step 4 - Improving PAN Using same technics as with

    the Expiration Date field
  97. Step 4 - Improving PAN Detect type of card

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

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

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

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

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

    it Display type of card Handle CVV length Display icon for each fields
  103. Keep it Safe for your Users (and for you)

  104. Keep it Safe for your Users (and for you) WindowManager.LayoutParams.FLAG_SECURE

    Allows to configure a Window as secure Blocking screen capture
  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
  106. Keep it Safe for your Users (and for you) Blocking

    screen capture
  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
  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
  109. Keep it Contextual

  110. Keep it Contextual Let the User know what he is

    paying Display the price directly on the Pay button
  111. Adapt to your Users

  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
  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
  114. Adapt to your Users Expanding list with one entry per

    payment method Multiple Payment Methods
  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
  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
  117. None
  118. Remember for Them

  119. –Me, right now “The better way to make a user

    fill in a form is to fill it for him”
  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
  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
  122. Remember for Them A saved payment card is probably the

    preferred way of paying, so make it the most accessible one
  123. Remember for Them Autofill Framework Coming to Android O

  124. Stay One Touch Away aka le Paiement Digital

  125. Stay One Touch Away Do not force the user to

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

    API
  127. Stay One Touch Away API 23 min, but compatibility version

    available in support library Requires android.permission.USE_FINGERPRINT (not dangerous) Fingerprint API
  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
  129. Stay One Touch Away FingerprintManager.CryptoObject Wrapper for using crypto objects

    with FingerprintManager Works on Cipher, Mac, and Signature objects Fingerprint API
  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
  131. Stay One Touch Away Signature Provides digital signature algorithm Used

    for authentication and integrity assurance of digital data Fingerprint API
  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
  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
  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
  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
  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
  137. Stay One Touch Away Consult a security expert to know

    exactly what you can do with storing credit card data! Fingerprint API
  138. Stay One Touch Away FingerprintManager.isHardwareDetected() Fingerprint API

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

  140. Stay One Touch Away FingerprintManager.authenticate( CryptoObject crypto, int flags, CancellationSignal

    cancel, AuthenticationCallback callback, Handler handler) Fingerprint API
  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
  142. Questions?

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

    • Fonts: Yanone Kaffeesatz & Signika