Android integration {Deprecated}
The SDK integration described on this page is no longer available. To integrate AffiniPay payments into your Android mobile app, see Deep linking from your Android mobile app.
Use this topic if you’re an AffiniPay partner with customers who aren’t already AffiniPay merchants and you want to brand your own sign up flow and incorporate it into your application. Otherwise, see the Quickstart to decide how to connect based on your integration type.</span>
Use these guidelines to incorporate the mobile SDK into your Android mobile app. See the Android API for additional details about implementing each screen.
- Prepare for integration.
- Add the SDK to your project.
- Add permissions.
- Add the Amount screen. (optional)
- Add the Card Entry screen. (required)
- Add the Customer Info screen. (recommended)
You should also review the orientation guidelines.
1: Prepare for integration
Before you begin:
- Obtain an AffiniPay merchant account from AffiniPay Support). You can use the test-mode secret key to test your integration.
- Ensure you are using Android minSdkVersion 26 or higher.
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:
- 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"
- Create the
goToCardEntryScreen
function, pass theinitParams
to navigate to the Card Entry screen, and load the fragment in thegoToScreen
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() }
- Create the navigation bar in the layout file along with your fragment container.
<androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/topContainer" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/drawable_rectangle" app:layout_constraintEnd_toEndOf="parent" android:visibility="gone" app:layout_constraintStart_toStartOf="parent"> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/toptext" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorPrimary" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintHorizontal_chainStyle="spread" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <ImageView android:id="@+id/ivBack" android:layout_width="wrap_content" android:layout_height="32dp" android:layout_marginStart="16dp" android:layout_marginTop="10dp" android:src="@drawable/ic_action_back" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="32dp" android:layout_marginTop="8dp" android:fontFamily="@font/opensans_regular" android:gravity="center_vertical" android:text="@string/card_entry" android:textColor="#ffffff" android:textSize="17dp" app:layout_constraintEnd_toStartOf="parent" app:layout_constraintStart_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/tvCancel" android:layout_width="wrap_content" android:layout_height="32dp" android:layout_marginTop="8dp" android:layout_marginEnd="16dp" android:background="@color/colorPrimary" android:fontFamily="@font/opensans_regular" android:gravity="center_vertical" android:text="@string/cancel" android:textColor="#ffffff" android:textSize="17dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
- 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() } }
- 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) }
- 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() } }
- 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. }
- Implement the callback function
onCustomerInfoReceived
onCustomerInformation
received, call thestartTokenization
method ofCardEntryHelper
class, and pass theinitParams
andtokenizationCompleteParams
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) }
- Implement the callback functions
onManualChargeDataReceived
,onSwipeChargeDataReceived
, andonEMVChargeDataReceived
.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 theCardEntryFragment
. 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:
- 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
Device | Orientations |
---|---|
mobile | Portrait |
tablet | All |