Implementing Value Added Services - AEVI-AppFlow/pos-android-sdk GitHub Wiki

This page provides details on how to write value added applications for AppFlow via integrating with the Payment Flow Service API, which provides integration points for third party applications to be called and read/augment data at various stages of a flow.

In order to develop a value added service, it is important to understand the various steps a payment goes through from initiation to completion and what data is passed between them at the various stages.

This guide will, together with JavaDocs and sample apps, provide you with an overview of this. Please ensure you have read Implementing Flow Services before continuing on this page.

References

For API docs, please see Javadocs.

Please see the Flow Service Sample for examples of value added service operations.

Pre-requisites

It is assumed that the Overview has already been read.

Recommended reading, in order;

Implementation

The goal here is to implement the appropriate service(s) and/or activities for those stages and expose them so that FPS can find them during application scanning.

Adding the service info provider

In order for FPS and client applications to know what your service supports, you need to implement a content provider that returns relevant information about your service.

This is done by extending BasePaymentFlowServiceInfoProvider, as the sample code from the Flow Service Sample below shows

public class PaymentFlowServiceInfoProvider extends BasePaymentFlowServiceInfoProvider {

    @Override
    protected PaymentFlowServiceInfo getPaymentFlowServiceInfo() {
        return new PaymentFlowServiceInfoBuilder()
                .withVendor("AEVI")
                .withDisplayName("Flow Service Sample")
                .withCanAdjustAmounts(true)
                .withCanPayAmounts(true, PaymentMethods.LOYALTY_POINTS, PaymentMethods.GIFT_CARD, PaymentMethods.CASH)
                .withSupportedFlowTypes(FlowTypes.SALE)
                .withCustomRequestTypes(ShowLoyaltyPointsBalanceService.SHOW_LOYALTY_POINTS_REQUEST)
                .build(getContext());
    }
}

The following fields are mandatory for value added flow services

  • Vendor
  • Display name
  • withCanPayAmounts() - If you provide any form of payment method or apply discounts to baskets
  • withCanAdjustAmounts() - If you add amounts, like tip, fees, charity, tax, etc

Note that if you fail to report that your service can pay or adjust amounts here, FPS will reject any attempts from your application to do so.

withSupportedFlowTypes() is optional - if left empty, it means FPS will call your flow service for any type and its up to your flow service to handle unsupported types. If types are specified, FPS will ensure your service only gets called for flows with types that your service supports. See Request Types for defined types.

In addition, withCustomRequestTypes() can be used to define custom types that is specific to your flow service. This may be a function to return some particular data like loyalty balance, or to display such data. It is entirely up to your application what data is required and returned and whether your service shows any UI for it or not. In order for other applications to know about this type however, it needs to be advertised. See Bespoke Data for details.

The provider then needs to be exposed in the application manifest.

        <provider
            android:name=".PaymentFlowServiceInfoProvider"
            android:authorities="<your authority>"
            android:exported="true">
            <intent-filter>
                <action android:name="com.aevi.sdk.flow.action.PROVIDE_SERVICE_INFO"/>
            </intent-filter>
        </provider>

Relevant flow stages

The following payment stages, as described in the general API intro, are relevant for value added services

  • SPLIT
  • PRE_TRANSACTION
  • POST_CARD_READING
  • POST_TRANSACTION
  • POST_FLOW (maybe)

In addition, if your application will be defining custom request types, you will need to handle the GENERIC stage.

If you are interested in your application being called after another application has handled a generic request, like tokenisation, you should support the POST_GENERIC stage. Note that you do not (should not) list supported flow types for POST_GENERIC applications - they will be called for all types.

The next sections will cover what can be done in what stages, and go over some common use cases.

Augmentation details

Depending on what function your application is offering and for what stages it will be called, there are different augmentation options. This section will break down all the augmentation options (for payment flows specifically) and for what stages and functions they may be useful. The next section will look at specific use cases and advise on how to model your service for that use case.

Pre transaction-processing augmentations

The majority of the relevant augmentations happen before the TRANSACTION_PROCESSING stage where a payment service determines the outcome of the transaction. After that point, all flow services can do is add references. This section will cover the augmentation options for the PRE_TRANSACTION and POST_CARD_READING stages. For both these stages, any augmentations are done via the PreTransactionModel. Note that PRE_FLOW is not covered here as it's intended for quite specific use cases.

Adding additional amounts

There are various scenarios for which adding additional amounts is relevant, be it for tipping, fees, donations, etc. The PreTransactionModel contains two methods for doing this;

  • setAdditionalAmount(String identifier, long amount)
  • setAdditionalAmountAsBaseFraction(String identifier, float fraction)

The first method allows you to specify the amount value in its sub-unit form, whereas the second method allows you to add an amount as a fraction of the request base amount value. This is useful for a variety of cases where the amount is a percentage of the base amount, such as fees, tipping, etc.

In both cases, the amount is identified via a string identifier. See Additional Amounts for details.

Adding a basket

The client/POS application may have provided a basket in the initial request. To provide sensible separation between what app/service added what items, flow services can add new baskets, but not add more items to the existing baskets (discounts excepted). This can be done with the PreTransactionModel.addNewBasket(Basket basket) method.

This is the recommended approach to add any form of "upsells" or extras, as it adds visibility of what specifically has been added, allowing receipts apps to show it clearly on the receipts. Note that the request amounts will be automatically updated to reflect this new basket.

Adding/updating customer details

A flow service can either add or update customer details. If the initial Payment does not contain any customer data, the flow service can create a new Customer model and add to the transaction. If however a Customer model already exists, a flow service can add

  • Tokens
  • Customer details as additional data

Either case is done via PreTransactionModel.addOrUpdateCustomerDetails(Customer customer). If adding, the Customer parameter is created by your service. If updating, you use the Customer model from the request, update it and then pass it in here.

Paying amounts

A flow service can pay off a portion or all of the requested amounts. The most common scenario for this is via loyalty rewards or points. This can be done via PreTransactionModel.setAmountsPaid(Amounts amountsPaid, String paymentMethod). The amounts must not exceed requested amounts and the payment method must be set. See Payment Methods for defined options.

This method is recommended when the payment has no relation to any basket items. If however the payment is provided as discounts to certain items in the basket, like offering a free coffee, then the recommended approach is to apply discounts to baskets which is covered in the next section.

Applying discounts to baskets

If your service provides discounts or rewards based on basket items, then this function will allow your service to apply discounts as items in the same basket. As an example, if the basket contains an item "Latte" with a cost of $3.50, you can via this method add a "free coffee reward" that would look something like "Reward: Free Latte" with a negative amount of $-3.50 to negate the cost of the original item.

See PreTransactionModel.applyDiscountsToBasket(String basketId, List<BasketItem> basketItems, String paymentMethod) for more info.

Adding request data

Any arbitrary data that may be required or useful for other flow services (or the POS app) can be added via the PreTransactionModel.addRequestData(String key, T... values) method.

Adding references after transaction processing

The PostTransactionModel offers a addReferences(String key, T... values) method to provide references back to the POS app for the transaction.

Specific use cases

In order to assist common types of applications, the information below covers what stages are typically relevant for these scenarios and what augmentation methods may be useful.

Loyalty

A loyalty application typically provides three main functions:

  • Applying discounts from customer use of earned loyalty points or rewards
  • Assigning newly earned points/rewards to a customer
  • Allowing new customers to sign up to the loyalty scheme

Applying discounts, has to be done prior to the payment app performing its processing of the payment. Therefore, your application should support the POST_CARD_READING stage, as at that time all the data to help identify the customer will be available. This can be customer data from the POS app, card token from the card reading stage, or the loyalty app may provide its own mechanism to identify a customer. If card tokens are not relevant, PRE_TRANSACTION is an equally good candidate.

For assigning points and/or signing up new customers, POST_TRANSACTION should be used. At this stage, the outcome of the transaction is known and the payment app may have provided a card token as well as part of the TRANSACTION_PROCESSING.

To apply discounts, you should make use of the PreTransactionModel where you have two options depending on how your loyalty scheme works.

  • Pay off all or a portion of the requested amounts
  • Apply discount to basket items

In the first case, you should use the setAmountsPaid() method, and in the latter case make use of the addItemsToExistingBasket() where you would add a discount line item with a negative amount to the main basket.

Splitting Bills

A split bill application allows customers, typically in a restaurant/cafe setting, to split a table bill amongst them. A Payment will be broken down into a Transaction per customer, where the split application dictates the amounts / basket items for each transaction. The Payment will be completed once the total requested amounts have been met, or if it is cancelled.

There is a specific designed stage for split applications - SPLIT, which is called repeatedly until the Payment is completed. Please see Considerations for split applications for more information how to write a best practice split application.

Tipping

If a POS application does not provide tipping as a function, a flow service may choose to offer this separately. The PRE_TRANSACTION stage is the most appropriate choice for general flows, but where it is not available, PRE_FLOW is also an option.

To apply a tip, see the setAdditionalAmount() or setAdditionalAmountAsBaseFraction() methods in the stage models.

Receipts

AppFlow enables handling of receipts to be managed by a flow service rather than the payment application or POS application. This is useful for receipt services that allow customers to gather all their receipts in a single location. It is also encouraged for more traditional receipt handling (like printing), as it means integration with printers / printing APIs can be done in a single application as opposed to embedding this functionality in the POS/payment application.

Receipt handling flow services should be called in the POST_TRANSACTION stage, when the transaction outcome is known.

There are quite a few considerations to be made for receipt handling in AppFlow - please see Considerations for receipt applications for in-depth info.

Feedback / Ratings

Various services can be made available allowing acquirers/merchants to get customer feedback at the end of a transaction, typically via some rating system.

If feedback is desired per customer, then POST_TRANSACTION is the relevant stage, or if feedback is desired for the overall payment, then POST_FLOW is more appropriate.

Ads / Promotions

A flow service can provide ads and promotions that can be read and shown by other services in the flow. As an example, a receipt application might add it to the receipt.

See Data Definitions for how to add and read relevant data for this.

Analytics / Reporting

It is possible for a flow service to gather and report information and statistics around transactions. As this would deal with potentially sensitive customer data, this would for the most case require a special acquirer application(s).

These applications can run asynchronously in the POST_FLOW stage, meaning that they can accept the PaymentResponse data, and let the flow finish whilst the service processes the data in the background. This is to ensure that such activities do not delay the flow, which would have a negative impact on the sale experience.

In addition, these types of applications would also be suitable for the POST_GENERIC stage for the generic flows.

Sending response

Once your service has completed all its augmentation of the flow it should respond to FPS. Responses can be sent once you have finished augmenting data in the stage model via the sendResponse() method.

NOTE: Even if you DO NOT augment the data in any way then your service must still call one of the sendResponse() / skip() / finish() methods to tell FPS that your service is done.

⚠️ **GitHub.com Fallback** ⚠️