Monitor the lifecycle of a checkout session
With Checkout Sheet Kit, app developers can register callbacks for key checkout lifecycle events such as failed or completed checkouts to monitor and log the status of a checkout session. Additionally, app developers can subscribe to pixel events to get a better understanding of customer behavior if they have the proper levels of consent.
Anchor to What you'll learnWhat you'll learn
In this tutorial, you'll learn how to do the following tasks:
- Register callbacks for key lifecycle events during the checkout session
- Integrate with web pixels to monitor customer behavior
Anchor to RequirementsRequirements
- You have the proper levels of consent to subscribe to this data
- You have an app using Checkout Sheet Kit
Anchor to Register callbacks for key lifecycle events during the checkout sessionRegister callbacks for key lifecycle events during the checkout session
To register callbacks for key lifecycle events during the checkout session:
- In iOS code, implement
ShopifyCheckoutSheetKitDelegate
delegate methods. - In Android code, override methods on an
DefaultCheckoutEventProcessor
object. - In React Native code, add event listeners to the object returned by calling
useShopifyCheckoutSheet()
.
Register callbacks for key checkout lifecycle events
extension MyViewController: ShopifyCheckoutSheetKitDelegate {
func checkoutDidComplete(event: CheckoutCompletedEvent) {
// Called when the checkout was completed successfully by the customer.
// Use this to update UI, reset cart state, etc.
}
func checkoutDidCancel() {
// Called when the checkout was canceled by the customer.
// Use this to call `dismiss(animated:)`, etc.
}
func checkoutDidFail(error: CheckoutError) {
// Called when the checkout encountered an error and was aborted. The callback
// provides a `CheckoutError` enum, with one of the following values:
// Internal error: exception within the Checkout SDK code
// You can inspect and log the Error and stacktrace to identify the problem.
case sdkError(underlying: Swift.Error)
// Issued when the provided checkout URL results in an error related to the store configuration.
// Note: The SDK only supports stores migrated to Shopify Extensions in Checkout.
case configurationError(message: String)
// Unavailable error: checkout cannot be initiated or completed, e.g. due to network or server-side error
// The provided message describes the error and may be logged and presented to the customer.
case checkoutUnavailable(message: String)
// Expired error: checkout session associated with provided checkout URL is no longer available.
// The provided message describes the error and may be logged and presented to the customer.
case checkoutExpired(message: String)
}
func checkoutDidClickLink(url: URL) {
// Called when the customer clicks a link within the checkout experience:
// - email address (`mailto:`),
// - telephone number (`tel:`),
// - web (`http:`)
// and is being directed outside the application.
}
// Called when the Checkout has emitted a standard or custom Web Pixel event.
// Note that the event must be handled by the consuming app, and will not be sent from inside the checkout.
// See below for more information.
func checkoutDidEmitWebPixelEvent(event: PixelEvent) {
switch event {
case .standardEvent(let standardEvent):
recordAnalyticsEvent(standardEvent)
case .customEvent(let customEvent):
recordAnalyticsEvent(customEvent)
}
}
}
val processor = object : DefaultCheckoutEventProcessor(activity) {
override fun onCheckoutCompleted(checkoutCompletedEvent: CheckoutCompletedEvent) {
// Called when the checkout was completed successfully by the customer.
// Use this to update UI, reset cart state, etc.
}
override fun onCheckoutCanceled() {
// Called when the checkout was canceled by the buyer.
// Note: This will also be received after closing a completed checkout
}
override fun onCheckoutFailed(error: CheckoutException) {
// Called when the checkout encountered an error and was aborted.
}
override fun onCheckoutLinkClicked(uri: Uri) {
// Called when the customer clicks a link within the checkout experience:
// - email address (`mailto:`)
// - telephone number (`tel:`)
// - web (http:)
// - deep link (e.g. myapp://checkout)
// and is being directed outside the application.
// Note: to support deep links on Android 11+ using the `DefaultCheckoutEventProcessor`,
// the client app should add a queries element in its manifest declaring which apps it should interact with.
// See the MobileBuyIntegration sample's manifest for an example.
// Queries reference - https://developer.android.com/guide/topics/manifest/queries-element
// If no app can be queried to deal with the link, the processor will log a warning:
// `Unrecognized scheme for link clicked in checkout` along with the uri.
}
override fun onWebPixelEvent(event: PixelEvent) {
// Called when a web pixel event is emitted in checkout.
// Use this to submit events to your analytics system, see below.
}
override fun onShowFileChooser(
webView: WebView,
filePathCallback: ValueCallback<Array<Uri>>,
fileChooserParams: FileChooserParams,
): Boolean {
// Called to tell the client to show a file chooser. This is called to handle HTML forms with 'file' input type,
// in response to the customer pressing the "Select File" button.
// To cancel the request, call filePathCallback.onReceiveValue(null) and return true.
}
override fun onGeolocationPermissionsShowPrompt(origin: String, callback: GeolocationPermissions.Callback) {
// Called to tell the client to show a geolocation permissions prompt as a geolocation permissions
// request has been made.
// Invoked for example if a customer uses `Use my location` for pickup points
}
override fun onGeolocationPermissionsHidePrompt() {
// Called to tell the client to hide the geolocation permissions prompt, e.g. as the request has been cancelled
}
override fun onPermissionRequest(permissionRequest: PermissionRequest) {
// Called when a permission has been requested, e.g. to access the camera
// implement to grant/deny/request permissions.
}
}
/*Subscribing to an event returns an EmitterSubscription object, which contains a remove() function to unsubscribe. Here's an example of how you might create an event listener in a React useEffect, ensuring to remove it on unmount.*/
// Using hooks
const shopifyCheckout = useShopifyCheckoutSheet();
useEffect(() => {
const close = shopifyCheckout.addEventListener('close', () => {
// Do something on checkout close
});
const completed = shopifyCheckout.addEventListener(
'completed',
(event: CheckoutCompletedEvent) => {
// Lookup order on checkout completion
const orderId = event.orderDetails.id;
},
);
const error = shopifyCheckout.addEventListener(
'error',
(error: CheckoutError) => {
// Do something on checkout error
// console.log(error.message)
},
);
const pixel = shopifyCheckout.addEventListener(
'pixel',
(event: PixelEvent) => {
// Dispatch web pixel events to third-party services
if (hasPermissionToTrack) {
sendEventToAnalyticsProvider(event);
}
},
);
return () => {
// It is important to clear the subscription on unmount to prevent memory leaks
close?.remove();
completed?.remove();
error?.remove();
pixel?.remove();
};
}, [shopifyCheckout]);
/*You can use the removeEventListeners(eventName) method to remove all event listeners.*/
Anchor to Integrate with web pixels to monitor customer behaviorIntegrate with web pixels to monitor customer behavior
For behavioral monitoring, Checkout Web Pixel standard and custom events are relayed to your application through the checkoutDidEmitWebPixelEvent
delegate hook.
App developers should only subscribe to these events if they have proper levels of consent from merchants and customers and are responsible for adhering to Apple's privacy policy and local regulations like GDPR and ePrivacy directive before disseminating these events to first-party and third-party systems. For a full picture, you may need to augment these events with customer and session information derived from your app's state. Here's an example of how you can intercept events:
Integrate with web pixels
class MyViewController: UIViewController {
private func sendEventToAnalytics(event: StandardEvent) {
// Send standard event to third-party providers
}
private func sendEventToAnalytics(event: CustomEvent) {
// Send custom event to third-party providers
}
private func recordAnalyticsEvent(standardEvent: StandardEvent) {
if hasPermissionToCaptureEvents() {
sendEventToAnalytics(event: standardEvent)
}
}
private func recordAnalyticsEvent(customEvent: CustomEvent) {
if hasPermissionToCaptureEvents() {
sendEventToAnalytics(event: CustomEvent)
}
}
}
extension MyViewController: ShopifyCheckoutSheetKitDelegate {
func checkoutDidEmitWebPixelEvent(event: PixelEvent) {
switch event {
case .standardEvent(let standardEvent):
recordAnalyticsEvent(standardEvent: standardEvent)
case .customEvent(let customEvent):
recordAnalyticsEvent(customEvent: customEvent)
}
}
}
fun onWebPixelEvent(event: PixelEvent) {
if (!hasPermissionToCaptureEvents()) {
return
}
when (event) {
is StandardPixelEvent -> processStandardEvent(event)
is CustomPixelEvent -> processCustomEvent(event)
}
}
fun processStandardEvent(event: StandardPixelEvent) {
val endpoint = "https://example.com/pixel?id=${accountID}&uid=${userId}"
val payload = AnalyticsPayload(
eventTime = event.timestamp,
action = event.name,
details = event.data.checkout
)
// Send events to third-party servers
httpClient.post(endpoint, payload)
}
// ... other functions, incl. processCustomEvent(event)
const shopifyCheckout = useShopifyCheckoutSheet();
useEffect(() => {
const pixel = shopifyCheckout.addEventListener(
'pixel',
(event: PixelEvent) => {
// Dispatch web pixel events to third-party services
if (hasPermissionToTrack) {
sendEventToAnalyticsProvider(event);
}
},
);
return () => {
pixel?.remove();
};
}, [shopifyCheckout]);
The customData
attribute of a CustomPixelEvent can take on any shape. As such, this attribute will be returned as a string. Client applications should define a custom data type and deserialize the customData
string into that type.