Card present payments

United States

You need to set up the Clover Go reader to accept payments. Then, you can use the PayRequest method to accept the customers' payments when they insert their card or tap it on the device.
Set up the Clover Go reader to accept payments.

For information about payment types, see Payments.

Prerequisites

Before you integrate with the Go SDK, complete the following:

  • Claim your Clover account and log in with your Clover credentials so that you can use the Clover standard OAuth flow for authentication.
  • Create and set up a developer account (including your test merchant settings) and an app in Clover sandbox Developer Dashboard.
  • Create at least one employee user to use within the OAuth flow.
  • Order a Clover Go Card Reader Developer Kit (Dev Kit) and set it up.

Field descriptions

PayRequest fields

ObjectTypeDescriptionRequired/ Optional
amountint64; booleanTotal amount payment in cents including taxes.
Format: Cents; Cannot be a negative value.
Maximum amount = 999999999
Required
finalBooleanIndicates how the payment is processed.
Values:
- True—Payment is processed as closed/settled and cannot be tip-adjusted or captured later. Capture flag also needs to be true. If a tip is not provided, then calculate the tip as zero (0).
- False—Payment is processed as Auth/PreAuth and needs to be closed.
See Closeout for more information.
Required
captureBooleanIndicates how the payment is captured.
Values:
- True—Set for final sale or a payment that is ready to be captured but can be tip-adjusted later. When true the payment is automatically and immediately captured, but it is adjustable until closeout runs; at that time it is closed or settled.
- False—Set for an open sale where the payment needs to be captured later. When false, in case of PreAuth, the payment is not automatically captured and subsequent call to capture is required.

A captured payment is closed/settled once closeout runs.
Length: Maximum 32 characters
Required
externalPaymentIdStringIdentifier (id) that the merchant keeps as a payment reference. This is useful to reference multiple payments in an order.
Length: Maximum 32 characters
Optional
externalReferenceIdStringAlso referred as Invoice Number.
Identifier (id) that is passed to merchant's gateway and displays in settlement records.
Length: Maximum 12 characters
Optional
taxAmountint64; booleanTax amount in cents. Included in the total amount but can also be set to display in transaction records and receipts.
Format: Cents; cannot be a negative value
Length: Maximum 999999999
Optional
tipAmountint64; booleanTip amount in cents including taxes.
Format: Cents; cannot be a negative value
Length: Maximum 999999999
If the final flag is false, this value is always null/nil (not zero (0))
If the final flag is true—that is, payment type is Sale—and tipAmount is null/nil, then it is calculated as zero (0).
Optional
keyedInCardKeyedInCardSee Manual entry payments for more information.
Value for card reader payments = null/nil.
Required only for a manual card entry.

PayResponse fields

ObjectTypeDescription
paymentPaymentPayment details auto-populate on successful payment.
issuesArray of PaymentIssueEnumArray of the enum of the concerns related to the payment. The integrator may want to void this payment in some scenarios.

Note: Integrator(s) decide how they want to proceed in the case of issues. For example:

- If the issue is SIGNATURE_REQUIRED, the integrator may need to implement a signature capture interface and save it with transaction information.
- If the issue is PARTIAL_PAYMENT, the integrator may make a call to void the payment or keep the payment and make another PayRequest to complete the transaction for the rest of the payment.

For integrators who work with other Clover systems or the older version of the Go SDK, this is similar to Payment Challenges that occur. On other systems when a Payment Challenge occurs, the payment is put on hold until the user continues or cancels the payment, or a timeout occurs. The process is simpler in the latest SDK. The payment process proceeds when there is a PaymentChallenge. The issues field populates with the corresponding enum. The integrator can call a void if they want to cancel the payment. For example, if a signature is required, you can match the signature with the receipt and call a void if it doesn't match.

PaymentIssueEnum

If a payment issue occurs, the payment still processes. Merchants can request a ReversePayment to void a payment if they have an issue such as a SignatureChallenge where signatures don't match. If a partial payment occurs, merchants can perform another transaction to collect the remaining amount.

ObjectDescription
Duplicate_PaymentPayment is detected as a duplicate payment.
Offline_PaymentPayment is detected as offline payment.

Note: Offline payments are not supported with this version of Clover Go software development kit (SDK). Integrators won't receive this enum.
Partial_PaymentCard allowed only partial payment of the full amount requested.
Signature_RequiredSignature is required for the transaction.
AVS_MismatchAddress Verification Service (AVS) detects an address mismatch.
CVV_MismatchCard verification value (CVV) detects a CVV mismatch.

📘

IMPORTANT

Review the resulting payment object's amount field to verify that the full amount requested was authorized. In some scenarios, only a partial amount may be authorized, and your app needs to issue further PayRequest instances to reach the full amount or reverse the transaction.

CloverError

CloverError can be any of the following:

Error typeDescription
CloverCardErrorTransaction failed because of card error—for example, card expired.
CloverMerchantBoardingErrorMerchant status returns a declined or pending status from the server.
CloverNetworkErrorStandard HTTP errors, captured during communications with our servers.
CloverReaderErrorErrors encountered while communicating with the card reader.
CloverTransactionErrorErrors encountered while processing a transaction
CloverGenericErrorAny error that doesn't fall into the error categories above.

Android card present payment steps

To create a card present payment:

  1. Connect the card reader and ensure it has more than 2% of battery power.
  2. Use PayRequest.
  3. Note the ChargeCardReaderState.

A successful request returns OnPaymentComplete with the PayResponse. An error such as a timeout returns OnPaymentError. Instead of PaymentChallenge being displayed, the payment proceeds further and the issues enum array contains any issues received through the process.

The integrator needs to call ReversePayment to cancel the transaction.

If the request fails—for example, if there is a timeout, a CloverError returns. See CloverError for further details.

Response details

ChargeCardReaderState

FieldDescription
OnPaymentCompleteIndicates a successful payment. Review the PayResponse for the successful payment details.
OnPaymentErrorIndicates an error occurred. The payment is voided. See CloverError for error descriptions.
OnReaderPaymentProgressCaptures the progress state in between. See PaymentProgressEvent for in-between progress events.

PaymentProgressEvent

FieldDescription
Card_InsertedCard reader detected that card is inserted.
Card_RemovedCard is removed after the dip.
Card_SwipedCard reader detected that card is swiped (in case the device supports it).
Card_TappedCard reader detected that card is tapped.
Contactless_Failed_Try_AgainFor some reason the contactless failed and try again or try other method(s) like dip.
EMV_Card_SwipedCard reader detected that Europay, Mastercard, and Visa (EMV) card is swiped (in case the device supports it).
EMV_Dip_Failed_Try_AgainCard dip failed. Try again or try another method such as contactless.
Insert_Or_Tap_CardCard reader is ready to take the payment.
Multiple_Contactless_Cards_DetectedMultiple cards are tried to do the payment.
Remove_CardIf the card is dipped, the card data is read and the card needs to be removed to proceed.
Request_In_FlightTransaction is started and the state is put in flight. If another transaction is tried while this is in progress, the other transactions fail. Clear the state onPaymentComplete/onPaymentError.
Swipe_FailedSwipe failed in instances where swipe is supported.

PayRequest example

fun chargeCardReader(charge: PayRequest): Flow<ChargeCardReaderState>

For a non-KeyedInCard PayRequest—such as no KeyedInCard details—ensure the card reader is connected and has more than 2% battery power.

PayRequest request = new PayRequest(
                amount,
                capture,
                isFinal,
                externalPaymentID,
                externalreferenceID,
                taxAmount,
                tipAmount,
                null //since this is a card present payment, keyed in card is null
        );
goSdk.chargeCardReader(
        activity,
        request,
        new GoSdkCallback<ChargeCardReaderState>() {
            @Override
            public void onNext(ChargeCardReaderState cardReaderState) {
                if (chargeCardReaderState instanceof ChargeCardReaderState.OnReaderPaymentProgress) {
                    Log.d(TAG, "Payment in progress");
                    updateUI(cardReaderState);
                } else if (chargeCardReaderState instanceof ChargeCardReaderState.OnPaymentComplete) {
                    Log.d(TAG, "Payment successful");
                    updateUI(cardReaderState);
                } else if (chargeCardReaderState instanceof ChargeCardReaderState.OnPaymentError) {
                    Log.d(TAG, "Payment failed");
                    updateUI(cardReaderState);
                } else {
                    //Something unexpected happened
                }
            }
 
            @Override
            public void onError(@NonNull Throwable e) {
                //handle error scenario
            }
        }
);
lifecycleScope.launch {
    /*
    Capture State
 
    Final/Sale - true
    Tippable/Auth - true
    Pre-Auth - false
    */
 
    val request = PayRequest(
        final = final,
        capture = capture,
        amount = amount, //Please note that amount is not nullable, unlike taxAmount and tipAmount
        taxAmount = taxAmount,
        tipAmount = tipAmount,
        externalPaymentId = externalID
    )
 
    goSdk.chargeCardReader(
        request
    ).collectLatest { cardReaderState ->
        when (cardReaderState) {
            is ChargeCardReaderState.OnPaymentComplete -> {
                println("Payment successful")
                updateUI(cardReaderState)
            }
            is ChargeCardReaderState.OnPaymentError -> {
                println("Payment failed")
                updateUI(cardReaderState)
            }
            is ChargeCardReaderState.OnReaderPaymentProgress -> {
                println("Payment in progress")
                updateUI(cardReaderState)
            }
            else -> {
                //Something unexpected happened
            }
        }
    }
}

iOS card present payment steps

To accept payment using a card reader:

  • Create a PayRequest object.
  • Pass it to the CloverPaymentSDK.shared.charge() function.
    • Successful transaction returns a .complete paymentEvent with the associated payment and the async stream closes.
    • Failed transaction returns an error and the async stream closes. During transaction processing, .progress paymentEvents is called to provide status and instructions to provide to the customer.
let payRequest = PayRequest(amount: 1000, taxAmount: 80, tipAmount: 200)
 
do {
   for try await paymentEvent in CloverPaymentSDK.shared.charge(payRequest: payRequest) {
      switch paymentEvent {
      case .complete(let payResponse):
         print("Payment complete")
         if payResponse.issues.contains(.duplicatePayment) {
            print("This payment is a possible duplicate")
         }
      case .progress(let progressEvent):
         switch event {
         case .insertOrTapCard: presentAlert(title: "Insert or Tap Card", message: nil)
         case .swipeInsertTapCard: presentAlert(title: "Swipe, Insert, or Tap Card", message: nil)
         case .removeCard: presentAlert(title: "Remove Card", message: nil)
         case .cardInserted: fallthrough
         case .cardRemoved: alert?.dismiss(animated: true)
         default: break
         }
      @unknown default: break
      }
   }
} catch {
   print("Payment Error \(error)")
}