Handle challenges during payment processing
Challenges
are potential issues the Clover device may encounter while attempting to process a Payment
. A Challenge
is triggered by a potential duplicate Payment
(DUPLICATE_CHALLENGE
) or an offline Payment
(OFFLINE_CHALLENGE
).
When a Challenge
is raised, the Clover device calls the ICloverConnectorListener.OnConfirmPaymentRequest()
method and passes a ConfirmPaymentRequest
object that contains the Payment
and the list of Challenges
it encountered.In response, the merchant must either accept the Payment
and any risk associated with it, or reject the Payment
. Until the Payment
is accepted or rejected, the Clover device displays on the screen—Merchant is verifying your payment screen.
Note
On-device semi-integrations that use
PaymentConnector
or Intents do not need to handle challenges. Secure Payments manage challenges instead.
Handle offline challenges
To accommodate the higher risk associated with an offline transaction, Clover provides you and the merchant with an OFFLINE_CHALLENGE
to let you decide whether or not you want to accept or reject the transaction. An OFFLINE_CHALLENGE
is raised when the Clover device attempts to process a card transaction while the device is not connected to the network. The Clover device is unable to verify with the payment gateway that the card is valid and has sufficient funds.
There are three common ways to handle offline challenges:
- Accept all payments processed offline, with the merchant assuming the associated risk.
- Reject all payments processed offline, minimizing the merchant's risk at the expense of potentially missing revenue.
- Let the POS indicate that the card transaction occurred offline, and state the risk associated with accepting the payment. The POS screen displays two buttons for accepting and rejecting the payment.
If the merchant chooses to accept the payment, the payment request is added to the device offline queue, and then sent to the payment gateway when the device regains network connectivity.
Handle duplicate payment challenges
A DUPLICATE_CHALLENGE
is raised when multiple Payments
are made with the same card type and the last 4 digits within the same hour.
Important
Clover recommends that you do not programmatically call
CloverConnector.RejectPayment()
on all instances ofDUPLICATE_CHALLENGE
.
To avoid false positives for DUPLICATE_CHALLENGE
, Clover suggests that you either:
- Implement your own logic that also compares the
Payment
amount. - Generate a pop-up that asks the merchant if the
Payment
is a duplicate.
Identify duplicate payment requests
Compare payment amounts
You can determine whether a DUPLICATE_CHALLENGE
was triggered by an actual duplicate Payment
request rather than a different Payment
made with the same card type and last 4 digits within the same hour. To do so compare the Payment
amount with the previous transaction amount. The following code snippet demonstrates how to implement this logic for Remote Pay Windows:
//1. Make a sale.
//2. Save the SaleResponse from OnSaleResponse(SaleResponse)
//3. Call cloverConnector.RetrievePayment(RetrievePaymentResponse)
OnRetrievePaymentResponse(RetrievePaymentResponse response)
{
if (response.Success)
{
uiThread.Send(delegate (object state)
{
String details = "No matching payment";
Payment payment = response.Payment;
if (payment != null)
{
if(payment.amount == lastSalesResponse.Payment.amount)
{
details = "Created:" + dateFormat(payment.createdTime) + "\nResult: " + payment.result
+ "\nPaymentId: " + payment.id + "\nOrderId: " + payment.order.id
+ "\nAmount: " + currencyFormat(payment.amount) + " Tip: " + currencyFormat(payment.tipAmount) + " Tax: " + currencyFormat(payment.taxAmount);
}
}
AlertForm.Show(this, response.QueryStatus.ToString(), details);
}, null);
}
else if (response.Result.Equals(ResponseCode.FAIL))
{
uiThread.Send(delegate (object state)
{
AlertForm.Show(this, response.Reason, response.Message);
}, null);
}
else if (response.Result.Equals(ResponseCode.CANCEL))
{
uiThread.Send(delegate (object state)
{
AlertForm.Show(this, response.Reason, response.Message);
}, null);
}
}
Request merchant verification
Alternatively, you can prompt the merchant to verify whether the Payment
is a duplicate.
bool lastChallenge = false;
for (int i = 0; i < request.Challenges.Count; i++)
{
uiThread.Send(delegate (object state)
{
if (i == request.Challenges.Count - 1) // if this is the last challenge
{
lastChallenge = true;
}
Challenge challenge = request.Challenges[i];
ConfirmPaymentForm confirmForm = new ConfirmPaymentForm(parentForm, challenge, lastChallenge);
confirmForm.FormClosed += (object s, FormClosedEventArgs ce) =>
{
if (confirmForm.Status == DialogResult.No)
{
cloverConnector.RejectPayment(request.Payment, challenge);
i = request.Challenges.Count;
}
else if (confirmForm.Status == DialogResult.OK) // Last challenge was accepted
{
cloverConnector.AcceptPayment(request.Payment);
}
confirmPaymentFormBusy.Set(); //release the confirmPaymentFormBusy WaitOne lock
};
confirmForm.Show();
}, null);
confirmPaymentFormBusy.WaitOne(); //wait here until Accept or Reject pressed
}
Accept and reject payments
To accept a Payment
by calling CloverConnector.AcceptPayment(Payment)
, use the Payment
obtained from ConfirmPaymentRequest.Payment
. The following snippet demonstrates how to do this with Remote Pay Windows:
cloverConnector.AcceptPayment(request.Payment);
To reject the Payment
and prevent it from proceeding, call CloverConnector.RejectPayment(Payment, Challenge)
using the:
Payment
obtained fromConfirmPaymentRequest.Payment
andChallenge
obtained fromConfirmPaymentRequest.Challenges
cloverConnector.RejectPayment(request.Payment, request.Challenges[i]);
If you have further questions, visit the Clover Developer Community, or contact us at [email protected].
Updated 11 months ago