Android integration

Use these guidelines to incorporate the mobile SDK into your Android mobile app. See the Android API for additional details about implementing each screen.

  1. Prepare for integration.
  2. Add the SDK to your project.
  3. Add permissions.
  4. Add the Amount screen. (optional)
  5. Add the Card Entry screen. (required)
  6. Add the Customer Info screen. (recommended)

You should also review the orientation guidelines.

1: Prepare for integration

Before you begin:

2: Add the SDK to your project

You must add the SDK to your project before you can start working with it. For more information, contact AffiniPay Support.

3: Add permissions

To support an AffiniPay mobile card reader, you must add the INTERNET permission to your AndroidManifest.xml file, within the manifest section and before the application section. This permission is required for an audio-jack card reader.

<uses-permission android:name="android.permission.INTERNET"/>

4: Add the Amount screen

How the amount to be paid is determined is application specific. Your app might allow the user to input a dollar amount or possibly sum the contents of a shopping cart. Once the amount is known, then your app can invoke our card-entry process to begin the payment process.

5: Add the Card Entry screen

You must include the Card Entry screen that enables the merchant to either swipe to provide card information or manually enter the card details. This screen is required to charge with the AffiniPay Payment Gateway.

To add the Card Entry screen:

  1. Create InitParams.
    private var publicKey: String? = null
    private var accountId: String? = null
    private var accountName: String? = null
    private var disableCardReader: Boolean? = false
    private var trustAccount: Boolean? = false
    private var requireCvv: Boolean? = false
    private var amount: String? = "53"
    

  2. Create the goToCardEntryScreen function, pass the initParams to navigate to the Card Entry screen, and load the fragment in the goToScreen function.
    override fun goToCardEntryScreen() {
      topContainer.visibility = View.VISIBLE
      fragment = ViewProvider.getCardEntryView(this,TokenizationInitParams(publicKey as String,
           amount as String, accountName as String, disableCardReader as Boolean, trustAccount as Boolean, requireCvv as Boolean))
      goToScreen(fragment as CardEntryFragment)
    }
    private fun goToScreen(newView: Fragment) {
      val fragmentTransaction = supportFragmentManager.beginTransaction()
      fragmentTransaction.replace(R.id.fragment_container, newView, "currentFragment").addToBackStack(newView.tag)
      fragmentTransaction.commit()
    }

  3. Create the navigation bar in the layout file along with your fragment container.

  4. Create the back button and cancel button listener in the onStart method.
    override fun onStart() {
     super.onStart()
    
     if (supportFragmentManager.backStackEntryCount == 0) {
       goToScreen(LoginAccountView())
     }
    
     ivBack.setOnClickListener {
       onBackPressed()
     }
    
     tvCancel.setOnClickListener {
       showDialog()
     }
    }

  5. Create the showDialog method to show the dialog on click of Cancel button.
    private fun showDialog(){
      val builder = this?.let {
        AlertDialog.Builder(it)
      }
      builder.setMessage(getString(R.string.card_entry_cancel_dialog_title))
      builder.setCancelable(false)
    
      builder.setPositiveButton(getString(R.string.card_entry_cancel_dialog_pos_button)) { di: DialogInterface, _: Int ->
        di.dismiss()
        onCancelButtonPress()
      }
    
      builder.setNegativeButton(getString(R.string.card_entry_cancel_dialog_neg_button)) { di: DialogInterface, _: Int ->
        di.dismiss()
      }
      val alert = builder.create()
      alert.show()
    
      alert.getButton(AlertDialog.BUTTON_POSITIVE).setBackgroundColor(Color.WHITE)
      alert.getButton(AlertDialog.BUTTON_NEGATIVE).setTextAppearance(R.style.PositiveAlertTextStyle)
      alert.getButton(AlertDialog.BUTTON_NEGATIVE).setBackgroundColor(Color.WHITE)
      alert.getButton(AlertDialog.BUTTON_NEGATIVE).setTextAppearance(R.style.NegativeAlertTextStyle)
     }

  6. Create the onBackPressed method to hide the navigation bar and go back to previous screen.
    override fun onBackPressed() {
      if (supportFragmentManager.backStackEntryCount > 1) {
     if(supportFragmentManager.backStackEntryCount == 3) {
       topContainer.visibility = View.VISIBLE
     } else {
       topContainer.visibility = View.GONE
     }
     supportFragmentManager.popBackStackImmediate()
      } else {
     finish()
      }
    }
    

  7. Implement the callback function onReturnCardData.
    override fun onReturnCardData(typeOfCharge: CardResult) {
     topContainer.visibility = View.GONE
     goToScreen(CustomerInfoFragment()) //This is what we have created in previous step.
      }

  8. Implement the callback function onCustomerInfoReceived on CustomerInformation received, call the startTokenization method of CardEntryHelper class, and pass the initParams and tokenizationCompleteParams to get the token.
    override fun onCustomerInfoReceived(tokenizationCompleteParams: TokenizationCompleteParams) {
     val cardEntryHelper = CardEntryHelper(this)
     cardEntryHelper.startTokenization(TokenizationInitParams(publicKey as String, amount as String,
             accountName as String, disableCardReader as Boolean, trustAccount as Boolean, requireCvv as Boolean), tokenizationCompleteParams)
      }

  9. Implement the callback functions onManualChargeDataReceived, onSwipeChargeDataReceived, and onEMVChargeDataReceived.
    override fun onManualChargeDataReceived(manualChargeResult: ChargeResult) {
       Log.d("ContainerActivity", "onManualChargeDataReceived${manualChargeResult.oneTimeToken}")
       compositeDisposable.add(chargeModel.makeCharge(publicKey as String, accountId as String, manualChargeResult.oneTimeToken,
               manualChargeResult.amount).subscribeOn
       (Schedulers.io())
               .observeOn(AndroidSchedulers.mainThread()).subscribe({ charge ->
                 (fragment as CardEntryFragment).completionCallback(true)
               }, { error ->
                 (fragment as CardEntryFragment).completionCallback(false)
                 Toast.makeText(this,error.message,Toast.LENGTH_LONG).show()
                 print(error.message)
               }))
     }
     override fun onSwipeChargeDataReceived(swipeChargeResult: ChargeResult) {
       Log.d("ContainerActivity", "onSwipeChargeDataReceived${swipeChargeResult.oneTimeToken}")
       compositeDisposable.add(chargeModel.makeCharge(publicKey as String, accountId as String, swipeChargeResult.oneTimeToken, swipeChargeResult.amount).subscribeOn
       (Schedulers.io())
               .observeOn(AndroidSchedulers.mainThread()).subscribe({ charge ->
                 (fragment as CardEntryFragment).completionCallback(true)
               }, { error ->
                 (fragment as CardEntryFragment).completionCallback(false)
                 Toast.makeText(this,error.message,Toast.LENGTH_LONG).show()
                 print(error.message)
               }))
     }
     override fun onEMVChargeDataReceived(emvChargeResult: ChargeResult) {
       Log.d("ContainerActivity", "onEMVChargeDataReceived${emvChargeResult.oneTimeToken}")
       compositeDisposable.add(chargeModel.makeEMVCharge(publicKey as String, accountId as String, emvChargeResult.oneTimeToken, emvChargeResult.amount, emvChargeResult
               .paymentDataSource,
               emvChargeResult.pointOfSale as PointOfSale).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe({ charge ->
         (fragment as CardEntryFragment).completionCallback(true)
       }, { error ->
         (fragment as CardEntryFragment).completionCallback(false)
         Toast.makeText(this,error.message,Toast.LENGTH_LONG).show()
         print(error.message)
       }))
     }

    Note: Upon receiving a response from your server, you should notify via the onReturnCardData method on the CardEntryFragment. If the SDK doesn’t receive any response from the callback, it will automatically assume the charge has failed after a certain amount of time has passed.

    Note: To ensure the security of payment details, never make a call to the AffiniPay Payment Gateway directly from your mobile app.

    See AffiniPay’s sample mobile and backend apps for an example of how this can be implemented.

6: Add the Customer Info screen

Prior to completing a transaction, additional customer information can be passed along with the transaction. Some customer information fields are required, and collecting them may also be application specific. For example, your application may already have all required customer information in its database. In which case it just needs to pass it along during the tokenization process. Other applications will need to provide a user interface for collecting this information. Be sure to gather the appropriate cardholder details.

To add a Customer Info screen:

  1. Create the ClientDetails fragment.
    import com.affinipay.cardreadersdk.charge.CardEntryCallbacks
    import com.affinipay.cardreadersdk.charge.CardEntryFragment
    import com.affinipay.cardreadersdk.charge.TokenizationInitParams
    import com.affinipay.cardreadersdk.customerinfo.CustomerInfoFragment
    class CustomerInfoFragment internal constructor() : Fragment() {
      private lateinit var customerInfoRequester: CustomerInfoRequester
      private var customerInfo = CustomerInfo()
      override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                             savedInstanceState: Bundle?): View? {
    
     return inflater.inflate(R.layout.activity_customer_details_input, container, false)
      }
      override fun onAttach(activity: Activity) {
     super.onAttach(activity)
    
     if (activity !is CustomerInfoRequester) {
       throw Exception(resources.getString(R.string.customer_details_requester_implementation_exception))
     }
    
     customerInfoRequester = activity
      }
      override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
     super.onViewCreated(view, savedInstanceState)
    
     next_button.setOnClickListener {
       customerInfo.email = "xx@yy.com"
       customerInfo.phone = "512-555-1212"
       customerInfo.reference = "AffiniPay Example App"
       customerInfo.city = "Austin"
       customerInfo.state = "TX"
       customerInfo.country = "United States"
       customerInfo.address1 = "501, Green Street"
       customerInfo.address2 = "Main Street"
       customerInfo.postalCode = "75757"
       sendCustomerDetails(customerInfo)
     }
      }
      private fun sendCustomerDetails(customerInfo: CustomerInfo) {
     customerInfoRequester.onCustomerInfoReceived(TokenizationCompleteParams(customerInfo))
      }
    }

Orientation guidelines

DeviceOrientations
mobilePortrait
tabletAll