import {userId} from './user.js'
const getMetaTag = () => document.querySelector("head meta[name=monetization]")
/**
* A Set including names of all events that Web Monetization API
* (document.monetization) emits.
*/
export const monetizeEvents = new Set(
['monetizationStopped', 'monetized', 'monetizeFailed']
)
/*
'activeMedia' is the reference to the component currently being monetized.
WM allows only one address to be monetized at a time, so in terms of
WMM components, this means that only one component can be monetized at a time.
*/
let activeMedia, mProgressAction, mediaOrigin
/** This is a description of updateMedia */
function updateMedia(wmm) {
if (activeMedia && activeMedia.paymentUrl !== wmm.paymentUrl)
activeMedia.dispatchEvent(new CustomEvent('mediaMonetizationStopped',
{account: {paymentUrl: activeMedia.paymentUrl()}}))
activeMedia = wmm
try {
mediaOrigin = (new URL(wmm.src)).origin
} catch (err) {
mediaOrigin = location.origin
}
}
/**
* Initializes monetization of WmmAudio or WmmVideo instance.
* Monetization is started when the instance in inserted in DOM and only
* one media can be monetizad at a time (limit of WM API).
* For this reason, the monetize.js module tracks the currently active
* media, and emits a 'mediaMonetizationStopped' event on the previously
* active media, when a new media takes its place a the actively monetized
* media.
* @param {object} wmm WmmAudio or WmmVideo instance
*/
export function initMediaMonetization(wmm) {
if (!wmm.paymentUrl)
return console.error("Add paymentUrl attribute (<wmm-video paymentUrl='...'>)")
const skipBackendVerification = JSON.parse(wmm.getAttribute('skipVerification'))
updateMedia(wmm)
setPaymentUrl(wmm.paymentUrl)
if (!skipBackendVerification)
pipeReceiptEventsToBackend()
}
export function mediaRemoved(wmm) {
if (wmm === activeMedia) {
document.querySelector("head meta[name='monetization']").remove()
activeMedia = null
}
}
// NOTE: not used, since currently this is done in config file
const setPaymentPointerWithReceiptService = (paymentPointer, receiptService) =>
setPaymentUrl(receiptService + encodeURIComponent(paymentPointer))
function setPaymentUrl(paymentUrl) {
let metaTag = getMetaTag()
if (metaTag && metaTag.content == paymentUrl) {
return
}
if (!metaTag) {
var mTag = document.createElement('meta')
mTag.name = "monetization"
document.head.appendChild(mTag)
} else {
console.info(`Web monetization address changed: ${paymentUrl}`)
}
mTag.content = paymentUrl
}
// Save resources by not checking every receipt.
// (backend still receives the correct amount, since recipe service combines all previous recipes)
const waitBeforeNextRecipe = 600
function pipeReceiptEventsToBackend() {
if (!getMetaTag()) {
throw Error("setPaymentUrl (payment address) before piping payment events")
}
if (!userId) {
throw Error("userId required to identify receipt owner")
}
if (!document.monetization) {
return console.warn("Monetization not supported by browser, install wallet.")
}
if (mProgressAction)
document.monetization.removeEventListener('monetizationprogress', mProgressAction)
let prevTime = null
document.monetization.addEventListener('monetizationprogress',
mProgressAction = async (ev) => {
if (prevTime + waitBeforeNextRecipe > Date.now()) return
prevTime = Date.now()
if (!activeMedia) return // media removed from dom
if (!ev.detail?.receipt)
return console.log('No receipt fond, skips backend verification')
ev.detail.userId = userId
const res = await fetch(mediaOrigin + '/verifyReceipt', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(ev.detail)
})
const result = await res.text(),
success = !isNaN(Number(result))
if (success) {
activeMedia.dispatchEvent(new CustomEvent("monetized", {detail: {accountBalance: parseFloat(result)}}))
} else {
activeMedia.dispatchEvent(new CustomEvent("monetizeFailed", {detail: result}))
}
})
}