Implementation of Donation Tickets in Open Event

Implementation of donation tickets in Open Event Project

This blog post explains the implementation details of donation tickets in the Open Event Project (Eventyay). Eventyay is the Open Event management solution which allows users to buy & sell tickets, organize events & promote their brand. This was developed at FOSSASIA. 

Prior to the integration of this feature, the organizer had the option to provide only paid and free tickets. These tickets had a fixed price and therefore, imbibing and integrating this into the system was relatively easier. The biggest challenge in the implementation of donation tickets was variable prices. The subtotal, total and validation checks had to updated dynamically and shouldn’t be breaking any of the previous features.  

The organizer requires an option to add donation tickets when they create/edit an event by specifying the appropriate minimum price, maximum price and quantity.

                         Organizer View – Donation Tickets

To integrate these features pertaining to donation tickets, fields for minimum and maximum prices had to be introduced into the tickets model. The maximum & minimum prices for free and paid tickets would be the same as the normal price but it’s variant for donations.

isDonationPriceValid: computed('[email protected]', '[email protected]', function() {

for (const donationTicket of this.donationTickets) { if (donationTicket.orderQuantity > 0) {

if (donationTicket.price < donationTicket.minPrice || donationTicket.price > donationTicket.maxPrice) { return false;

}

}

}

return true;

})

Check for valid donation price

To validate the minimum and maximum prices, ember validations have been implemented which checks whether the min price is lesser than or equal to the max  price to ensure a proper flow. Also, in addition to front-end validations, server side checks have also been implemented to ensure that incorrect data does now propagate through the server.

In addition to that, these checks also had to be integrated in the pre-existing shouldDisableOrderButton computed property. Therefore, if the order has invalid donation tickets, the order button would be disabled.

For the public event page, the donation tickets segment have a section which specifies the price range in which the price must lie in. If the user enters a price out of the valid range, a validation error occurs.

The way in which these validation rules have been implemented was the biggest challenge in this feature as multiple sets of donation tickets might be present. As each set of donation tickets have a different price range, these validation rules had to be generated dynamically using semantic ui validations


donationTicketsValidation: computed('[email protected]', '[email protected]', '[email protected]', function() {

const validationRules = {};

for (let donationTicket of this.donationTickets) { validationRules[donationTicket.id] = {

identifier : donationTicket.id,

optional : true,

rules :

[

{

type : `integer[${donationTicket.minPrice}..${donationTicket.maxPrice}]`,

prompt : this.l10n.t(`Please enter a donation amount between ${donationTicket.minPrice} and ${donationTicket.maxPrice}`)

}

]

};

}

return validationRules;

})

Dynamic validation rule generation for donation tickets

Each donation ticket had to be looped through to add a validation rule corresponding to the donation ticket’s ID. These rules were then returned from a computed property.

Resources:

Related work and code repo:

Tags:

Eventyay, FOSSASIA, Flask, SQLAlchemy, Open Event, Python, JWT

Continue Reading

Integration of AliPay Payment Gateway using Stripe Sources

Image result for alipay logo svg

Integration of AliPay Payment Gateway using Stripe Sources

This blog post explains the process of how Stripe Sources has been leveraged to integrate AliPay to extend the payment options in China.

Stripe provides a plethora of payment options configurable with Sources

Source objects allow you to accept a variety of payment methods with a single API. A source represents a customer’s payment instrument, and can be used with the Stripe API to create payments. Sources can be charged directly, or attached to customers for later reuse.

Alipay is a push-based, single-use and synchronous method of payment. This means that your customer takes action to authorize the push of funds through a redirect. There is immediate confirmation about the success or failure of a payment.

   Workflow of Alipay on the backend

During the payment process, a Source API object is created and your customer is redirected to AliPay for authorization.

Payment Flow

  1. Create a Source Object with the parameters currency, redirect_url and amount.
  2. Create a page for completion of customer authorization by specifying the redirect_url.
  3. Change the source status from pending to chargeable.
  4. Confirm the payment by redirecting to the confirmation page and process the refund for any unsuccessful payments(if deducted)

Configuration Manager

Initially, we define a class named as AliPayPaymentsManager which handles the configuration of API keys and has methods to create source objects and make them chargeable.

class AliPayPaymentsManager(object):
    """
    Class to manage AliPay Payments
    """

    @staticmethod
    def create_source(amount, currency, redirect_return_uri):
        stripe.api_key = get_settings()['alipay_publishable_key']
        response = stripe.Source.create(type='alipay',
                                        currency=currency,
                                        amount=amount,
                                        redirect={
                                            'return_url': redirect_return_uri
                                        }
                                        )
        return response

    @staticmethod
    def charge_source(order_identifier):
        order = safe_query(db, Order, 'identifier', order_identifier, 'identifier')
        stripe.api_key = get_settings()['alipay_secret_key']
        charge = stripe.Charge.create(
                 amount=int(order.amount),
                 currency=order.event.payment_currency,
                 source=order.order_notes,
                 )
        return charge

                Methods to create & charge the source

The challenge of charging the source & redirection

After creating the source object, we need to make the source object chargeable. For this purpose, the user must be redirected to the external AliPay payment gateway page where the source object is authorized and its status is changed to chargeable. The main challenge to overcome here was the method in which the redirect link was attached to the object. The parameter _external is really crucial for this process as Flask would only recognize the url as an external url if this parameter is passed to url_for.

@alipay_blueprint.route('/create_source/<string:order_identifier>', methods=['GET', 'POST'])
def create_source(order_identifier):
    """
    Create a source object for alipay payments.
    :param order_identifier:
    :return: The alipay redirection link.
    """
    try:
        order = safe_query(db, Order, 'identifier', order_identifier, 'identifier')
        source_object = AliPayPaymentsManager.create_source(amount=int(order.amount), currency='usd',
                                                            redirect_return_uri=url_for('alipay_blueprint.alipay_return_uri',
                                                            order_identifier=order.identifier, _external=True))
        order.order_notes = source_object.id
        save_to_db(order)
        return jsonify(link=source_object.redirect['url'])
    except TypeError:
        return BadRequestError({'source': ''}, 'Source creation error').respond()

Route which creates the source and returns external redirection url

After the source object is created, the status changes to pending. To charge the user, the source object must become chargeable. For this, we need to redirect the user to an external page where the payment can be authorized and the source object can become chargeable.

External Authorization Page for AliPay

After authorizing the payment, we redirect to the url given in return_redirect_uri which is configured with the source object which tries to charge the source object as it is now chargeable. After this, a POST request is sent to the return_redirect_uri route which handles success and failure cases

@alipay_blueprint.route('/alipay_return_uri/', methods=['GET', 'POST'])
def alipay_return_uri(order_identifier):
    """
    Charge Object creation & Order finalization for Alipay payments.
    :param order_identifier:
    :return: JSON response of the payment status.
    """
    try:
        charge_response = AliPayPaymentsManager.charge_source(order_identifier)
        if charge_response.status == 'succeeded':
            order = safe_query(db, Order, 'identifier', order_identifier, 'identifier')
            order.status = 'completed'
            save_to_db(order)
            return redirect(make_frontend_url('/orders/{}/view'.format(order_identifier)))
        else:
            return jsonify(status=False, error='Charge object failure')
    except TypeError:
        return jsonify(status=False, error='Source object status error')

After AliPay authorization, the order is saved and payment is completed

Resources:

Related work and code repo:

Tags:

Eventyay, FOSSASIA, Flask, Ember.js, Open Event, Python, AliPay, Stripe

Continue Reading
Close Menu