Skip to content

Commit 2ce97f8

Browse files
committed
Improve external CLI developer experience
1 parent aa01754 commit 2ce97f8

27 files changed

Lines changed: 334 additions & 55 deletions

‎CHANGELOG.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
1414
- Guarded raw Quickpay API access.
1515
- Human-readable output and sanitized machine-readable JSON.
1616
- Bounded, origin-validated, cycle-safe pagination.
17+
- Signed callback replay and foreground callback watching for local development.
1718
- Atomic credential storage with restrictive filesystem permissions.
18-
- Tracked PHAR distribution with complete source-integrity verification.
19+
- Generated PHAR release workflow with checksums and provenance attestations.
1920
- PHP 8.4 and 8.5 CI, PHPStan level 8, Pint, dependency audit, and a 90% coverage gate.

‎RELEASING.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ contact Quickpay without a key. After the public package is discoverable, also
204204
verify:
205205

206206
```bash
207-
skills add tehwave/quickpay-cli
207+
npx skills add tehwave/quickpay-cli
208208
```
209209

210210
## 9. After publication

‎app/Commands/Api/ApiRequestCommand.php‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@
2020
*/
2121
class ApiRequestCommand extends AuthenticatedCommand
2222
{
23-
protected $signature = 'api {method} {path}
24-
{--query=*}
25-
{--data=*}
26-
{--data-json=}
27-
{--header=*}
28-
{--yes}
29-
{--json}';
23+
protected $signature = 'api
24+
{method : GET, POST, PUT, PATCH, or DELETE}
25+
{path : Relative Quickpay API path, for example /payments}
26+
{--query=* : Query parameter as key=value; repeatable}
27+
{--data=* : JSON request field as key=value; repeatable}
28+
{--data-json= : JSON object request body}
29+
{--header=* : Additional safe header as name:value; repeatable}
30+
{--yes : Skip the interactive confirmation for mutations}
31+
{--json : Write machine-readable JSON}';
3032

3133
protected $description = 'Send a request to the Quickpay API';
3234

‎app/Commands/Authentication/AuthCommand.php‎

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
namespace App\Commands\Authentication;
44

55
use App\Console\BaseCommand;
6+
use App\Console\Output\JsonOutput;
7+
use App\Credentials\ApiKey;
68
use App\Credentials\ApiKeyResolver;
79
use App\Credentials\Exceptions\CredentialException;
810
use App\Quickpay\AuthenticatedQuickpayFactory;
@@ -11,7 +13,9 @@
1113

1214
class AuthCommand extends BaseCommand
1315
{
14-
protected $signature = 'auth';
16+
protected $signature = 'auth
17+
{--check : Exit non-zero when no active credential is configured}
18+
{--json : Write machine-readable JSON}';
1519

1620
protected $description = 'Show the active Quickpay authentication status';
1721

@@ -23,15 +27,19 @@ public function handle(ApiKeyResolver $credentials, AuthenticatedQuickpayFactory
2327
return $this->failure($exception->getMessage());
2428
}
2529

26-
$this->line('Credential source: '.($apiKey?->source() ?? 'none'));
27-
$this->line('API base URL: '.QuickpayClient::BASE_URL);
28-
$this->line('API version: '.QuickpayClient::API_VERSION);
30+
if (! $this->option('json')) {
31+
$this->writeHumanStatusHeader($apiKey);
32+
}
2933

3034
if ($apiKey === null) {
31-
$this->line('Scope: not authenticated');
32-
$this->comment('Run quickpay login to store a merchant API key.');
35+
if ($this->option('json')) {
36+
$this->writeJsonStatus(null, null);
37+
} else {
38+
$this->line('Scope: not authenticated');
39+
$this->comment('Run quickpay login to store a merchant API key.');
40+
}
3341

34-
return self::SUCCESS;
42+
return $this->option('check') ? self::FAILURE : self::SUCCESS;
3543
}
3644

3745
try {
@@ -57,8 +65,30 @@ public function handle(ApiKeyResolver $credentials, AuthenticatedQuickpayFactory
5765
return $this->failure('Quickpay returned an invalid scope.');
5866
}
5967

60-
$this->line("Scope: {$scope}");
68+
if ($this->option('json')) {
69+
$this->writeJsonStatus($apiKey, $scope);
70+
} else {
71+
$this->line("Scope: {$scope}");
72+
}
6173

6274
return self::SUCCESS;
6375
}
76+
77+
private function writeHumanStatusHeader(?ApiKey $apiKey): void
78+
{
79+
$this->line('Credential source: '.($apiKey?->source() ?? 'none'));
80+
$this->line('API base URL: '.QuickpayClient::BASE_URL);
81+
$this->line('API version: '.QuickpayClient::API_VERSION);
82+
}
83+
84+
private function writeJsonStatus(?ApiKey $apiKey, ?string $scope): void
85+
{
86+
$this->getOutput()->write(JsonOutput::value([
87+
'authenticated' => $scope !== null,
88+
'credential_source' => $apiKey?->source(),
89+
'api_base_url' => QuickpayClient::BASE_URL,
90+
'api_version' => QuickpayClient::API_VERSION,
91+
'scope' => $scope,
92+
], ''));
93+
}
6494
}

‎app/Commands/Payments/AbstractPaymentMutationCommand.php‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,12 @@ private function writeMutationSummary(
9898
'Currency' => $payment['currency'] ?? null,
9999
'State' => $payment['state'] ?? null,
100100
'Accepted' => $payment['accepted'] ?? null,
101-
'Balance' => $payment['balance'] ?? null,
101+
'Balance (minor units)' => $payment['balance'] ?? null,
102102
'Operation' => $this->operation(),
103103
];
104104

105105
if ($amount !== null) {
106-
$fields['Amount'] = $amount;
106+
$fields['Amount (minor units)'] = $amount;
107107
}
108108

109109
foreach ($fields as $label => $value) {

‎app/Commands/Payments/CancelPaymentCommand.php‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@
44

55
class CancelPaymentCommand extends AbstractPaymentMutationCommand
66
{
7-
protected $signature = 'payments:cancel {id} {--synchronized} {--callback-url= : Callback URL for Quickpay servers (not localhost)} {--yes} {--json}';
7+
protected $signature = 'payments:cancel
8+
{id : Quickpay payment ID}
9+
{--synchronized : Request a synchronized Quickpay response}
10+
{--callback-url= : Callback URL for Quickpay servers (not localhost)}
11+
{--yes : Skip the interactive confirmation}
12+
{--json : Write machine-readable JSON}';
813

914
protected $description = 'Cancel a Quickpay payment';
1015

‎app/Commands/Payments/CapturePaymentCommand.php‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@
66

77
class CapturePaymentCommand extends AbstractPaymentMutationCommand
88
{
9-
protected $signature = 'payments:capture {id} {amount} {--synchronized} {--callback-url= : Callback URL for Quickpay servers (not localhost)} {--yes} {--json}';
9+
protected $signature = 'payments:capture
10+
{id : Quickpay payment ID}
11+
{amount : Capture amount as a positive integer in minor units}
12+
{--synchronized : Request a synchronized Quickpay response}
13+
{--callback-url= : Callback URL for Quickpay servers (not localhost)}
14+
{--yes : Skip the interactive confirmation}
15+
{--json : Write machine-readable JSON}';
1016

1117
protected $description = 'Capture an authorized Quickpay payment';
1218

‎app/Commands/Payments/CreatePaymentCommand.php‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@
1212

1313
class CreatePaymentCommand extends AuthenticatedCommand
1414
{
15-
protected $signature = 'payments:create {order-id} {currency=DKK} {--field=*} {--json}';
15+
protected $signature = 'payments:create
16+
{order-id : Quickpay order ID containing 4-20 characters}
17+
{currency=DKK : Three-letter currency code}
18+
{--field=* : Additional request field as key=value; repeatable}
19+
{--json : Write machine-readable JSON}';
1620

1721
protected $description = 'Create a Quickpay payment';
1822

‎app/Commands/Payments/CreatePaymentLinkCommand.php‎

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,17 @@ class CreatePaymentLinkCommand extends AuthenticatedCommand
2222
'payment-methods' => 'payment_methods',
2323
];
2424

25-
protected $signature = 'payments:link {id} {amount}
26-
{--continue-url=}
27-
{--cancel-url=}
25+
protected $signature = 'payments:link
26+
{id : Quickpay payment ID}
27+
{amount : Link amount as a positive integer in minor units}
28+
{--continue-url= : Browser destination after successful payment}
29+
{--cancel-url= : Browser destination after cancelled payment}
2830
{--callback-url= : Callback URL for Quickpay servers (not localhost)}
29-
{--language=}
30-
{--payment-methods=}
31-
{--auto-capture}
32-
{--field=*}
33-
{--json}';
31+
{--language= : Payment-window language}
32+
{--payment-methods= : Allowed payment methods}
33+
{--auto-capture : Request automatic capture}
34+
{--field=* : Additional request field as key=value; repeatable}
35+
{--json : Write machine-readable JSON}';
3436

3537
protected $description = 'Create a Quickpay payment link';
3638

‎app/Commands/Payments/GetPaymentCommand.php‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111

1212
class GetPaymentCommand extends AuthenticatedCommand
1313
{
14-
protected $signature = 'payments:get {id} {--operations-size=} {--json}';
14+
protected $signature = 'payments:get
15+
{id : Quickpay payment ID}
16+
{--operations-size= : Maximum operations included; use 0 to omit them}
17+
{--json : Write machine-readable JSON}';
1518

1619
protected $description = 'Get a Quickpay payment';
1720

@@ -44,7 +47,7 @@ public function handle(AuthenticatedQuickpayFactory $quickpay, PaymentPresenter
4447
$this->newLine();
4548
$this->line('Operations');
4649
$this->table(
47-
['ID', 'Type', 'Amount', 'Pending', 'Quickpay status', 'Acquirer status', 'Callback', 'Created'],
50+
['ID', 'Type', 'Amount (minor units)', 'Pending', 'Quickpay status', 'Acquirer status', 'Callback', 'Created'],
4851
$presenter->operations($operations, $apiKey),
4952
);
5053
}

0 commit comments

Comments
 (0)