UNPKG

@habit.analytics/habit-smartlink-reactcomponent

Version:

A React component for Habit SmartLink integration.

215 lines (159 loc) 6.31 kB
# smartlink-script.js — Integration Guide Framework-agnostic Smartlink embed. No npm, no build step, no framework required. Drop the file in your project and include it with a `<script>` tag. --- ## Quick start ```html <!DOCTYPE html> <html> <head> <script src="smartlink-script.js"></script> </head> <body> <!-- 1. A container element where the iframe will be injected --> <div id="smartlink-container"></div> <script> var smartlink = createSmartlink({ container: document.getElementById('smartlink-container'), hash: 'YOUR_HASH', prePaymentMethod: async function (quoteId) { const res = await fetch('/api/payments', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ quoteId }), }); const data = await res.json(); return { success: true, payment_id: data.id }; }, onPaymentSuccess: function (paymentData) { console.log('Payment complete', paymentData); }, }); </script> </body> </html> ``` --- ## Options reference | Option | Type | Required | Description | |---|---|---|---| | `container` | `HTMLElement` | Yes | DOM element where the iframe is appended. | | `hash` | `string` | Yes | Smartlink hash that identifies your flow. | | `pin` | `string` | No | Optional PIN for the Smartlink flow. | | `env` | `string` | No | `'int'` · `'qa'` · `'default'`. Auto-detected from `window.location` if omitted (see [Environment detection](#environment-detection)). | | `prePaymentMethod` | `async (quoteId) => { success, payment_id }` | Yes | Called when the iframe needs a payment ID. Must return an object with `success: boolean` and `payment_id: string`. | | `onPaymentSuccess` | `(paymentData: string) => void` | No | Called when the payment step completes successfully. | | `onCancelled` | `() => void` | No | Called when the user cancels or closes the flow. | | `onError` | `({ message, details }) => void` | No | Called when the iframe reports an error. | | `customStyle` | `Object` | No | CSS properties applied directly to the iframe element (e.g. `{ width: '100%', height: '700px' }`). | --- ## Return value `createSmartlink` returns an object with a single method: ```js var smartlink = createSmartlink({ ... }); // Removes the iframe from the DOM and unbinds all event listeners. smartlink.destroy(); ``` Call `destroy()` when the user navigates away, closes a modal, or whenever you need to tear down the embed. --- ## Environment detection If `env` is not provided, the script inspects `window.location.href` and maps it automatically: | URL contains | Resolved env | Iframe target | |---|---|---| | `integrations` | `int` | `distributors.integrations.habit.io` | | `localhost` | `int` | `distributors.integrations.habit.io` | | `qa` | `qa` | `distributors.qa.habit.io` | | anything else | `default` | `distributors.habit.io` | > **Local development** is intentionally routed to `int` (not a local iframe server) so that developers always test against a real hosted environment. To override explicitly: ```js createSmartlink({ ..., env: 'qa' }); ``` --- ## Default iframe dimensions The iframe is injected with the same default dimensions as the React component: ``` width: 320px height: 650px ``` The iframe will update its own height dynamically via `SMARTLINK_RESIZE` messages as the flow progresses. You can set initial overrides via `customStyle`: ```js createSmartlink({ ... customStyle: { width: '100%', maxWidth: '480px', height: '700px' }, }); ``` --- ## Framework-specific examples ### Vue 3 ```js import { onMounted, onUnmounted, ref } from 'vue'; const containerRef = ref(null); let smartlink = null; onMounted(() => { smartlink = createSmartlink({ container: containerRef.value, hash: 'YOUR_HASH', prePaymentMethod: (quoteId) => myApi.createPayment(quoteId), onPaymentSuccess: (data) => emit('success', data), }); }); onUnmounted(() => smartlink?.destroy()); ``` ```html <template> <div ref="containerRef" /> </template> ``` ### Angular ```ts import { Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core'; @Component({ template: '<div #container></div>' }) export class SmartlinkComponent implements OnInit, OnDestroy { @ViewChild('container', { static: true }) containerRef!: ElementRef; private smartlink: any; ngOnInit() { this.smartlink = (window as any).createSmartlink({ container: this.containerRef.nativeElement, hash: 'YOUR_HASH', prePaymentMethod: (quoteId: string) => this.paymentService.create(quoteId), onPaymentSuccess: (data: string) => this.handleSuccess(data), }); } ngOnDestroy() { this.smartlink?.destroy(); } } ``` ### Svelte ```svelte <script> import { onMount, onDestroy } from 'svelte'; let container; let smartlink; onMount(() => { smartlink = createSmartlink({ container, hash: 'YOUR_HASH', prePaymentMethod: (quoteId) => api.createPayment(quoteId), onPaymentSuccess: (data) => console.log(data), }); }); onDestroy(() => smartlink?.destroy()); </script> <div bind:this={container} /> ``` --- ## Message protocol The script handles all `postMessage` communication internally. For reference, these are the messages exchanged with the iframe: | Direction | Message type | When | |---|---|---| | iframe parent | `SMARTLINK_READY` | Iframe has mounted and is ready to start | | parent iframe | `SMARTLINK_INIT` | Sent immediately after `SMARTLINK_READY` | | iframe parent | `SMARTLINK_PREPAYMENT_METHOD` | Iframe needs a `payment_id` for a given `quote_id` | | parent iframe | `SMARTLINK_PREPAYMENT_METHOD_COMPLETE` | Parent resolved `prePaymentMethod` and sends back `{ success, payment_id }` | | iframe parent | `SMARTLINK_RESIZE` | Iframe content changed height/width | | iframe parent | `SMARTLINK_STEP_COMPLETE` | A step finished (payment success fires `onPaymentSuccess`) | | iframe parent | `SMARTLINK_CANCELLED` | User cancelled the flow | | iframe parent | `SMARTLINK_ERROR` | An error occurred inside the iframe | All messages are origin-validated only messages from the expected Smartlink domain are accepted.