Commit 6fbf7d52 authored by Aral Balkan's avatar Aral Balkan

Implement cancel patronage feature

parent 37d75a97
......@@ -16,66 +16,79 @@
</style>
</head>
<body>
<h1>Your patronage</h1>
<section id='resume-your-patronage' ${isPaused ? '' : 'hidden'}>
<p class='your-patronage-is-paused-message'>Your patronage is paused.</p>
<section id='cancelled-patronage' ${isActive ? 'hidden' : ''}>
<h1>Cancelled patronage</h1>
<h2>Thank you for supporting us</h2>
<p>Your patronage started ${patronSince.relative} (${patronSince.absolute})</span> and was cancelled <span id='cancelled-at-relative'>${cancelledAt.relative}</span> (<span id='cancelled-at-absolute'>${cancelledAt.absolute})</span>.</p>
<p>Your last payment was taken ${currentPeriodStart.relative} (${currentPeriodStart.absolute}).</p>
<p>To become a patron again, please visit our <a href='/fund-us'>fund us</a> page.</p>
</section>
<h2>Resume your patronage</h2>
<section id='active-patronage' ${isActive ? '' : 'hidden'}>
<h1>Your patronage</h1>
<p>This will resume your patronage effective immediately. You can pause it again at any point.</p>
<section id='resume-your-patronage' ${isPaused ? '' : 'hidden'}>
<p class='your-patronage-is-paused-message'>Your patronage is paused.</p>
<form id='resume'>
<input id='resumeButton' type='submit' value='Resume my patronage' />
</form>
</section>
<h2>Resume your patronage</h2>
<p>Status: <span id='status'>offline</span></p>
<p>This will resume your patronage effective immediately. You can pause it again at any point.</p>
<div id='received'></div>
<form id='resume'>
<input id='resume-button' type='submit' value='Resume my patronage' />
</form>
</section>
${newPatronMessage}
<p>Status: <span id='status'>offline</span></p>
<p><strong>Thank you for supporting our work!</strong> If you haven’t bookmarked this page, please do so now. You can return here at any time to update, pause, or cancel your patronage. If you ever want to talk to a human being, you can contact Aral and Laura at <a href='mailto:support@small-tech.org'>support@small-tech.org</a> or at <a href='https://social.small-tech.org/support'>support@social.small-tech.org</a>.</p>
${newPatronMessage}
<h2>At a glance</h2>
<p><strong>Thank you for supporting our work!</strong> If you haven’t bookmarked this page, please do so now. You can return here at any time to update, pause, or cancel your patronage. If you ever want to talk to a human being, you can contact Aral and Laura at <a href='mailto:support@small-tech.org'>support@small-tech.org</a> or at <a href='https://social.small-tech.org/support'>support@social.small-tech.org</a>.</p>
<ul>
<li><strong>Status:</strong> <span id='patronage-status'>${patronageStatus}</span></li>
<li><strong>Patron since:</strong> ${patronSinceRelative} (${patronSinceAbsolute})</strong>
<li><strong>Amount:</strong><span id='patronageAmountDisplay'>${patronageAmount}</span>/month</li>
<li><strong>Last payment:</strong> ${currentPeriodStartRelative} (${currentPeriodStartAbsolute})</li>
<li><strong>Next payment:</strong> ${currentPeriodEndRelative} (${currentPeriodEndAbsolute})</li>
</ul>
<h2>At a glance</h2>
<h2>Update your patronage</h2>
<ul>
<li><strong>Status:</strong> <span id='patronage-status'>${patronageStatus}</span></li>
<li><strong>Patron since:</strong> ${patronSince.relative} (${patronSince.absolute})</strong>
<li><strong>Amount:</strong><span id='patronageAmountDisplay'>${patronageAmount}</span>/month</li>
<li><strong>Last payment:</strong> ${currentPeriodStart.relative} (${currentPeriodStart.absolute})</li>
<li><strong>Next payment:</strong> ${currentPeriodEnd.relative} (${currentPeriodEnd.absolute})</li>
</ul>
<p>This will update your patronage amount effective immediately.</p>
<section id='update-your-patronage'>
<h2>Update your patronage</h2>
<form id='update'>
<label for='amount'>Amount (€/month):<label>
<input id='patronageAmount' type='text' value='${patronageAmount}'>
<br><br>
<input id='updateButton' type='submit' value='Update my patronage' />
</form>
<p>This will update your patronage amount effective immediately.</p>
<section id='pause-your-patronage' ${isPaused ? 'hidden' : ''}>
<h2>Pause your patronage</h2>
<form id='update'>
<label for='amount'>Amount (€/month):<label>
<input id='patronageAmount' type='text' value='${patronageAmount}'>
<br><br>
<input id='update-button' type='submit' value='Update my patronage' />
</form>
</section>
<p>This will pause your patronage effective immediately. You can resume it again at any point.</p>
<section id='pause-your-patronage' ${isPaused ? 'hidden' : ''}>
<h2>Pause your patronage</h2>
<form id='pause'>
<input id='pauseButton' type='submit' value='Pause my patronage' />
</form>
</section>
<p>This will pause your patronage effective immediately. You can resume it again at any point.</p>
<h2>Cancel your patronage</h2>
<form id='pause'>
<input id='pause-button' type='submit' value='Pause my patronage' />
</form>
</section>
<p>This will stop your monthly patronage effective immediately.</p>
<section id='cancel-your-patronage'>
<h2>Cancel your patronage</h2>
<form id='cancel'>
<input type='submit' value='Cancel my patronage' />
</form>
<p>This will stop your monthly patronage effective immediately.</p>
<form id='cancel'>
<input id='cancel-button' type='submit' value='Cancel my patronage' />
</form>
</section>
</section>
<script>
const subscriptionId = '${subscriptionId}'
......
......@@ -21,8 +21,8 @@ window.addEventListener('load', _ => {
switch (response.for) {
case 'update':
$('#updateButton').value = 'Update my patronage'
$('#updateButton').disabled = false
$('#update-button').value = 'Update my patronage'
$('#update-button').disabled = false
$('#patronageAmount').value = ''
$('#patronageAmountDisplay').innerHTML = response.amount
Modal.fire(
......@@ -34,8 +34,8 @@ window.addEventListener('load', _ => {
case 'pause':
$('#pause-your-patronage').hidden = true
$('#pauseButton').value = 'Pause my patronage'
$('#pauseButton').disabled = false
$('#pause-button').value = 'Pause my patronage'
$('#pause-button').disabled = false
$('#resume-your-patronage').hidden = false
$('#patronage-status').innerHTML = 'paused'
Modal.fire(
......@@ -47,8 +47,8 @@ window.addEventListener('load', _ => {
case 'resume':
$('#resume-your-patronage').hidden = true
$('#resumeButton').value = 'Resume my patronage'
$('#resumeButton').disabled = false
$('#resume-button').value = 'Resume my patronage'
$('#resume-button').disabled = false
$('#pause-your-patronage').hidden = false
$('#patronage-status').innerHTML = 'active'
Modal.fire(
......@@ -57,6 +57,20 @@ window.addEventListener('load', _ => {
'success'
)
break
case 'cancel':
$('#cancel-button').value = 'Cancel my patronage'
$('#cancel-button').disabled = false
$('#active-patronage').hidden = true
$('#cancelled-patronage').hidden = false
$('#cancelled-at-relative').innerHTML = response.cancelledAt.relative
$('#cancelled-at-absolute').innerHTML = response.cancelledAt.absolute
Modal.fire(
'Patronage canceled',
`Your patronage has been canceled effective immediately. If you want to become a patron again in the future, please visit our <a href='/fund-us'>Fund Us</a> page. Thank you again for supporting us.`,
'success'
)
break
}
}
......@@ -91,18 +105,18 @@ window.addEventListener('load', _ => {
if (commandName === 'update') {
command.amount = $('#patronageAmount').value
$('#updateButton').value = 'Updating…'
$('#updateButton').disabled = true
$('#update-button').value = 'Updating…'
$('#update-button').disabled = true
}
if (commandName === 'pause') {
$('#pauseButton').value = 'Pausing…'
$('#pauseButton').disabled = true
$('#pause-button').value = 'Pausing…'
$('#pause-button').disabled = true
}
if (commandName === 'resume') {
$('#resumeButton').value = 'Resuming…'
$('#resumeButton').disabled = true
$('#resume-button').value = 'Resuming…'
$('#resume-button').disabled = true
}
let proceed = true
......@@ -110,6 +124,10 @@ window.addEventListener('load', _ => {
if (!proceed) return
if (commandName === 'cancel') {
$('#cancel-button').value = 'Cancelling…'
}
socket.send(JSON.stringify(command))
})
}
......
......@@ -9,6 +9,10 @@ const clientSideJavaScript = fs.readFileSync('.dynamic/client/patron.js')
const sweetAlert2 = fs.readFileSync('.dynamic/client/sweetalert2.min.js')
const styles = fs.readFileSync('.dynamic/client/patron.css')
//
// Refactor: also used in WebSocket route. Pull out.
//
// Converts a Stripe (Unix) timestamp string to a JavaScript date object.
// (JS expects the timestamp in miliseconds so we pad three decimal places).
function timestampToDate(timestamp) {
......@@ -20,6 +24,15 @@ function lowercaseFirstLetter (s) {
return s.charAt(0).toLowerCase() + s.slice(1)
}
function parseTimestamp(timestamp) {
const timestampAsDate = timestampToDate(timestamp)
const dateAsMoment = moment(timestampAsDate)
return {
relative: dateAsMoment.fromNow(),
absolute: lowercaseFirstLetter(dateAsMoment.calendar())
}
}
module.exports = async (request, response, next) => {
function render (template) {
......@@ -38,32 +51,25 @@ module.exports = async (request, response, next) => {
const session = await stripe.checkout.sessions.retrieve(sessionId)
const subscription = await stripe.subscriptions.retrieve(session.subscription)
console.log(subscription)
const subscriptionId = subscription.id
const subscriptionStatus = subscription.status
const itemId = subscription.items.data[0].id
const planId = subscription.plan.id
const patronSince = timestampToDate(subscription.billing_cycle_anchor)
const patronSinceMoment = moment(patronSince)
const patronSinceRelative = patronSinceMoment.fromNow()
const patronSinceAbsolute = lowercaseFirstLetter(patronSinceMoment.calendar())
const currentPeriodStart = timestampToDate(subscription.current_period_start)
const currentPeriodStartMoment = moment(currentPeriodStart)
const currentPeriodStartRelative = currentPeriodStartMoment.fromNow()
const currentPeriodStartAbsolute = lowercaseFirstLetter(currentPeriodStartMoment.calendar())
const patronSince = parseTimestamp(subscription.billing_cycle_anchor)
const currentPeriodStart = parseTimestamp(subscription.current_period_start)
const currentPeriodEnd = parseTimestamp(subscription.current_period_end)
const currentPeriodEnd = timestampToDate(subscription.current_period_end)
const currentPeriodEndMoment = moment(currentPeriodEnd)
const currentPeriodEndRelative = currentPeriodEndMoment.fromNow()
const currentPeriodEndAbsolute = lowercaseFirstLetter(currentPeriodEndMoment.calendar())
let cancelledAt = {relative: null, absolute: null}
if (subscription.canceled_at !== null) {
cancelledAt = parseTimestamp(subscription.canceled_at)
}
const patronageAmount = subscription.quantity
const isActive = subscriptionStatus === 'active'
const isPaused = subscription.discount !== null
const patronageStatus = subscriptionStatus === 'active' ? (isPaused ? 'paused' : 'active') : subscriptionStatus
const patronageStatus = isActive ? (isPaused ? 'paused' : 'active') : subscriptionStatus
// Newly-created?
let newPatronMessage = ''
......
......@@ -2,6 +2,33 @@ const stripe = require('stripe')('sk_test_GT9DfvDjhljTdIe3rKyEHtyU00RVQ0FCNP')
const pausePatronageCouponId = 'VKQ30S0b'
//
// Refactor: also used in HTTPS GET route. Pull out.
//
const moment = require('moment')
// Converts a Stripe (Unix) timestamp string to a JavaScript date object.
// (JS expects the timestamp in miliseconds so we pad three decimal places).
function timestampToDate(timestamp) {
return new Date(parseInt(`${timestamp}000`))
}
function lowercaseFirstLetter (s) {
if (typeof s !== 'string') return ''
return s.charAt(0).toLowerCase() + s.slice(1)
}
function parseTimestamp(timestamp) {
const timestampAsDate = timestampToDate(timestamp)
const dateAsMoment = moment(timestampAsDate)
return {
relative: dateAsMoment.fromNow(),
absolute: lowercaseFirstLetter(dateAsMoment.calendar())
}
}
module.exports = (webSocket, request) => {
webSocket.on('message', async data => {
......@@ -61,7 +88,15 @@ module.exports = (webSocket, request) => {
break;
case 'cancel':
console.log('CANCEL')
try {
result = await stripe.subscriptions.del(command.subscriptionId)
} catch (error) {
webSocket.send(JSON.stringify({for: command.action, error}))
}
webSocket.send(JSON.stringify({
for: command.action,
cancelledAt: parseTimestamp(result.canceled_at)
}))
break;
default:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment