Create Offer Presentation Experience
This section describes how to create an offer presentation experience for your users, and includes the following topics:
Create Offer Presentation Experience
Create Offer Presentation Experience
Create Offer Presentation Experience
Create Offer Presentation Experience
1.0. Enable Offer Notification via IQL Qualification Webhook
Ensure that your application backend configures a qualification webhook with Zendrive's IQL service. This webhook will be called every time a user gets qualified for an offer.
We recommend that you inform your users of successful offer qualification via a push notification. Alternatively, the mobile applications can also call the adUnit
API upon starting, to discern if the user has been qualified for an offer. If the user is not qualified for an offer, the adUnit
API will return an empty response.
Refer to the IQL API documentation for the qualification webhook here:
1.1 Integrate IQL Qualification Webhook Into the Application
Use the following code snippet to integrate the IQL qualification webhook into your application's backend.
@api_view(['POST'])
def qualification_webhook(request):
try:
original_data = request.data
payload_data = format_webhook_payload(original_data)
driver_id = payload_data['user']
driver = User.objects.get(driver_id=driver_id)
push_service = PushService()
push_service.send_qualification_push(driver)
return Response(status=status.HTTP_200_OK)
except User.DoesNotExist:
logger.error("Qualification web-hook was sent for a non-existent user"
+ driver_id)
return Response(status=status.HTTP_400_BAD_REQUEST)
except PushService.Error as e:
logger.exception(e)
return Response(status=status.HTTP_200_OK)
w
1.2. Send a Push Notification When The Webhook is Received
Use the following code snippet to dispatch a push notification to your application's backend when you receive the qualification webhook.
class PushService:
def send_qualification_push(self, driver):
self.__send_push(
driver,
message=None,
extra={
"qualification_status": "qualified",
"msg": "Yay! You qualified for a great offer."
})
def send_heartbeat_push(self, driver):
self.__send_push(driver, message=None, extra={"type": "heartbeat"})
def __send_push(self, driver, message, extra, content_available=1):
gcm_devices = GCMDevice.objects.filter(user=driver)
apns_devices = APNSDevice.objects.filter(user=driver)
if gcm_devices.exists() or apns_devices.exists():
try:
gcm_devices.send_message(message=message, extra=extra)
except Exception:
raise PushService.GCMError(
"Unable to send push notification to Android devices of user, {}."
.format(driver.driver_id))
try:
apns_devices.send_message(
message=message,
content_available=content_available,
extra=extra)
except Exception:
raise PushService.APNSError(
"Unable to send push notification to iOS devices of user, {}."
.format(driver.driver_id))
else:
raise PushService.NoRegisteredDevicesError(
"{} does not have any registered device token.".format(
driver.driver_id))
class Error(Exception):
"""
Base exception for PushService.
"""
class NoRegisteredDevicesError(Error):
"""
Raised if the user doesn't have any registered device tokens.
"""
class GCMError(Error):
"""
Raised upon failure to send push to Android devices.
"""
class APNSError(Error):
"""
Raised upon failure to send push to iOS devices.
"""
1.3. Refresh Driver Status After Qualification Push Notification
This section describes how to register for and handle remote notifications of IQL qualifications.
Use the following code snippet to request the user for notification permission:
class DashboardViewController {
...
...
func askNotificationPermission() {
UNUserNotificationCenter.current().delegate = appDelegate
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options:[.badge, .alert, .sound]) { (granted, error) in
guard granted else { return }
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
...
...
}
Use the following code to handle the notification received and update qualification status.
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
…
…
…
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
DDLogInfo("Received remote notification with userInfo: \(userInfo)")
if let _ = userInfo["qualification_status"] as? String {
guard let loginRepo = IQLRef.sharedInstance.loginRepository else {
fatalError("LoginRepository does not exist")
}
if loginRepo.programState != nil {
loginRepo.state = .Qualified
UserDefaults.standard.set(true, forKey: qualifiedKey)
}
}
}
Update the device token to IQL backend to enable push notifications when a user qualifies.
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
...
...
checkNotificationsPermissionStatus { isAuthorized in
if isAuthorized{
UNUserNotificationCenter.current().delegate = self
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
...
...
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
UserDefaults.standard.set(deviceTokenString, forKey: deviceTokenKey)
IQLRef.sharedInstance.jobManager.enqueue(job: SendDeviceTokenJob())
}
...
...
}
class SendDeviceTokenJob: Job {
func run() {
let core = IQLRef.sharedInstance
guard let driverId = core.loginRepository.currentUser?.driverId else {
fatalError("User id doesn't exist")
}
guard let deviceToken = UserDefaults.standard.value(forKey: "deviceToken") as? String else {
DDLogDebug("device token is missing")
return
}
core.api.sendToken(driverId: driverId, token: deviceToken, onSuccess: { postTokenData in
if postTokenData[0] as String == "success" {
DDLogDebug("Success: device token sent")
}
}, onFailure: { failureResponse in
DDLogDebug("Failure - \(failureResponse)")
DDLogDebug("End: device token sent failed")
}, onError: { error in
DDLogDebug("Error - \(error)")
DDLogDebug("End: device token sent Error")
})
}
}
sw
1.4. Check Periodically If the User Is Qualified Or Not
If the user doesn't receive the qualification notification, we use a pull mechanism to determine user qualification. We use the offer_available
(Boolean key) from the GET driver_status
API to present the offer experience to the user. Refresh the driver status details every time the application becomes active, if the last update is older than 5 minutes. Refer to the driverStatus Endpointfor more information.
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
...
...
func sceneDidBecomeActive(_ scene: UIScene) {
if IQLRef.sharedInstance.loginRepository.isLoggedIn && StatusUtils.sharedInstance.shouldRefreshStatusDetails() {
IQLRef.sharedInstance.jobManager.enqueue(job: FetchIQLDriverStatusJob())
}
}
...
...
}
class StatusUtils {
static let sharedInstance = StatusUtils()
let postDateKey: String = "previousStatusCall"
private init() { }
func shouldRefreshStatusDetails() -> Bool {
let currentDate = Date()
if let postDate = UserDefaults.standard.object(forKey: postDateKey) as? Date {
let postDateMinute = postDate.timeIntervalSinceReferenceDate/60
let currentDateMinute = currentDate.timeIntervalSinceReferenceDate/60
let minuteDifference = currentDateMinute - postDateMinute
if minuteDifference <= 5 {
return false
}
}
return true
}
}
class FetchIQLDriverStatusJob: Job {
func run() {
DDLogDebug("Start: Fetch driver status")
let core = IQLRef.sharedInstance
guard let driverId = core.loginRepository.currentUser?.driverId else {
fatalError("User id doesn't exist")
}
core.api.getDriverStatus(driverId: driverId,
onSuccess: { driverStatus in
core.iqlDriverStatusRepository.refreshDriverStatus(driverStatus: driverStatus)
}, onFailure: { failure in
DDLogDebug("Failure - \(failure)")
DDLogDebug("End: Update driver status Failed")
}) { error in
DDLogDebug("Error - \(error)")
DDLogDebug("End: Update driver status Error")
}
}
}
class API {
...
...
func getDriverStatus(driverId: String, onSuccess: ((_ driverStatus: IQLDriverStatus) -> Void)?, onFailure: ((_ failureResponse: FailureResponse) -> Void)?, onError: ((_ error: APIError) -> Void)? ) {
executeGetRequest(
endpoint: .getDriverStatus,
pathParams: ["driver_id": driverId],
onSuccess: onSuccess, onFailure: onFailure, onError: onError)
}
...
...
}
class IQLDriverStatusRepository {
...
...
func refreshDriverStatus(driverStatus: IQLDriverStatus) {
if (driverStatus.metrics?.offerAvailable ?? false) {
loginRepo?.state = .Qualified
UserDefaults.standard.set(true, forKey: "isQualified")
IQLRef.sharedInstance.jobManager.enqueue(job: FetchADUnitJob())
}
}
}
2.0. Present An Offer Using IQL adUnit
API
adUnit
APIThe adUnit
API fetches the advertisement details after the user qualifies for an offer. Refer to the documentation for the adUnit
API here:
Refer to the adUnit
API wrapper implementation in Python here:
2.1. Fetch IQL Advertisement Unit Details
Check for the offer_available
flag in the driverStatus
API response. If the response states true, then fetch ad unit details, using the code given in the Developer Instructions tab below. Refer to the adUnit Endpoint V2.0 for more information.

2.2. Build UI Experience To Present Offer Details
Refer to the code snippet and reference code implementation link provided in the following tabs, to build an UI to present the available offers details to the user.
class DashboardController: BaseViewController {
...
...
func updateViews() {
...
...
if loginRepo.state == .Qualified && core.iqlAdUnitRepository.getAdUnitDetails() != nil {
buildOfferPage(rootView: offerCardContainer)
scrollView.subviews(offerCardContainer, performanceView, activationContainer, cardsContainer)
...
...
} else {
...
...
}
...
...
}
}
2.3. Capture Additional User Data
After the user claims the offer, prompt the user to provide additional information which will be sent to insurers for further processing.
The design flow when the publisher has all the user details to present the offer.

The design flow when the publisher does not have all the user details to present the offer.

3.0. IQL Reference Application UX Design
Here is the overall design implementation for the IQL Reference Application:
4.0. Publisher Integration Testing Checklist
Use the following testing checklist to create an offer presentation experience:
Offer Presentation:
Test that the push notification for the offer is displayed correctly and takes the user to the offer page when tapped.
Verify that the user is able to input their personal information accurately. Additionally, validate that existing user fields within the application are correctly pre-filled.