mayar 0.1.7 → 0.1.9
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 +46 -15
- package/package.json +1 -1
- package/src/api.js +1 -1
- package/src/cli.js +11 -1
- package/src/commands/customer.js +24 -2
- package/src/commands/review.js +42 -0
- package/src/commands/transaction.js +12 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# mayar
|
|
2
2
|
|
|
3
|
-
Command-line interface for the [Mayar](https://docs.mayar.id) API.
|
|
3
|
+
Command-line interface for the [Mayar](https://docs.mayar.id) API. Zero runtime dependencies — Node.js 18+ stdlib only.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -18,21 +18,42 @@ cd mayar-cli
|
|
|
18
18
|
npm link # exposes a `mayar` command on your PATH
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
##
|
|
21
|
+
## Authentication
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Resolution order: `--api-key` flag → `MAYAR_API_KEY` env → saved config file.
|
|
24
|
+
|
|
25
|
+
**Option 1 — environment variable (recommended for CI/agents):**
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
export MAYAR_API_KEY=your_api_key_here
|
|
29
|
+
mayar whoami
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Option 2 — saved config (interactive first run):**
|
|
33
|
+
|
|
34
|
+
The first time you invoke any command without a key configured, the CLI prompts for your production API key (input is masked). Paste it once; it's stored at `~/.config/mayar/config.json` (chmod 600, follows [XDG Base Directory](https://specification.freedesktop.org/basedir-spec/latest/)) and reused on subsequent runs.
|
|
24
35
|
|
|
25
36
|
```bash
|
|
26
37
|
mayar balance
|
|
27
|
-
# ███╗ ███╗ █████╗ ██╗ ██╗ █████╗ ██████╗
|
|
28
|
-
# ...
|
|
29
38
|
# Welcome to Mayar CLI.
|
|
30
39
|
# No API key found. Get yours from https://web.mayar.id → Integration → API Key.
|
|
31
40
|
# Paste your production API key: ************
|
|
32
41
|
# ✓ Saved to /Users/you/.config/mayar/config.json
|
|
33
42
|
```
|
|
34
43
|
|
|
35
|
-
|
|
44
|
+
**Option 3 — non-interactive setup:**
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
mayar api-key YOUR_KEY_HERE
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Option 4 — per-command override:**
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
mayar --api-key YOUR_KEY_HERE balance
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Get your API key from [web.mayar.id](https://web.mayar.id) → Integration → API Key.
|
|
36
57
|
|
|
37
58
|
## Commands
|
|
38
59
|
|
|
@@ -40,11 +61,11 @@ You can also run `mayar init` explicitly to (re-)configure the key, or pass `--a
|
|
|
40
61
|
Setup
|
|
41
62
|
init Run first-time setup (interactive, masked input)
|
|
42
63
|
api-key <key> Save API key non-interactively (e.g. for scripts)
|
|
43
|
-
config show Show masked saved key
|
|
64
|
+
config show Show masked saved key and config path
|
|
44
65
|
config reset Remove the saved key
|
|
45
66
|
|
|
46
67
|
Account
|
|
47
|
-
whoami Show merchant identity (decoded from JWT
|
|
68
|
+
whoami Show merchant identity (decoded from JWT) + verify key
|
|
48
69
|
balance GET /hl/v1/balance
|
|
49
70
|
|
|
50
71
|
Invoices
|
|
@@ -72,10 +93,17 @@ Single payment requests
|
|
|
72
93
|
Customers
|
|
73
94
|
customer list [--page ...] GET /hl/v1/customer
|
|
74
95
|
customer create --data <json|@file> POST /hl/v1/customer/create
|
|
96
|
+
customer search <email> GET /hl/v1/customer/detail?email=
|
|
97
|
+
customer update <from> <to> POST /hl/v1/customer/update
|
|
98
|
+
customer magic-link <email> POST /hl/v1/customer/login/portal
|
|
75
99
|
|
|
76
100
|
Transactions
|
|
77
101
|
tx list [--page ...] GET /hl/v1/transactions (paid)
|
|
78
102
|
tx unpaid [--page ...] GET /hl/v1/transactions/unpaid
|
|
103
|
+
tx daily GET /hl/v1/transactions/daily
|
|
104
|
+
|
|
105
|
+
Reviews
|
|
106
|
+
review list [--page ...] GET /hl/v1/reviews
|
|
79
107
|
|
|
80
108
|
Dynamic QR
|
|
81
109
|
qrcode <amount> POST /hl/v1/qrcode/create
|
|
@@ -94,14 +122,16 @@ Global flags
|
|
|
94
122
|
-v, --version Show version
|
|
95
123
|
|
|
96
124
|
Environment
|
|
97
|
-
MAYAR_API_KEY
|
|
125
|
+
MAYAR_API_KEY API key — used when --api-key flag is absent and no config is saved
|
|
126
|
+
MAYAR_API_URL Override API base URL (default: https://api.mayar.id)
|
|
98
127
|
```
|
|
99
128
|
|
|
100
|
-
Resolution order: `--api-key` flag → `MAYAR_API_KEY` env → saved config.
|
|
101
|
-
|
|
102
129
|
## Examples
|
|
103
130
|
|
|
104
131
|
```bash
|
|
132
|
+
# Verify active user / API key
|
|
133
|
+
mayar whoami
|
|
134
|
+
|
|
105
135
|
# Account balance
|
|
106
136
|
mayar balance
|
|
107
137
|
|
|
@@ -146,10 +176,10 @@ mayar invoice list --json | jq '.data[] | {id, status}'
|
|
|
146
176
|
|
|
147
177
|
## Config
|
|
148
178
|
|
|
149
|
-
| Key | Value
|
|
150
|
-
| -------- |
|
|
179
|
+
| Key | Value |
|
|
180
|
+
| -------- | ------------------------------------------------------------------------------------------- |
|
|
151
181
|
| Path | `$XDG_CONFIG_HOME/mayar/config.json`, defaulting to `~/.config/mayar/config.json` (chmod 600) |
|
|
152
|
-
| Endpoint | `https://api.mayar.id` (
|
|
182
|
+
| Endpoint | `MAYAR_API_URL` env or `https://api.mayar.id` (default) |
|
|
153
183
|
|
|
154
184
|
Legacy installs that wrote to `~/.mayar/config.json` are migrated automatically on first run.
|
|
155
185
|
|
|
@@ -157,6 +187,7 @@ To rotate keys: `mayar config reset && mayar init`.
|
|
|
157
187
|
|
|
158
188
|
## Notes
|
|
159
189
|
|
|
160
|
-
- Sandbox is intentionally not supported — this CLI hits production only.
|
|
161
190
|
- All requests use `Authorization: Bearer <key>`. Errors print `API <status> — <message>` and exit non-zero.
|
|
162
191
|
- `--data @file.json` reads from disk; `--data '{...}'` reads inline JSON.
|
|
192
|
+
- `MAYAR_API_URL` overrides the base URL — useful for staging or custom proxy environments.
|
|
193
|
+
- `mayar whoami` decodes the JWT locally and verifies the key live against `/hl/v1/balance`.
|
package/package.json
CHANGED
package/src/api.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const https = require('https');
|
|
2
2
|
const { URL } = require('url');
|
|
3
3
|
|
|
4
|
-
const BASE_URL = 'https://api.mayar.id';
|
|
4
|
+
const BASE_URL = process.env.MAYAR_API_URL || 'https://api.mayar.id';
|
|
5
5
|
const VERSION = require('../package.json').version;
|
|
6
6
|
|
|
7
7
|
function request(method, pathname, { apiKey, body, query } = {}) {
|
package/src/cli.js
CHANGED
|
@@ -43,10 +43,17 @@ ${ui.bold('Single payment requests:')}
|
|
|
43
43
|
${ui.bold('Customers:')}
|
|
44
44
|
customer list [--page N --pageSize N]
|
|
45
45
|
customer create --data <json|@file.json>
|
|
46
|
+
customer search <email> Look up a customer by email
|
|
47
|
+
customer update <fromEmail> <toEmail>
|
|
48
|
+
customer magic-link <email> Email a portal login link to the customer
|
|
46
49
|
|
|
47
50
|
${ui.bold('Transactions:')}
|
|
48
51
|
tx list [--page N --pageSize N] Paid transactions
|
|
49
52
|
tx unpaid [--page N --pageSize N] Unpaid transactions
|
|
53
|
+
tx daily Today's totals (volume + count)
|
|
54
|
+
|
|
55
|
+
${ui.bold('Reviews:')}
|
|
56
|
+
review list [--page N --pageSize N]
|
|
50
57
|
|
|
51
58
|
${ui.bold('Dynamic QR:')}
|
|
52
59
|
qrcode <amount>
|
|
@@ -66,9 +73,10 @@ ${ui.bold('Global flags:')}
|
|
|
66
73
|
|
|
67
74
|
${ui.bold('Environment:')}
|
|
68
75
|
MAYAR_API_KEY Used when --api-key is not given and no config is saved
|
|
76
|
+
MAYAR_API_URL Override API base URL (default: https://api.mayar.id)
|
|
69
77
|
|
|
70
78
|
${ui.dim('Resolution order: --api-key flag > MAYAR_API_KEY env > saved config.')}
|
|
71
|
-
${ui.dim('Endpoint: https://api.mayar.id (
|
|
79
|
+
${ui.dim('Endpoint: MAYAR_API_URL env or https://api.mayar.id (default)')}
|
|
72
80
|
${ui.dim('Config: ~/.config/mayar/config.json (chmod 600)')}
|
|
73
81
|
`;
|
|
74
82
|
|
|
@@ -169,6 +177,8 @@ async function run(argv) {
|
|
|
169
177
|
qr: './commands/qrcode',
|
|
170
178
|
qrcode: './commands/qrcode',
|
|
171
179
|
webhook: './commands/webhook',
|
|
180
|
+
review: './commands/review',
|
|
181
|
+
reviews: './commands/review',
|
|
172
182
|
};
|
|
173
183
|
const handler = handlers[cmd];
|
|
174
184
|
if (!handler) {
|
package/src/commands/customer.js
CHANGED
|
@@ -2,10 +2,10 @@ const api = require('../api');
|
|
|
2
2
|
const ui = require('../ui');
|
|
3
3
|
const { checkResp, readData } = require('../util');
|
|
4
4
|
|
|
5
|
-
const USAGE = 'Usage: mayar customer <list|create>';
|
|
5
|
+
const USAGE = 'Usage: mayar customer <list|create|search|update|magic-link>';
|
|
6
6
|
|
|
7
7
|
async function run({ apiKey, flags, positional }) {
|
|
8
|
-
const [sub] = positional;
|
|
8
|
+
const [sub, ...rest] = positional;
|
|
9
9
|
switch (sub) {
|
|
10
10
|
case 'list': {
|
|
11
11
|
const res = await api.request('GET', '/hl/v1/customer', {
|
|
@@ -26,6 +26,28 @@ async function run({ apiKey, flags, positional }) {
|
|
|
26
26
|
const res = await api.request('POST', '/hl/v1/customer/create', { apiKey, body });
|
|
27
27
|
checkResp(res); ui.jsonOut(res.body); return;
|
|
28
28
|
}
|
|
29
|
+
case 'search': {
|
|
30
|
+
if (!rest[0]) throw new Error('Usage: mayar customer search <email>');
|
|
31
|
+
const res = await api.request('GET', '/hl/v1/customer/detail', {
|
|
32
|
+
apiKey, query: { email: rest[0] },
|
|
33
|
+
});
|
|
34
|
+
checkResp(res); ui.jsonOut(res.body); return;
|
|
35
|
+
}
|
|
36
|
+
case 'update': {
|
|
37
|
+
if (rest.length < 2) throw new Error('Usage: mayar customer update <fromEmail> <toEmail>');
|
|
38
|
+
const res = await api.request('POST', '/hl/v1/customer/update', {
|
|
39
|
+
apiKey, body: { fromEmail: rest[0], toEmail: rest[1] },
|
|
40
|
+
});
|
|
41
|
+
checkResp(res); ui.jsonOut(res.body); return;
|
|
42
|
+
}
|
|
43
|
+
case 'magic-link':
|
|
44
|
+
case 'magiclink': {
|
|
45
|
+
if (!rest[0]) throw new Error('Usage: mayar customer magic-link <email>');
|
|
46
|
+
const res = await api.request('POST', '/hl/v1/customer/login/portal', {
|
|
47
|
+
apiKey, body: { email: rest[0] },
|
|
48
|
+
});
|
|
49
|
+
checkResp(res); ui.jsonOut(res.body); return;
|
|
50
|
+
}
|
|
29
51
|
default:
|
|
30
52
|
throw new Error(USAGE);
|
|
31
53
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const api = require('../api');
|
|
2
|
+
const ui = require('../ui');
|
|
3
|
+
const { checkResp } = require('../util');
|
|
4
|
+
|
|
5
|
+
const USAGE = 'Usage: mayar review <list>';
|
|
6
|
+
|
|
7
|
+
function fmtDate(v) {
|
|
8
|
+
if (v == null) return '';
|
|
9
|
+
if (typeof v === 'number') return new Date(v).toISOString();
|
|
10
|
+
return String(v);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function run({ apiKey, flags, positional }) {
|
|
14
|
+
const [sub] = positional;
|
|
15
|
+
switch (sub) {
|
|
16
|
+
case undefined:
|
|
17
|
+
case 'list': {
|
|
18
|
+
const res = await api.request('GET', '/hl/v1/reviews', {
|
|
19
|
+
apiKey, query: { page: flags.page, pageSize: flags.pageSize },
|
|
20
|
+
});
|
|
21
|
+
checkResp(res);
|
|
22
|
+
if (flags.json) return ui.jsonOut(res.body);
|
|
23
|
+
const data = (res.body && res.body.data) || [];
|
|
24
|
+
const rows = data.map((r) => ({
|
|
25
|
+
id: r.id,
|
|
26
|
+
rating: r.rating,
|
|
27
|
+
customer: (r.customer && (r.customer.name || r.customer.email)) || '',
|
|
28
|
+
message: r.message || '',
|
|
29
|
+
status: r.status || '',
|
|
30
|
+
createdAt: fmtDate(r.createdAt),
|
|
31
|
+
}));
|
|
32
|
+
ui.table(rows, ['id', 'rating', 'customer', 'message', 'status', 'createdAt']);
|
|
33
|
+
const m = res.body || {};
|
|
34
|
+
process.stdout.write(ui.dim(`page ${m.page ?? '?'} / ${m.pageCount ?? '?'} · total ${m.total ?? data.length}`) + '\n');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
default:
|
|
38
|
+
throw new Error(USAGE);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = { run };
|
|
@@ -2,7 +2,7 @@ const api = require('../api');
|
|
|
2
2
|
const ui = require('../ui');
|
|
3
3
|
const { checkResp } = require('../util');
|
|
4
4
|
|
|
5
|
-
const USAGE = 'Usage: mayar tx <list|unpaid>';
|
|
5
|
+
const USAGE = 'Usage: mayar tx <list|unpaid|daily>';
|
|
6
6
|
|
|
7
7
|
function fmtDate(v) {
|
|
8
8
|
if (v == null) return '';
|
|
@@ -45,6 +45,17 @@ async function run({ apiKey, flags, positional }) {
|
|
|
45
45
|
if (flags.json) return ui.jsonOut(res.body);
|
|
46
46
|
renderTx(res.body); return;
|
|
47
47
|
}
|
|
48
|
+
case 'daily': {
|
|
49
|
+
const res = await api.request('GET', '/hl/v1/transactions/daily', { apiKey });
|
|
50
|
+
checkResp(res);
|
|
51
|
+
if (flags.json) return ui.jsonOut(res.body);
|
|
52
|
+
const d = (res.body && res.body.data) || {};
|
|
53
|
+
if (d.date) process.stdout.write(`${ui.bold('Date:')} ${d.date}\n`);
|
|
54
|
+
if (d.tpvCount != null) process.stdout.write(`${ui.bold('Total volume:')} ${Number(d.tpvCount).toLocaleString('id-ID')}\n`);
|
|
55
|
+
if (d.trxCount != null) process.stdout.write(`${ui.bold('Transactions:')} ${d.trxCount}\n`);
|
|
56
|
+
if (!d.date && !d.tpvCount && !d.trxCount) ui.jsonOut(res.body);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
48
59
|
default:
|
|
49
60
|
throw new Error(USAGE);
|
|
50
61
|
}
|