Manual entry payments

United States

For manual entry payments, your app provides required information about the payment and customer, including credit card details collected by a form in your app. The manual entry method encrypts sensitive cardholder data and routes the request and response to and from the Clover server. The PayResponse object is returned. The card reader is not used, and does not need to be connected for this type of transaction.

📘

IMPORTANT

In this payment method, the credit card information is manually entered in plain-text format. Due to this your app is not certified as Point to Point Encryption (P2PE)-compliant, even though the card data is encrypted as quickly as possible after it is entered.

For information about the sale, auth, pre-auth payment methods, 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
keyedInCardkeyedInCardRequired for a manual card entry. See KeyedInCard for further details.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

KeyedInCard

ObjectTypeDescriptionRequired/ Optional
cardNumberEncryptedStringEncrypted primary account number (PAN). Encrypted through initializer on iOS; while Android takes unencrypted (as of now) and encrypts it in SDK before sending the request.Required
first6StringFirst 6 digits of the card number.Required
last4StringLast 4 digits of the card number.Required
isCardPresentBooleanIndicates if card is present.
Values:
- True—Card is present
- False—Card not present (CNP)
Required
billingAddressStringBilling address (Billing Street and number) registered with the card provider.Optional
billing ZipStringBilling postal code (ZIP) registered with the card providerOptional
cardTypeEnum: CardTypeCard type used. A wrong cardType may not necessarily fail the transaction. Use Unknown card type if card type is not listed.Required
expirationCardExpirationDateCard expiration month (first 2 digits) and year (last 2 digits). Format: mmyy Length: 4 digitsRequired
cvvStringCard verification value (CVV) of the cardRequired

PayResponse fields

ObjectTypeDescription
paymentPaymentPayment details autopopulate on successful payment.
IssuesArray of PaymentIssueEnumOriginally payment challenges were displayed. Now the payment proceeds if there is a payment challenge and issues are filled in for a successful payment.

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 info. If the issue is DUPLICATE_PAYMENT or PARTIAL_PAYMENT, the integrator may make a call to void the payment.

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_PaymentDuplicate payment.
Offline_PaymentOffline payment.

Note: Offline payments are not supported with this version of Clover Go 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) mismatch.
CVV_MismatchCard verification value (CVV) mismatch.

📘

IMPORTANT

Review the resulting payment object 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 manual entry payment steps

To create a manual entry payment:

  1. Send a PayRequest.
  2. 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.

Request details

Send a PayRequest with KeyedInCard details.

Response details

ChargeCardReaderState

FieldDescription
OnPaymentCompleteIndicates a successful payment. Review the PayResponse for the successful payment details.
OnPaymentErrorIndicates an error occurred and payment is voided.

PayRequest example

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

For a KeyedInCard PayRequest, the card reader does not need not to be connected.

For a non-manual (without KeyedInCard) PayRequest, see Card present payments.

KeyedInCard keyedInCard = new KeyedInCard(
                "123456789012345",
                "123456",
                "1234",
                false,
                CardType.UNKNOWN, //The CardType class contains the available options
                "1234",
                "1234",
                "Clover Go St",
                "12345"
        );
 
PayRequest request = new PayRequest(
                amount,
                capture,
                isFinal,
                externalPaymentID,
                externalreferenceID,
                taxAmount,
                tipAmount,
                keyedInCard
        );
 
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 keyedInCard = KeyedInCard(
        cardNumberEncrypted = "123456789012345",
        first6 = "123456",
        last4 = "1234",
        isCardPresent = false,
        cardType = CardType.UNKNOWN, //The CardType class contains the available options
        expiration = "1234",
        cvv = "1234",
        billingAddress = "Clover Go St",
        billingZip = "12345"
    )
 
    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,
        keyedInCard = keyedInCard
    )
 
    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

To take a payment using a card reader:

  1. Create a PayRequest object.
  2. Pass it to the CloverPaymentSDK.charge( function.
  3. Include a KeyedInCard object as part of the payRequest, with the input about the payment and customer, including credit card details coming from a form in your app.

If the transaction succeeds, a .complete paymentEvent is called with the associated payment and the async stream closes. If the transaction fails, an error displays and the async stream closes. During transaction processing, .progress paymentEvents is called to provide status and instructions to the customer.

let keyedInCard = KeyedInCard(cardNumber: pan, expDate: expDate, cvv: cvv) //values manually input from your form
let payRequest = PayRequest(amount: 1000, taxAmount: 80, tipAmount: 200)
payRequest.keyedInCard = keyedInCard    
 
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)")
}