Advanced Flows

Cancel the Transaction

The POS should use the TransactionManager.abort() method to cancel the transaction in progress. The corresponding PaymentCompletedEvent.getStatus() will return StatusCode.ABORTED to indicate that it was aborted. If the event does not have this status, then it’s possible that calling abort did not happen in time, and the POS should either reverse the transaction using the TransactionManager.processVoid() method, or simply accept the completed result.

Inline Tipping with Tip Options

The merchant would like the customer to select a tip from a pre-configured menu, determining how much gratuity should be added to the purchase. This menu can be presented before the card is presented, or after, depending on the POS’ preference.

 @startuml
 !include uml_styles.iuml
 hide footbox

 participant POS order 10
 participant POI order 20
 actor Customer order 30
 |||
 group If card presentation is desired first
     POS ->> POI : Start pre-auth payment
     POI <-> Customer : Perform payment
     ...
     POS <<-- POI : Payment completed event
 end
 |||
 POS ->> POI : Present user options
 POI -> Customer : Display tip options
 POI <- Customer : Select option
 POS <<-- POI : User input event with the selected option
 POS -> POS : Update payment amount with selected option.
 alt If pre-auth was performed
     POS ->> POI : Start pre-auth completion payment
 else Else if payment has not yet been done
     POS ->> POI : Start payment
 end
 POI <-> Customer : Perform payment
 ...
 POS <<-- POI : Payment completed event
 |||
 @enduml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Override // Overridden from the CommerceListenerAdapter
public void handleUserInputEvent(UserInputEvent event) {
    if (UserInputEvent.RECEIVED_TYPE.equals(event.getType())) {
        Values valueFromCustomer = event.getValues();
        if (event.getInputType() == InputType.MENU_OPTIONS) {
            if (event.getStatus() == StatusCode.SUCCESS) {
                int selectedIndex = valueFromCustomer.getSelectedIndices()[0];
                // Use selected index to determine which button they selected.
                // In this example, index 0 is 20%, 1 is 15%, etc.
            } // else handle error
        } // else handle other input response
    } // else handle input request from POI
}

final String header = "Please select a tip";
final String[] buttons = new String[] {
        "20%",
        "15%",
        "10%",
        "None"
};
mTransactionManager.presentUserOptions(header, buttons);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// Overridden from the CommerceListenerAdapter
override fun handleUserInputEvent(event: UserInputEvent) {
    if (UserInputEvent.RECEIVED_TYPE.equals(event.getType())) {
        val valueFromCustomer = event.getValues()
        if (event.getInputType() == InputType.MENU_OPTIONS) {
            if (event.getStatus() == StatusCode.SUCCESS) {
                val selectedIndex = valueFromCustomer.getSelectedIndices()[0]
                // Use selected index to determine which button they selected.
                // In this example, index 0 is 20%, 1 is 15%, etc.
            } // else handle error
        } // else handle other input response
    } // else handle input request from POI
}

val header = "Please select a tip"
val buttons = arrayOf("20%", "15%", "10%", "None")
mTransactionManager.presentUserOptions(header, buttons)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// Overridden from the VFICommerceListenerAdapter
override func handle(_ event: VFIUserInputEvent?) {
    var valueFromCustomer = event?.getValues()
    if (VFIInputType.MENUOPTIONS == event?.getInputType()) {
        if (VFIStatusCodeSuccess == event?.getStatus()) {
            var selectedIndex = valueFromCustomer?.getSelectedIndices()[0]
            // Use selected index to determine which button they selected.
            // In this example, index 0 is 20%, 1 is 15%, etc.
        } // else handle error
    } // else handle other input response
}

let header = "Please select a tip"
let buttons = ["20%", "15%", "10%", "None"]
sdk.getTransactionManager()?.presentUserOptions(header, buttonLabels: buttons)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private readonly EventHandler<UserInputEvent> user_input_event_handler_;

private void HandleEvent(UserInputEvent event)
{
    if (event.Type == UserInputEvent.RECEIVED_TYPE)
    {
        Values value_from_customer = event.ResponseValues;
        if (event.InputType == InputType.MENU_OPTIONS)
        {
            if (event.Status == StatusCode.SUCCESS)
            {
                int selected_index = value_from_customer.SelectedIndices[0];
                // Use selected index to determine which button they selected.
                // In this example, index 0 is 20%, 1 is 15%, etc.
            } // else handle error
        } // else handle other input response
    } // else handle input request from POI
}

// ...
// Link event handler after initialization is complete but before requesting input
user_input_event_handler_ = async (sender, args) =>
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
        () => HandleEvent(args));
};
payment_sdk_.TransactionManager.HandleUserInputEvent += user_input_event_handler_;

// ...
// Present the tip options.
string header = "Please select a tip";
string[] buttons = new string[] {"20%", "15%", "10%", "None"};
payment_sdk_.TransactionManager.PresentUserOptions(header, buttons);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Overridden from the CommerceListenerAdapter
void handleUserInputEvent (
    const std::shared_ptr<verifone_sdk::UserInputEvent>& event) override {
    if (event->getType() == verifone_sdk::UserInputEvent::RECEIVED_TYPE) {
      auto values = event->getValues();
      if (event->getInputType() == InputType::MENU_OPTIONS) {
        if (event->getStatus() == StatusCode::SUCCESS) {
          auto selected_indices = event->getValues()->getSelectedIndices();
          // Use selected indices to determine which button they selected.
          // In this example, index 0 is 20%, 1 is 15%, etc.
        } // else handle error
      } // else handle other input response
    } // else handle input request from POI
}
// ...


std::vector<std::string> options = {"20%", "15%", "10%", "None"};
auto status = psdk->getTransactionManager()->presentUserOptions(
    "Please select a tip", options);
if (status->getStatus() == verifone_sdk::StatusCode::SUCCESS) {
  //operation initiated successfully
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public void HandleUserInputEvent(UserInputEvent sdk_event)
{
    if (sdk_event.Type == UserInputEvent.RECEIVED_TYPE)
    {
        Values value_from_customer = sdk_event.Values;
        if (sdk_event.InputType == VerifoneSdk.InputType.MENU_OPTIONS)
        {
            if (sdk_event.Status == 0)
            {
                int selected_index = value_from_customer.SelectedIndices[0];
                // Use selected index to determine which button they selected.
                // In this example, index 0 is 20%, 1 is 15%, etc.
            } // else handle error
        } // else handle other input response
    } // else handle input request from POI
}

// ...
// Present the tip options.
string header = "Please select a tip";
string[] buttons = new string[] {"20%", "15%", "10%", "None"};
payment_sdk_.TransactionManager.PresentUserOptions(header, buttons);

See also

Handling Requests for User Input from the Payment Application

There will be times when the POI requires input from the cashier. In these cases, the CommerceListener will receive an event of type UserInputEvent.REQUEST_TYPE. The event contains a RequestParameters object, which contains all of the different values and type of request that should be displayed to the cashier. Any time the response is required by the request, the listener must display this information to the cashier for the current flow to proceed. The receiving application must construct a new Values object, populating it with the data received from the cashier, then attach it to the response object from UserInputEvent.generateResponse(), and send it back to the payment application using TransactionManager.sendEventResponse(response).

 @startuml
 !include uml_styles.iuml
 hide footbox

 actor Cashier order 10
 participant POS order 20
 participant POI order 30
 |||
 POS ->> POI : Login
 ...
 POS <<- POI : Request User Input
 POS -> POS : Create dialog based on the Request Parameters
 Cashier <<- POS : Present dialog
 Cashier -> POS : Provide input
 POS -> POS : Put input into the Values object
 POS -> POS : Attach the values to the response
 POS ->> POI : Send event response back to POI. The POI continues the flow.
 ...
 |||
 @enduml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@Override // Overridden from the CommerceListenerAdapter
public void handleUserInputEvent(UserInputEvent event) {
    if (UserInputEvent.REQUEST_TYPE.equals(event.getType())) {
        RequestParameters requestParameters =
                        event.getRequestParameters();
        UserInputEventResponse response = event.generateUserInputEventResponse();
        // Based on requestParameters.getInputType(), present the appropriate dialog.
        // Keep track of the response, it is needed once the input is collected.
    }
}

// Send back the response once the input is collected.
Values responseValues = response.getResponseValues();
// Set the appropriate value on the values object based on the input type.
response.setValues(responseValues);
mTransactionManager.sendInputResponse(response);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// Overridden from the CommerceListenerAdapter
override fun handleUserInputEvent(event: UserInputEvent) {
    if (UserInputEvent.REQUEST_TYPE.equals(event.getType())) {
        val requestParameters = event.getRequestParameters()
        val response = event.generateUserInputEventResponse()
        // Based on requestParameters.getInputType(), present the appropriate dialog.
        // Keep track of the response, it is needed once the input is collected.
    }
}

// Send back the response once the input is collected.
val responseValues = response.getResponseValues()
// Set the appropriate value on the values object based on the input type.
response.setValues(responseValues)
mTransactionManager.sendInputResponse(response)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// Overridden from the VFICommerceListenerAdapter
override func handle(_ event: VFIUserInputEvent?) {
    if (VFIUserInputEventREQUESTTYPE == event?.getType()) {
        var requestParameters: VFIRequestParameters? = event?.getRequestParameters()
        var response: VFIUserInputEventResponse? = event?.generateResponse()
        // Based on requestParameters.getInputType(), present the appropriate dialog.
        // Keep track of the response, it is needed once the input is collected.
    }
}

// Send back the response once the input is collected.
let responseValues = response?.getValues()
// Set the appropriate value on the values object based on the input type.
response?.setValues(responseValues)
sdk.getTransactionManager()?.sendInputResponse(response)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private readonly EventHandler<UserInputEvent> user_input_event_handler_;

private void HandleUserInputvent(UserInputEvent event)
{
    if (event.Type.Value == UserInputEvent.REQUEST_TYPE)
    {
        var request_parameters = event.RequestParameters;
        var response = event.GenerateUserInputEventResponse();
        // Based on requestParameters.getInputType(), present the appropriate dialog.
        // Keep track of the response, it is needed once the input is collected.
    }
}

// ...
// Link event handler after initialization is complete but before requesting input
user_input_event_handler_ = async (sender, args) =>
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
        () => HandleEvent(args));
};
payment_sdk_.TransactionManager.HandleUserInputEvent += user_input_event_handler_;

// ...
// Send back the response once the input is collected.
var responseValues = response.ResponseValues;
// Set the appropriate value on the values object based on the input type.
response.ResponseValues(responseValues);
transaction_manager_.SendInputResponse(response);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// Overridden from the CommerceListenerAdapter
void handleUserInputEvent (
        const std::shared_ptr<verifone_sdk::UserInputEvent>& event) override {
    if (event->getType() == verifone_sdk::UserInputEvent::REQUEST_TYPE
        && event_->getStatus() == verifone_sdk::StatusCode::SUCCESS) {
      auto request_parameters = event->getRequestParameters();
      auto response = event->generateUserInputEventReponse();
      // Based on requestParameters.getInputType(), present the appropriate dialog.
      // Keep track of the response, it is needed once the input is collected.
    }
}
// ...

// Send back the response once the input is collected.
auto responseValues = response->getResponseValues();
// Set the appropriate value on the values object based on the input type.
response->setResponeValues(responseValues);
psdk->getTransactionManager()->sendInputResponse(response);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public void HandleUserInputEvent(UserInputEvent sdk_event)
{
    if (sdk_event.Type == UserInputEvent.REQUEST_TYPE)
    {
        var request_parameters = sdk_event.RequestParameters;
        var response = sdk_event.GenerateUserInputEventResponse();
        // Based on requestParameters.getInputType(), present the appropriate dialog.
        // Keep track of the response, it is needed once the input is collected.
    }
}



// ...
// Send back the response once the input is collected.
var responseValues = response.ResponseValues;
// Set the appropriate value on the values object based on the input type.
response.ResponseValues(responseValues);
payment_sdk_.TransactionManager.SendInputResponse(response);

See also

Manually Keying In Card Information

This section describes the procedure for the merchant to manually key in the card information when making a payment. This is required when the card reader is unable to read the card information due to a device malfunction in reading the chip or magnetic strip. In these cases, the Payment.setRequestedCardPresentationMethods() must be called with the parameter PresentationMethod.KEYED. The card information will then be keyed into the terminal.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// Please note that creating Payment also creates AmountTotals
Payment payment = Payment.create();
ArrayList<PresentationMethod> presentationMethods = new ArrayList<PresentationMethod>();
presentationMethods.add(PresentationMethod.KEYED);
payment.setRequestedPaymentType(PaymentType.CREDIT);
payment.getRequestedAmounts.setSubtotal(ConversionUtility.parseAmount(8.00)
                           .setTax(ConversionUtility.parseAmount(1.00))
                           .setGratuity(ConversionUtility.parseAmount(1.00))
                           .setTotal(ConversionUtility.parseAmount(10.00));
payment.setTransactionType(TransactionType.PAYMENT);
payment.setRequestedCardPresentationMethods(presentationMethods);
1
2
3
4
5
6
// Please note that creating Payment also creates AmountTotals
val payment = Payment.create()
payment.setRequestedCardPresentationMethods({PresentationMethod.KEYED})
// Configure amounts and other payment properties before starting.
transactionManager.startPayment(payment)
// Listener receives the PaymentCompletedEvent.
1
2
3
4
5
6
var payment = VFIPayment.create()
payment?.setRequestedCardPresentationMethods([
NSNumber(value: VFIPresentationMethod.KEYED.rawValue)])
// Configure amounts and other payment properties before starting.
sdk.getTransactionManager()?.start(payment)
// Listener receives the PaymentCompletedEvent.
1
2
3
4
5
6
7
var payment = new Payment
{
    RequestedCardPresentationMethods = new List<PresentationMethod> { PresentationMethod.KEYED }
    // Configure amounts and other payment properties before starting.
};
transactionManager.StartPayment(payment);
// Listener receives the PaymentCompletedEvent.
1
2
3
4
5
auto payment = verifone_sdk::Payment::create();
payment->setRequestedCardPresentationMethods({verifone_sdk::PresentationMethod::KEYED});
// Configure amounts and other payment properties before starting.
transaction_manager->startPayment(payment);
// Listener receives the PaymentCompletedEvent.
1
2
3
4
var payment = Payment.Create();
payment.RequestedCardPresentationMethods = new List<PresentationMethod> { PresentationMethod.KEYED };
payment_sdk_.TransactionManager.StartPayment(payment);
// Listener receives the PaymentCompletedEvent.

Handling External Changes to the Basket/Amount

It is possible for external apps to request changes to the basket and/or amount during four triggers:

CP Message Events

Listener Event

CP ID

CP Trigger Class

BasketAdjustedEvent

CP_SYSTEM_REQUESTS_BASKET_ADJUSTMENT

BasketAdjustmentRequest

LoyaltyReceivedEvent

CP_SYSTEM_REQUESTS_LOYALTY

LoyaltyRequest

AmountAdjustedEvent

CP_SYSTEM_REQUESTS_AMOUNT_ADJUSTMENT

AmountAdjustmentRequest

None

CP_SYSTEM_REQUESTS_PAYMENT_AMOUNT_ADJUSTMENT

PaymentAmountAdjustmentRequest

The first 3 require “adjudication”, that is, the POS can decide to accept or reject any of the modifications performed during these triggers. This can be done either automatically or by presenting an appropriate UI to the cashier, depending on the preference of the POS.

To accept or reject the modifications, get a Response object using the generateResponse() method, then set the accepted modifications on the response object. Any modifications not set on the response will be rejected. Send back the response using TransactionManager.sendEventResponse(response).

 @startuml
 !include uml_styles.iuml
 hide footbox

 actor Cashier as cashier order 10
 participant POS as pos order 20
 participant "Payment App" as pay order 30
 participant "CP App" as cp order 40
 |||
 cashier <--> pos: Possibly select payment type\nor somehow indicate the basket is ready
 pos ->> pay: Finalize basket
 pay <<->> cp: Resolve Basket Adjustment
 group If CP Apps make any changes
     pos <<- pay: Basket Adjusted Event
     cashier <-> pos: Optionally allow cashier to decide
     pos -> pos: Apply changes and/or\nexplicitly leave out items
     pos ->> pay : Send event response.
     |||
 end
 pos <<- pay: Basket Event with type BasketAction.FINALIZED
 cashier <--> pos: Possibly select payment type\nor somehow indicate payment is ready,\nor skip straight to start payment
 pos ->> pay: Start payment
 pay <<->> cp: Resolve Loyalty Adjustment
 group If CP Apps make any changes
     pos <<- pay: Loyalty Adjusted Event
     cashier <-> pos: Optionally allow cashier to decide
     pos -> pos: Apply changes and/or\nexplicitly leave out items
     pos ->> pay : Send event response.
     |||
 end
 pay <<->> cp: Resolve Amount Adjustment
 group If CP Apps make any changes
     pos <<- pay: Loyalty Adjusted Event
     cashier <-> pos: Optionally allow cashier to decide
     pos -> pos: Apply changes and/or\nexplicitly leave out items
     pos ->> pay : Send event response.
     |||
 end
 pay <<->> cp: Resolve Payment Amount Adjustment
 pos <<- pay: Payment Completed Event
 |||
 @enduml

Note

The CP_SYSTEM_REQUESTS_PAYMENT_AMOUNT_ADJUSTMENT, is included in the payment response, since it cannot be rejected by the POS.

BasketAdjustedEvent

Sent when a CP App decides that the cart is eligible for a specific deal or interacts with the customer to collect a donation amount. The CP App adds the offer(s) or donation(s) which are then sent back to the POS after the Payment App collects them from all of the CP Apps.

Use BasketAdjustedEvent.getAdjustments() to retrieve the adjustments requested by the CP App(s). Iterate through the Offer(s) and Donation(s) in the BasketAdjustment, either removing them from the current object or adding them to a separate new BasketAdjustment. In either case, get the appropriate BasketAdjustedEvent.Response from BasketAdjustedEvent.generateResponse(), and Response.setFinalAdjustments() to identify the accepted adjustments and the new total amount for the payment.

If no adjustments are accepted, simply send back the BasketAdjustedEvent.Response without setting the final adjustments.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@Override // Overridden from the CommerceListenerAdapter
public void handlBasketAdjustedEvent(
        BasketAdjustedEvent event) {
    if (BasketAdjustedEvent.TYPE.equals(event.getType())) {
        BasketAdjustment adjustments = event.getAdjustments();
        // Keep this response object to fill in with the accepted adjustments.
        BasketAdjustedEventResponse response =
              event.generateBasketEventResponse();
        // Either decide directly which adjustments are accepted, or allow
        // the cashier to review and decide.
    }
}

// Sometime later, send back the response after selecting the accepted adjustments.
response.setFinalAdjustments(adjustments, amountTotals);
transactionManager.sendEventResponse(
    BasketAdjustedEventResponse.asCommerceResponse(response));
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// Overridden from the CommerceListenerAdapter
override fun handleBasketAdjustedEvent(event: BasketAdjustedEvent) {
    if (BasketAdjustedEvent.TYPE.equals(event.getType())) {
        val adjustments = event.getAdjustments()
        // Keep this response object to fill in with the accepted adjustments.
        val response = basketAdjustedEvent.generateBasketEventResponse()
        // Either decide directly which adjustments are accepted, or allow
        // the cashier to review and decide.
    }
}

// Sometime later, send back the response after selecting the accepted adjustments.
response.setFinalAdjustments(adjustments, amountTotals)
transactionManager.sendEventResponse(
    BasketAdjustedEventResponse.asCommerceResponse(response))
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// Overridden from the VFICommerceListenerAdapter
override func handle(_ event: VFIBasketAdjustedEvent?) {
    if (VFIBasketAdjustedEventTYPE == event?.getType()) {
        adjustments = event?.getAdjustments()
        // Keep this response object to fill in with the accepted adjustments.
        response = event?.generateResponse()
        // Either decide directly which adjustments are accepted, or allow
        // the cashier to review and decide.
    }
}

// Sometime later, send back the response after selecting the accepted adjustments.
response?.setFinalAdjustments(adjustments, amountTotals: amountTotals)
transactionManager?.sendEventResponse(
    VFIBasketAdjustedEventResponse.asCommerceResponse(response))
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
private void HandleBasketAdjustedvent(BasketAdjustedEvent event)
{
    if (event.Type.Value == BasketAdjustedEvent.TYPE)
    {
        var adjustments = event.Adjustment;
        var response = event.GenerateBasketAdjustedEventResponse();
        // Based on requestParameters.getInputType(), present the appropriate dialog.
        // Keep track of the response, it is needed once the input is collected.
    }
}

// Sometime later, send back the response after selecting the accepted adjustments.
response.SetFinalAdjustments(adjustments, amount_totals);
CommerceResponse commerceResponse = response as CommerceResponse;
payment_sdk_.TransactionManager.SendEventResponse(commerceResponse);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// Overridden from the CommerceListenerAdapter
void handleBasketAdjustedEvents(
    const std::shared_ptr<verifone_sdk::BasketAdjustedEvents>& event) override {
  if (event->getType() == BasketAdjustedEvents::TYPE) {
    auto basket_adjustments = event->getAdjustments();
    auto response = event->generateBasketAdjustedEventResponse();
    // Based on requestParameters.getInputType(), present the appropriate dialog.
    // Keep track of the response, it is needed once the input is collected.
  }
}

// Sometime later, send back the response after selecting the accepted adjustments.
response->setFinalAdjustments(adjustments, amount_totals);
psdk->getTransactionManager()->sendEventResponse(
    std::dynamic_pointer_cast<verifone_sdk::CommerceResponse>(response));
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
private void HandleBasketAdjustedvent(BasketAdjustedEvent sdk_event)
{
    if (sdk_event.Type == BasketAdjustedEvent.EVENT_TYPE)
    {
        var adjustments = sdk_event.Adjustments;
        var response = sdk_event.GenerateBasketAdjustedEventResponse();
        // Based on requestParameters.getInputType(), present the appropriate dialog.
        // Keep track of the response, it is needed once the input is collected.
    }
}

// Sometime later, send back the response after selecting the accepted adjustments.
response.SetFinalAdjustments(adjustments, amount_totals);
CommerceResponse commerceResponse = BasketAdjustedEventResponse.AsCommerceResponse(response);
mw.Dispatcher.Invoke(() => { mw.payment_sdk_.TransactionManager.SendEventResponse(commerceResponse); });

LoyaltyReceivedEvent

This is sent when a CP App collects loyalty information about the customer, and finds that they are eligible for a reward of some kind. The CP App adds the Offer that they are eligible for, which is communicated back to the POS to decide if it can be applied to the current order or not.

Use LoyaltyReceivedEvent.getLoyaltyAdjustments() to get each adjustment grouped by CP App, or use LoyaltyReceivedEvent.getLoyaltyOffersList() to get all of the adjustments aggregated in a single list, without regard to the CP App. Get the appropriate LoyaltyReceivedEvent.Response from LoyaltyReceivedEvent.generateResponse() and LoyaltyReceivedEvent.Response.setLoyaltyOffers(offers) to identify the accepted offers.

Update the final amount using Payment.setRequestedAmountTotals() on either the original payment object or the one returned from LoyaltyReceivedEvent.Response.getResponsePayment(). If using a payment object other than the one returned from the response, set this payment on the response using LoyaltyReceivedEvent.Response.updatePayment(payment).

Finally, it is optional to use LoyaltyReceivedEvent.Response.selectLoyaltyIdentifier(identifier) to select which loyalty ought to be associated with the current Transaction. This can allow the system to auto-populate the email address and/or phone number if receiving a receipt using either of these, or for some other prompts that may require this information from the user.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Override // Overridden from the CommerceListenerAdapter
public void handlLoyaltyReceivedEvent(
        LoyaltyReceivedEvent event) {
    if (LoyaltyReceivedEvent.TYPE.equals(event.getType())) {
        // Offers per loyalty app
        LoyaltyAdjustment[] loyaltyAdjustments = event.getLoyaltyAdjustments();
        // Aggregated offers
        ArrayList<Offer> offers = event.getLoyaltyOffersList();
        // Keep this response object to fill in with the accepted adjustments.
        LoyaltyReceivedEventResponse response = event.generateResponse();
        // Either decide directly which offers are accepted, or allow
        // the cashier to review and decide.
    }
}

// Sometime later, send back the response after selecting the accepted offers.
response.setLoyaltyOffers(offers);
Payment payment = response.getResponsePayment();
// update amount totals to changedAmountTotals based on accepted offers
payment.setRequestedAmountTotals(changedAmountTotals);
transactionManager.sendEventResponse(
        LoyaltyReceivedEventResponse.asCommerceResponse(response));
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// Overridden from the CommerceListenerAdapter
override fun handleLoyaltyReceivedEvent(event: LoyaltyReceivedEvent) {
    if (LoyaltyReceivedEvent.TYPE.equals(event.getType())) {
        // Offers per loyalty app
        val loyaltyAdjustments = event.getLoyaltyAdjustments()
        // Aggregated offers
        val offers = event.getLoyaltyOffersList()
        // Keep this response object to fill in with the accepted adjustments.
        val response = event.generateLoyaltyReceivedEventResponse()
        // Either decide directly which offers are accepted, or allow
        // the cashier to review and decide.
    }
}

// Sometime later, send back the response after selecting the accepted offers.
response.setLoyaltyOffers(offers)
val payment = response.getResponsePayment()
// update amount totals to changedAmountTotals based on accepted offers
payment.setRequestedAmountTotals(changedAmountTotals)
transactionManager.sendEventResponse(
        LoyaltyReceivedEventResponse.asCommerceResponse(response))
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var loyaltyAdjustments: [VFILoyaltyAdjustment]?
var offers: [VFIOffer]?
var response: VFILoyaltyReceivedEventResponse?
var payment: VFIPayment?
var transactionManager: VFITransactionManager?

// Overridden from the VFICommerceListenerAdapter
override func handle(_ event: VFILoyaltyReceivedEvent?) {
    if (VFILoyaltyReceivedEventTYPE == event?.getType()) {
    // Offers per loyalty app
    loyaltyAdjustments = event?.getLoyaltyAdjustments()
    // Aggregated offers
    offers = event?.getLoyaltyOffersList()
    // Keep this response object to fill in with the accepted adjustments.
    response = event?.generateResponse()
    // Either decide directly which offers are accepted, or allow
    // the cashier to review and decide.
}

// Sometime later, send back the response after selecting the accepted offers.
response?.setLoyaltyOffers(offers)
payment = response?.getResponsePayment()
// update amount totals to changedAmountTotals based on accepted offers
payment.setRequestedAmountTotals(changedAmountTotals)
transactionManager?.sendEventResponse(
    VFILoyaltyReceivedEventResponse.asCommerceResponse(response))
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
private readonly EventHandler<LoyaltyReceivedEvent> loyalty_event_handler_;

private void HandleEvent(LoyaltyReceivedEvent event)
{
    if (event.Type.Value == LoyaltyReceivedEvent.EVENT_TYPE)
    {
        // Offers per loyalty app
        Array<LoyaltyAdjustments> loyalty_adjustments = event.GetLoyaltyAdjustments();
        // Aggregated offers
        ArrayList<Offer> offers = event.GetLoyaltyOffers();
        // Keep this response object to fill in with the accepted adjustments.
        LoyaltyReceivedEventResponse response = event.GenerateLoyaltyReceivedEventResponse();
        // Either decide directly which offers are accepted, or allow
        // the cashier to review and decide.
    }
}

// ...
// Link event handler after initialization is complete
loyalty_event_handler_ = async (sender, args) =>
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
        () => HandleEvent(args));
};
payment_sdk_.TransactionManager.HandleLoyaltyReceivedEvent += loyalty_event_handler_;

// ...
// Send back the response after selecting the accepted offers.
response.SetLoyaltyOffers(offers);
Payment payment = response.ResponsePayment();
// update amount totals to changed_amount_totals based on accepted offers
payment.RequestedAmounts(changed_amount_totals);
CommerceResponse commerceResponse = response as CommerceResponse;
payment_sdk_.TransactionManager.SendEventResponse(commerceResponse);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// Overridden from the CommerceListenerAdapter
void handleLoyaltyReceivedEvent(
    const std::shared_ptr<verifone_sdk::LoyaltyReceivedEvent>& event) override {
  if (event->getType() == LoyaltyReceivedEvent::TYPE) {
    // Offers per loyalty app
    auto loyalty_adjustments = event->getLoyaltyAdjustments();
    // Aggregated offers
    auto offers = event->getLoyaltyOffersList();
    // Keep this response object to fill in with the accepted adjustments.
    auto response = event->generateLoyaltyReceivedEventResponse();
    // Either decide directly which offers are accepted, or allow
    // the cashier to review and decide.
  }
}

// Sometime later, send back the response after selecting the accepted offers.
response->setLoyaltyOffers(offers);
auto payment = response->getResponsePayment();
payment->setRequestedAmountTotals(changed_amount_totals);
psdk->getTransactionManager()->sendEventResponse(
    std::dynamic_pointer_cast<verifone_sdk::CommerceResponse>(response));
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void HandleLoyaltyReceivedEvent(LoyaltyReceivedEvent sdk_event)
{
    if (sdk_event.Type == LoyaltyReceivedEvent.EVENT_TYPE)
    {
        // Offers per loyalty app
        IList<VerifoneSdk.LoyaltyAdjustment> loyalty_adjustments = sdk_event.LoyaltyAdjustments;
        // Aggregated offers
        IList<Offer> offers = sdk_event.LoyaltyOffersList;
        // Keep this response object to fill in with the accepted adjustments.
        LoyaltyReceivedEventResponse response = sdk_event.GenerateLoyaltyReceivedEventResponse();
        // Either decide directly which offers are accepted, or allow
        // the cashier to review and decide.
    }
}

// ...
// Send back the response after selecting the accepted offers.
response.SetLoyaltyOffers(offers);
response.LoyaltyOffers = offers;
Payment payment = response.ResponsePayment;
// update amount totals to changed_amount_totals based on accepted offers
payment.RequestedAmounts = changed_amount_totals;
CommerceResponse commerceResponse = LoyaltyReceivedEventResponse.AsCommerceResponse(response);
mw.Dispatcher.Invoke(() => { mw.payment_sdk_.TransactionManager.SendEventResponse(commerceResponse); });

Note

Use LoyaltyReceivedEvent.getLoyaltyAdjustments() to get more information about the customer, including the LoyaltyIdentifier information that is being used for the specific loyalty program. This information can streamline further interactions with the customer.

AmountAdjustedEvent

This is sent to allow CP Apps to change the amount, generally based on the payment type such as the card bin range, or based on some interaction with the customer. These can increase or decrease the overall amount, depending on the value in the AmountAdjustment, and it can be either determined by a percentage AmountAdjustment.getAdjustmentPercentage() or by a fixed amount AmountAdjustment.getAdjustmentValue(), depending on which is set.

Use AmountAdjustedEvent.getAdjustments() to get the proposed changes, and AmountAdjustedEvent.generateResponse() to get the AmountAdjustedEvent.Response. Iterate through the AmountAdjustment(s), and add the accepted adjustment(s) to the response using AmountAdjustedEvent.Response.setAdjustments(adjustments).

Update the final amount using Payment.setRequestedAmountTotals(updatedTotals) on either the original payment object or the one returned from AmountAdjustedEvent.Response.getPayment(). If using a payment object other than the one returned from the response, set this payment on the response using AmountAdjustedEvent.Response.updatePayment(payment).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
@Override // Overridden from the CommerceListenerAdapter
public void handlAmountAdjustedEvent(
        AmountAdjustedEvent event) {
    if (AmountAdjustedEvent.TYPE.equals(event.getType())) {
        // Offers per loyalty app
        AmountAdjustment[] amountAdjustments = event.getAdjustments();
        // Keep this response object to fill in with the accepted adjustments.
        AmountAdjustedEventResponse response =
                event.generateAmountAdjustedEventResponse();
        // Either decide directly which offers are accepted, or allow
        // the cashier to review and decide.
    }
}

// Sometime later, send back the response after selecting the accepted offers.
response.setAdjustments(amountAdjustments);
Payment payment = response.getPayment();
payment.setRequestedAmountTotals(changedAmountTotals);
transactionManager.sendEventResponse(
        AmountAdjustedEventResponse.asCommerceResponse(response));
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// Overridden from the CommerceListenerAdapter
override fun handleAmountAdjustedEvent(event: AmountAdjustedEvent) {
    if (AmountAdjustedEvent.TYPE.equals(event.getType())) {
        // Offers per loyalty app
        val amountAdjustments = event.getAdjustments()
        // Keep this response object to fill in with the accepted adjustments.
        val response = event.generateAmountAdjustedEventResponse()
        // Either decide directly which offers are accepted, or allow
        // the cashier to review and decide.
    }
}

// Sometime later, send back the response after selecting the accepted offers.
response.setAdjustments(amountAdjustments)
val payment = response.getPayment()
payment.setRequestedAmountTotals(changedAmountTotals)
transactionManager.sendEventResponse(
        AmountAdjustedEventResponse.asCommerceResponse(response))
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var amountAdjustments: [VFIAmountAdjustment]?
var response: VFIAmountAdjustedEventResponse?
var payment: VFIPayment?
var transactionManager: VFITransactionManager?
var changedAmountTotals: VFIAmountTotals?

// Overridden from the VFICommerceListenerAdapter
override func handle(_ event: VFIAmountAdjustedEvent?) {
    if (event?.getType() == VFIAmountAdjustedEventTYPE) {
        // Offers per loyalty app
        amountAdjustments = event?.getAdjustments()
        // Keep this response object to fill in with the accepted adjustments.
        response = event?.generateResponse()
        // Either decide directly which offers are accepted, or allow
        // the cashier to review and decide.
    }
}

// Sometime later, send back the response after selecting the accepted offers.
response.setAdjustments(amountAdjustments)
payment = response.getPayment()
payment?.setRequestedAmounts(changedAmountTotals)
transactionManager?.sendEventResponse(
    VFIAmountAdjustedEventResponse.asCommerceResponse(response))
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
private void HandleAmountAdjustedEvent(AmountAdjustedEvent event)
{
    if (event.Type.Value == AmountAdjustedEvent.EVENT_TYPE)
    {
        // Offers per loyalty app
        Array<LoyaltyAdjustments> amount_adjustments = event.GetAdjustments();
        // Keep this response object to fill in with the accepted adjustments.
        AmountAdjustedEventResponse response =
                event.GenerateAmountAdjustedEventResponse();
        // Either decide directly which offers are accepted, or allow
        // the cashier to review and decide.
    }
}

// Sometime later, send back the response after selecting the accepted offers.
response.SetAdjustments(amount_adjustments);
response.Payment.RequestedAmounts(changed_amount_totals);
CommerceResponse commerceResponse = response as CommerceResponse;
payment_sdk_.TransactionManager.SendEventResponse(commerceResponse);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// Overridden from the CommerceListenerAdapter
void handleAmountAdjustedEvent(
    -const std::shared_ptr<verifone_sdk::AmountAdjustedEvent>& event) override {
  if (event->getType() == AmountAdjustedEvent::TYPE) {
    // Offers per loyalty app
    auto amount_adjustments = event->getAdjustments();
    // Keep this response object to fill in with the accepted adjustments.
    auto response = event->generateAmountAdjustedEventRepsonse;
    // Either decide directly which offers are accepted, or allow
    // the cashier to review and decide.
  }
}
  ...

// Sometime later, send back the response after selecting the accepted offers.
response->setAdjustments(amount_adjustments);
auto payment = response->getPayment();
payment->setRequestedAmountTotals(changed_amount_totals);
psdk->getTransactionManager()->sendEventResponse(std::dynamic_pointer_cast
    <verifone_sdk::CommerceResponse>(response));
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
private void HandleAmountAdjustedEvent(AmountAdjustedEvent sdk_event)
{
    if (sdk_event.Type == AmountAdjustedEvent.EVENT_TYPE)
    {
        // Offers per loyalty app
        IList<AmountAdjustment> amount_adjustments = sdk_event.Adjustments;
        // Keep this response object to fill in with the accepted adjustments.
        AmountAdjustedEventResponse response =
                sdk_event.GenerateAmountAdjustedEventResponse();
        // Either decide directly which offers are accepted, or allow
        // the cashier to review and decide.
    }
}

// Sometime later, send back the response after selecting the accepted offers.
response.Adjustments(amount_adjustments);
response.Payment.RequestedAmounts = changed_amount_totals;
CommerceResponse commerceResponse = AmountAdjustedEventResponse.AsCommerceResponse(response);
mw.Dispatcher.Invoke(() => { mw.payment_sdk_.TransactionManager.SendEventResponse(commerceResponse); });