iOS—Clover Go SDK release notes

v1.0.0

United States

CloverPayments software development kit (SDK) v1.0.0 for iOS® contains the following changes, features, improvements, and bug fixes.

Initialize CloverPayments SDK

The CloverPayments SDK follows a singleton pattern—a design pattern that restricts the instantiation of a class to one object.

To initialize the SDK:

  1. Call CloverPaymentSDK.shared.setup(configuration).
  2. Pass in your setup() configuration object to replace the previously available init function.
  3. Provide the same functionality in a singleton and async/await compatible manner.

Previously static vars and functions were moved to instance vars on the singleton.

Changed the name of the published module

To avoid potential name-spacing concerns, the name of the published module is updated from CloverPaymentSDK to CloverPayments.

Update your import statements to "import CloverPayments".

🚧

Important

Code that calls into the SDK's API still flows through the main class, which retains the CloverPaymentSDK name.

Moved primary interfaces to async/await

The primary interfaces are moved to the async/await pattern use of delegates and Swift.Result. The base functionality is the same; however, you need to reorganize some code.

Example—Perform a charge with the prior delegate pattern:

func makePayment(payRequest: PayRequest) {
   CloverPaymentSDK.charge(payRequest: payRequest, delegate: self, completion: { payResponse in
      NSLog("PaymentComplete: \(payResponse)")
   )
}
 
...
 
func onProgress(event: PaymentProgressEvent) {
   NSLog("Payment Progress: \(event)")
}

Example—Perform a charge with the async/await pattern:

func makePayment(payRequest: PayRequest) {
   Task { @MainActor in
      do {
         for try await paymentEvent in CloverPaymentSDK.shared.charge(payRequest: payRequest) {
            switch paymentEvent {
            case .complete(let payResponse):
               print("Payment Complete: \(payResponse)")
            case .progress(let progressEvent):
               NSLog("Payment Progress: \(progressEvent")
            @unknown default:
               break
            }
         }
      } catch {
         onError(error: error)
      }
   }
}

Adopting the async/await pattern

The async/await pattern is:

  • Adopted across setup, replacing init, getPreviousConnectedReader/s, scan, connect, reverse, capture, tipAdjust, closeout, and sendReceipt.
  • Not adopted for any synchronous variables and functions: sdkVersion, state, currentMerchant, receivedOAuthCodeOrToken, isLoggedIn, logout, connectedReader, disconnect, getSettingValue, and getSettingValueInBoolIfPossible.
  • Was not adopted in the Token Change callbacks: addOnTokenChangeCallback and removeOnTokenChangeCallback.

See also: Performing activities not allowed before ready state

Implemented a simple SDK state

A simple SDKState is implemented, available as CloverPaymentSDK.shared.state, to separate the init and setup functions. This lets you make calls to the SDK before it is ready to use. SDKState values are:

  • notReady—Default startup condition of the SDK. Call setup to progress the SDK's state.
  • preparing—SDK is currently performing setup tasks
  • ready—SDK is setup and ready for you to make calls.

Performing activities not allowed before ready state

The async/await pattern is adopted across all asynchronous tasks; therefore, an error displays if you try to perform an activity that is not allowed before the SDK has reached the ready state. This includes calling: scan, connect, charge, reverse, credit, capture, tipAdjust, closeout, or sendReceipt while the SDK is not in a ready state. The error that displays is: CloverGenericError with the code .illegalState.

Unified model objects

Model objects for passing parameters to the SDK are unified between the iOS and Android SDKs, where possible.

Added a sendReceipt interface

You can provide a customer’s phone number or email, or both on the sendReceipt(SendReceiptRequest) interface.

Added a singular getPreviousConnectedReader function

Added a getPreviousConnectedReader function to retrieve the most recently connected card reader.

Updated the configuration object

The OAuth process configuration is updated and a few variable names are standardized.

OldNewChange
APIKey: StringapiKey: StringRenamed
APISecret: StringapiSecret: StringRenamed
environment: CloverEnvironment.EnvironmentTypeenvironment: CloverEnvironment.EnvironmentTypeNo change
OAuthFlowAppID:StringappID:StringRenamed
OAuthFlowAppSecret:StringappSecret:StringRenamed
OAuthFlowRedirectURI:StringoauthFlowType : OAuthFlowTypeReplaced String uniform resource identifier (URI) with the OAuthFlowType configuration object. Identical functionality is available by passing in:

FullOAuth(redirectURI: String)

Full OAuth functionality is defined as letting the SDK do all of OAuth for you. If the SDK does not have a valid token, the SDK moves to the default external browser passing in the redirectURI. The string that is returned needs to be passed to the SDK in the same manner as before. See documentation on OAuth and/or the Quick Start Guide for details.

You can also implement the OAuth flow in your code.
Pass in:
PartialOAuth(codeResponse: OAuthCodeResponse?)

The OAuthCodeResponse is the response you received from the OAuth server. Note that you should only pass in a codeResponse once per response from the OAuth Server. In between instances, the SDK manages the token for you still, and securely retains it in Keychain.

To set up the SDK with Partial OAuth for subsequent launches of your app, pass in a PartialOAuth object with the codeResponse as nil.

Added CocoaPods (beta)

The SDK is available on CocoaPods in a closed beta. The code is published on the Cocoapods main trunk:

pod 'CloverPayments', '~> 1.0.0-rc'

Simplified device discovery

The API for discovering Bluetooth devices is simplified.

  1. On your device, call the scan function.
  2. Wait for five seconds for card readers to be discovered.
  3. If your device isn't discovered, verify the device is powered on and within range, then scan again.

Task { @MainActor in
    do {
        for try await cardReader in CloverPayments.shared.scan() {
            print("Found Reader: \(cardReader.name ?? cardReader.id)")
        }
    } catch {
        print("Discovery error")
    }
}

Reconnecting to last connected device

To reconnect to the last connected device, call connect(reader: nil) (passing nil as a parameter). If the SDK previously connected to a device, it searches for and connects to that device. An error displays if the connection fails.

Removed duplicate payment challenge

To closer align with the Clover REST API, challenges that don’t apply to the payment flow have been removed. Applicable challenges are moved to a PaymentIssues array returned with the PayResponse.

The only challenge that is important to an integrator is the onDuplicateDetected challenge, which is returned in PayResponse.issues as a .duplicatePayment, with the challenge auto-accepted during the flow. Inspect the PayResponse for issues and, if necessary, void the payment if an issue is discovered.

To check for a duplicate payment:

if payResponse.issues.contains(.duplicatePayment) {
  print("This payment was flagged as a possible duplicate"
}

Errors

With the move to async/await, errors display instead of being passed back in a failure block. Identify the errors in your code. Displayed errors are a subtype of CloverError, and are grouped by topic categories.

  • CloverGenericError—Any error that doesn't belong to a defined category.
  • CloverMerchantBoardingError—Displays if the merchant status returns a .declined or .pending status from the server.
  • CloverNetworkError—Standard HTTP errors, captured during communications with the Clover server.
  • CloverReaderError—Errors encountered while communicating with the card reader.
  • CloverRkiError—Errors encountered while performing Remote Key Injection (RKI) on the card reader.
  • CloverTransactionError—Errors encountered while processing a transaction