Account Linking

This guide provides a brief overview for how to get started using Account Linking.

Initialize the SDK

import UIKit
import BlinkReceipt
import BlinkEReceipt

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        BRScanManager.shared().licenseKey = "YOUR-LICENSE-KEY"
        BRScanManager.shared().prodIntelKey = "YOUR-PROD-INTEL-KEY"
        BRAccountLinkingManager.shared()
        return true
    }

}

Note

A licenseKey or prodIntelKey can be obtained by emailing blinkreceipt@microblink.com.


Create account connection

Account Linking offers two UI/UX experiences when linking and verifying a merchant’s connection.

1. Host App Authentication

Using this flow, a host app can provide a native prompt for users to populate credentials. All other interactions with the merchant are done with the webview hidden in a background with exception when 2FA, Captcha or other user input is required.

Use collected credentials to create BRAccountLinkingConnection object.

let username = "user@domain.com"
let password = "secure-password"
let connection = BRAccountLinkingConnection(retailer: .walmart, username: username, password: password)
connection.configuration.dayCutoff = 30

2. Retailer Webview Authentication

This flow eliminates the need for a host app to provide any native UX to collect credentials. Instead, the SDK presents a webview with the merchant’s regular authentication protocol. The webview stays populated as the user completes whatever authentication flow is required by the merchant. The webview disappears after a successful authentication is complete.

Create BRAccountLinkingConnection object by only specifying the retailer

let connection = BRAccountLinkingConnection(retailer: .walmart)
connection.configuration.dayCutoff = 30

Already linked connections can be switched back and forth by updating the webviewAuthEnabled value

if let connection = BRAccountLinkingManager.shared().getLinkedRetailerConnection(.walmart) {
  connection.webviewAuthEnabled = true // Enable `Retailer Webview Authentication`
  connection.webviewAuthEnabled = false // Disable `Retailer Webview Authentication`
  BRAccountLinkingManager.shared().update(connection)
}
let connection = BRAccountLinkingConnection(retailer: account)
let taskId = BRAccountLinkingManager.shared().linkAccount(with: connection, withCompletion: { error, viewController, sessionId in
  if (error == .verificationNeeded) {
    // Display UIViewController for 2FA verification, CAPTCHA or other user input
  } else if (error == .verificationCompleted) {
    // Dismiss the previously presented UIViewController
  } else {
    if (error == .noCredentials) {
      // Error: Credentials have not been provided
    } else if (error == .internal) {
      // Error: Unexpected error
    } else if (error == .parsingFail) {
      // Error: General error
    } else if (error == .invalidCredentials) {
      // Error: Probable cause of the failure is invalid credentials
    } else if (error == .cancelled) {
      // Operation has been cancelled.
      // Dismiss any previously presented user input UIViewController
    } else if (error == .webViewClosed) {
      // Operation has been terminated due to dismissing of the webview.
      // Dismiss any previously presented user input UIViewController
    } else if (error == .unsupportedRetailer) {
      // Error: An attempt to verify account with invalid retailer
    }
    // Account has been linked successfully
  }
})


Re-authenticate a connection

let retailer = BRAccountLinkingRetailer.walmart

// This is optional 
// You can check connection's isAuthenticated status to avoid unnecessary re-authentication
if let connection = BRAccountLinkingManager.shared().getLinkedRetailerConnection(retailer) {
  if connection.isAuthenticated {
    return
  }
}

let taskId = BRAccountLinkingManager.shared().loginUser(forLinkedRetailer: retailer, withCompletion: { error, viewController, sessionId in
  if (error == .verificationNeeded) {
    // Display UIViewController for 2FA verification, CAPTCHA or other user input
  } else if (error == .verificationCompleted) {
    // Dismiss the previously presented UIViewController
  } else {
    if (error == .noCredentials) {
      // Error: Credentials have not been provided
    } else if (error == .internal) {
      // Error: Unexpected error
    } else if (error == .parsingFail) {
      // Error: General error
    } else if (error == .invalidCredentials) {
      // Error: Probable cause of the failure is invalid credentials
    } else if (error == .cancelled) {
      // Operation has been cancelled.
      // Dismiss any previously presented user input UIViewController
    } else if (error == .webViewClosed) {
      // Operation has been terminated due to dismissing of the webview.
      // Dismiss any previously presented user input UIViewController
    } else if (error == .unsupportedRetailer) {
      // Error: An attempt to verify account with invalid retailer
    }
    // Re-authentication has been completed successfully
  }
})


Grab New Orders

Once credentials are verified and a retailer is linked, we will search for purchases in a user’s account.

let taskId = BRAccountLinkingManager.shared().grabNewOrders(for: .walmart) { retailer, order, remaining, viewController, errorCode, sessionID in
  if (errorCode == .verificationNeeded) {
    // Display UIViewController for 2FA verification, CAPTCHA or other user input
  } else if errorCode == .verificationCompleted {
    // Dismiss the previously presented UIViewController
  } else if errorCode == .none {
    // Orders will be returned here         
    if (remaining <= 0) {
      // Grab Orders completed successfully
      // No more orders to fetch
    }            
  }  else if (error == .webViewClosed) {
    // Operation has been terminated due to dismissing of the webview.
    // Dismiss any previously presented user input UIViewController
  } else if (error == .unsupportedRetailer) {
    // Error: An attempt to verify account with invalid retailer
  } else {
    // Grab Orders Failed. Check error for more info
  }
}

Note

The SDK will maintain an internal date of the last successful search for each retailer so that each time you attempt to retrieve new messages, you will only get messages since the last successful check, and subject to the day cutoff that you set (default is 15 days).


Grab New Orders (App In Background)

Account Linking also provides an option for apps to periodically check for new orders while the app is in background. Follow the steps below if you want to enable background fetch in your app.

1. iOS Background Task

  • Create task identifier by adding background task support in your app. More info here

  • Enable background fetch by using the task identifier from step 1

BRAccountLinkingManager.shared().enableBackgroundFetch(withIdentifier: taskIdentifier)

2. Receive results from a background operation

  • Implement backgroundFetchCompletion callback so the app can receive messages from a background job that started via backogrund task
BRAccountLinkingManager.shared().backgroundFetchCompletion = { retailer, errorCode, sessionId, order in }

Example:

import UIKit
import BlinkReceipt
import BlinkEReceipt

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        BRScanManager.shared().licenseKey = "YOUR-LICENSE-KEY"
        BRScanManager.shared().prodIntelKey = "YOUR-PROD-INTEL-KEY"

        // Enable background refresh via background task
        let taskIdentifier = "Host App Background Task Identifier"
        BRAccountLinkingManager.shared().enableBackgroundFetch(withIdentifier: taskIdentifier)

        // A callback for background task sync operations
        BRAccountLinkingManager.shared().backgroundFetchCompletion = { retailer, errorCode, sessionId, order in
          // Same flow as Grab Orders above
        }
        return true
    }
}