How to Make Promotional Codes Applicable on Tickets During Ordering in Open Event Frontend

This blog illustrate how to enable application of promotional codes on tickets during ordering tickets in Open Event Frontend to avail discounts and access to special tickets. Open event allows organizers to add some promotional codes on some tickets, which can be used by users to avail additional offers on tickets while ordering. Promotional codes can be of three types: Discount Codes: Allows customers to buy a ticket at discounted rates. Access Codes: Allows customers to access some hidden tickets which are accessible only to special customers. Discount + Access Code: Allows customer to access special tickets and avail discount at the same time. Creating a discount/access code: Organizers and admin can create an access code or a discount code from the event dashboard. They can specify the validity period of the code and can also specify the tickets on which the code will be applicable. Validating promotional code after user enters the code: User is allowed to enter the promotional code on events page upon selecting the tickets. IF promotional code is valid then suitable discount is provided on applicable tickets and if promotional code is an access code then hidden tickets for which the promotional code is valid are shown. To check the validity of the promotional code we deal with the following APIs on the open event server: GET             /v1/discount-codes/{Code}              (For Discount code) GET             /v1/access-codes/{Code}                  (For Access code) Code snippet to check the validity for access code is given below: let promotionalCode = this.get('promotionalCode'); let order = this.get('order'); try { let accessCode = await this.get('store').findRecord('access-code', promotionalCode, {}); order.set('accessCode', accessCode); let tickets = await accessCode.get('tickets'); tickets.forEach(ticket => { ticket.set('isHidden', false); this.get('tickets').addObject(ticket); this.get('accessCodeTickets').addObject(ticket); this.set('invalidPromotionalCode', false); }); } catch (e) { this.set('invalidPromotionalCode', true); }   Full code can be seen here https://github.com/fossasia/open-event-frontend/blob/development/app/components/public/ticket-list.js Similarly for discount code we fetch the details of the discount code via the api and then validate the code. After the validation we apply the discount to the tickets applicable. Code snippet for the discount code part is given below: try { let discountCode = await this.get('store').findRecord('discount-code', promotionalCode, { include: 'tickets' }); let discountType = discountCode.get('type'); let discountValue = discountCode.get('value'); order.set('discountCode', discountCode); let tickets = await discountCode.get('tickets'); tickets.forEach(ticket => { let ticketPrice = ticket.get('price'); if (discountType === 'amount') { ticket.set('discount', Math.min(ticketPrice, discountValue)); this.get('discountedTickets').addObject(ticket); } else { ticket.set('discount', ticketPrice * (discountValue / 100)); this.get('discountedTickets').addObject(ticket); } this.set('invalidPromotionalCode', false); }); } catch (e) { if (this.get('invalidPromotionalCode')) { this.set('invalidPromotionalCode', true); } }   Full code can be seen https://github.com/fossasia/open-event-frontend/blob/development/app/components/public/ticket-list.js After promotional codes are verified we apply them to the selected tickets. In this way we apply the promotional codes to the tickets. Resources Link to PR: https://github.com/fossasia/open-event-frontend/pull/1631 Discount Code: open event server: https://open-event-api-dev.herokuapp.com/#discount-codes Access Code: open event server: https://open-event-api-dev.herokuapp.com/#access-codes  

Continue ReadingHow to Make Promotional Codes Applicable on Tickets During Ordering in Open Event Frontend

Implement Charges Endpoint in the Open Event Android App

In the Open Event Android App we first create an attendee and then the order when a user tries to buy a ticket but the end user will only know that the transaction is successful when money gets deducted from his account. This is where we use the charges endpoint of the Open Event Server to complete the payment. Let’s see how this is being done in the Open Event Android App. We are sending a POST request from the app to the following endpoint. The order identifier is added in the url to uniquely identify the order for which we are charging the user. The body of the POST request contains the Charge object with the required information. @POST("orders/{orderIdentifier}/charge") fun chargeOrder(@Path("orderIdentifier") orderIdentifier: String, @Body charge: Charge): Single<Charge>   This is how the model class Charge looks like. We specify the type at the top. Then we can either send a stripe or paypal token to the server which contains all the details of the payment. Message and status fields are returned from the server after the getting a success response. The status is true if the payment has been successfully made otherwise it is false. The message field gives us the feedback about what kind of error we got if the payment was not successful otherwise we get a success response. @Type("charge") data class Charge( @Id(IntegerIdHandler::class) val id: Int, val stripe: String? = null, val paypal: String? = null, val message: String? = null, val status: Boolean? = null )   This is how we send the stripe token to the server. If the card details are correct then we receive the stripe token in the onSuccess method and send it to the server using the Charges endpoint. override fun onSuccess(token: Token) { //Send this token to server val charge = Charge(attendeeFragmentViewModel.getId().toInt(), token.id, null) attendeeFragmentViewModel.completeOrder(charge) }   This is the function that is being used to send the POST request. It takes a charge object as an argument and then we use that in the chargeOrder method. The orderIdentifier variable that we see in the chargeOrder function is a lateinit variable that gets initialized when an order has been returned from the server. When this request is being made in the background thread we show user the progress bar. When we receive a success response from the server, we update the value of the message variable with the value returned from the server and show it to the user. If the value of the status is true that means that the payment has been successfully completed. fun completeOrder(charge: Charge) { compositeDisposable.add(orderService.chargeOrder(orderIdentifier.toString(), charge) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnSubscribe { progress.value = true }.doFinally { progress.value = false }.subscribe({ message.value = it.message paymentCompleted.value = it.status if (it.status != null && it.status) { Timber.d("Successfully charged for the order!") } else { Timber.d("Failed charging the user") } }, { message.value = "Payment not completed!" Timber.d(it, "Failed charging the user") })) } Resources ReactiveX official documentation: http://reactivex.io/ Vogella RxJava 2 - Tutorial: http://www.vogella.com/tutorials/RxJava/article.html Androidhive RxJava Tutorial:…

Continue ReadingImplement Charges Endpoint in the Open Event Android App

Generating a Stripe token in the Open Event Android App

To implement the payment functionality in the Open Event Android App using credit cards we are using Stripe. Let’s see how this is being done. We are taking the sensitive information about the user like the card details and sending it to Stripe’s servers which will return a token encrypting users information which we will use to send it to the Open Event Server to complete the payment. We need to add the library in the build.gradle file in the dependency block. //Stripe implementation 'com.stripe:stripe-android:6.1.2'   Next we add Stripe’s Card Input Widget in our layout file. This widget is used to input card details like the card number, expiry date and CVC. It automatically validates the card details. It has a minimum width of 320 px. By default we are setting it’s visibility to gone that is it won’t be visible unless the user selects the payment option. <com.stripe.android.view.CardInputWidget android:id="@+id/cardInputWidget" android:layout_width="match_parent" android:layout_height="wrap_content" android:visibility="gone" />   The visibility of the input field for card details is determined here. We want to show the Input widget only when the user selects Stripe as the mode of payment. override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) { selectedPaymentOption = paymentOptions[p2] if (selectedPaymentOption == "Stripe") rootView.cardInputWidget.visibility = View.VISIBLE else rootView.cardInputWidget.visibility = View.GONE }   Next we store the user’s card details in a variable. If any of the details be it card number, expiry date or CVC isn’t correct, the value of the variable becomes null and then we show a message to the user. val cardDetails: Card? = cardInputWidget.card if (cardDetails == null) Toast.makeText(context, "Invalid card data", Toast.LENGTH_LONG).show()   This is the most important part where we receive the Stripe token. We are sending a request in a background thread to the Stripe servers using the Stripe API_KEY. In the onSuccess method we receive the token if everything went successfully while in the onError method we display the errors to the user. cardDetails?.let { context?.let { contextIt -> Stripe(contextIt).createToken( it, API_KEY, object : TokenCallback { override fun onSuccess(token: Token) { //Send this token to server Toast.makeText(context, "Token received from Stripe", Toast.LENGTH_LONG).show() } override fun onError(error: Exception) { Toast.makeText(context, error.localizedMessage.toString(), Toast.LENGTH_LONG).show() } }) } }   Resources Stripe Android documentation: https://stripe.com/docs/mobile/android   Stripe Documentation: https://stripe.com/docs/quickstart Stripe Android Github: https://github.com/stripe/stripe-android

Continue ReadingGenerating a Stripe token in the Open Event Android App

Stripe Authorization in Open Event Server

Stripe is a popular software platform for online payments. Since Open Event  allows the event organizers to sell tickets, an option to accept payments through Stripe is extremely beneficial to the organizer. Stripe allows accepting payments on other’s behalf using Connect. Connect is the Stripe’s full stack solution for platforms that need to process payments and process to multiple parties. This blog post goes over how Event organizers are able to link their Stripe accounts in order to accept payments later. Registering the platform The Admin of the Open Event Server will create an account on Stripe and register the platform. Upon creating the  account he/she will get a secret_key and publishable_key.  Moreover on registering the platform a client_id will be provided. These keys are saved in the application settings and only the Admin is authorized to view or change them. Connecting the Organiser’s account The Open Event Frontend has a wizard for creating an Event. It provides the organiser an option to connect his/her Stripe account in order to accept payments. Upon clicking the following button, the organiser is directed to Stripe where he/she can fill the required details.   The button directs the organizer to the following URL: https://connect.stripe.com/oauth/authorize?response_type=code&client_id=client_id&scope=read_write&redirect_uri=redirect_uri The above URL has the following parameters: client_id – The client ID acquired when registering your platform.required. response_type – Response type. The value is always code. required. redirect_uri – The URL to redirect the customer to after authorization. scope - We need it to be read_write in order to be able to charge on behalf of the customer later. After successfully entering the required details, the organizer is redirected to the redirect_url as specified in the above URL with a query parameter named as authorization_code. The Frontend sends this code to the Server using the Stripe Authorization endpoint which will be discussed in detail below. Fetching Tokens from Stripe The Server accepts the authorization_code by exposing the Stripe Authorization endpoint. It then uses it to fetch organizer’s details and token from Stripe and stores it for future use. The schema for Stripe Authorization is extremely simple. We require the client to send an authorization_code which will be used to fetch the details. Stripe_publishable_key of the event organizer is exposed via the endpoint and will be used by the Frontend later. class StripeAuthorizationSchema(Schema): """ Stripe Authorization Schema """ class Meta: """ Meta class for StripeAuthorization Api Schema """ type_ = 'stripe-authorization' self_view = 'v1.stripe_authorization_detail' self_view_kwargs = {'id': '<id>'} inflect = dasherize id = fields.Str(dump_only=True) stripe_publishable_key = fields.Str(dump_only=True) stripe_auth_code = fields.Str(load_only=True, required=True) event = Relationship(attribute='event', self_view='v1.stripe_authorization_event', self_view_kwargs={'id': '<id>'}, related_view='v1.event_detail', related_view_kwargs={'stripe_authorization_id': '<id>'}, schema="EventSchema", type_='event') We use the Requests library in order to fetch the results. First we fetch the client_id that we had stored in the application settings using a helper method called get_credentials. We then use it along with the authorization_code in order to make a POST request to Stripe Connect API. The full method is given below for reference. @staticmethod def get_event_organizer_credentials_from_stripe(stripe_auth_code): """ Uses the stripe_auth_code to get the other…

Continue ReadingStripe Authorization in Open Event Server

Using Order Endpoints in Open Event API Server

The main feature i.e., Ordering API is added into API server. These endpoints provide the ability to work with the ordering system. This API is not simple like other as it checks for the discount codes and various other things as well. The process in layman terms is very simple, first, a user must be registered or added as an attendee into Server without any order_id associated and then the attendee details will be sent to API server as a relationship. Things needed to take care: Validating the discount code and ensure it is not exhausted Calculating the total amount on the server side by applying coupon Do not calculate amount if the user is the event admin Do not use coupon if user is event admin Handling payment modes and generating payment links Ensure that default status is always pending, unless the user is event admin Creating Order Prerequisite Before initiating the order, attendee records needs to be created associated with the event. These records will not have any order_id associated with them initially. The Order API will add the relationships. Required Body Order API requires you to send event relationship and attendee records to create order_tickets Permissions Only organizers can provide custom amount and status. Others users will get their status as pending and amount will be recalculated in server. The response will reflect the calculated amount and updated status. Also to initiate any order, user must be logged in. Guest can not create any order Payment Modes There are three payment modes, free, stripe and paypal. If payment_mode is not provided then API will consider it as “free”. Discount Codes Discount code can be sent as a relationship to the API. The Server will validate the code and will act accordingly. Validating Discount Codes Discount codes are checked to ensure they are valid, first check ensures that the user is not co-organizer # Apply discount only if the user is not event admin if data.get('discount') and not has_access('is_coorganizer', event_id=data['event']): Second, check ensures that the discount code is active if not discount_code.is_active:   raise UnprocessableEntity({'source': 'discount_code_id'}, "Inactive Discount Code") The third, Check ensures its validity is not expired if not (valid_from <= now <= valid_till):   raise UnprocessableEntity({'source': 'discount_code_id'}, "Inactive Discount Code") Fourth Check ensure that the quantity is not exhausted if not TicketingManager.match_discount_quantity(discount_code, data['ticket_holders']):   raise UnprocessableEntity({'source': 'discount_code_id'}, 'Discount Usage Exceeded') Lastly, the fifth check ensures that event id matches with given discount associated event if discount_code.event.id != data['event'] and discount_code.user_for == TICKET:   raise UnprocessableEntity({'source': 'discount_code_id'}, "Invalid Discount Code") Calculating Order Amount The next important thing is to recalculate the order amount and it will calculated only if user is not the event admin if not has_access('is_coorganizer', **view_kwargs):   TicketingManager.calculate_update_amount(order) API Response The API response apart from general fields will provide you the payment-url depending upon the payment mode you selected. Stripe : will give payment-url as stripe Paypal: will provide the payment completing url in payment-url This all explains the flow and requirements to create an order. Order API consists…

Continue ReadingUsing Order Endpoints in Open Event API Server

Implementing Payment and Tax System for Open-Event

So I implemented the payment system and tax system for the payment part in the ticketing system. The first step was making a list of available countries and currency options for the system. So I created and added the following list: PAYMENT_COUNTRIES = { 'United States', 'Argentina', 'Australia', 'Austria', 'Belgium', 'Brazil', 'Canada', 'Cyprus', 'Czech Republic', 'Denmark', 'Estonia', 'Finland', 'France', 'Germany', 'Greece', 'Hong Kong', 'Hungary', 'Ireland', 'Israel', 'Italy', 'Japan', 'Latvia', 'Lithuania', 'Luxemborg', 'Malaysia', 'Malta', 'Mexico', 'Netherlands', 'New Zealand', 'Norway', 'Philippines', 'Poland', 'Portugal', 'Singapore', 'Slovakia', 'Slovenia', 'Spain', 'Sweden', 'Switzerland', 'Taiwan', 'United Kingdom', } PAYMENT_CURRENCIES = { 'ARS Argentine Peso $', 'AUD Australian Dollars A$', 'BRL Brazilian Real R$', 'CAD Canadian Dollars C$', 'CZK Czech Koruna Kč', 'DKR Danish Krone Dkr', 'EUR Euros €', 'HKD Hong Kong Dollar HK$', 'HUF Hungarian Forint Ft', 'ILS Israeli Shekels ₪', 'JPY Japanese Yen ¥', 'MYR Malaysian Ringgits RM', 'MXN Mexican Pesos Mex$', 'NZD New Zealand Dollar NZ$', 'NOK Norwegian Krone Nkr', 'PHP Philippine Pesos ₱', 'PLN Polish Zloty zł', 'GBP Pounds Sterling £', 'SGD Singapore Dollar SG$', 'SEK Swedish Krona Skr', 'CHF Swiss Franc Fr', 'TWD Taiwan New Dollars NT$', 'THB Thai baht ฿', 'USD U.S. Dollars $', } This is the list of currently supported countries and currencies. Thus the user can choose from the above list in the following step on the first page of event creation: If the user chooses a Paid ticket or a Donation ticket then he/she has to compulsorily choose a country and a currency for the event. Next in line is the system of payments - the way in which the organizer wants payments to be made for his/her event. They include the option of Online Payments and Offline Payments. The organizer has to tick the checkbox in order to enable the payment option. On enabling the PayPal and Stripe checkboxes he/she is represented with the following: On enabling PayPal option the organizer has to enter the PayPal email and for Stripe he has to connect it with Stripe account. And the final step is the addition of tax for the event. The organizer can choose whether he/she wants to enable tax for the event or not: On choosing Yes he is presented with the tax form: If the organizer wants to send invoices then on enabling invoices another form is displayed with details pertaining to Business Address, Registered Name etc.... Finally we have two options that whether we want to display the tax as separate fee or include in the price of tickets.

Continue ReadingImplementing Payment and Tax System for Open-Event

PayPal Express Checkout in Python

As per the PayPal documentation ... Express Checkout is a fast, easy way for buyers to pay with PayPal. Express Checkout eliminates one of the major causes of checkout abandonment by giving buyers all the transaction details at once, including order details, shipping options, insurance choices, and tax totals. The basic steps for using express checkout to receive one-time payments are: Getting the PayPal API credentials. Making a request to the API with the transaction details to get a token Using the token to send the users to the PayPal payment page Capturing the payment and charging the user after the user completes the payment at PayPal. We will be using PayPal's Classic NVP (Name-value pair) API for implementing this. Getting PayPal API Credentials To begin with, we'll need API Credentials. We'll be using the Signature API credentials which consists of API Username API Password Signature To obtain these, you can follow the steps at Creating and managing NVP/SOAP API credentials - PayPal Developer. You'll be getting two sets of credentials. Sandbox and Live. We'll just stick to the Sandbox for now. Now, we need sandbox test accounts for making and receiving payments. Head over to Creating Sandbox Test Accounts - PayPal Developer and create two sandbox test accounts. One would be the facilitator and one would be the buyer. PayPal NVP Servers All the API actions will take place by making a request to the PayPal server. PayPal has 4 different NVP servers for 4 different purposes. https://api-3t.sandbox.paypal.com/nvp - Sandbox "testing" server for use with API signature credentials. https://api-3t.paypal.com/nvp- PayPal "live" production server for use with API signature credentials. https://api.sandbox.paypal.com/nvp - Sandbox "testing" server for use with API certificate credentials. https://api.paypal.com/nvp - PayPal "live" production server for use with API certificate credentials. We'll be using the Sandbox "testing" server for use with API signature credentials. Creating a transaction and obtaining the token To create a transaction, we'll need to make a request with all the transaction details. We can use Python requests library to easily make the requests. All requests are POST. We'll be calling the SetExpressCheckout method of the NVP API to obtain the token. import requests import urlparse data = { 'USER': credentials['USER'], 'PWD': credentials['PWD'], 'SIGNATURE': credentials['SIGNATURE'], 'SUBJECT': credentials['FACILITATOR_EMAIL'], 'METHOD': 'SetExpressCheckout', 'VERSION': 93, 'PAYMENTREQUEST_0_PAYMENTACTION': 'SALE', 'PAYMENTREQUEST_0_AMT': 100, 'PAYMENTREQUEST_0_CURRENCYCODE': 'USD', 'RETURNURL': 'http://localhost:5000/paypal/return/', 'CANCELURL': 'http://localhost:5000/paypal/cancel/' } response = requests.post('https://api-3t.sandbox.paypal.com/nvp', data=data) token = dict(urlparse.parse_qsl(response.text))['TOKEN'] Here, USER represents your Sandbox API Username. PWD represents your Sanbox API Password. SIGNATURE represents your Sandbox Signature. SUBJECT represents the facilitator's email ID. PAYMENTREQUEST_0_AMT is the total transaction amount. PAYMENTREQUEST_0_CURRENCYCODE is the 3 digit ISO 4217 Currency code. RETURNURL is where the user will be sent to after the transaction CANCELURL is where the user will be sent to if he/she cancels the transaction. A URL-Encoded, Name-value pair response would be obtained. We can decode that into a dict by using Python's urlparse modules. From the response, we're extracting the TOKEN which we will use to generate the payment URL for the user. This token…

Continue ReadingPayPal Express Checkout in Python

Ticketing System in Open-Event

So we implemented the ticketing system in the open-event. Basically we provide the user with two options - either add his/her own ticket url or use our own ticketing system. If the ticketing module is turned off then there is no option and the user has to add a Ticket URL. Thus only Add Ticket URL is shown if the ticketing switch is turned off. However if the ticket switch is turned ON then we display our own ticketing system i.e. provide with an option to choose to the user. Now the ticket feature can be either Free, Paid or by donation. If the ticket feature is free then just the normal ticketing details are entered by the user. However if Paid option is selected then a payment system is displayed to the user  where he/she has to choose the country and the currency in which the user will make the payment. The user can pay through PayPal and we can also decide whether we want to add Tax to the event or not.

Continue ReadingTicketing System in Open-Event