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 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.
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
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. } } |
See also
PaymentCompletedEvent.getTransactionResult()
Void during a Sale¶
It is possible to void payments in the middle of a session that is performing a sale.
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.
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.
See also
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.
See also
Verifone Confidential
This documentation is protected by law from any form of duplication unless prior permission is obtained from the officers of Verifone.
Verifone Payment SDK v3.55.5