Implement JWT Refresh token in Open Event Attendee App

In open event attendee android app earlier only access token is used, which causes HTTP 401 many time due to expiry of the token. It needs to re sign-in for the user. Now we have implemented refresh token authorization with the open event server using retrofit and OkHttp. Retrofit is one of the most popular HTTP client for Android. When calling API, we may require authentication using a token. Usually, the token is expired after a certain amount of time and needs to be refreshed using the refresh token. The client would need to send an additional HTTP request in order to get the new token. Imagine you have a collection of many different APIs, each of them requires token authentication. If you have to handle refresh token by modifying your code one by one, it will take a lot of time and of course, it's not a good solution. In this blog, I'm going to show you how to handle refresh token on each API calls automatically if the token expires. How refresh token works?Add authenticator to OkHttpNetwork call and handle responseConclusionResources  Let’s analyze every step in detail. How Refresh Token Works? Whether tokens are opaque or not is usually defined by the implementation. Common implementations allow for direct authorization checks against an access token. That is, when an access token is passed to a server managing a resource, the server can read the information contained in the token and decide itself whether the user is authorized or not (no checks against an authorization server are needed). This is one of the reasons tokens must be signed (using JWS, for instance). On the other hand, refresh tokens usually require a check against the authorization server.  Add Authenticator to OkHTTP OkHttp will automatically ask the Authenticator for credentials when a response is 401 Not Authorised retrying last failed request with them. class TokenAuthenticator: Authenticator { override fun authenticate(route: Route?, response: Response): Request? { // Refresh your access_token using a synchronous api request val newAccessToken = service.refreshToken(); // Add new header to rejected request and retry it return response.request().newBuilder() .header(AUTHORIZATION, newAccessToken) .build() } } Add the authenticatior to OkHttp: val builder = OkHttpClient().newBuilder() .authenticator(TokenAuthenticator()) Network Call and Handle Response API call with retrofit: @POST("/auth/token/refresh") fun refreshToken(): Single<RefreshResponse> Refresh Response: @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy::class) data class RefreshResponse( val refreshToken: String ) In a Nutshell Refresh tokens improve security and allow for reduced latency and better access patterns to authorization servers. Implementations can be simple using tools such as JWT + JWS. If you are interested in learning more about tokens (and cookies), check our article here. Resources Android - Retrofit 2 Refresh Access Token with OkHttpClient and Authenticator: https://www.woolha.com/tutorials/android-retrofit-2-refresh-access-token-with-okhttpclient-and-authenticatorRefresh Tokens: When to Use Them and How They Interact with JWTs: https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/ Tags Eventyay, open-event, FOSSASIA, GSoC, Android, Kotlin, Refresh tokens

Continue ReadingImplement JWT Refresh token in Open Event Attendee App

Apply Shimmer Effect for Progress in Open Event Attendee Application

The open event attendee is an android app which allows users to discover events happening around the world using the Open Event Platform. It consumes the APIs of the open event server to get a list of available events and can get detailed information about them. Shimmer effect was created by Facebook to indicate a loading status, so instead of using ProgressBar or the usual loader use Shimmer for a better design and user interface. They also open-sourced a library called Shimmer both for Android and iOS so that every developer could use it for free. Add Shimmer libraryCreate a placeholder for shimmerApply the effect with live dataConclusionResources Let’s analyze every step in detail. Add Shimmer Library  Add Shimmer Library to build.gradle : // Cards Shimmer Animation implementation 'com.facebook.shimmer:shimmer:0.5.0' Create reasouces Add shimmer background color to colors.xml: <color name="shimmer_background">#dddddd</color> Create a placeholder layout: <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="@dimen/layout_margin_medium" app:cardBackgroundColor="@android:color/white" app:cardCornerRadius="@dimen/card_corner_radius" app:cardElevation="@dimen/layout_margin_none" android:foreground="?android:attr/selectableItemBackground" android:background="@android:color/white"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="@dimen/layout_margin_large" android:layout_gravity="center" android:orientation="vertical"> <ImageView android:layout_width="match_parent" android:layout_height="@dimen/item_image_view_160dp" android:scaleType="centerCrop" android:background="@color/shimmer_background"/> <LinearLayout android:layout_width="match_parent" android:layout_marginTop="@dimen/layout_margin_medium" android:layout_height="wrap_content" android:orientation="horizontal"> <View android:layout_width="@dimen/card_width_45dp" android:layout_height="@dimen/item_image_view" android:background="@color/shimmer_background" android:layout_marginEnd="@dimen/padding_large" android:layout_marginRight="@dimen/padding_large" android:gravity="center_horizontal" android:orientation="vertical" android:layout_marginTop="@dimen/padding_medium"> </View> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:paddingBottom="@dimen/padding_large" android:paddingTop="@dimen/padding_medium"> <View android:layout_width="match_parent" android:layout_height="@dimen/view_height_25dp" android:layout_marginBottom="@dimen/layout_margin_small" android:background="@color/shimmer_background"/> <View android:layout_width="match_parent" android:layout_height="@dimen/view_height_25dp" android:background="@color/shimmer_background"/> </LinearLayout> </LinearLayout> </LinearLayout> </androidx.cardview.widget.CardView> Add shimmer in your fragment/activity layout resources file: <com.facebook.shimmer.ShimmerFrameLayout android:id="@+id/shimmer_view_container" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="15dp" android:orientation="vertical" shimmer:duration="800"> <!-- Adding 7 rows of placeholders --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <include layout="@layout/data_placeholder_layout" /> <include layout="@layout/data_placeholder_layout" /> <include layout="@layout/data_placeholder_layout" /> <include layout="@layout/data_placeholder_layout" /> <include layout="@layout/data_placeholder_layout" /> <include layout="@layout/data_placeholder_layout" /> <include layout="@layout/data_placeholder_layout" /> </LinearLayout> </com.facebook.shimmer.ShimmerFrameLayout> Apply Shimmer with LiveData Declare live data variable in view model: private val mutableShowShimmer = MediatorLiveData<Boolean>() val showShimmer: MediatorLiveData<Boolean> = mutableShowShimmer Handle progress in the view model: compositeDisposable += eventPagedList .subscribeOn(Schedulers.io()) .doOnSubscribe { mutableShowShimmer.value = true }.finally { mutableShowShimmer.value = false } Handle shimmer with observing the live data in fragment/activity: eventsResultsViewModel.showShimmer .nonNull() .observe(viewLifecycleOwner, Observer { if (it) { rootView.shimmer_view_container.startShimmer() } else { rootView.shimmer_view_container.stopShimmer() } rootView.shimmer_view_container.isVisible = it }) GIF Resources Show shimmer progress in Android: https://medium.com/mindorks/android-design-shimmer-effect-fa7f74c68a93 Tags Eventyay, open-event, Shimmer, Facebook, MVVM, Fossasia, GSoC, Android, Kotlin

Continue ReadingApply Shimmer Effect for Progress in Open Event Attendee Application

Enable Server Configuration with Okhttp and Retrofit in Open Event Attendee Application

The open event attendee is an android app which allows users to discover events happening around the world using the Open Event Platform. It consumes the APIs of the open event server to get a list of available events and can get detailed information about them. We are using default API for eventyay app. Server configuration is something when we replace backend API with a new one and perform the same applications with the different server. As it is a fully open-source project on F-droid, so we have enabled the server configuration field for the F-droid build variant.  Retrofit and okhttp for network callsCreate a feasible UI and set the link to preferencesCreate interceptor for changing API URLAdd interceptor in okhttp client builderConclusionResources  Let’s analyze every step in detail. Retrofit and Okhttp for Network Call Using Retrofit for your Android app's networking can make your life so much easier. However, Retrofit's design requires a single Retrofit instance for each API with a different base URL. Consequently, if your app is talking to two or more APIs (under different URLs), you'll need to deal with at least two Retrofit instances. Retrofit is a type-safe REST client for Android, Java, and Kotlin developed by Square. The library provides a powerful framework for authenticating and interacting with APIs and sending network requests with OkHttp. OkHttp communicating with the server-  Design UI and set the link to preferences with MVVM Create a simple dialog with a checkbox with default URL and a EditText: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <CheckBox android:id="@+id/urlCheckBox" android:layout_margin="@dimen/layout_margin_large" android:layout_width="match_parent" android:layout_height="wrap_content" /> <com.google.android.material.textfield.TextInputLayout style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense" android:id="@+id/urlTextInputLayout" android:layout_margin="@dimen/layout_margin_large" android:hint="@string/other_url" android:layout_width="match_parent" android:layout_height="wrap_content"> <com.google.android.material.textfield.TextInputEditText android:id="@+id/urlEditText" android:layout_width="match_parent" android:layout_height="wrap_content" /> </com.google.android.material.textfield.TextInputLayout> </LinearLayout> Handle visibility if the dialog and display for only F-droid build: preferenceScreen.findPreference<PreferenceCategory>(getString(R.string.key_server_configuration))?.isVisible = BuildConfig.FLAVOR == FDROID_BUILD_FLAVOR Set current API to preference screen: preferenceScreen.findPreference<Preference>(getString(R.string.key_api_url))?.title = settingsViewModel.getApiUrl() Get API from View model:  fun getApiUrl(): String { return preference.getString(API_URL) ?: BuildConfig.DEFAULT_BASE_URL } Setup alert dialog: if (preference?.key == getString(R.string.key_api_url)) { showChangeApiDialog() } private fun showChangeApiDialog() { val layout = layoutInflater.inflate(R.layout.dialog_api_configuration, null) layout.urlCheckBox.text = BuildConfig.DEFAULT_BASE_URL val dialog = AlertDialog.Builder(requireContext()) .setView(layout) .setPositiveButton(getString(R.string.change)) { _, _ -> val url = if (layout.urlCheckBox.isChecked) BuildConfig.DEFAULT_BASE_URL else layout.urlEditText.text.toString() if (url === settingsViewModel.getApiUrl()) return@setPositiveButton settingsViewModel.changeApiUrl(url) view?.snackbar("API URL changed to $url") findNavController().popBackStack(R.id.eventsFragment, false) } .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> dialog.cancel() } .setCancelable(false) .show() dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false layout.urlCheckBox.setOnCheckedChangeListener { _, isChecked -> layout.urlTextInputLayout.isVisible = !isChecked dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = isChecked } Set URL to preferences in the view model and end current session: fun changeApiUrl(url: String) { preference.putString(API_URL, url) logout() } Create Interceptor to Handle New API URL Here default API URL is set to the retrofit already:  Retrofit.Builder() .client(get()) .baseUrl(baseUrl) .build() As we discussed earlier OkHttp handles every network call for the application. So here we track the URL host from the okhttp interceptor. If the URL host is equaled to the default API URL host, then we can say that it is an API call and then we can replace same with the host getting from preferences if it is not null and set the…

Continue ReadingEnable Server Configuration with Okhttp and Retrofit in Open Event Attendee Application

Handle app links and apply unit tests in Open Event Attendee Application

The open event attendee is an android app which allows users to discover events happening around the world using the Open Event Platform. It consumes the APIs of the open event server to get a list of available events and can get detailed information about them. Users following links on devices have one goal in mind: to get to the content they want to see. As a developer, you can set up Android App Links to take users to a link's specific content directly in your app, bypassing the app-selection dialog, also known as the disambiguation dialog. Because Android App Links leverage HTTP URLs and association with a website, users who don't have your app installed go directly to content on your site. A unit test generally exercises the functionality of the smallest possible unit of code (which could be a method, class, or component) in a repeatable way. You should build unit tests when you need to verify the logic of specific code in your app. Why unit test cases?Setup app link intent in the appApply unit test casesConclusionResources Let’s analyze every step in detail. Why unit test cases? As it is already discussed what the app link intents are, and it is basically for providing a better user experience for the application. These are some following reason why unit test cases should use -  Rapid feedback on failures.Early failure detection in the development cycle.Safer code refactoring, letting you optimize code without worrying about regressions.Stable development velocity, helping you minimize technical debt. JUnit4 library is used for unit tests. Setup app link intent in the app Declare the frontend host according to build flavor in the app level gradle file: buildTypes { release { resValue "string", "FRONTEND_HOST", "eventyay.com" } debug { resValue "string", "FRONTEND_HOST", "open-event-fe.netlify.com" } } Handle the app link intent in Manifest file by adding intent filter under main activity decleartion: <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="https" android:host="@string/FRONTEND_HOST"/> </intent-filter> Manifest will through the intent in the main activity file. Now handle the intent in main activity: override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) handleAppLinkIntent(intent) } override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) handleAppLinkIntent(intent) } private fun handleAppLinkIntent(intent: Intent?) { val uri = intent?.data ?: return val bundle = AppLinkUtils.getArguments(uri) val destinationId = AppLinkUtils.getDestinationId(uri) if (destinationId != null) { navController.navigate(destinationId, bundle) } } Here a new class/object AppLinkUtils is defined which will return destination fragment id and the argument/data according to the intent URI. Apply unit test cases: First, implement the libraries in the gradle file -  1. JUnit for unit tests, 2. Robolectric for using android classes in the test class: testImplementation 'junit:junit:4.12' testImplementation 'org.robolectric:robolectric:3.4.2' Create a test class for testing the app link functions and run it with RoboLectricTestRunner: private const val EVENT = "event" private const val RESET_PASSWORD = "resetPassword" private const val VERIFY_EMAIL = "verifyEmail" @RunWith(RobolectricTestRunner::class) class AppLinkUtilsTest { private fun getAppLink(type: String): Uri { return when (type) { EVENT -> Uri.parse("https://eventyay.com/e/5f6d3feb") RESET_PASSWORD -> Uri.parse("https://eventyay.com/reset-password?token=822980340478781748445098077144") VERIFY_EMAIL -> Uri.parse("https://eventyay.com/verify?token=WyJsaXZlLmhhcnNoaXRAaG") else ->…

Continue ReadingHandle app links and apply unit tests in Open Event Attendee Application

Configure location feature using MVVM in Open Event Attendee Application

The open event attendee is an android app which allows users to discover events happening around the world using the Open Event Platform. It consumes the APIs of the open event server to get a list of available events and can get detailed information about them. It deals with events based on location, but we have to take the location as an input from the user. While in many cases, we have to search for events on our current location only. To make this work, I have added a current location option, where the app will get our location and search for nearby events. Earlier we had to enter our current location as well to search nearby events. Model–view–viewmodel is a software architectural pattern. MVVM facilitates separation of development of the graphical user interface – be it via a markup language or GUI code – from the development of the business logic or back-end logic (the data model). Why Model-view-ViewModel?Setup Geo location View ModelConfigure location feature with MVVMConclusionResources Let’s analyze every step in detail. Advantages of using Model-view-ViewModel A clean separation of different kinds of code should make it easier to go into one or several of those more granular and focused parts and make changes without worrying.External and internal dependencies are in separate pieces of code from the parts with the core logic that you would like to test.Observation of mutable live data whenever it is changed. Setup the Geolocation view model Created new kotlin class name GeoLocationViewModel which contains a configure function for current location: package org.fossasia.openevent.general.search class GeoLocationViewModel : ViewModel() { fun configure(activity: Activity) { } } The GeoLocationViewModel class implement as VIewModel(). Now add the class in module inside Modules.kt : val viewModelModule = module { viewModel { GeoLocationViewModel() } } Configure location feature with GeoLocationViewModel: First, add play store location service implementation inside dependencies of build.gradle: // Location Play Service playStoreImplementation 'com.google.android.gms:play-services-location:16.0.0' Now we need location permissions to implement this feature. Adding user permissions in the manifest file: <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> Now ask user for the location permission: private fun checkLocationPermission() { val permission = context?.let { ContextCompat.checkSelfPermission(it, Manifest.permission.ACCESS_COARSE_LOCATION) } if (permission != PackageManager.PERMISSION_GRANTED) { requestPermissions(arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION), LOCATION_PERMISSION_REQUEST) } } Check for device location is enabled, if not send an intent to turn location on. The method is written inside configure function: val service = activity.getSystemService(Context.LOCATION_SERVICE) var enabled = false if (service is LocationManager) enabled = service.isProviderEnabled(LocationManager.NETWORK_PROVIDER) if (!enabled) { val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS) activity.startActivity(intent) return } Now create Mutable live data for current location inside the view model class : private val mutableLocation = MutableLiveData<String>() val location: LiveData<String> = mutableLocation Now implement location request and location callback inside configure method: val locationRequest: LocationRequest = LocationRequest.create() locationRequest.priority = LocationRequest.PRIORITY_LOW_POWER val locationCallback = object : LocationCallback() { override fun onLocationResult(locationResult: LocationResult?) { if (locationResult == null) { return } for (location in locationResult.locations) { if (location != null) { val latitude = location.latitude val longitude = location.longitude try { val geocoder = Geocoder(activity,…

Continue ReadingConfigure location feature using MVVM in Open Event Attendee Application

Add Navigation Architecture Component in Eventyay Attendee

The eventyay attendee is an android app which allows users to discover events happening around the world using the Open Event Platform. It consumes the APIs of the open event server to get a list of available events and can get detailed information about them. The project earlier used Fragment transition to handle fragment with multiple activities. Multiple activities are used because it is very complex to handle all fragment with a single activity using fragment transition. If we try to make a single activity application with fragment transition, it contains a lot of bugs with the back stack. But we can use Navigation Architecture Component to efficiently make Eventyay attendee a single activity. It is the perfect technology to handle all fragments and authentication in a single activity with no bigs or back stack. Issues with fragment transition and multiple activitiesWhy navigation architecture component?Process for using Navigation Architecture Component in the applicationAnimation of fragments with Navigation Architecture ComponentConclusionResources  Let’s analyze every step in detail. Issues with fragment transition and multiple activities Need to handle back stack on each Fragment TransitionHard to debugThis can leave your app in an unknown state when receiving multiple click events or during configuration changes.Fragment instances can be createdMultiple activities make the app slower due to using multiple intents Why navigation architecture component? Handling Fragment transactions Handling Up and Back actions correctly by defaultProviding standardized resources for animations and transitionsTreating deep linking as a first-class operationIncluding Navigation UI patterns, such as navigation drawers and bottom navigation, with minimal additional workProviding type safety when passing information while navigatingVisualizing and editing navigation graphs with Android Studio's Navigation Editor Steps involved in using Navigation Architecture Component in the application Adding dependencies to build.gradle: //Navigation implementation 'android.arch.navigation:navigation-fragment:1.0.0-alpha09' implementation 'android.arch.navigation:navigation-ui:1.0.0-alpha09' Create new resources file for navigation graph under res -> navigation: Add fragments in navigation_graph.xml with navigation editor. Let’s take one example: <?xml version="1.0" encoding="utf-8"?> <navigation xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/mobile_navigation" app:startDestination="@id/eventsFragment"> <fragment android:id="@+id/eventsFragment" android:name="org.fossasia.openevent.general.event.EventsFragment" android:label="EventsFragment" /> </navigation> Here app:startDestination attribute is for the very first fragment after which app exit on back pressed. Replace Frame layout with a fragment in the main activity layout file. And add the above navigation graph to it:  <fragment android:id="@+id/frameContainer" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="0dp" android:background="@android:color/white" android:layout_weight="1" app:defaultNavHost="true" app:navGraph="@navigation/navigation_graph" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> Now, this fragment layout is directly connected with all fragments added in the navigation graph. We only need to navigate the fragment. Set bottom navigation drawer with the navigation architecture component -  First, we need to make the ids of menu component same as the ids of fragments in the navigation graph. Bottom navigation menu: <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/eventsFragment" android:icon="@drawable/ic_baseline_event_24px" android:title="@string/events" /> <item android:id="@+id/searchFragment" android:icon="@drawable/ic_search_grey_24dp" android:title="@string/search" /> <item android:id="@+id/favoriteFragment" android:icon="@drawable/ic_baseline_favorite_border_24px" android:title="@string/likes" /> <item android:id="@+id/orderUnderUserFragment" android:icon="@drawable/ic_baseline_ticket_24dp" android:title="@string/tickets" /> <item android:id="@+id/profileFragment" android:icon="@drawable/ic_baseline_account_circle_24px" android:title="@string/profile" /> </menu> Setup the bottom navigation menu with navigation controller: // import require libraries import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.NavigationUI.setupWithNavController // Set nav controller with host fragment using Kotlin smart cast val hostFragment = supportFragmentManager.findFragmentById(R.id.frameContainer) if (hostFragment is NavHostFragment) navController = hostFragment.navController setupWithNavController(navigationMenu, navController) Navigate…

Continue ReadingAdd Navigation Architecture Component in Eventyay Attendee

Add PayPal Payment integration in Open Event Attendee Application

The open event attendee is an android app which allows users to discover events happening around the world using the Open Event Platform. It consumes the APIs of the open event server to get a list of available events and can get detailed information about them. PayPal is a very common method to pay for anything throughout the world. It is a highly popular platform and it’s only right that there should be an option to pay through PayPal on Eventyay attendee for tickets. This blog will explain how and why I added PayPal payment feature in the application with a sandbox account. Why PayPal?Get API key for a sandbox accountIntegration with Android StudioConclusionResources Let’s analyze every step in detail. Advantages of using PayPal Payment integration Provide UI to gather payment information from the userGet your credentials, which identify your PayPal account as the payment receiver. Specifically, obtain a client ID and secret.Returns a proof of payment to your app.Provides the user their goods or services. Setup Sandbox account and get the API key Go to https://developer.paypal.com/ and sign up for a developer account: After Sign up, go to the dashboard and create an app:  Now in app credentials go to sandbox accounts. Here you can find your API key. Now, Create a new sandbox account with entering some amount of money for testing purposes: PayPal SDK integration in the application Add PayPal SDK in build.gradle dependencies: //PayPal compile 'com.paypal.sdk:paypal-android-sdk:2.16.0' Store the API key in the android manifest file: <meta-data android:name="com.paypal.android.API_KEY" android:value="${PAYPAL_CLIENT_ID}"/> Get the API key in the fragment where PayPal payment is required: private lateinit var PAYPAL_API_KEY: String PAYPAL_API_KEY = activity?.packageManager?.getApplicati<meta-data android:name="com.paypal.android.API_KEY" android:value="${PAYPAL_CLIENT_ID}"/>onInfo(activity?.packageName, PackageManager.GET_META_DATA) ?.metaData?.getString(PAYPAL_KEY).toString() Start PayPal services on create view: val payPalConfiguration = PayPalConfiguration() .environment(PayPalConfiguration.ENVIRONMENT_SANDBOX) .clientId(PAYPAL_API_KEY)val intent = Intent(context, PaymentActivity::class.java)intent.putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, payPalConfiguration)activity?.startService(intent) Now start the Payment conditionally with same intent:  val payment = PayPalPayment(BigDecimal(amount.toString()), "USD", "Pay for tickets", PayPalPayment.PAYMENT_INTENT_SALE) intent.putExtra(PaymentActivity.EXTRA_PAYMENT, payment) startActivityForResult(intent, PAYPAL_REQUEST_CODE) Handle the result after payment is done: override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == PAYPAL_REQUEST_CODE) { if (resultCode == Activity.RESULT_OK) { val paymentConfirmation = data?.getParcelableExtra<PaymentConfirmation>(PaymentActivity.EXTRA_RESULT_CONFIRMATION) if (paymentConfirmation != null) { val paymentInfo = paymentConfirmation.toJSONObject() val tokenId = paymentInfo.getJSONObject("response").getString("id") Timber.d(paymentInfo.toString(4)) // Send the token to server val charge = Charge(attendeeViewModel.getId().toInt(), tokenId, null) attendeeViewModel.completeOrder(charge) } } else if (resultCode == Activity.RESULT_CANCELED) Toast.makeText(context, "Payment canceled!", Toast.LENGTH_SHORT).show() else if (resultCode == PaymentActivity.RESULT_EXTRAS_INVALID) Toast.makeText(context, "Invalid Payment Configuration", Toast.LENGTH_SHORT).show() } } GIF In a nutshell With almost 250 million users worldwide, PayPal is an extremely popular platform for monetary transactions and it’s quite essential that every application an option to use it. Given the nature of Eventyay attendee and its pan-world appeal, I have added PayPal as a payment system for tickets to any event. Resources PayPal Android SDK guide: PayPal Android SDKPayPal SDK repo: PayPal-Android-SDK Tags PayPal, Android, Payments, FOSSASIA, GSoC, AndroidPayments, Kotlin

Continue ReadingAdd PayPal Payment integration in Open Event Attendee Application