Hiding Payment Options for Free Tickets in Open Event Android

Hiding Payment Options for Free Tickets in Open Event Android Payment Options spinner allows the user to pick any convenient payment method for an order however, Payment Options should not be shown if the total worth of order is zero or the event is free. This blog post will guide you on how its implemented in Open Event Android. Following are the sequence of steps that are followed for this Allow the user to select tickets and their quantity Use DAO method to get the prices of the tickets selected by the user Iterate through the List and keep storing the prices Display Payment Selector for order with the total amount greater than zero Given below is DAO method to get the list of prices for different tickets. The method makes a simple select statement and returns price attribute of tickets with id in passed ids. A single list of float is returned by the function.  @Query("SELECT price from Ticket WHERE id in (:ids)")    fun getTicketPriceWithIds(ids : List<Int>): Single<List<Float>> This DAO method is then exposed to other parts (ViewModels, Fragments) using service layer class method getTicketPriceWithIds which just makes calls to DAO method and return the result provided by it. Service classes are used for separation of concerns. fun getTicketPriceWithIds(ids: List<Int>): Single<List<Float>> {        return ticketsDao.getTicketPriceWithIds(ids)    } When a list of tickets and their quantity is specified by the user the prices of the ticket are fetched using the above-explained methods. These are then stored it in a List of pair with the first element being ticket id and second its quantity. fun updatePaymentSelectorVisibility(ticketIdAndQty: List<Pair<Int, Int>>?) {        val ticketIds = ArrayList<Int>()        ticketIdAndQty?.forEach { if (it.second > 0) ticketIds.add(it.first) }        compositeDisposable.add(ticketService.getTicketPriceWithIds(ticketIds)                .subscribeOn(Schedulers.io())                .observeOn(AndroidSchedulers.mainThread())                .subscribe({                    var total = 0.toFloat()                    it?.forEach {                        if (it.toFloat() > 0) total += it.toFloat()                    }                    paymentSelectorVisibility.value = total != 0.toFloat()                }, {                    Timber.e(it, "Error Loading tickets!")                }))    } The above method controls the visibility boolean for payment selector, it essentially iterates over the List<Pair<Int, Int>> and add all the ticket id to a temporary list if quantity for that ticket is greater than zero after this prices for the tickets in temporary list is fetched and sum total is calculated. If this total is greater than zero the visibility boolean is set else reset. Finally an observable is set on this boolean which automatically updates the UI whenever boolean changes accordingly. attendeeFragmentViewModel.paymentSelectorVisibility.observe(this, Observer {            if (it !=null && it) {                rootView.paymentSelector.visibility = View.VISIBLE            } else {                rootView.paymentSelector.visibility = View.GONE            }         }) Resources Room and RxJava :  https://medium.com/google-developers/room-rxjava-acb0cd4f3757 Room :  https://developer.android.com/topic/libraries/architecture/room

Continue ReadingHiding Payment Options for Free Tickets in Open Event Android

Speaker details in the Open Event Orga App

The Open Event Organiser Android App is currently released in the Alpha phase on the Google Play Store here. This blog post explains how the speaker details feature has been implemented in the app. Model The model for Speaker is pretty straightforward. It includes the personal details of the speaker such as name, biography, country, social media profiles, designation etc. Apart from these details, every instance of speaker is associated with a single event. A speaker will also have multiple instances of sessions. Full implementation of the speaker’s model can be found here. Network Call We use Retrofit in order to make the network call and Jackson Factory to deserialize the data received from the call into an instance of the speaker model. The following endpoint provides us with the required information: https://open-event-api-dev.herokuapp.com/speakers/{speaker_id} Repository In any typical android application using both network calls and data persistence, there is a need of a repository class to handle them. Speaker Repository handles the network call to the API in order to fetch the speaker details. It then saves the data returned by the api into the database asynchronously. It also ensures that we send the latest data that we have stored in the database to the view model. Given below is the full implementation for reference: @Override public Observable<Speaker> getSpeaker(long speakerId, boolean reload) { Observable<Speaker> diskObservable = Observable.defer(() -> repository .getItems(Speaker.class, Speaker_Table.id.eq(speakerId)).take(1) ); Observable<Speaker> networkObservable = Observable.defer(() -> speakerApi.getSpeaker(speakerId) .doOnNext(speaker -> repository .save(Speaker.class, speaker) .subscribe())); return repository .observableOf(Speaker.class) .reload(reload) .withDiskObservable(diskObservable) .withNetworkObservable(networkObservable) .build(); } ViewModel The View Model is responsible for fetching the necessary details from the repository and displaying it in the view. It handles all the view binding logic. The most important method in the SpeakerDetailsViewModel is the getSpeakers method. It accepts a speaker id from the fragment, queries the repository for the details of the speaker and returns it back to the fragment in the form of a LiveData. Below is the full implementation of the getSpeakers method: protected LiveData<Speaker> getSpeaker(long speakerId, boolean reload) { if (speakerLiveData.getValue() != null && !reload) return speakerLiveData; compositeDisposable.add(speakerRepository.getSpeaker(speakerId, reload) .compose(dispose(compositeDisposable)) .doOnSubscribe(disposable -> progress.setValue(true)) .doFinally(() -> progress.setValue(false)) .doOnNext(speaker -> speakerLiveData.setValue(speaker)) .flatMap(speaker -> sessionRepository.getSessionsUnderSpeaker(speakerId, reload)) .toList() .subscribe(sessionList -> sessionLiveData.setValue(sessionList), throwable -> error.setValue(ErrorUtils.getMessage(throwable)))); return speakerLiveData; } We add the disposable to a composite disposable and dispose it in the onCleared method of the View Model. The full implementation of the View Model can be found here. Fragment The SpeakerDetailsFragment acts as the view and is responsible for everything the user sees on the screen. It accepts the id of the speaker whose details are to be displayed in the constructor. When an instance of the fragment is created it sets up it’s view model and inflates it’s layout using the Data binding framework. @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { context = getContext(); binding = DataBindingUtil.inflate(inflater, R.layout.speaker_details_fragment, container, false); speakerDetailsViewModel = ViewModelProviders.of(this, viewModelFactory).get(SpeakerDetailsViewModel.class); speakerId = getArguments().getLong(SPEAKER_ID); AppCompatActivity activity = ((AppCompatActivity) getActivity()); activity.setSupportActionBar(binding.toolbar); ActionBar actionBar = activity.getSupportActionBar(); if (actionBar != null) { actionBar.setHomeButtonEnabled(true); actionBar.setDisplayHomeAsUpEnabled(true); }…

Continue ReadingSpeaker details in the Open Event Orga App

Ticket Quantity Spinner in Open Event Android

Spinners are basically drop down menu which provides an easy way to select an item from a set of items. Spinner is used in Open Event Android to allow user select quantity of tickets as shown in the image above. This blog post will guide you on how its implemented in Open Event Android. Add Spinner to the XML file <Spinner            android:id="@+id/orderRange"            android:layout_width="30dp"            android:layout_height="30dp"            android:spinnerMode="dialog"            android:textSize="@dimen/text_size_small" /> The above code spinnet will create a spinner type view with 30dp height and width, spinnerMode can be dialog or dropDown following are example spinner for both of the modes left being dialog type and right dropDown type.                  (Image Source: Stack Overflow) Set Up Adapter and Populate Data on Spinner To show the list of acceptable Quantities for a Ticket, create an ArrayList of String and add all values from ticket.minOrder to ticket.maxOrder along with a zero option. Since ArrayList is of String and the values are integer they need to be converted to String using Integer.toString(i) method. Following code snippet will give you more understanding about it. val spinnerList = ArrayList<String>()            spinnerList.add("0")            for (i in ticket.minOrder..ticket.maxOrder) {                spinnerList.add(Integer.toString(i))            } We will also need to set up an onItemSelectedListener to listen for Item Selections and override onItemSelected and onNothingSelected functions in it. Whenever an item is selected onItemSelected is called with arguments such as view, position of the selected item, id etc, these can be used to find the selected Quantity from the ArrayList of acceptable Quantities.           itemView.orderRange.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {                override fun onItemSelected(parent: AdapterView<*>, view: View, pos: Int, id: Long) {                    itemView.order.text = spinnerList[pos]                    selectedListener?.onSelected(ticket.id, spinnerList[pos].toInt())                }                override fun onNothingSelected(parent: AdapterView<*>) {                }            } Since Spinner is an Adapter Based view we need to create a SpinnerAdapter and attach it with the Spinner. If you want to create custom Spinners you would have to create a custom adapter for it along with a layout for its row elements or else one can use library-provided layout. Given below is the implementation for library-provided spinner row type. There are different options available for row type we are using R.layout.select_dialog_singlechoice because we need single selectable Quantity spinner along with Radio button for every ticket.      itemView.orderRange.adapter = ArrayAdapter(itemView.context, R.layout.select_dialog_singlechoice, spinnerList) Resources Android Developers Documentation : https://developer.android.com/guide/topics/ui/controls/spinner Stack OverFlow: https://www.javatpoint.com/android-spinner-example

Continue ReadingTicket Quantity Spinner in Open Event Android

Mapping Events to Load from Database

Mapping Events to Load from Database In Open Event Android whenever App is started events are fetched for the location given by the user, since we have locally added isFavorite extra field for every event it is necessary to be updated for all the events returned in the API response before inserting it into our database otherwise all the favorite related information would be lost. This blog post will guide you on how its done in Open Event Android. The sequence of steps followed Take the IDs of events being saved into the database Use DAO method which does “SELECT id from Event where favorite = 1 AND id in :eventIds and pass API returned eventIds to this function Set the old favorited on new event objects Save them in database Let's see how all of these steps are performed in greater details. Whenever user gives in a location following function is called fun getEventsByLocation(locationName: String): Single<List<Event>> {        return eventApi.searchEvents("name", locationName).flatMap { apiList ->            val eventIds = apiList.map { it.id }.toList()            eventDao.getFavoriteEventWithinIds(eventIds).flatMap { favIds ->                updateFavorites(apiList, favIds)            }        }    } Here we are extracting all the Ids of events returned in the API response and then calling getFavoriteEventWithinIds on it. The latter takes the list of eventIds and return the Ids of events which are favorite out of them. This is then passed to the function updateFavorite along with the API returned Events. Following is the implementation of updateFavorite method. fun updateFavorites(apiEvents: List<Event>, favEventIds: List<Long>): Single<List<Event>> {        apiEvents.map { if (favEventIds.contains(it.id)) it.favorite = true }        eventDao.insertEvents(apiEvents)        val eventIds = apiEvents.map { it.id }.toList()        return eventDao.getEventWithIds(eventIds)    } updateFavorite checks for all events in the list of events whether if its Id is present in the favorite event ids it sets favorite field of that event true. After the favorites for the list of events are updated they are inserted into the database using DAO method insertEvents. The last task is to take the fetch these events again from the database, to do this first we extract the Ids of the events we just inserted into the database and call the DAO method getEventsWithIds passing the extracted eventIds, getEventsWithids simply returns the Events with given Ids. Given below are the implementations of the functions getEventWithIds and getFavoriteEventWithinIds @Query("SELECT * from Event WHERE id in (:ids)")    fun getEventWithIds(ids: List<Long>): Single<List<Event>> @Query("SELECT id from Event WHERE favorite = 1 AND id in (:ids)")    fun getFavoriteEventWithinIds(ids : List<Long>): Single<List<Long>> getEventWithIds simply makes a select query and checks for events whose ids lies in the ids passed to the method getFavoriteEventWithinids returns the Ids of the favorite event out of the list of event id passed to the method. Resources Android Developers Documetation for Room : https://developer.android.com/topic/libraries/architecture/room RxJava Documentation : https://github.com/ReactiveX/RxJava

Continue ReadingMapping Events to Load from Database

Setting an Event Favorite in Open Event Android

The favorite events feature in Open Event Android allows any user to favorite an event and those events can are available in the favorite events fragment which is easily accessible from the bottom navigation bar. This blog article will walk you through on how favorite works in Open Event Android. There are a couple of different ways to do it, in Open Event Android we are keeping track of favorite events using a favorite Boolean stored along with the information of the event ie we have added a favorite boolean field inside the event entity class. However, this method has its own pros and cons. The big plus point of this method is the fact that we can simply check the favorite field of the event object to check if that particular event is favorite or not. But since we have set onConflict strategy as replace in the event insert DAO method (shown below)  @Insert(onConflict = REPLACE)    fun insertEvents(events: List<Event>) This leads to a big problem when the same event is fetched or is present in the response while inserting the favorite field will be set to default value (false) and the favorite information would be lost. Hence extra care needs to be taken while updating events. Given following is the event model class for serializing / deserializing JSON responses. Note the extra field favorite added here which helps us to provide user favorite feature locally. @Type("event") @JsonNaming(PropertyNamingStrategy.KebabCaseStrategy::class) @Entity data class Event(       @Id(LongIdHandler::class)       @PrimaryKey       val id: Long,       val name: String,       val identifier: String,       val isMapShown: Boolean = false,       val favorite: Boolean = false ) Since we added a new field to the Event model class, to access the favorite events we will have to create DAO methods. To fetch all the favorite events we will have to create a Query method which returns all the events wherever favorite property is set. This can be done using a simple select statement which looks for events with favorite boolean set ie. 1, the method implementation is shown below. @Query("SELECT * from Event WHERE favorite = 1")    fun getFavoriteEvents(): Flowable<List<Event>> To set an event favorite we will have to add one more method to the EventDao. We can create a generalized method which sets according to the boolean passed as a parameter. The function implementation is shown below, setFavorite takes in EventId, Id of the event which has to be updated and the boolean favorite which is what the new value for the favorite field is. setFavorite makes an SQL update query and matches for EventId and sets favorite for that event. @Query("UPDATE Event SET favorite = :favorite WHERE id = :eventId")    fun setFavorite(eventId: Long, favorite: Boolean) In Open Event Android we use the service class which can be used to expose the DAO methods to the rest of the project. The idea behind creating service layer is the separation of concerns this service method is then called from the view model function (following the MVVM approach). Given following is the implementation of…

Continue ReadingSetting an Event Favorite in Open Event Android

Plot a Horizontal Bar Graph using MPAndroidChart Library in SUSI.AI Android App

Graphs and charts provide a visual representation of the data. They provide a clearer and quicker understanding of the impact of certain statistics. Thus, SUSI.AI Android app makes use of bar charts to display statistics related to user ratings for SUSI skills. This blog guides through the steps to create a Horizontal Bar Chart, using MPAndroidChart library, that has been used in the SUSI.AI Android app skill details page to display the five star skill rating by the users. On vertical axis : Labels of the rating shown On horizontal axis : Percentage of total number of users who rated the skill with the corresponding number of stars on the vertical axis Step - 1 : Add the required dependencies to your build.gradle. (a) Project level build.gradle allprojects { repositories { maven { url 'https://jitpack.io' } } } (b) App level build.gradle dependencies { implementation 'com.github.PhilJay:MPAndroidChart:v3.0.3' }   Step - 2 : Create an XML layout. <?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- Add a Horizontal Bar Chart using MPAndroidChart library --> <com.github.mikephil.charting.charts.HorizontalBarChart android:id="@+id/skill_rating_chart" android:layout_width="match_parent" android:layout_height="match_parent" /> </android.support.constraint.ConstraintLayout>   Step - 3 : Create an Activity and initialize the Horizontal Bar Chart. class MainActivity : Activity { lateinit var skillRatingChart : HorizontalBarChart override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.chart) setSkillGraph( ) } }   Step - 4 : Create a method in your MainActivity to set up the basic properties and the axes. /** * Set up the axes along with other necessary details for the horizontal bar chart. */ fun setSkillGraph(){ skillRatingChart = skill_rating_chart //skill_rating_chart is the id of the XML layout skillRatingChart.setDrawBarShadow(false) val description = Description() description.text = "" skillRatingChart.description = description skillRatingChart.legend.setEnabled(false) skillRatingChart.setPinchZoom(false) skillRatingChart.setDrawValueAboveBar(false) //Display the axis on the left (contains the labels 1*, 2* and so on) val xAxis = skillRatingChart.getXAxis() xAxis.setDrawGridLines(false) xAxis.setPosition(XAxis.XAxisPosition.BOTTOM) xAxis.setEnabled(true) xAxis.setDrawAxisLine(false) val yLeft = skillRatingChart.axisLeft //Set the minimum and maximum bar lengths as per the values that they represent yLeft.axisMaximum = 100f yLeft.axisMinimum = 0f yLeft.isEnabled = false //Set label count to 5 as we are displaying 5 star rating xAxis.setLabelCount(5) //Now add the labels to be added on the vertical axis val values = arrayOf("1 *", "2 *", "3 *", "4 *", "5 *") xAxis.valueFormatter = XAxisValueFormatter(values) val yRight = skillRatingChart.axisRight yRight.setDrawAxisLine(true) yRight.setDrawGridLines(false) yRight.isEnabled = false //Set bar entries and add necessary formatting setGraphData() //Add animation to the graph skillRatingChart.animateY(2000) } Here is the XAxisValueFormatter class that is used to add the custom labels to the vertical axis : public class XAxisValueFormatter implements IAxisValueFormatter { private String[] values; public XAxisValueFormatter(String[] values) { this.values = values; } @Override public String getFormattedValue(float value, AxisBase axis) { // "value" represents the position of the label on the axis (x or y) return this.values[(int) value]; } }   Step - 5 : Set the bar entries. /** * Set the bar entries i.e. the percentage of users who rated the skill with * a certain number of stars. * * Set the colors for different bars and the bar width of the bars. */ private fun…

Continue ReadingPlot a Horizontal Bar Graph using MPAndroidChart Library in SUSI.AI Android App

Use Timber for Logging in SUSI.AI Android App

As per the official GitHub repository of Timber : “Timber is a logger with a small, extensible API which provides utility on top of Android's normal Log class”. It is a flexible logging library for Android which makes logging a lot more convenient. In this blog you will learn how to use Timber in SUSI.AI Android app. To begin, add Timber to your build.gradle and refresh your gradle dependencies. implementation 'com.jakewharton.timber:timber:4.7.0’ Two easy steps to use Timber: Install any Tree instances that you want in the onCreate() of your application class. Call Timber's static methods everywhere throughout the app. You can add the following code to your application class : @Override public void onCreate() { super.onCreate(); ….. if (BuildConfig.DEBUG) { Timber.plant(new Timber.DebugTree() { //Add the line number to the tag @Override protected String createStackElementTag(StackTraceElement element) { return super.createStackElementTag(element) + ": " + element.getLineNumber(); } }); } else { //Release mode Timber.plant(new ReleaseLogTree()); } } private static class ReleaseLogTree extends Timber.Tree { @Override protected void log(int priority, String tag, @NonNull String message, Throwable throwable) { if (priority == Log.DEBUG || priority == Log.VERBOSE || priority == Log.INFO) { return; } if (priority == Log.ERROR) { if (throwable == null) { Timber.e(message); } else { Timber.e(throwable, message); } } } } Timber ships with a ‘Debug Tree’ that provides all the basic facilities that you are very used to in the common Android.log framework. Timber has all the logging levels that are used in the normal Android logging framework which are as follows: Log.e: Use this tag in places like inside a catch statement where you are aware that an error has occurred and therefore you're logging an error. Log.w: Use this to log stuff you didn't expect to happen but isn't necessarily an error. Log.i: Use this to post useful information to the log. For instance, a message that you have successfully connected to a server. Log.d: Use this for debugging purposes. For instance, if you want to print out a bunch of messages so that you can log the exact flow of your program or if you want to keep a log of variable values. Log.v: Use this if, for some reason, you need to log every little thing in a particular part of your app. Log.wtf: Use this when you encounter a terrible failure.   You can use Timber.e, Timber.w, Timber.i, Timber.d and Timber.v respectively for the logging levels mentioned above. However, there is a small difference. In Android logging framework the exception is passed as the last parameter. But, when using Timber, you need to provide the exception as the first parameter. Interestingly, while using Timber you don’t have to provide a TAG in logging calls because it automatically does that for you. It uses the file name, where you are logging from, as the TAG. To log an exception you can simply write: Timber.e(exception, "Message"); Or a null message if you want: Timber.e(exception, null); A simple message can be logged sent via Crashlytics too: Timber.e("An error message"); Did you…

Continue ReadingUse Timber for Logging in SUSI.AI Android App

Implementing Settings for Lux Meter Instrument in PSLab Android App

In PSLab android app, we have included sensor instruments which use either inbuilt sensor of smartphone or external sensor to record sensor data. For example, Lux Meter uses the light sensor to record lux data but the problem is that these instruments doesn’t contain settings option to configure the sensor record-setting like which sensor to use, change the update period etc. Therefore, we need to create a settings page for the Lux Meter instrument which allows the user to modify the properties of the sensor instrument. For that I will use Android Preference APIs to build a settings interface that is similar to setting activity in any other android app. The main building block of the settings activity is the Preference object. Each preference appears as a single setting item in an activity and it corresponds to key-value pair which stores the settings in default Shared Preferences file. Every time any setting is changed by the user the Android will store the updated value of the setting in the default shared preferences which we can read in any other activity across the app. In the following steps I will describe instruction on how to create the setting interface in Android app:  Step1 Include the dependency First, we need to include the dependency for v7 Preference Support Library by including the following code: dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:23.1.1' compile 'com.android.support:design:23.1.1' compile 'com.android.support:preference-v7:23.1.1' }  Step 2 Creating the preferences screen in XML For this step, I have created a preference screen which will be inflated when the settings fragment is being created. I created a file named  “lux_meter_settings.xml” and place it in the res/xml/ directory. The root node for the XML file must be a <PreferenceScreen> element. We have to add each Preference within this element. Each child I added within the <PreferenceScreen> element appears as a single item in the list of settings. The preference which I have used are: <EdittextPreference> This preference opens up a dialog box with edit text and stores whatever value is written by the user in the edit text. I have used this preference for inputting update period and high limit of data during recording. <CheckboxPreference> shows an item with a checkbox for a setting that is either enabled or disabled. The saved value is a boolean (true if it's checked). I have used this preference for enabling or disabling location data with the recorded data. <ListPreference> opens a dialog with a list of radio buttons. The saved value can be any one of the supported value types. I have used this preference to allow the user to choose between multiple sensor types. <?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <EditTextPreference android:key="setting_lux_update_period" android:title="@string/update_period" android:dialogTitle="@string/update_period" android:defaultValue="1000" android:dialogMessage="Please provide time interval(in ms) at which data will be updated" android:summary="Update period is 900ms"/> <EditTextPreference android:key="setting_lux_high_limit" android:title="High Limit" android:dialogTitle="High Limit" android:defaultValue="2000" android:dialogMessage="Please provide maximum limit of LUX value to be recorded" android:summary="High Limit is 2000 Lux"/> <CheckBoxPreference android:defaultValue="false" android:key="include_location_sensor_data" android:summary="Include the location data in the logged file" android:title="Include Location…

Continue ReadingImplementing Settings for Lux Meter Instrument in PSLab Android App

Paypal Integration in Open Event Server

The Open Event Server enables organizers to manage events from concerts to conferences and meetups. It offers features for events with several tracks and venues. This blog post explains how Paypal has been integrated in the Open Event Server in order to accept payments for tickets. The integration of Paypal in the server involved the following steps: An endpoint to accept the Paypal token from the client applications. Using the token to get the approved payment details. Capturing the payment using the fetched payment details. Endpoint for Paypal token The server exposes an endpoint to get the Paypal token in order to accept payments. api.route(ChargeList, 'charge_list', '/orders/<identifier>/charge', '/orders/<order_identifier>/charge') The above endpoint accepts the Paypal token and uses that to get the payment details from Paypal and then capture the payments. Getting Approved Payment Details We use the Paypal Name-Value pair API in the project. First we get the credentials of the event organizer who will be accepting the payments using a call to the get_credentials helper method. It returns the data as the following dictionary: credentials = { 'USER': settings['paypal_live_username'], 'PWD': settings['paypal_live_password'], 'SIGNATURE': settings['paypal_live_signature'], 'SERVER': 'https://api-3t.paypal.com/nvp', 'CHECKOUT_URL': 'https://www.paypal.com/cgi-bin/webscr', 'EMAIL': '' if not event or not event.paypal_email or event.paypal_email == "" else event.paypal_email } Next, we use the credentials to get the approved payment details from paypal using the following code snippet. @staticmethod def get_approved_payment_details(order, credentials=None): if not credentials: credentials = PayPalPaymentsManager.get_credentials(order.event) if not credentials: raise Exception('PayPal credentials have not been set correctly') data = { 'USER': credentials['USER'], 'PWD': credentials['PWD'], 'SIGNATURE': credentials['SIGNATURE'], 'SUBJECT': credentials['EMAIL'], 'METHOD': 'GetExpressCheckoutDetails', 'VERSION': PayPalPaymentsManager.api_version, 'TOKEN': order.paypal_token } if current_app.config['TESTING']: return data response = requests.post(credentials['SERVER'], data=data) return json.loads(response.text) Capturing the payments After successfully fetching the payment details, the final step is to capture the payment. We set the amount to be charged to the amount of the order and the payer_id to be the payer id received from step 2. Then we simply make a POST request to the Paypal nvp server and capture the payments. The below method is responsible for executing this task: @staticmethod def capture_payment(order, payer_id, currency=None, credentials=None): if not credentials: credentials = PayPalPaymentsManager.get_credentials(order.event) if not credentials: raise Exception('PayPal credentials have not be set correctly') if not currency: currency = order.event.payment_currency if not currency or currency == "": currency = "USD" data = { 'USER': credentials['USER'], 'PWD': credentials['PWD'], 'SIGNATURE': credentials['SIGNATURE'], 'SUBJECT': credentials['EMAIL'], 'METHOD': 'DoExpressCheckoutPayment', 'VERSION': PayPalPaymentsManager.api_version, 'TOKEN': order.paypal_token, 'PAYERID': payer_id, 'PAYMENTREQUEST_0_PAYMENTACTION': 'SALE', 'PAYMENTREQUEST_0_AMT': order.amount, 'PAYMENTREQUEST_0_CURRENCYCODE': currency, } response = requests.post(credentials['SERVER'], data=data) return json.loads(response.text) References Paypal NVP : https://developer.paypal.com/docs/classic/api/NVPAPIOverview/ Paypal Python SDK: https://github.com/paypal/PayPal-Python-SDK Flask REST JSON API: flask-rest-jsonapi

Continue ReadingPaypal Integration in Open Event Server