Skip to content

Instantly share code, notes, and snippets.

@whs
Created December 25, 2018 06:41
Show Gist options
  • Save whs/4d998b621f3acd2ae2a7195fa42483a7 to your computer and use it in GitHub Desktop.
Save whs/4d998b621f3acd2ae2a7195fa42483a7 to your computer and use it in GitHub Desktop.
<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>
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');
{
"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