www/_src/assets/scripts/order/monero.js

200 lines
6.3 KiB
JavaScript

'use strict'
const formatUSD = (v) => v.toLocaleString(undefined, {
style: 'currency', currency: 'USD' })
const cancel = () =>
window.location = '/shop/cart/'
// Parse querystring (https://stackoverflow.com/a/901144/)
const qs = new Proxy(new URLSearchParams(window.location.search), {
get: (searchParams, prop) => searchParams.get(prop),
})
let socket
class moneroCheckoutItem { constructor(data) {
let self = this
self.name = data.name
self.price = data.price
self.qty = data.qty
self.subtotal = data.subtotal
} }
class moneroCheckoutTransaction { constructor(data) {
let self = this
self.amount = data.amount/1000000000000
self.confirmations = ko.observable(0)
self.double_spend_seen = ko.observable(false)
self.fee = data.fee/1000000000000
self.height = data.height
self.datetime = new Date(data.timestamp)
self.date = self.datetime.toLocaleDateString()
self.time = self.datetime.toLocaleTimeString()
self.tx_hash = data.tx_hash
self.unlock_time = ko.observable(0)
self.locked = ko.observable(true)
} }
class moneroCheckout { constructor(data) {
let self = this
self.items = ko.observableArray([])
self.shipname = ko.observable('')
self.contactname = ko.observable('')
self.phone = ko.observable('')
self.email = ko.observable('')
self.addr1 = ko.observable('')
self.addr2 = ko.observable('')
self.city = ko.observable('')
self.zip = ko.observable('')
self.state = ko.observable('')
self.tax = ko.observable('')
self.shipping = ko.observable('')
self.subtotal = ko.observable('')
self.total = ko.observable('')
self.totalxmr = ko.observable('')
self.totalxmr_pretty = ko.pureComputed(() =>
`${self.totalxmr()} XMR`
)
self.orderid = ko.observable('')
self.xmr_address = ko.observable('')
self.submitted = ko.observable(0)
self.submitted_pretty = ko.pureComputed( () =>
`${self.submitted()} XMR`
)
self.unsubmitted = ko.pureComputed( () =>
self.totalxmr() - self.submitted()
)
self.unlocked = ko.observable(0)
self.unlocked_pretty = ko.pureComputed( () =>
`${self.unlocked()} XMR`
)
self.isSubmitted = ko.pureComputed( () =>
( self.submitted() >= self.totalxmr() )
)
self.isPaid = ko.pureComputed( () =>
( self.unlocked() >= self.totalxmr() )
)
self.transactions = ko.observableArray([])
self.xmr_uri = ko.pureComputed(() =>
`monero:${self.xmr_address()}?tx_amount=${self.unsubmitted()}&tx_description=sales@slvit.us_${self.orderid()}`
)
let xmr_qr = new QRCode(
document.getElementById('xmr_qr'),
self.xmr_uri()
)
self.xmr_uri.subscribe(() => {
xmr_qr.clear()
xmr_qr.makeCode(self.xmr_uri())
})
self.checkingStatus = ko.observable('')
// Get order info from querystring
self.orderid(qs.order)
//self.xmr_address(qs.xmr_address)
// Load order from API
;(async () => {
let res; try {
res = await fetch(`${API_DOMAIN}/order/${qs.order}`, {
headers: {'Accept':'application/json'},
})
if (!res.ok) throw res.statusText
} catch (err) {
console.error(`Failed to fetch order from API!\n${err}`)
}; let order; try {
order = await res.json()
} catch (err) {
console.error(`Failed to parse JSON:\n${err}`)
}
self.xmr_address(order.xmr_address)
self.subtotal(formatUSD(order.subtotal))
self.shipping(formatUSD(order.shipping.amount))
self.tax(formatUSD(order.tax))
self.total(formatUSD(order.total))
self.orderid(order.id)
self.totalxmr(order.totalxmr)
order.items.forEach((item) =>
self.items.push({
name: item.name,
price: formatUSD(parseFloat(item.price)),
qty: item.qty,
subtotal: formatUSD(item.subtotal),
})
)
self.shipname(order.shipping.address.name)
self.addr1(order.shipping.address.addr1)
self.addr2(order.shipping.address.addr2)
self.city(order.shipping.address.city)
self.zip(order.shipping.address.zip)
self.state(order.shipping.address.state)
self.contactname(order.contact.name)
self.phone(order.contact.phone)
self.email(order.contact.email)
// Poll for transactions
let poll_in = MONERO_CHECKOUT_POLL_SECS
let isChecking = false
const getTransactions = async () => {
isChecking = true
let res; try {
res = await fetch(`${API_DOMAIN}/xmr-receive/${order.xmr_address}`)
} catch (err) {
return console.error(`Failed to get update about xmr address ${order.xmr_address}\n${err}`)
} if (!res.ok)
return console.error(`Got a bad response when requesting update on this xmr address: ${res.status}\n${res}`)
let resData; try {
resData = await res.json()
} catch (err) {
return console.error(`Failed to parse response JSON: ${err}`)
} finally {
isChecking = false
poll_in = MONERO_CHECKOUT_POLL_SECS
}
resData.transactions.forEach( (tx) => {
const existingTransactions = self.transactions()
.filter((i) => (i.tx_hash===tx.tx_hash))
if (existingTransactions.length) {
existingTransactions[0].confirmations(tx.confirmations)
existingTransactions[0].double_spend_seen(tx.double_spend_seen)
existingTransactions[0].unlock_time(tx.unlock_time)
existingTransactions[0].locked(tx.locked)
} else {
self.transactions.push(new moneroCheckoutTransaction(tx))
self.transactions.sort( (a,b) =>
(a.height===b.height)?0
:(a.height<b.height)?-1:1
)
}
})
self.submitted(resData.amount.covered.total/1000000000000)
self.unlocked(resData.amount.covered.unlocked/1000000000000)
}; getTransactions(); setInterval(() => {
if (poll_in<=0) {
self.checkingStatus('Checking for transactions...')
if (!isChecking) getTransactions()
} else {
self.checkingStatus(`Checking for transactions in ${
poll_in--}...`)
}
}, 1000)
// Websockets
// socket = io(API_DOMAIN,{query:{orderid:data.id}})
// socket.on('mp-cb', (body) => {
// console.log(body)
// body.transactions.forEach( (tx) => {
// const existingTransactions = self.transactions()
// .filter((i) => (i.tx_hash===tx.tx_hash))
// if (existingTransactions.length>0) {
// existingTransactions[0].confirmations(tx.confirmations)
// existingTransactions[0].double_spend_seen(tx.double_spend_seen)
// existingTransactions[0].unlock_time(tx.unlock_time)
// existingTransactions[0].locked(tx.locked)
// } else self.transactions.push(tx)
// })
// self.submitted(body.amount.covered.total)
// self.unlocked(body.amount.covered.unlocked)
// })
} )()
} }; ko.applyBindings(new moneroCheckout())