Created
December 25, 2018 06:41
-
-
Save whs/4d998b621f3acd2ae2a7195fa42483a7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<meta charset="UTF-8"> | |
<meta property="og:type" content="website" /> | |
<meta property="og:title" content="{% if amount %}Pay {{amount}}{% else %}Pay me{% endif %}" /> | |
<meta property="og:image" content="{{qrUrl}}"> | |
<meta property="og:url" content="{{url}}"> | |
<meta property="og:description" content=""> | |
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1.0"> | |
<title>{% if amount %}Pay {{amount}}{% else %}Pay me{% endif %}</title> | |
<style> | |
body{ | |
margin: 0; padding: 0; | |
background-color: #333; | |
color: #333; | |
font-family: -apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif; | |
} | |
#inner{ | |
width: 100%; | |
max-width: 400px; | |
margin: auto; | |
margin-top: 20px; | |
background-color: white; | |
padding: 10px; | |
box-sizing: border-box; | |
text-align: center; | |
font-size: 9pt; | |
box-shadow: #222 0px 3px 30px; | |
} | |
img{ | |
width: 100%; | |
image-rendering: pixelated; | |
} | |
#ref{ | |
cursor: pointer; | |
} | |
</style> | |
<div id="inner"> | |
<img src="{{qr}}"> | |
<p>Ref ID: <span id="ref">{{id}}</span></p> | |
{% if amount %}<p>Amount: {{amount}}</p>{% endif %} | |
</div> | |
<script> | |
document.getElementById("ref").addEventListener("click", function(){ | |
window.getSelection().selectAllChildren(this); | |
document.execCommand("copy"); | |
}); | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const Koa = require('koa'); | |
const Router = require('koa-trie-router'); | |
const nunjucks = require('nunjucks'); | |
const promptpay = require('promptpay-qr'); | |
const qrcode = require('qrcode'); | |
const stream = require('stream'); | |
const PROMPTPAY_IDS = { | |
k: '004999000071927', | |
}; | |
const DEFAULT_ID = process.env['DEFAULT_ID'] || 'k'; | |
const app = new Koa(); | |
app.proxy = true; | |
const router = new Router(); | |
nunjucks.configure('views', { autoescape: true }); | |
router.use((ctx, next) => { | |
if (process.env.NODE_ENV === 'production') { | |
ctx.set('Cache-Control', 'public,max-age=2592000'); | |
} | |
return next(); | |
}); | |
router.get('/', async (ctx, next) => { | |
await response(ctx, { | |
id: PROMPTPAY_IDS[DEFAULT_ID], | |
}); | |
return next(); | |
}); | |
router.get('/:amount(\\d{1,10}(\\.\\d{2}|))', async (ctx, next) => { | |
await response(ctx, { | |
id: PROMPTPAY_IDS[DEFAULT_ID], | |
amount: parseFloat(ctx.params.amount), | |
}); | |
return next(); | |
}); | |
router.get('/:account', async (ctx, next) => { | |
let id = PROMPTPAY_IDS[ctx.params.account]; | |
if (!id) { | |
return next(); | |
} | |
await response(ctx, { | |
id: id, | |
}); | |
return next(); | |
}); | |
router.get('/:account/:amount(\\d{1,10}(\\.\\d{2}|))', async (ctx, next) => { | |
let id = PROMPTPAY_IDS[ctx.params.account]; | |
if (!id) { | |
return next(); | |
} | |
await response(ctx, { | |
id: id, | |
amount: parseFloat(ctx.params.amount), | |
}); | |
return next(); | |
}); | |
// qr | |
router.get('/_qr/', async (ctx, next) => { | |
await qrResponse(ctx, { | |
id: PROMPTPAY_IDS[DEFAULT_ID], | |
}); | |
return next(); | |
}); | |
router.get('/_qr/:amount(\\d{1,10}(\\.\\d{2}|))', async (ctx, next) => { | |
await qrResponse(ctx, { | |
id: PROMPTPAY_IDS[DEFAULT_ID], | |
amount: parseFloat(ctx.params.amount), | |
}); | |
return next(); | |
}); | |
router.get('/_qr/:account', async (ctx, next) => { | |
let id = PROMPTPAY_IDS[ctx.params.account]; | |
if (!id) { | |
return next(); | |
} | |
await qrResponse(ctx, { | |
id: id, | |
}); | |
return next(); | |
}); | |
router.get( | |
'/_qr/:account/:amount(\\d{1,10}(\\.\\d{2}|))', | |
async (ctx, next) => { | |
let id = PROMPTPAY_IDS[ctx.params.account]; | |
if (!id) { | |
return next(); | |
} | |
await qrResponse(ctx, { | |
id: id, | |
amount: parseFloat(ctx.params.amount), | |
}); | |
return next(); | |
} | |
); | |
async function response(ctx, data) { | |
ctx.set('Content-Type', 'text/html'); | |
let payload = promptpay(data.id, data); | |
let qr = await qrcode.toDataURL(payload, { | |
errorCorrectionLevel: 'H', | |
scale: 1, | |
dark: '#333333ff', | |
}); | |
ctx.body = nunjucks.render('index.html', { | |
id: data.id, | |
amount: data.amount, | |
qr: qr, | |
qrUrl: `${ctx.origin}/_qr${ctx.path}`, | |
url: ctx.href, | |
}); | |
} | |
async function qrResponse(ctx, data) { | |
ctx.set('Content-Type', 'image/png'); | |
ctx.status = 200; | |
let payload = promptpay(data.id, data); | |
let scale = parseInt(ctx.query.scale, 10); | |
if (isNaN(scale)) { | |
scale = 4; | |
} | |
scale = Math.min(Math.max(scale, 4), 100); | |
let inOutStream = new stream.Transform({ | |
transform(chunk, encoding, callback) { | |
this.push(chunk); | |
callback(); | |
}, | |
}); | |
qrcode.toFileStream(inOutStream, payload, { | |
errorCorrectionLevel: 'H', | |
scale: scale, | |
}); | |
inOutStream.pipe(ctx.res); | |
return new Promise((resolve) => { | |
inOutStream.on('finish', resolve); | |
}); | |
} | |
app.use(router.middleware()); | |
app.listen(3000); | |
console.log('http://localhost:3000'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"main": "index.js", | |
"dependencies": { | |
"koa": "^2.5.2", | |
"koa-trie-router": "^2.1.6", | |
"nunjucks": "^3.1.3", | |
"promptpay-qr": "^0.4.4", | |
"qrcode": "^1.2.0" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment