Amadis

Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

Version 1 Next »

As example, we will be demonstrating a Sale to POI payment request and validate the input request on the sale terminal.

  1. payment request sent to POI terminal from Sale terminal.

<?xml version="1.0"?>
<SaleToPOIRequest>
    <MessageHeader MessageClass="Service" MessageCategory="Payment" MessageType="Request" ServiceID="2" SaleID="ArkosSales" POIID="PreCertificationAmadis1" />
    <PaymentRequest>
        <SaleData SaleReferenceID="ArkosSale42">
            <SaleTransactionID TransactionID="1" TimeStamp="2019-08-21T14:48:05+05:00" />
        </SaleData>
        <PaymentTransaction>
            <AmountsReq Currency="EUR" RequestedAmount="250.00" />
        </PaymentTransaction>
        <PaymentData PaymentType="Normal" />
    </PaymentRequest>
</SaleToPOIRequest>

  1. We chose to use Merchant confirmation of a partially approved transaction as example to validate the input request (Merchant confirmation of a partially approved transaction ).

  1. When it is time for merchant to confirm partial transaction approval, the POI will send an input request to the sale terminal. Here is the code that satisfies this condition.

    public boolean merchantConfirmPartiallyApprovedTransaction(DisplayParams displayParams, byte[] bytes) {
        Log.d(LOG_TAG, "merchantConfirmPartiallyApprovedTransaction");

        ArkosContext arkosContext = new ArkosContext(bytes);
        String amount = CurrencyFormat.format(arkosContext.getAmount());
        String requestedAmount = CurrencyFormat.format(arkosContext.getRequestedAmount());
        String leftToPay = CurrencyFormat.format(arkosContext.getLeftToPay());

        String prompt = getString(R.string.confirm_partial_approval);
        return PaymentSystem.getRetailHandler().SendInputRequest(prompt);
    }
    public synchronized boolean SendInputRequest(String display) {
        if (bConnected) {
            Log.d(LOG_TAG, "POI input request: " + display);
            InputResponse resp = retailModule.requestInput(OutgoingInputRequest.BasicConfirmation(display, 60));
            if(resp.wrap().getResult().equals("Success")){
                return resp.wrap().getConfirmation();
            }
        }
        return false;
    }

The requestInput() function has been added in ASL and is available in ASL-2.8.10a.aar.

You can refer to Android-demo-fixed project or the RetailHandler class as example.

package ca.amadis.arkospay;

import android.util.Log;

import androidx.lifecycle.Lifecycle;

import ca.amadis.arkoslib.PaymentResultID;
import ca.amadis.asl.retail.NexoEvent;
import ca.amadis.asl.retail.NexoEventType;
import ca.amadis.asl.retail.NexoRetailModule;
import ca.amadis.asl.retail.NexoSignal;
import ca.amadis.asl.retail.display.Qualification;
import ca.amadis.asl.retail.event.ErrorEvent;
import ca.amadis.asl.retail.request.InputRequest;
import ca.amadis.asl.retail.request.InputResponse;
import ca.amadis.asl.retail.request.OutgoingInputRequest;
import ca.amadis.asl.tlv.TlvTree;

public class RetailHandler extends Thread {

    private final String LOG_TAG = "Retail";

    private final MainActivity activity;
    final private int port = 2824;
    private NexoRetailModule retailModule;
    private boolean bConnected = false; // A Retail client is connected
    private boolean bSuspended = false; // Retail actions are suspended

    private NexoEvent pendingEvent = null;

    public NexoEvent getEvent() {
        return pendingEvent;
    }

    public interface RequestListener {
        void onRequest(NexoEventType type);
    }

    private RequestListener mRequestListener = null;

    public void setOnRequest(RequestListener listener) {
        mRequestListener = listener;
    }


    public RetailHandler(MainActivity activity) {
        setName("RetailActions");
        this.activity = activity;
        start();
    }

    @Override
    public void run() {

        setPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
        retailModule = NexoRetailModule.StartServer(port);
        NexoRetailModule.EnableLogging();

        Log.d(LOG_TAG, "Server listening on Port " + port);
        while (true) {
            NexoEvent event = retailModule.poll();
            if (event != null && event.eventType != NexoEventType.None) {
                processEvent(event);
            }
        }
    }

    public synchronized void Pause() {

        if (!bSuspended && bConnected) {
            retailModule.signal(NexoSignal.Busy);
            Log.v(LOG_TAG, "Polling suspended");
        }
        bSuspended = true;
    }

    public synchronized void Resume() {

        if (bSuspended && bConnected) {
            retailModule.signal(NexoSignal.Available);
            Log.v(LOG_TAG, "Polling resumed");
        }
        if (pendingEvent != null) {
            ReplyFailure("Aborted");
        }
        bSuspended = false;
    }

    public synchronized void ReplyFailure(String reason) {
        if (pendingEvent != null) {
            String eventName = pendingEvent.eventType.toString();
            Log.e("Retail", eventName + " Failure: " + reason);
            if (pendingEvent.eventType == NexoEventType.InputRequest) {
                retailModule.respondInputFailure(pendingEvent);
            } else {
                retailModule.respondBasicFailure(pendingEvent, reason);
            }
            pendingEvent = null;
        }
    }

    public synchronized void ReplySuccess() {
        if (pendingEvent != null) {
            String eventName = pendingEvent.eventType.toString();
            Log.i(LOG_TAG, eventName + " Success");
            retailModule.respondBasicSuccess(pendingEvent);
            pendingEvent = null;
        }
    }

    public synchronized void ReplyInputSuccess(int selected) {
        if (pendingEvent != null) {
            InputRequest inputEvent = (InputRequest) pendingEvent;
            String selectedStr = inputEvent.choices[selected];
            Log.i(LOG_TAG, "InputRequest Success " + selected + " = " + selectedStr);
            retailModule.respondInputSuccess(pendingEvent, selected);
            pendingEvent = null;
        }
    }

    public synchronized void ReplyPaymentResult(PaymentResultID result) {
        if (pendingEvent != null) {

            TlvTree reply = TlvTree.Empty();
            byte[] receiptData = ReceiptHandler.load(ReceiptHandler.Type.Transaction);
            reply.AddBinSerialized(0xFF01, receiptData);
            reply.AddChild(0xFF02); // TODO add merchant Data
            Log.i(LOG_TAG, "PaymentRequest send Result: " + result.toString());
            retailModule.respond(pendingEvent, reply.AsBytes());
            pendingEvent = null;
        }
    }

    public synchronized void SendPOIReplication(String display) {
        if (bConnected && pendingEvent != null) {
            if (pendingEvent.eventType == NexoEventType.PaymentRequest) {
                Log.d(LOG_TAG, "POI Replication: " + display);
                retailModule.requestDisplay(display, Qualification.POIReplication);
            }
        }
    }

    public synchronized boolean SendInputRequest(String display) {
        if (bConnected) {
            Log.d(LOG_TAG, "POI input request: " + display);
            InputResponse resp = retailModule.requestInput(OutgoingInputRequest.BasicConfirmation(display, 60));
            if(resp.wrap().getResult().equals("Success")){
                return resp.wrap().getConfirmation();
            }
        }
        return false;
    }
    private void processEventRequest(NexoEvent event) {

        NexoEventType type = event.eventType;

        if (mRequestListener == null) {
            Log.e(LOG_TAG, "Not any listener");
            retailModule.respondBasicFailure(event, "Not supported");
        }
        // Ignore Retail request when app is not in resumed state
        else if (activity.getLifecycle().getCurrentState() != Lifecycle.State.RESUMED) {
            Log.w("Retail", type.toString() + "Ignored : App Paused");
            retailModule.respondBasicFailure(event, "Paused");
        }
        // Process request
        else {
            if (pendingEvent != null) {
                Log.w(LOG_TAG, pendingEvent.eventType.toString() + "Aborted (new incoming event)");
                pendingEvent = null;
                ReplyFailure("New incoming event");
            }
            pendingEvent = event;
            activity.runOnUiThread(() -> mRequestListener.onRequest(type));
        }
    }

    private synchronized void processEvent(NexoEvent event) {

        Log.i(LOG_TAG, "Event " + event.eventType.toString());

        switch (event.eventType) {
            case Login:
                bConnected = true;
                if (bSuspended) {
                    retailModule.signal(NexoSignal.Busy);
                }
                break;

            case Logout:
            case Disconnection:
                bConnected = false;
                break;

            case DisplayRequest:
            case AdminRequest:
            case InputRequest:
            case PaymentRequest:
            case ReversalRequest:
                processEventRequest(event);
                break;

            case ErrorReport:
                ErrorEvent ee = (ErrorEvent) event;
                Log.e(LOG_TAG, "Error Report: " + ee.errorDescription);
                break;

            default:
                Log.e(LOG_TAG, "Unknown Event Type =" + event.eventType.toString());
                retailModule.respondBasicFailure(event, "Unknown event type");
                break;
        }
    }

}

getResult() function checks if result is a success. A success means we were able to receive a response.

An example of a response you can send with the confirmation can be found below:

<?xml version="1.0"?>
<SaleToPOIResponse>
	<MessageHeader MessageClass="Device" MessageCategory="Input" MessageType="Response" DeviceID="1374" SaleID="ArkosSales" POIID="PreCertificationAmadis1"/>
	<InputResponse>
		<InputResult Device="CashierInput" InfoQualify="Input">
			<Response Result="Success"/>
			<Input InputCommand="GetConfirmation">
			<ConfirmedFlag>true</ConfirmedFlag>
			</Input>
		</InputResult>
	</InputResponse>
</SaleToPOIResponse>

  • No labels