Performing Other Transactions

Linking Payments

Many of the payment flows require a following payment to be linked to a previous payment. Each completed payment contains a string returned from Payment.getAppSpecificData(). This string is used internally by the Payment Service, providing a convenient, global way to link payments together across different hosts and processor integrations. Configure the app-specific data on a payment object either by creating an empty payment object and setting this field using Payment.setAppSpecificData(String) or using the Payment(String) constructor that accepts the app specific data.


Perform a Pre-authorization Operations

The TransactionType.PAYMENT is the default setting when Payment is created. For initializing pre-authorization. set it to TransactionType.PRE_AUTHORIZATION prior to calling TransactionManager.startPayment(). Once pre-authorization is approved, for any follow on pre-authorization transactions, either pass the original Payment object from the pre-auth or configure a new payment object with the app-specific data from the original pre-auth, and update the the requested amounts if it is applicable. On approved pre-authorizations, for any Increment, Decrement, Extend, Reauth, set the transaction type to TransactionType.PRE_AUTHORIZATION_UPDATE and call TransactionManager.startPayment(), for Cancel or Partial Cancel, call TransactionManager.processVoid(). For completing the pre-authorization transaction, set the transaction type to TransactionType.PRE_AUTHORIZATION_COMPLETION and call TransactionManager.startPayment().

Pre-auth initialization

1
2
3
4
5
6
7
8
// A session must be open to perform a payment

// Create the payment and set the transaction type to pre-authorization.
Payment payment = Payment.create();
payment.setTransactionType(TransactionType.PRE_AUTHORIZATION);
// Configure amounts and other payment properties before starting.
transactionManager.startPayment(payment);
// Listener receives the PaymentCompletedEvent.
1
2
3
4
5
6
7
8
// A session must be open to perform a payment

// Create the payment and set the transaction type to pre-authorization.
val payment = Payment.create()
payment.setTransactionType(TransactionType.PRE_AUTHORIZATION)
// Configure amounts and other payment properties before starting.
transactionManager.startPayment(payment)
// Listener receives the PaymentCompletedEvent.
1
2
3
4
5
6
7
8
// A session must be open to perform a payment

// Create the payment and set the transaction type to pre-authorization.
var payment = VFIPayment.create()
payment?.setTransactionType(transactionType: VFITransactionType.PREAUTHORIZATION)
// Configure amounts and other payment properties before starting.
sdk.getTransactionManager()?.startPayment(payment)
// Listener receives the PaymentCompletedEvent.
1
2
3
4
5
6
7
8
// A session must be open to perform a payment

// Create the payment and set the transaction type to pre-authorization.
Payment payment = new Payment();
payment.TransactionType(TransactionType.PRE_AUTHORIZATION);
// Configure amounts and other payment properties before starting.
transactionManager.StartPayment(payment);
// Listener receives the PaymentCompletedEvent.
1
2
3
4
5
6
7
8
// A session must be open to perform a payment

// Create the payment and set the transaction type to pre-authorization.
auto payment = Payment::create();
payment->setTransactionType(TransactionType::PRE_AUTHORIZATION);
// Configure amounts and other payment properties before starting.
transaction_manager->startPayment(payment);
// Listener receives the PaymentCompletedEvent.
1
2
3
4
5
6
7
8
// A session must be open to perform a payment

// Create the payment and set the transaction type to pre-authorization.
var payment = Payment.Create();
payment.TransactionType = TransactionType.PRE_AUTHORIZATION;
// Configure amounts and other payment properties before starting.
transactionManager.StartPayment(payment);
// Listener receives the PaymentCompletedEvent.

Pre-auth update

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// A session must be open to perform a payment

// Create the payment.
Payment payment = Payment.create();
// For pre-auth operations of increment, decrement, extend, reauth,set the
// transaction type to pre-authorization update and configure the requested amounts
// if it is applicable.
payment.setTransactionType(TransactionType.PRE_AUTHORIZATION_UPDATE);
payment.setRequestedAmounts(changedAmountTotals);
// Set the AppSpecificData from the approved pre-authorization here to link the two.
payment.setAppSpecificData(dataFromPreAuth);
// Configure the other payment properties before starting.
transactionManager.startPayment(payment);
// Listener receives the PaymentCompletedEvent.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// A session must be open to perform a payment

// Create the payment.
val payment = Payment.create()
// For pre-auth operations of increment, decrement, extend, reauth,set the
// transaction type to pre-authorization update and configure the requested amounts
// if it is applicable.
payment.setTransactionType(TransactionType.PRE_AUTHORIZATION_UPDATE)
payment.setRequestedAmounts(changedAmountTotals)
// Set the AppSpecificData from the approved pre-authorization here to link the two.
payment.setAppSpecificData(dataFromPreAuth)
// Configure the other payment properties before starting.
transactionManager.startPayment(payment)
// Listener receives the PaymentCompletedEvent.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// A session must be open to perform a payment

// Create the payment.
var payment = VFIPayment.create()
// For pre-auth operations of increment, decrement, extend, reauth,set the
// transaction type to pre-authorization update and configure the requested amounts
// if it is applicable.
payment?.setTransactionType(transactionType: VFITransactionType.PREAUTHORIZATIONUPDATE)

payment?.setRequestedAmounts(changedAmountTotals)
// Set the AppSpecificData from the approved pre-authorization here to link the two.
payment.setAppSpecificData(dataFromPreAuth)
// Configure the other payment properties before starting.
sdk.getTransactionManager()?.startPayment(payment)
// Listener receives the PaymentCompletedEvent.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// A session must be open to perform a payment

// Create the payment.
Payment payment = new Payment();
// For pre-auth operations of increment, decrement, extend, reauth,set the
// transaction type to pre-authorization update and configure the requested amounts
// if it is applicable.
payment.TransactionType(TransactionType.PRE_AUTHORIZATION_UPDATE);
payment.setRequestedAmounts(changedAmountTotals);
// Set the AppSpecificData from the approved pre-authorization here to link the two.
payment.AppSpecificData(dataFromPreAuth);
// Configure the other payment properties before starting.
payment_sdk_.TransactionManager.StartPayment(payment);
// Listener receives the PaymentCompletedEvent.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// A session must be open to perform a payment

// Create the payment.
auto payment = Payment::create();
// For pre-auth operations of increment, decrement, extend, reauth,set the
// transaction type to pre-authorization update and configure the requested amounts
// if it is applicable.
payment->setTransactionType(TransactionType::PRE_AUTHORIZATION_UPDATE);
payment->setRequestedAmounts(changedAmountTotals);
// Set the AppSpecificData from the approved pre-authorization here to link the two.
payment->setAppSpecificData(dataFromPreAuth);
// Configure amounts and other payment properties before starting.
psdk->getTransactionManager()->startPayment(payment);
// Listener receives the PaymentCompletedEvent.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// A session must be open to perform a payment

// Create the payment.
var payment = Payment.Create();
// For pre-auth operations of increment, decrement, extend, reauth,set the
// transaction type to pre-authorization update and configure the requested amounts
// if it is applicable.
payment.TransactionType = TransactionType.PRE_AUTHORIZATION_UPDATE;
payment.RequestedAmounts = changedAmountTotals;
// Set the AppSpecificData from the approved pre-authorization here to link the two.
payment.AppSpecificData = dataFromPreAuth;
// Configure the other payment properties before starting.
payment_sdk_.TransactionManager.StartPayment(payment);
// Listener receives the PaymentCompletedEvent.

Pre-auth cancel

1
2
3
4
5
6
7
8
9
// A session must be open to perform a payment

// Create the payment.
Payment payment = Payment.create();
// Set the AppSpecificData from the approved pre-authorization here to link the two.
payment.setAppSpecificData(dataFromPreAuth);
// Configure amounts and other payment properties before starting.
transactionManager.processVoid(payment);
// Listener receives the PaymentCompletedEvent.
1
2
3
4
5
6
7
8
9
// A session must be open to perform a payment

// Create the payment.
val payment = Payment.create()
// Set the AppSpecificData from the approved pre-authorization here to link the two.
payment.setAppSpecificData(dataFromPreAuth)
// Configure amounts and other payment properties before starting.
transactionManager.processVoid(payment)
// Listener receives the PaymentCompletedEvent.
1
2
3
4
5
6
7
8
9
// A session must be open to perform a payment

// Create the payment.
var payment = VFIPayment.create()
// Set the AppSpecificData from the approved pre-authorization here to link the two.
payment.setAppSpecificData(dataFromPreAuth)
// Configure amounts and other payment properties before starting.
sdk.getTransactionManager()?.processVoid(payment)
// Listener receives the PaymentCompletedEvent.
1
2
3
4
5
6
7
8
9
// A session must be open to perform a payment

// Create the payment.
Payment payment = new Payment();
// Set the AppSpecificData from the approved pre-authorization here to link the two.
payment.AppSpecificData(dataFromPreAuth);
// Configure amounts and other payment properties before starting.
payment_sdk_.TransactionManager.ProcessVoid(payment);
// Listener receives the PaymentCompletedEvent.
1
2
3
4
5
6
7
8
9
// A session must be open to perform a payment

// Create the payment.
auto payment = Payment::create();
// Set the AppSpecificData from the approved pre-authorization here to link the two.
payment->setAppSpecificData(dataFromPreAuth);
// Configure amounts and other payment properties before starting.
psdk->getTransactionManager()->processVoid(payment);
// Listener receives the PaymentCompletedEvent.
1
2
3
4
5
6
7
8
9
// A session must be open to perform a payment

// Create the payment.
var payment = Payment.Create();
// Set the AppSpecificData from the approved pre-authorization here to link the two.
payment.AppSpecificData = dataFromPreAuth;
// Configure amounts and other payment properties before starting.
payment_sdk_.TransactionManager.ProcessVoid(payment);
// Listener receives the PaymentCompletedEvent.

Pre-auth completion

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// A session must be open to perform a payment

// Create the payment and set the transaction type to pre-authorization completion.
Payment payment = Payment.create();
payment.setTransactionType(TransactionType.PRE_AUTHORIZATION_COMPLETION);
// Set the AppSpecificData from the approved pre-authorization here to link the two.
payment.setAppSpecificData(dataFromPreAuth);
// Configure amounts and other payment properties before starting.
transactionManager.startPayment(payment);
// Listener receives the PaymentCompletedEvent.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// A session must be open to perform a payment

// Create the payment and set the transaction type to pre-authorization completion.
val payment = Payment.create()
payment.setTransactionType(TransactionType.PRE_AUTHORIZATION_COMPLETION)
// Set the AppSpecificData from the approved pre-authorization here to link the two.
payment.setAppSpecificData(dataFromPreAuth)
// Configure amounts and other payment properties before starting.
transactionManager.startPayment(payment)
// Listener receives the PaymentCompletedEvent.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// A session must be open to perform a payment

// Create the payment and set the transaction type to pre-authorization completion.
var payment = VFIPayment.create()
payment?.setTransactionType(transactionType: VFITransactionType.PREAUTHORIZATIONCOMPLETION)
// Set the AppSpecificData from the approved pre-authorization here to link the two.
payment.setAppSpecificData(dataFromPreAuth)
// Configure amounts and other payment properties before starting.
sdk.getTransactionManager()?.startPayment(payment)
// Listener receives the PaymentCompletedEvent.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// A session must be open to perform a payment

// Create the payment and set the transaction type to pre-authorization completion.
Payment payment = new Payment();
payment.TransactionType(TransactionType.PRE_AUTHORIZATION_COMPLETION);
// Set the AppSpecificData from the approved pre-authorization here to link the two.
payment.AppSpecificData(dataFromPreAuth);
// Configure amounts and other payment properties before starting.
payment_sdk_.TransactionManager.StartPayment(payment);
// Listener receives the PaymentCompletedEvent.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// A session must be open to perform a payment

// Create the payment and set the transaction type to pre-authorization completion.
auto payment = Payment::create();
payment->setTransactionType(TransactionType::PRE_AUTHORIZATION_COMPLETION);
// Set the AppSpecificData from the approved pre-authorization here to link the two.
payment->setAppSpecificData(dataFromPreAuth);
// Configure amounts and other payment properties before starting.
psdk->getTransactionManager()->startPayment(payment);
// Listener receives the PaymentCompletedEvent.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// A session must be open to perform a payment

// Create the payment and set the transaction type to pre-authorization completion.
var payment = Payment.Create();
payment.TransactionType = TransactionType.PRE_AUTHORIZATION_COMPLETION;
// Set the AppSpecificData from the approved pre-authorization here to link the two.
payment.AppSpecificData = dataFromPreAuth;
// Configure amounts and other payment properties before starting.
payment_sdk_.TransactionManager.StartPayment(payment);
// Listener receives the PaymentCompletedEvent.

Getting pre-auth operations history

Payment.getOperationsHistory() get the history of operations performed on a payment including pre-authorizations.It returns the list of operations that have been performed on the current payment. If null then the operations history is unknown. If an empty list, then there is no history.

Linking Pre-Auth and Pre-Auth Completion

After configuring the Transaction.PRE_AUTHORIZATION_COMPLETION_TYPE on the transaction object, either pass the original Payment object from the pre-auth or configure a new payment object with the app-specific data from the original pre-auth. The requested amounts can be updated on the payment object to reflect any changes, such as gratuity, or left empty to simply complete the original amount that was authorized. See Setup and Start a Payment for more information on updating the requested amounts.

 @startuml
 !include uml_styles.iuml
 hide footbox

 participant POS order 10
 participant POI order 20
 |||
 POS <- POI: Receives PaymentCompletedEvent for PRE_AUTHORIZATION_TYPE
 POS <- POS: Stores App Specific Data from Payment.
 ...Collects tip information from customer...
 POS -> POS: Configure new payment object with the App Specific\nData and the new requested AmountTotals.
 POS -> POI: Configure PRE_AUTHORIZATION_COMPLETION_TYPE transaction
 note right
   Configuring the payment
   and transaction can be
   done in any order.
 end note
 POS ->> POI: transactionManager.startPayment(payment)
 ...POI collects card information and gets approval from host...
 POS <- POI: Receive PaymentCompletedEvent for the pre-auth completion
 |||
 @enduml


Perform a Void / Reversal

A void is used to remove a payment from the batch before it is settled, stopping the funds from transferring to the merchant from the customer. This generally results in fewer fees for the merchant, and is a better choice than refunds when possible. This can appear the same as a refund to the cashier, while the difference between the call is handled “behind the scenes.”

After the batch is settled, or if the POS does not keep track of the settled state, use refunds to return the funds to the customer (see perform-refund).

Note

  • The payment that is to be voided is referred to as the “original payment”.

  • Void and reversal are the same from the perspective of the SDK.

 @startuml
 !include uml_styles.iuml
 hide footbox

 participant POS order 20
 participant POI order 30
 |||
 POS ->> POI: Start session
 POS <<- POI: Session started
 POS ->> POI: Process Void
 POS <<- POI: PaymentCompletedEvent
 POS -> POS: Handle Void event
 POS ->> POI: End session
 POS <<- POI: Session ended
 |||
 @enduml

Starting a Void

Voids are performed using the original completed payment. The payment object can be the object as received in the PaymentCompletedEvent or can be a new Payment object using the App Specific Data from the original completed payment.

1
2
3
4
5
6
7
// A session must be open

// Construct a Payment with the AppSpecificData from the original
// completed payment.
Payment payment = Payment.create();
payment.setAppSpecificData(completedAppSpecificData);
transactionManager.processVoid(payment);
1
2
3
4
5
6
7
// A session must be open

// Construct a Payment with the AppSpecificData from the original
// completed payment.
val payment = Payment.create()
payment.setAppSpecificData(completedAppSpecificData)
transactionManager.processVoid(payment)
1
2
3
4
5
6
7
// A session must be open

// Construct a Payment with the AppSpecificData from the original
// completed payment.
var payment = VFIPayment.create()
payment?.setAppSpecificData(completedAppSpecificData)
sdk.getTransactionManager()?.processVoid(payment)
1
2
3
4
5
6
// A session must be open

// Construct a Payment with the AppSpecificData from the original
// completed payment.
Payment payment = new Payment { AppSpecificData = completed_app_specific_data };
psdk.TransactionManager.processVoid(payment);
1
2
3
4
5
6
7
// A session must be open

// Construct a Payment with the AppSpecificData from the original
// completed payment.
auto payment = Payment::create();
payment->setAppSpecificData(completed_app_specific_data);
transaction_manager->processVoid(payment);
1
2
3
4
5
6
7
// A session must be open

// Construct a Payment with the AppSpecificData from the original
// completed payment.
Payment payment = Payment.Create();
AppSpecificData = completed_app_specific_data;
psdk.TransactionManager.processVoid(payment);

See also

Linking Payments

Handling the Void Event

The CommerceListener receives a PaymentCompletedEvent with type TransactionEvent.TRANSACTION_ENDED once the Void is complete. Retrieve the new Payment object from the event (PaymentCompletedEvent.getPayment()), then examine the the auth result of the payment (Payment.getAuthResult()) to see if it is AuthorizationResult.VOIDED, AuthorizationResult.VOID_DECLINED, AuthorizationResult.USER_CANCELLED, or AuthorizationResult.CANCELLED_EXTERNALLY. The payment IDs such as Payment.getReferencePaymentId() and Payment.getReferenceLocalPaymentId() will match the corresponding values of the original payment, i.e., the new Void payment’s Reference Payment ID will match the original payment’s Payment ID, and the new Void payment’s Reference Local Payment ID will match the original payment’s Local Payment ID.

As with all events, the status will be non-zero for failures, and zero for success.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@Override // Overridden from the CommerceListenerAdapter
public void handlePaymentCompletedEvent(PaymentCompletedEvent event) {
    if (TransactionEvent.TRANSACTION_ENDED.equals(event.getType())) {
        if (event.getStatus() == StatusCode.SUCCESS) {
            Payment payment = event.getPayment();
            AuthorizationResult authorizationResult = payment.getAuthResult();
            // Confirm the authorization result is VOIDED instead
            // of VOID_DECLINED or some other result.
        }
        // Else handle failure by examining the status code and message.
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// Overridden from the CommerceListenerAdapter
override fun handlePaymentCompletedEvent(
        event: PaymentCompletedEvent) {
    if (TransactionEvent.TRANSACTION_ENDED.equals(event.getType())) {
        if (event.getStatus() == StatusCode.SUCCESS) {
            val payment = event.getPayment()
            val authorizationResult = payment.getAuthResult()
            // Confirm the authorization result is VOIDED instead
            // of VOID_DECLINED or some other result.
        }
        // Else handle failure by examining the status code and message.
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// Overridden from the VFICommerceListenerAdapter
override func handle(_ event: VFIPaymentCompletedEvent?) {
    if (event?.getType() == VFITransactionEventTRANSACTIONENDED) {
        if (event?.getStatus() == VFIStatusCodeSuccess) {
            let payment = event?.getPayment()
            let authorizationResult = payment?.getAuthResult()
            // Confirm the authorization result is
            // VFIAuthorizationResultVOIDED instead of
            // VFIAuthorizationResultVOIDDECLINED or some other result.
        }
        // Else handle failure by examining the status code and message.
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
private void HandlePaymentCompletedEvent(PaymentCompletedEvent event)
{
    if (event.Type.Value == TransactionEvent.TRANSACTION_ENDED)
    {
        if (event.Status == 0)
        {
            Payment payment = event.Payment;
            AuthorizationResult authorization_result = payment.AuthResult;
            // Confirm the authorization result is VOIDED instead
            // of VOID_DECLINED or some other result.
        }
        // Else handle failure by examining the status code and message.
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// Overridden from the CommerceListenerAdapter
void handlePaymentCompletedEvent(
    const std::shared_ptr<verifone_sdk::PaymentCompletedEvent>& event) override {
    if (event->getType() == TransactionEvent::TRANSACTION_ENDED) {
      if (event->getStatus() == StatusCode::SUCCESS) {
          auto payment = event->getPayment();
          auto auth_result = payment->getAuthResult();
          // Confirm the authorization result is VOIDED instead
          // of VOID_DECLINED or some other result.
      }
      // Else handle failure by examining the status code and message.
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
private void HandlePaymentCompletedEvent(PaymentCompletedEvent sdk_event)
{
    if (sdk_event.Type == TransactionEvent.TRANSACTION_ENDED)
    {
        if (sdk_event.Status == 0)
        {
            Payment payment = sdk_event.Payment;
            AuthorizationResult authorization_result = payment.AuthResult.Value;
            // Confirm the authorization result is VOIDED instead
            // of VOID_DECLINED or some other result.
        }
        // Else handle failure by examining the status code and message.
    }
}

Void during a Sale

It is possible to void payments in the middle of a session that is performing a sale.

 @startuml
 !include uml_styles.iuml
 hide footbox

 participant POS order 20
 participant POI order 30
 |||
 POS ->> POI: Start session
 POS <<- POI: Session started
 POS <<->> POI: Process payments
 ...
 POS -> POS: Select payment to Void
 POS ->> POI: Process Void
 POS <<- POI: PaymentCompletedEvent
 POS -> POS: Handle Void event
 POS <<->> POI: Continue processing payments
 ...
 POS ->> POI: End session
 POS <<- POI: Session ended
 |||
 @enduml

1
2
3
4
5
Transaction transaction = transactionManager.getTransaction();
// In this example, voiding the second payment for the current order.
Payment payment = transaction.getPayments().get(1);
transactionManager.processVoid(payment);
// Once the event is received for the void, continue processing the current order.
1
2
3
4
5
6
val transaction = transactionManager.getTransaction()
// In this example, voiding the second payment for the current order.
val payment = transaction.getPayments().get(1)
payment.setTransactionType(TransactionType.VOID_TYPE)
transactionManager.processVoid(payment)
// Once the event is received for the void, continue processing the current order.
1
2
3
4
5
6
var transaction = sdk.getTransactionManager()?.getTransaction()
// In this example, voiding the second payment for the current order.
var payment = transaction?.getPayments()[1]
payment?.setTransactionType(transactionType: VFITransactionType.VOIDTYPE)
sdk.getTransactionManager()?.processVoid(payment)
// Once the event is received for the void, continue processing the current order.
1
2
3
4
5
Transaction transaction = transactionManager.Transaction;
// In this example, voiding the second payment for the current order.
Payment payment = transaction.Payments[1];
transactionManager.ProcessVoid(payment);
// Once the event is received for the void, continue processing the current order.
1
2
3
4
5
auto transaction_manager = psdk->getTransactionManager();
auto transaction = transaction_manager->getTransaction();
// In this example, voiding the second payment for the current order.
transaction_manager->processVoid(transaction->getPayments().at(1));
// Once the event is received for the void, continue processing the current order.
1
2
3
4
5
Transaction transaction = payment_sdk_.TransactionManager.Transaction;
// In this example, voiding the second payment for the current order.
Payment payment = transaction.Payments[1];
payment_sdk_.TransactionManager.ProcessVoid(payment);
// Once the event is received for the void, continue processing the current order.

Perform a Refund

A refund returns the funds from the merchant to the customer. There are two important types of refunds, linked and unlinked. Only one refund is performed at a time, unlike voids (see perform-void), though multiple refunds may be performed within the same session.

 @startuml
 !include uml_styles.iuml
 hide footbox

 participant POS order 20
 participant POI order 30
 actor Customer order 40
 |||
 POS ->> POI: Start session
 POS <<- POI: Session started
 loop While there are refunds for this order
   alt Linked Refund
     POS -> POS: Link refund payment to original payment
     POS -> POS: Optionally set amount to be refunded
   else Unlinked Refund
     POS -> POS: Create new refund payment
     POS -> POS: Set amount to be refunded
   end
   POS ->> POI: Process refund
   alt Linked Refund
     POI <-> Customer: Possibly request card presentation.
   else Unlinked Refund
     POI <-> Customer: Always request card presentation.
   end
   POS <<- POI: PaymentCompletedEvent
   POS -> POS: Handle refund event
 end
 POS ->> POI: End session
 POS <<- POI: Session ended
 |||
 @enduml

Creating a Linked Refund

A linked refund has reference information to the original payment, providing this information to the host and sometimes even allowing the refund to be processed without the customer presenting their card. Linked refunds are much less likely to be identified as possible fraud, and are universally supported by the payment hosts.

To link a refund, either set the App Specific Data from the original payment on a new Payment (see Linking Payments), or less commonly, send the original Payment directly.

Setting the requested amount totals is optional for linked refunds. Leaving the requested amounts unset will refund the entire amount. The request totals should never be set higher than the original amount for a linked refund, this will generally cause the refund to be declined

Note

It is mandatory to set the requested amount totals for linked refunds for the Verifone SCA regional solution. The Transaction Amount is a mandatory field for refunds in both SSI and UGP gateway protocol specifications.

Creating an Unlinked Refund

An unlinked refund is a simple amount that is pushed from the merchant account into the customer’s account. Unlinked refunds are generally supported by the different hosts, but are not supported universally. Care should be taken when using unlinked refunds to make sure that it is supported by the merchant’s host. Simply create a new Payment for this type of refund and set the requested amount totals (Payment.setRequestedAmounts(totals)).

Partial Refunds

Partial refunds may be performed by setting the requested amount totals (Payment.setRequestedAmountTotals(totals)) to a value below the original amount.

Performing the Refund

Use the Payment for either the linked or unlinked refund, calling transactionManager.processRefund(payment). The CommerceListener receives a PaymentCompletedEvent with type TransactionEvent.TRANSACTION_PAYMENT_COMPLETED once the refund is complete. Similarly to other payments, if the StatusCode returned from PaymentCompletedEvent.getStatus() is non-zero (non-SUCCESS), either StatusCode.FAILED, or StatusCode.CANCELLED, then an error occurred and the event will not necessarily contain more information besides the StatusCode and PaymentCompletedEvent.getMessage(). If the StatusCode is StatusCode.SUCCESS, the refund Payment is retrieved using :event.getTransaction().getPayments().get(0).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Override
public TransactionEventResponse handlePaymentCompletedEvent(PaymentCompletedEvent event) {
    if (TransactionEvent.TRANSACTION_ENDED.equals(event.getType())) {
    if (event.getStatus() == StatusCode.SUCCESS) {
        // The refund was successful.
        Payment payment = event.getTransaction().getPayments().get(0);
        // Information, such as the auth code, are available as well.
    } else if (event.getStatus() == StatusCode.CANCELLED) {
        // The refund was cancelled, either by the user or externally.
    } else {
        // The refund failed in some way.
        if (event.getTransaction() != null
                && event.getTransaction().getPayments() != null
                && !event.getTransaction().getPayments().isEmpty()) {
                Payment payment = event.getTransaction().getPayments().get(0);
                // The auth response text may contain information from the payment host with
                // more information regarding why it was not successful.
                payment.getAuthResponseText();
            }
        }
    }
    return event.generateTransactionEventResponse();
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fun handlePaymentCompletedEvent(event: PaymentCompletedEvent) : TransactionEventResponse {
    if (TransactionEvent.TRANSACTION_ENDED.equals(event.getType())) {
        if (event.getStatus() == StatusCode.SUCCESS) {
            // The refund was successful.
            val payment = event.getTransaction().getPayments().get(0)
            // Information, such as the auth code, are available as well.
        } else if (event.getStatus() == StatusCode.CANCELLED) {
            // The refund was cancelled, either by the user or externally.
        } else {
            // The refund failed in some way.
            if (event.getTransaction() != null
                && event.getTransaction().getPayments() != null
                && !event.getTransaction().getPayments().isEmpty()) {
                    val payment = event.getTransaction().getPayments().get(0)
                    // The auth response text may contain information from the payment host with
                    // more information regarding why it was not successful.
                    payment.getAuthResponseText()
                }
            }
        }
    }
    return event.generateTransactionEventResponse()
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// Overridden from the VFICommerceListenerAdapter
override func handle(_ event: VFIPaymentCompletedEvent?) {
    if (event?.getType() == VFITransactionEventTRANSACTIONENDED) {
        if (event?.getStatus() == VFIStatusCodeSuccess) {
            let payment = event?.getTransaction()?.getPayments().first
        } else if (event?.getStatus() == VFIStatusCodeCancelled) {
            // the refund was cancelled, either by the user or externally.
        } else {
            // the refund failed in some way
            if let txn = event?.getTransaction(),
                let payment = txn.getPayments().first {
                // more information regarding why it was not successful.
                payment.getAuthResponseText()
            }
        }
    }
}
 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
private void HandlePaymentCompletedEvent(PaymentCompletedEvent event)
{
    if (event.Type.Value == TransactionEvent.TRANSACTION_ENDED)
    {
        if (event.Status == 0)
        {
            // The refund was successful.
            Payment payment = event.GetTransaction.Payments[0];
            // Information, such as the auth code, are available as well.
        }
        else if (event.Status == -11)
        {
            // The refund was cancelled, either by the user or externally.
        }
        else
        {
            // The refund failed in some way.
            if (event.GetTransaction != null
                && event.GetTransaction.Payments != null
                && event.GetTransaction.Payments.Count > 0)
            {
                Payment payment = event.GetTransaction.Payments[0];
                // The auth response text may contain information from the payment host with
                // more information regarding why it was not successful.
                payment.AuthResponseText;
            }
        }
    }
    return event.GenerateTransactionEventResponse();
}
 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
class Listener: public verifone_sdk::CommerceListener {
 public:
  std::string getUniqueListenerId() override {
    return "ListenerId";
  }

  std::shared_ptr<verifone_sdk::PaymentCompletedEventResponse> handlePaymentCompletedEvent(const std::shared_ptr<verifone_sdk::PaymentCompletedEvent>& event) override {
    if (event->getType() == verifone_sdk::TransactionEvent::TRANSACTION_ENDED) {
      if (event->getStatus() == StatusCode::SUCCESS) {
        // Refund processed successfully
      } else if (event->getStatus() == StatusCode::CANCELLED) {
        // The refund was cancelled, either by the user or externally.
      } else {
        // Handle failure by examining the status code and message.
      }
    }
    return nullptr;
  }

  ...
}

...
auto payment = verifone_sdk::Payment::create();
payment->setRequestedAmountTotals(amount_totals_to_refund);
payment->setPaymentId("payment-id-to-refund");
payment->setLocalPaymentId("localPaymentId");
//set app specific data for linking if necessary
payment->setAppSpecificData(app_specific_data)
psdk->getPaymentManager->processRefund(payment);
 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
public void HandlePaymentCompletedEvent(PaymentCompletedEvent sdk_event)
{
    if (sdk_event.Type == TransactionEvent.TRANSACTION_ENDED)
    {
        if (sdk_event.Status == 0)
        {
            // The refund was successful.
            var payment = sdk_event.Transaction.Payments[0];
            // Information, such as the auth code, are available as well.
        }
        else if (sdk_event.Status == -11)
        {
            // The refund was cancelled, either by the user or externally.
        }
        else
        {
            // The refund failed in some way.
            if (sdk_event.Transaction != null
                && sdk_event.Transaction.Payments != null
                && sdk_event.Transaction.Payments.Count > 0)
            {
                Payment payment = sdk_event.Transaction.Payments[0];
                // The auth response text may contain information from the payment host with
                // more information regarding why it was not successful.
                String auth_response = payment.AuthResponseText;
            }
        }
    }
    sdk_event.GenerateTransactionEventResponse();
}


Using Gift Cards

Gift card operations are performed when the customer wants to use a gift card or when it is the required method of payment.

Identifying a gift card payment

Upon receipt of the payment completed event, to identify a gift card payment, we need to check Payment.getPaymentType() == Payment.PaymentType.STORED_VALUE and Payment.getStoredValueCardInformation().getCardType() == CardType.GIFT_CARD.

Performing other gift card actions

To perform gift card specific operations, use TransactionManager.activateStoredValueCard(payment) TransactionManager.loadStoredValueCard(payment), TransactionManager.unloadStoredValueCard(payment) and TransactionManager.getStoredValueCardBalance(cardinformation)

Starting a gift card only payment

To require a gift card to be used, use Payment.setRequestedPaymentType(PaymentType.STORED_VALUE), then set a StoredValueCardInformation with CardType as CardType.GIFT_CARD on the payment object, before sending it for start payment.

Using Electronic Benefit Transfer (EBT) Cards

EBT card operations are performed when this is the available or required method of payment.

Identifying an EBT card payment

Upon receipt of the payment completed event, to identify an EBT card payment, we need to check Payment.getPaymentType() == Payment.PaymentType.EBT and Payment.getCardInformation().getCardType() == CardType.EBT_CARD.

Starting an EBT card payment

To require an EBT card to be used, use Payment.setRequestedPaymentType(PaymentType.EBT), then set a CardInformation with CardType as CardType.EBT_CARD on the payment object, before sending it for start payment.