@tamara-solution/checkout
Version:
Script will be embedded in merchant's site to checkout. The merchant's don't need to redirect to tamara's site.
233 lines (178 loc) • 6.04 kB
JavaScript
import Style from '@/helpers/style'
import debug from '@/helpers/debug'
import { getType, isFunction } from '@/helpers/type'
import { appendHTML, removeElement } from '@/helpers/document-html'
import generateFrameElement from '@/helpers/frame-template'
import { FrameError, TamaraTypeError } from '@/helpers/frame-error'
import { isValidTamaraURL, isLocalhost } from '@/helpers/validate-domain'
import FrameEvents from '@/constants/frame-events'
import CheckoutEvents from '@/constants/checkout-events'
class CheckoutFrame {
constructor() {
this.config = {}
this.eventHandlers = {}
this.styles = new Style({})
this.resizeListener = null
this.Events = Object.freeze({ ...FrameEvents })
this._listenEventFromIframe()
}
init(config) {
this.config = config || {}
const { style: customestyle = {} } = config || {}
this.styles = new Style({ ...customestyle })
}
checkout(checkoutURL) {
this.callListener(this.Events.STARTED, { started: true })
const params = {
url: this._getCheckoutURL(checkoutURL),
styles: this._getStyle(),
loaded: this._callbackLoaded.bind(this),
}
const { frameElement, iframe, wrapperIframe } = generateFrameElement(params)
this.resizeListener = this._recalculateStyle.bind(
this,
wrapperIframe,
iframe
)
window.addEventListener('resize', this.resizeListener)
appendHTML('body', frameElement)
}
addEventHandlers(event, handler) {
if (this.config.debug) {
debug('ADD Event hanlders >>> ', event)
}
if (getType(handler) !== 'function') {
throw new TamaraTypeError('Handler must be a function!')
}
if (getType(this.eventHandlers[event]) !== 'array') {
this.eventHandlers[event] = []
}
if (this.config.debug) {
debug('Event hanlders >>> Before ADD >>> ', this.eventHandlers)
}
const foundIndex = this.eventHandlers[event].indexOf(handler)
if (foundIndex > -1) {
throw new FrameError('The handler is exist already!')
}
this.eventHandlers[event].push(handler)
if (this.config.debug) {
debug('Event hanlders >>> After ADDED >>> ', this.eventHandlers)
debug('ADD Event hanlders >>> SUCCESS')
}
}
removeEventHandlers(event, handler) {
if (this.config.debug) {
debug('REMOVE Event hanlders >>> ', event)
}
if (getType(handler) !== 'function') {
throw new TamaraTypeError('Handler must be a function!')
}
if (getType(this.eventHandlers[event]) !== 'array') {
return true
}
if (this.config.debug) {
debug('Event hanlders >>> Before REMOVE >>> ', this.eventHandlers)
}
const foundIndex = this.eventHandlers[event].indexOf(handler)
if (foundIndex > -1) {
// Delete an element at foundIndex
this.eventHandlers[event].splice(foundIndex, 1)
}
if (this.config.debug) {
debug('Event hanlders >>> After REMOVED >>> ', this.eventHandlers)
debug('REMOVE Event hanlders >>> SUCCESS')
}
}
removeAllEventHandlers(event) {
if (this.config.debug) {
debug('REMOVE_ALL Event hanlders >>> ', event)
}
if (getType(handler) !== 'function') {
throw new TamaraTypeError('Handler must be a function!')
}
if (getType(this.eventHandlers[event]) !== 'array') {
return true
}
if (this.config.debug) {
debug('Event hanlders >>> Before REMOVE_ALL >>> ', this.eventHandlers)
}
this.eventHandlers[event] = []
if (this.config.debug) {
debug('Event hanlders >>> After REMOVED >>> ', this.eventHandlers)
debug('REMOVE_ALL Event hanlders >>> SUCCESS')
}
}
callListener(event, data = {}) {
const handlers = this.eventHandlers[event]
if (this.config.debug) {
debug('TRIGGER Event >>> ', event, data)
}
if (getType(handlers) === 'array' && handlers.length > 0) {
handlers.forEach(function (handler) {
if (isFunction(handler)) {
handler(data)
}
})
}
}
_recalculateStyle(wrapperIframe, iframe) {
const styles = this._getStyle()
const wrapperStyle = styles.wrapper || ''
const iframeStyle = styles.iframe || ''
wrapperIframe.setAttribute('style', wrapperStyle)
iframe.setAttribute('style', iframeStyle)
}
_getStyle() {
return {
frame: this.styles.getFrameStyle(),
wrapper: this.styles.getWrapperStyle(),
iframe: this.styles.getIframeStyle(),
background: this.styles.getBackgroundStyle(),
}
}
_getCheckoutURL(checkoutURL) {
// Only accept localhost in development mode
const isLocalhostURL = isLocalhost(checkoutURL)
if (!isValidTamaraURL(checkoutURL) && isLocalhostURL) {
throw new FrameError("The URL is not tamara's domain.")
}
return `${checkoutURL}&checkoutFrame=iframe`
}
_callbackLoaded() {
this.callListener(this.Events.LOADED, { loaded: true })
}
_listenEventFromIframe() {
const EVENT_MAPPING = {
[CheckoutEvents.ORDER_SUCCESS]: this._handleOrderSuccess.bind(this),
[CheckoutEvents.ORDER_FAILED]: this._handleOrderFailed.bind(this),
[CheckoutEvents.ORDER_CANCELED]: this._handleOrderCanceled.bind(this),
}
window.addEventListener('message', function (evt) {
const { type, event, data } = evt.data || {}
if (type === 'tamara_checkout_message') {
const handler = EVENT_MAPPING[event]
if (isFunction(handler)) {
handler(data)
}
}
})
}
_handleOrderSuccess(data) {
this.callListener(this.Events.SUCCESS, data)
this._closeIframe()
}
_handleOrderFailed(data) {
this.callListener(this.Events.FAILED, data)
this._closeIframe()
}
_handleOrderCanceled(data) {
this.callListener(this.Events.CANCELED, data)
this._closeIframe()
}
_closeIframe() {
removeElement('tamara-checkout-frame')
this.callListener(this.Events.CLOSED)
window.removeEventListener('resize', this.resizeListener)
}
}
export default CheckoutFrame