mayar 0.2.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +117 -46
- package/package.json +1 -1
- package/src/cli.js +114 -32
- package/src/commands/balance.js +1 -1
- package/src/commands/bundling.js +36 -0
- package/src/commands/credit.js +72 -0
- package/src/commands/customer.js +5 -7
- package/src/commands/discount.js +52 -0
- package/src/commands/installment.js +44 -0
- package/src/commands/invoice.js +53 -25
- package/src/commands/membership.js +92 -0
- package/src/commands/payment-link.js +23 -0
- package/src/commands/payment.js +30 -14
- package/src/commands/product.js +97 -18
- package/src/commands/qrcode.js +35 -5
- package/src/commands/review.js +90 -18
- package/src/commands/saas.js +18 -0
- package/src/commands/software.js +17 -0
- package/src/commands/transaction.js +39 -12
- package/src/commands/webhook.js +46 -13
- package/src/util.js +28 -1
- package/node_modules/@mayaross/auth/README.md +0 -204
- package/node_modules/@mayaross/auth/index.js +0 -146
- package/node_modules/@mayaross/auth/package.json +0 -10
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const api = require('../api');
|
|
2
|
+
const ui = require('../ui');
|
|
3
|
+
const { checkResp, readData } = require('../util');
|
|
4
|
+
|
|
5
|
+
const USAGE = 'Usage: mayar discount <create|get|validate|check>';
|
|
6
|
+
|
|
7
|
+
async function run({ apiKey, flags, positional }) {
|
|
8
|
+
const [sub, ...rest] = positional;
|
|
9
|
+
switch (sub) {
|
|
10
|
+
case 'create': {
|
|
11
|
+
const body = readData(flags.data);
|
|
12
|
+
if (!body) throw new Error('mayar discount create requires --data <json|@file>');
|
|
13
|
+
const res = await api.request('POST', '/hl/v2/coupons/create', { apiKey, body });
|
|
14
|
+
checkResp(res); ui.jsonOut(res.body); return;
|
|
15
|
+
}
|
|
16
|
+
case 'get':
|
|
17
|
+
case 'detail': {
|
|
18
|
+
if (!rest[0]) throw new Error('Usage: mayar discount get <id>');
|
|
19
|
+
const res = await api.request('GET', `/hl/v2/coupons/${encodeURIComponent(rest[0])}`, { apiKey });
|
|
20
|
+
checkResp(res); ui.jsonOut(res.body); return;
|
|
21
|
+
}
|
|
22
|
+
case 'validate': {
|
|
23
|
+
if (!rest[0] || !rest[1]) throw new Error('Usage: mayar discount validate <couponCode> <paymentLinkId>');
|
|
24
|
+
const res = await api.request('POST', '/hl/v2/coupons/validate', {
|
|
25
|
+
apiKey, body: { couponCode: rest[0], paymentLinkId: rest[1] },
|
|
26
|
+
});
|
|
27
|
+
checkResp(res);
|
|
28
|
+
if (flags.json) return ui.jsonOut(res.body);
|
|
29
|
+
const d = (res.body && res.body.data) || {};
|
|
30
|
+
const c = d.coupon || {};
|
|
31
|
+
const valid = d.valid ? ui.green('✓ valid') : ui.red('✗ invalid');
|
|
32
|
+
process.stdout.write(`${ui.bold('Result:')} ${valid}\n`);
|
|
33
|
+
if (c.code) process.stdout.write(`${ui.bold('Code:')} ${c.code}\n`);
|
|
34
|
+
if (c.discountType) process.stdout.write(`${ui.bold('Discount type:')} ${c.discountType}\n`);
|
|
35
|
+
if (c.discountValue != null) process.stdout.write(`${ui.bold('Discount:')} ${c.discountValue}${c.discountType === 'percentage' ? '%' : ''}\n`);
|
|
36
|
+
if (c.minimumPurchase != null) process.stdout.write(`${ui.bold('Min. purchase:')} ${c.minimumPurchase}\n`);
|
|
37
|
+
if (c.eligibleCustomerType) process.stdout.write(`${ui.bold('Eligible:')} ${c.eligibleCustomerType}\n`);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
case 'check': {
|
|
41
|
+
if (!rest[0]) throw new Error('Usage: mayar discount check <couponCode>');
|
|
42
|
+
const res = await api.request('POST', '/hl/v2/coupons/check', {
|
|
43
|
+
apiKey, body: { couponCode: rest[0] },
|
|
44
|
+
});
|
|
45
|
+
checkResp(res); ui.jsonOut(res.body); return;
|
|
46
|
+
}
|
|
47
|
+
default:
|
|
48
|
+
throw new Error(USAGE);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = { run };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const api = require('../api');
|
|
2
|
+
const ui = require('../ui');
|
|
3
|
+
const { checkResp, readData, pagination, cursorFooter } = require('../util');
|
|
4
|
+
|
|
5
|
+
const USAGE = 'Usage: mayar installment <list|get|create>';
|
|
6
|
+
|
|
7
|
+
async function run({ apiKey, flags, positional }) {
|
|
8
|
+
const [sub, ...rest] = positional;
|
|
9
|
+
switch (sub) {
|
|
10
|
+
case undefined:
|
|
11
|
+
case 'list': {
|
|
12
|
+
const res = await api.request('GET', '/hl/v2/installments', {
|
|
13
|
+
apiKey, query: pagination(flags, { status: flags.status, customerId: flags.customerId }),
|
|
14
|
+
});
|
|
15
|
+
checkResp(res);
|
|
16
|
+
if (flags.json) return ui.jsonOut(res.body);
|
|
17
|
+
const data = (res.body && res.body.data) || [];
|
|
18
|
+
const rows = data.map((i) => ({
|
|
19
|
+
id: i.id, name: i.name, email: i.email,
|
|
20
|
+
amount: i.amount, tenure: (i.installment && i.installment.tenure) || '',
|
|
21
|
+
status: i.status,
|
|
22
|
+
}));
|
|
23
|
+
ui.table(rows, ['id', 'name', 'email', 'amount', 'tenure', 'status']);
|
|
24
|
+
cursorFooter(res.body, data.length);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
case 'get':
|
|
28
|
+
case 'detail': {
|
|
29
|
+
if (!rest[0]) throw new Error('Usage: mayar installment get <id>');
|
|
30
|
+
const res = await api.request('GET', `/hl/v2/installments/${encodeURIComponent(rest[0])}`, { apiKey });
|
|
31
|
+
checkResp(res); ui.jsonOut(res.body); return;
|
|
32
|
+
}
|
|
33
|
+
case 'create': {
|
|
34
|
+
const body = readData(flags.data);
|
|
35
|
+
if (!body) throw new Error('mayar installment create requires --data <json|@file>');
|
|
36
|
+
const res = await api.request('POST', '/hl/v2/installments/create', { apiKey, body });
|
|
37
|
+
checkResp(res); ui.jsonOut(res.body); return;
|
|
38
|
+
}
|
|
39
|
+
default:
|
|
40
|
+
throw new Error(USAGE);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = { run };
|
package/src/commands/invoice.js
CHANGED
|
@@ -1,50 +1,78 @@
|
|
|
1
1
|
const api = require('../api');
|
|
2
2
|
const ui = require('../ui');
|
|
3
|
-
const { checkResp, readData } = require('../util');
|
|
3
|
+
const { checkResp, readData, pagination, cursorFooter } = require('../util');
|
|
4
4
|
|
|
5
|
-
const USAGE = 'Usage: mayar invoice <list|get|close|reopen|create>';
|
|
5
|
+
const USAGE = 'Usage: mayar invoice <list|get|close|reopen|status|edit|filter|create>';
|
|
6
|
+
const STATUS_ACTIONS = ['open', 'close', 'active', 'closed', 'unlisted'];
|
|
7
|
+
|
|
8
|
+
function renderList(body) {
|
|
9
|
+
const data = (body && body.data) || [];
|
|
10
|
+
const rows = data.map((i) => ({
|
|
11
|
+
id: i.id,
|
|
12
|
+
customer: (i.customer && (i.customer.name || i.customer.email)) || '',
|
|
13
|
+
amount: i.amount,
|
|
14
|
+
status: i.status,
|
|
15
|
+
createdAt: typeof i.createdAt === 'number' ? new Date(i.createdAt).toISOString() : (i.createdAt || ''),
|
|
16
|
+
}));
|
|
17
|
+
ui.table(rows, ['id', 'customer', 'amount', 'status', 'createdAt']);
|
|
18
|
+
cursorFooter(body, data.length);
|
|
19
|
+
}
|
|
6
20
|
|
|
7
21
|
async function run({ apiKey, flags, positional }) {
|
|
8
22
|
const [sub, ...rest] = positional;
|
|
9
23
|
switch (sub) {
|
|
10
24
|
case 'list': {
|
|
11
|
-
const res = await api.request('GET', '/hl/
|
|
12
|
-
apiKey,
|
|
25
|
+
const res = await api.request('GET', '/hl/v2/invoices', {
|
|
26
|
+
apiKey,
|
|
27
|
+
query: pagination(flags, { status: flags.status, search: flags.search }),
|
|
28
|
+
});
|
|
29
|
+
checkResp(res);
|
|
30
|
+
if (flags.json) return ui.jsonOut(res.body);
|
|
31
|
+
renderList(res.body); return;
|
|
32
|
+
}
|
|
33
|
+
case 'filter': {
|
|
34
|
+
if (!flags.email) throw new Error('mayar invoice filter requires --email <email>');
|
|
35
|
+
const res = await api.request('GET', '/hl/v2/invoices/filter', {
|
|
36
|
+
apiKey,
|
|
37
|
+
query: pagination(flags, { email: flags.email, status: flags.status, search: flags.search }),
|
|
13
38
|
});
|
|
14
39
|
checkResp(res);
|
|
15
40
|
if (flags.json) return ui.jsonOut(res.body);
|
|
16
|
-
|
|
17
|
-
const rows = data.map((i) => ({
|
|
18
|
-
id: i.id,
|
|
19
|
-
customer: (i.customer && (i.customer.name || i.customer.email)) || '',
|
|
20
|
-
amount: i.amount,
|
|
21
|
-
status: i.status,
|
|
22
|
-
createdAt: i.createdAt,
|
|
23
|
-
}));
|
|
24
|
-
ui.table(rows, ['id', 'customer', 'amount', 'status', 'createdAt']);
|
|
25
|
-
const m = res.body || {};
|
|
26
|
-
process.stdout.write(ui.dim(`page ${m.page ?? '?'} / ${m.pageCount ?? '?'} · total ${m.total ?? data.length}`) + '\n');
|
|
27
|
-
return;
|
|
41
|
+
renderList(res.body); return;
|
|
28
42
|
}
|
|
29
43
|
case 'get': {
|
|
30
44
|
if (!rest[0]) throw new Error('Usage: mayar invoice get <id>');
|
|
31
|
-
const res = await api.request('GET', `/hl/
|
|
45
|
+
const res = await api.request('GET', `/hl/v2/invoices/${encodeURIComponent(rest[0])}`, { apiKey });
|
|
32
46
|
checkResp(res); ui.jsonOut(res.body); return;
|
|
33
47
|
}
|
|
34
|
-
case 'close':
|
|
35
|
-
|
|
36
|
-
|
|
48
|
+
case 'close':
|
|
49
|
+
case 'reopen': {
|
|
50
|
+
if (!rest[0]) throw new Error(`Usage: mayar invoice ${sub} <id>`);
|
|
51
|
+
const action = sub === 'reopen' ? 'open' : 'close';
|
|
52
|
+
// v2 consolidates status changes under a single action endpoint.
|
|
53
|
+
const res = await api.request('POST', `/hl/v2/invoices/${encodeURIComponent(rest[0])}/${action}`, { apiKey });
|
|
37
54
|
checkResp(res); ui.jsonOut(res.body); return;
|
|
38
55
|
}
|
|
39
|
-
case '
|
|
40
|
-
if (!rest[0]) throw new Error(
|
|
41
|
-
|
|
56
|
+
case 'status': {
|
|
57
|
+
if (!rest[0] || !rest[1]) throw new Error(`Usage: mayar invoice status <id> <${STATUS_ACTIONS.join('|')}>`);
|
|
58
|
+
if (!STATUS_ACTIONS.includes(rest[1])) {
|
|
59
|
+
throw new Error(`Invalid action "${rest[1]}". Must be one of: ${STATUS_ACTIONS.join(', ')}`);
|
|
60
|
+
}
|
|
61
|
+
const res = await api.request('POST', `/hl/v2/invoices/${encodeURIComponent(rest[0])}/${rest[1]}`, { apiKey });
|
|
62
|
+
checkResp(res); ui.jsonOut(res.body); return;
|
|
63
|
+
}
|
|
64
|
+
case 'edit': {
|
|
65
|
+
if (!rest[0]) throw new Error('Usage: mayar invoice edit <id> --data <json|@file>');
|
|
66
|
+
const body = readData(flags.data);
|
|
67
|
+
if (!body) throw new Error('mayar invoice edit requires --data <json|@file>');
|
|
68
|
+
body.id = body.id || rest[0];
|
|
69
|
+
const res = await api.request('POST', `/hl/v2/invoices/${encodeURIComponent(rest[0])}/update`, { apiKey, body });
|
|
42
70
|
checkResp(res); ui.jsonOut(res.body); return;
|
|
43
71
|
}
|
|
44
72
|
case 'create': {
|
|
45
73
|
const body = readData(flags.data);
|
|
46
|
-
if (!body) throw new Error('mayar invoice create requires --data <json|@file
|
|
47
|
-
const res = await api.request('POST', '/hl/
|
|
74
|
+
if (!body) throw new Error('mayar invoice create requires --data <json|@file>');
|
|
75
|
+
const res = await api.request('POST', '/hl/v2/invoices/create', { apiKey, body });
|
|
48
76
|
checkResp(res); ui.jsonOut(res.body); return;
|
|
49
77
|
}
|
|
50
78
|
default:
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const api = require('../api');
|
|
2
|
+
const ui = require('../ui');
|
|
3
|
+
const { checkResp, readData, pagination, cursorFooter } = require('../util');
|
|
4
|
+
|
|
5
|
+
const USAGE = 'Usage: mayar membership <members|tiers|register|get|update|cancel|create-invoice>';
|
|
6
|
+
|
|
7
|
+
async function run({ apiKey, flags, positional }) {
|
|
8
|
+
const [sub, ...rest] = positional;
|
|
9
|
+
switch (sub) {
|
|
10
|
+
case 'members': {
|
|
11
|
+
if (!flags.productId) throw new Error('mayar membership members requires --productId <id>');
|
|
12
|
+
const res = await api.request('GET', '/hl/v2/memberships/members', {
|
|
13
|
+
apiKey, query: pagination(flags, { productId: flags.productId }),
|
|
14
|
+
});
|
|
15
|
+
checkResp(res);
|
|
16
|
+
if (flags.json) return ui.jsonOut(res.body);
|
|
17
|
+
const data = (res.body && res.body.data) || [];
|
|
18
|
+
const rows = data.map((m) => ({
|
|
19
|
+
id: m.id,
|
|
20
|
+
customer: (m.customer && (m.customer.name || m.customer.email)) || '',
|
|
21
|
+
tier: (m.membershipTier && m.membershipTier.name) || m.membershipTierId || '',
|
|
22
|
+
period: m.membershipMonthlyPeriod ?? '',
|
|
23
|
+
status: m.status || '',
|
|
24
|
+
}));
|
|
25
|
+
ui.table(rows, ['id', 'customer', 'tier', 'period', 'status']);
|
|
26
|
+
cursorFooter(res.body, data.length);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
case 'tiers': {
|
|
30
|
+
if (!flags.productId) throw new Error('mayar membership tiers requires --productId <id>');
|
|
31
|
+
const res = await api.request('GET', '/hl/v2/memberships/tiers', {
|
|
32
|
+
apiKey, query: pagination(flags, { productId: flags.productId }),
|
|
33
|
+
});
|
|
34
|
+
checkResp(res);
|
|
35
|
+
if (flags.json) return ui.jsonOut(res.body);
|
|
36
|
+
const data = (res.body && res.body.data) || [];
|
|
37
|
+
const rows = data.map((t) => ({
|
|
38
|
+
id: t.id, name: t.name, amount: t.amount, status: t.status,
|
|
39
|
+
}));
|
|
40
|
+
ui.table(rows, ['id', 'name', 'amount', 'status']);
|
|
41
|
+
cursorFooter(res.body, data.length);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
case 'register': {
|
|
45
|
+
const body = readData(flags.data);
|
|
46
|
+
if (!body) throw new Error('mayar membership register requires --data <json|@file>');
|
|
47
|
+
const res = await api.request('POST', '/hl/v2/memberships/members/create', { apiKey, body });
|
|
48
|
+
checkResp(res); ui.jsonOut(res.body); return;
|
|
49
|
+
}
|
|
50
|
+
case 'get':
|
|
51
|
+
case 'detail': {
|
|
52
|
+
if (!rest[0]) throw new Error('Usage: mayar membership get <memberId> --productId <id>');
|
|
53
|
+
if (!flags.productId) throw new Error('mayar membership get requires --productId <id>');
|
|
54
|
+
const res = await api.request('GET', `/hl/v2/memberships/members/${encodeURIComponent(rest[0])}`, {
|
|
55
|
+
apiKey, query: { productId: flags.productId },
|
|
56
|
+
});
|
|
57
|
+
checkResp(res); ui.jsonOut(res.body); return;
|
|
58
|
+
}
|
|
59
|
+
case 'update': {
|
|
60
|
+
if (!rest[0]) throw new Error('Usage: mayar membership update <memberId> --productId <id> [--data <json|@file>]');
|
|
61
|
+
if (!flags.productId) throw new Error('mayar membership update requires --productId <id>');
|
|
62
|
+
const body = readData(flags.data) || {};
|
|
63
|
+
body.productId = body.productId || flags.productId;
|
|
64
|
+
const res = await api.request('POST', `/hl/v2/memberships/members/${encodeURIComponent(rest[0])}/update`, {
|
|
65
|
+
apiKey, body,
|
|
66
|
+
});
|
|
67
|
+
checkResp(res); ui.jsonOut(res.body); return;
|
|
68
|
+
}
|
|
69
|
+
case 'cancel': {
|
|
70
|
+
if (!rest[0]) throw new Error('Usage: mayar membership cancel <memberId> --productId <id>');
|
|
71
|
+
if (!flags.productId) throw new Error('mayar membership cancel requires --productId <id>');
|
|
72
|
+
const res = await api.request('POST', `/hl/v2/memberships/members/${encodeURIComponent(rest[0])}/cancel`, {
|
|
73
|
+
apiKey, body: { productId: flags.productId },
|
|
74
|
+
});
|
|
75
|
+
checkResp(res); ui.jsonOut(res.body); return;
|
|
76
|
+
}
|
|
77
|
+
case 'create-invoice':
|
|
78
|
+
case 'createinvoice':
|
|
79
|
+
case 'invoice': {
|
|
80
|
+
if (!rest[0]) throw new Error('Usage: mayar membership create-invoice <memberId> --productId <id>');
|
|
81
|
+
if (!flags.productId) throw new Error('mayar membership create-invoice requires --productId <id>');
|
|
82
|
+
const res = await api.request('POST', `/hl/v2/memberships/members/${encodeURIComponent(rest[0])}/invoice/create`, {
|
|
83
|
+
apiKey, body: { productId: flags.productId },
|
|
84
|
+
});
|
|
85
|
+
checkResp(res); ui.jsonOut(res.body); return;
|
|
86
|
+
}
|
|
87
|
+
default:
|
|
88
|
+
throw new Error(USAGE);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = { run };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const api = require('../api');
|
|
2
|
+
const ui = require('../ui');
|
|
3
|
+
const { checkResp, readData } = require('../util');
|
|
4
|
+
|
|
5
|
+
const USAGE = 'Usage: mayar payment-link edit <id> --data <json|@file>';
|
|
6
|
+
|
|
7
|
+
// Companion to `mayar product edit --type payment-link` — surfaces the alternate
|
|
8
|
+
// v2 route at /hl/v2/payment-links/{id}/update, which the docs confirm shares a
|
|
9
|
+
// handler with /hl/v2/products/payment-link/{id}/update.
|
|
10
|
+
async function run({ apiKey, flags, positional }) {
|
|
11
|
+
const [sub, ...rest] = positional;
|
|
12
|
+
if (sub !== 'edit') throw new Error(USAGE);
|
|
13
|
+
if (!rest[0]) throw new Error(USAGE);
|
|
14
|
+
const body = readData(flags.data);
|
|
15
|
+
if (!body) throw new Error('mayar payment-link edit requires --data <json|@file>');
|
|
16
|
+
body.id = body.id || rest[0];
|
|
17
|
+
const res = await api.request('POST', `/hl/v2/payment-links/${encodeURIComponent(rest[0])}/update`, {
|
|
18
|
+
apiKey, body,
|
|
19
|
+
});
|
|
20
|
+
checkResp(res); ui.jsonOut(res.body);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = { run };
|
package/src/commands/payment.js
CHANGED
|
@@ -1,44 +1,60 @@
|
|
|
1
1
|
const api = require('../api');
|
|
2
2
|
const ui = require('../ui');
|
|
3
|
-
const { checkResp, readData } = require('../util');
|
|
3
|
+
const { checkResp, readData, pagination, cursorFooter } = require('../util');
|
|
4
4
|
|
|
5
|
-
const USAGE = 'Usage: mayar payment <list|get|close|reopen|create>';
|
|
5
|
+
const USAGE = 'Usage: mayar payment <list|get|close|reopen|status|edit|create>';
|
|
6
|
+
const STATUS_ACTIONS = ['open', 'close', 'active', 'closed', 'unlisted'];
|
|
6
7
|
|
|
7
8
|
async function run({ apiKey, flags, positional }) {
|
|
8
9
|
const [sub, ...rest] = positional;
|
|
9
10
|
switch (sub) {
|
|
10
11
|
case 'list': {
|
|
11
|
-
const res = await api.request('GET', '/hl/
|
|
12
|
-
apiKey, query:
|
|
12
|
+
const res = await api.request('GET', '/hl/v2/payments', {
|
|
13
|
+
apiKey, query: pagination(flags, { status: flags.status }),
|
|
13
14
|
});
|
|
14
15
|
checkResp(res);
|
|
15
16
|
if (flags.json) return ui.jsonOut(res.body);
|
|
16
17
|
const data = (res.body && res.body.data) || [];
|
|
17
18
|
const rows = data.map((p) => ({
|
|
18
|
-
id: p.id, name: p.name, amount: p.amount, status: p.status,
|
|
19
|
+
id: p.id, name: p.name, amount: p.amount, status: p.status,
|
|
20
|
+
createdAt: typeof p.createdAt === 'number' ? new Date(p.createdAt).toISOString() : (p.createdAt || ''),
|
|
19
21
|
}));
|
|
20
22
|
ui.table(rows, ['id', 'name', 'amount', 'status', 'createdAt']);
|
|
23
|
+
cursorFooter(res.body, data.length);
|
|
21
24
|
return;
|
|
22
25
|
}
|
|
23
26
|
case 'get': {
|
|
24
27
|
if (!rest[0]) throw new Error('Usage: mayar payment get <id>');
|
|
25
|
-
const res = await api.request('GET', `/hl/
|
|
28
|
+
const res = await api.request('GET', `/hl/v2/payments/${encodeURIComponent(rest[0])}`, { apiKey });
|
|
26
29
|
checkResp(res); ui.jsonOut(res.body); return;
|
|
27
30
|
}
|
|
28
|
-
case 'close':
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
case 'close':
|
|
32
|
+
case 'reopen': {
|
|
33
|
+
if (!rest[0]) throw new Error(`Usage: mayar payment ${sub} <id>`);
|
|
34
|
+
const action = sub === 'reopen' ? 'open' : 'close';
|
|
35
|
+
const res = await api.request('POST', `/hl/v2/payments/${encodeURIComponent(rest[0])}/${action}`, { apiKey });
|
|
31
36
|
checkResp(res); ui.jsonOut(res.body); return;
|
|
32
37
|
}
|
|
33
|
-
case '
|
|
34
|
-
if (!rest[0]) throw new Error(
|
|
35
|
-
|
|
38
|
+
case 'status': {
|
|
39
|
+
if (!rest[0] || !rest[1]) throw new Error(`Usage: mayar payment status <id> <${STATUS_ACTIONS.join('|')}>`);
|
|
40
|
+
if (!STATUS_ACTIONS.includes(rest[1])) {
|
|
41
|
+
throw new Error(`Invalid action "${rest[1]}". Must be one of: ${STATUS_ACTIONS.join(', ')}`);
|
|
42
|
+
}
|
|
43
|
+
const res = await api.request('POST', `/hl/v2/payments/${encodeURIComponent(rest[0])}/${rest[1]}`, { apiKey });
|
|
44
|
+
checkResp(res); ui.jsonOut(res.body); return;
|
|
45
|
+
}
|
|
46
|
+
case 'edit': {
|
|
47
|
+
if (!rest[0]) throw new Error('Usage: mayar payment edit <id> --data <json|@file>');
|
|
48
|
+
const body = readData(flags.data);
|
|
49
|
+
if (!body) throw new Error('mayar payment edit requires --data <json|@file>');
|
|
50
|
+
body.id = body.id || rest[0];
|
|
51
|
+
const res = await api.request('POST', `/hl/v2/payments/${encodeURIComponent(rest[0])}/update`, { apiKey, body });
|
|
36
52
|
checkResp(res); ui.jsonOut(res.body); return;
|
|
37
53
|
}
|
|
38
54
|
case 'create': {
|
|
39
55
|
const body = readData(flags.data);
|
|
40
|
-
if (!body) throw new Error('mayar payment create requires --data <json|@file
|
|
41
|
-
const res = await api.request('POST', '/hl/
|
|
56
|
+
if (!body) throw new Error('mayar payment create requires --data <json|@file>');
|
|
57
|
+
const res = await api.request('POST', '/hl/v2/payments/create', { apiKey, body });
|
|
42
58
|
checkResp(res); ui.jsonOut(res.body); return;
|
|
43
59
|
}
|
|
44
60
|
default:
|
package/src/commands/product.js
CHANGED
|
@@ -1,8 +1,33 @@
|
|
|
1
1
|
const api = require('../api');
|
|
2
2
|
const ui = require('../ui');
|
|
3
|
-
const { checkResp } = require('../util');
|
|
3
|
+
const { checkResp, readData, pagination, cursorFooter } = require('../util');
|
|
4
4
|
|
|
5
|
-
const USAGE = 'Usage: mayar product <list|get|close|reopen|
|
|
5
|
+
const USAGE = 'Usage: mayar product <list|search|type|get|close|reopen|status|transactions|create|edit|sort>';
|
|
6
|
+
const SORT_TYPES = ['generic_link', 'event', 'webinar', 'digital_product'];
|
|
7
|
+
const STATUS_ACTIONS = ['open', 'close', 'active', 'closed', 'unlisted'];
|
|
8
|
+
|
|
9
|
+
// Map user-friendly --type values to their v2 endpoint paths.
|
|
10
|
+
const CREATE_PATHS = {
|
|
11
|
+
ebook: '/hl/v2/products/digital-product/create',
|
|
12
|
+
digital: '/hl/v2/products/digital-product/create',
|
|
13
|
+
'digital-product': '/hl/v2/products/digital-product/create',
|
|
14
|
+
event: '/hl/v2/products/event/create',
|
|
15
|
+
webinar: '/hl/v2/products/webinar/create',
|
|
16
|
+
generic: '/hl/v2/products/create',
|
|
17
|
+
'generic-link': '/hl/v2/products/create',
|
|
18
|
+
'payment-link': '/hl/v2/products/payment-link/create',
|
|
19
|
+
paymentlink: '/hl/v2/products/payment-link/create',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const EDIT_PATHS = {
|
|
23
|
+
ebook: (id) => `/hl/v2/products/digital-product/${id}/update`,
|
|
24
|
+
digital: (id) => `/hl/v2/products/digital-product/${id}/update`,
|
|
25
|
+
'digital-product': (id) => `/hl/v2/products/digital-product/${id}/update`,
|
|
26
|
+
event: (id) => `/hl/v2/products/event/${id}/update`,
|
|
27
|
+
webinar: (id) => `/hl/v2/products/webinar/${id}/update`,
|
|
28
|
+
'payment-link': (id) => `/hl/v2/products/payment-link/${id}/update`,
|
|
29
|
+
paymentlink: (id) => `/hl/v2/products/payment-link/${id}/update`,
|
|
30
|
+
};
|
|
6
31
|
|
|
7
32
|
function renderList(body) {
|
|
8
33
|
const data = (body && body.data) || [];
|
|
@@ -16,16 +41,16 @@ function renderList(body) {
|
|
|
16
41
|
checkoutLink: p.linkPayment || '',
|
|
17
42
|
}));
|
|
18
43
|
ui.table(rows, ['id', 'name', 'type', 'amount', 'status', 'productLink', 'checkoutLink']);
|
|
19
|
-
|
|
20
|
-
process.stdout.write(ui.dim(`page ${m.page ?? '?'} / ${m.pageCount ?? '?'} · total ${m.total ?? data.length}`) + '\n');
|
|
44
|
+
cursorFooter(body, data.length);
|
|
21
45
|
}
|
|
22
46
|
|
|
23
47
|
async function run({ apiKey, flags, positional }) {
|
|
24
48
|
const [sub, ...rest] = positional;
|
|
25
49
|
switch (sub) {
|
|
26
50
|
case 'list': {
|
|
27
|
-
const res = await api.request('GET', '/hl/
|
|
28
|
-
apiKey,
|
|
51
|
+
const res = await api.request('GET', '/hl/v2/products', {
|
|
52
|
+
apiKey,
|
|
53
|
+
query: pagination(flags, { search: flags.search, type: flags.type, stock: flags.stock }),
|
|
29
54
|
});
|
|
30
55
|
checkResp(res);
|
|
31
56
|
if (flags.json) return ui.jsonOut(res.body);
|
|
@@ -33,18 +58,17 @@ async function run({ apiKey, flags, positional }) {
|
|
|
33
58
|
}
|
|
34
59
|
case 'search': {
|
|
35
60
|
if (!rest[0]) throw new Error('Usage: mayar product search <keyword>');
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
apiKey, query: { search, page: flags.page, pageSize: flags.pageSize },
|
|
61
|
+
const res = await api.request('GET', '/hl/v2/products', {
|
|
62
|
+
apiKey, query: pagination(flags, { search: rest.join(' ') }),
|
|
39
63
|
});
|
|
40
64
|
checkResp(res);
|
|
41
65
|
if (flags.json) return ui.jsonOut(res.body);
|
|
42
66
|
renderList(res.body); return;
|
|
43
67
|
}
|
|
44
68
|
case 'type': {
|
|
45
|
-
if (!rest[0]) throw new Error('Usage: mayar product type <
|
|
46
|
-
const res = await api.request('GET', `/hl/
|
|
47
|
-
apiKey, query:
|
|
69
|
+
if (!rest[0]) throw new Error('Usage: mayar product type <type>');
|
|
70
|
+
const res = await api.request('GET', `/hl/v2/products/types/${encodeURIComponent(rest[0])}`, {
|
|
71
|
+
apiKey, query: pagination(flags, { search: flags.search }),
|
|
48
72
|
});
|
|
49
73
|
checkResp(res);
|
|
50
74
|
if (flags.json) return ui.jsonOut(res.body);
|
|
@@ -52,17 +76,72 @@ async function run({ apiKey, flags, positional }) {
|
|
|
52
76
|
}
|
|
53
77
|
case 'get': {
|
|
54
78
|
if (!rest[0]) throw new Error('Usage: mayar product get <id>');
|
|
55
|
-
const res = await api.request('GET', `/hl/
|
|
79
|
+
const res = await api.request('GET', `/hl/v2/products/${encodeURIComponent(rest[0])}`, { apiKey });
|
|
56
80
|
checkResp(res); ui.jsonOut(res.body); return;
|
|
57
81
|
}
|
|
58
|
-
case '
|
|
59
|
-
if (!rest[0]) throw new Error('Usage: mayar product
|
|
60
|
-
const res = await api.request('GET', `/hl/
|
|
82
|
+
case 'transactions': {
|
|
83
|
+
if (!rest[0]) throw new Error('Usage: mayar product transactions <id>');
|
|
84
|
+
const res = await api.request('GET', `/hl/v2/products/${encodeURIComponent(rest[0])}/transactions`, {
|
|
85
|
+
apiKey,
|
|
86
|
+
query: pagination(flags, { status: flags.status, customerId: flags.customerId, fields: flags.fields }),
|
|
87
|
+
});
|
|
61
88
|
checkResp(res); ui.jsonOut(res.body); return;
|
|
62
89
|
}
|
|
90
|
+
case 'close':
|
|
63
91
|
case 'reopen': {
|
|
64
|
-
if (!rest[0]) throw new Error(
|
|
65
|
-
const
|
|
92
|
+
if (!rest[0]) throw new Error(`Usage: mayar product ${sub} <id>`);
|
|
93
|
+
const action = sub === 'reopen' ? 'open' : 'close';
|
|
94
|
+
const res = await api.request('POST', `/hl/v2/products/${encodeURIComponent(rest[0])}/${action}`, { apiKey });
|
|
95
|
+
checkResp(res); ui.jsonOut(res.body); return;
|
|
96
|
+
}
|
|
97
|
+
case 'status': {
|
|
98
|
+
if (!rest[0] || !rest[1]) throw new Error(`Usage: mayar product status <id> <${STATUS_ACTIONS.join('|')}>`);
|
|
99
|
+
if (!STATUS_ACTIONS.includes(rest[1])) {
|
|
100
|
+
throw new Error(`Invalid action "${rest[1]}". Must be one of: ${STATUS_ACTIONS.join(', ')}`);
|
|
101
|
+
}
|
|
102
|
+
const res = await api.request('POST', `/hl/v2/products/${encodeURIComponent(rest[0])}/${rest[1]}`, { apiKey });
|
|
103
|
+
checkResp(res); ui.jsonOut(res.body); return;
|
|
104
|
+
}
|
|
105
|
+
case 'create': {
|
|
106
|
+
const type = flags.type;
|
|
107
|
+
if (!type) throw new Error(`mayar product create requires --type <${Object.keys(CREATE_PATHS).join('|')}>`);
|
|
108
|
+
const path = CREATE_PATHS[type];
|
|
109
|
+
if (!path) throw new Error(`Unknown type "${type}". Valid: ${Object.keys(CREATE_PATHS).join(', ')}`);
|
|
110
|
+
const body = readData(flags.data);
|
|
111
|
+
if (!body) throw new Error('mayar product create requires --data <json|@file>');
|
|
112
|
+
const res = await api.request('POST', path, { apiKey, body });
|
|
113
|
+
checkResp(res); ui.jsonOut(res.body); return;
|
|
114
|
+
}
|
|
115
|
+
case 'sort': {
|
|
116
|
+
if (!rest[0]) throw new Error(`Usage: mayar product sort <${SORT_TYPES.join('|')}>`);
|
|
117
|
+
if (!SORT_TYPES.includes(rest[0])) {
|
|
118
|
+
throw new Error(`Invalid type "${rest[0]}". Must be one of: ${SORT_TYPES.join(', ')}`);
|
|
119
|
+
}
|
|
120
|
+
// Sort endpoint uses snake_case `starting_after` (unlike every other v2 list endpoint
|
|
121
|
+
// which uses camelCase `startingAfter`). Build the query directly rather than via
|
|
122
|
+
// pagination() to preserve the exact wire name.
|
|
123
|
+
const query = {};
|
|
124
|
+
const limit = flags.limit ?? flags.pageSize;
|
|
125
|
+
const after = flags.after ?? flags.startingAfter ?? flags['starting-after'];
|
|
126
|
+
if (limit) query.limit = limit;
|
|
127
|
+
if (after) query.starting_after = after;
|
|
128
|
+
const res = await api.request('POST', `/hl/v2/payment-links/sort/${encodeURIComponent(rest[0])}`, {
|
|
129
|
+
apiKey, query,
|
|
130
|
+
});
|
|
131
|
+
checkResp(res);
|
|
132
|
+
if (flags.json) return ui.jsonOut(res.body);
|
|
133
|
+
renderList(res.body); return;
|
|
134
|
+
}
|
|
135
|
+
case 'edit': {
|
|
136
|
+
if (!rest[0]) throw new Error('Usage: mayar product edit <id> --type <T> --data <json|@file>');
|
|
137
|
+
const type = flags.type;
|
|
138
|
+
if (!type) throw new Error(`mayar product edit requires --type <${Object.keys(EDIT_PATHS).join('|')}>`);
|
|
139
|
+
const pathFn = EDIT_PATHS[type];
|
|
140
|
+
if (!pathFn) throw new Error(`Unknown type "${type}". Valid: ${Object.keys(EDIT_PATHS).join(', ')}`);
|
|
141
|
+
const body = readData(flags.data);
|
|
142
|
+
if (!body) throw new Error('mayar product edit requires --data <json|@file>');
|
|
143
|
+
body.id = body.id || rest[0];
|
|
144
|
+
const res = await api.request('POST', pathFn(encodeURIComponent(rest[0])), { apiKey, body });
|
|
66
145
|
checkResp(res); ui.jsonOut(res.body); return;
|
|
67
146
|
}
|
|
68
147
|
default:
|
package/src/commands/qrcode.js
CHANGED
|
@@ -2,12 +2,42 @@ const api = require('../api');
|
|
|
2
2
|
const ui = require('../ui');
|
|
3
3
|
const { checkResp } = require('../util');
|
|
4
4
|
|
|
5
|
+
const USAGE = 'Usage: mayar qrcode <amount> | mayar qrcode static | mayar qrcode channels';
|
|
6
|
+
|
|
5
7
|
async function run({ apiKey, flags, positional }) {
|
|
6
|
-
const [
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
const [first] = positional;
|
|
9
|
+
|
|
10
|
+
if (first === 'static') {
|
|
11
|
+
const res = await api.request('GET', '/hl/v2/qr-codes/static', { apiKey });
|
|
12
|
+
checkResp(res);
|
|
13
|
+
if (flags.json) return ui.jsonOut(res.body);
|
|
14
|
+
const d = (res.body && res.body.data) || {};
|
|
15
|
+
if (d.url) process.stdout.write(`${ui.bold('Static QRIS:')} ${ui.cyan(d.url)}\n`);
|
|
16
|
+
else ui.jsonOut(res.body);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (first === 'channels') {
|
|
21
|
+
const res = await api.request('GET', '/hl/v2/payment-channels', { apiKey });
|
|
22
|
+
checkResp(res);
|
|
23
|
+
if (flags.json) return ui.jsonOut(res.body);
|
|
24
|
+
const body = res.body || {};
|
|
25
|
+
const channels = (body.data && body.data.channels) || body.data || [];
|
|
26
|
+
if (!Array.isArray(channels)) return ui.jsonOut(res.body);
|
|
27
|
+
const rows = channels.map((c) => ({
|
|
28
|
+
name: c.name || '',
|
|
29
|
+
type: c.type || '',
|
|
30
|
+
code: c.code || '',
|
|
31
|
+
status: c.status === true ? 'enabled' : (c.status === false ? 'disabled' : String(c.status ?? '')),
|
|
32
|
+
}));
|
|
33
|
+
ui.table(rows, ['name', 'type', 'code', 'status']);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!first) throw new Error(USAGE);
|
|
38
|
+
const amount = Number(first);
|
|
39
|
+
if (!Number.isFinite(amount) || amount <= 0) throw new Error(USAGE);
|
|
40
|
+
const res = await api.request('POST', '/hl/v2/qr-codes/create', { apiKey, body: { amount } });
|
|
11
41
|
checkResp(res);
|
|
12
42
|
if (flags.json) return ui.jsonOut(res.body);
|
|
13
43
|
const d = (res.body && res.body.data) || {};
|