## Summary - Makes Stripe Terminal card payment intents always use 25% moms in the API, independent of any client-supplied `tax_percentage`. - Updates amount calculation, metadata persistence, stored-intent reuse matching, the authoritative OpenAPI contracts, and operation-specific Writerside outputs. - Prevents double charging and false order closure across stale, concurrently succeeded, partially recorded, or mismatched intents. - Serializes payment create/capture/closure with order-item changes and every order-to-invoice-collection reassignment through shared database locks. - Converts expected lock contention and reconciliation cases into deliberate 409 responses. ## Exact-head evidence Current head: `3a0f70d315a94d2efe586a2188d2c54f8ff11cd4` - PHP syntax passed for all changed runtime files. - Focused Orders suite: **42 tests / 293 assertions passed**. - `git diff --check` passed. - Fresh exact-head Tests and Qodana are running. - Every Codex finding has a concrete reply; a fresh exact-head review is requested below. ## Safety behavior - Caller-controlled VAT is absent from request contracts; fixed 25% moms is server-owned. - A succeeded payment is preserved, requires the full expected `amount_received`, and cannot close a changed/mismatched or already-claimed collection. - A compatible partially recorded Stripe closure is completed idempotently; conflicting partial state fails closed for manual reconciliation. - Every cancellation/delete caller honors a concurrent-success result and never falsely reports a completed payment as cleared. - Price changes and invoice-collection reassignment share the payment lock through validation, capture, post-capture reload, and closure. - Reader changes are persisted only for reusable matching intents, so stale intent cancellation targets the original terminal. - Accepted legacy succeeded intents normalize stored tax to 25% before response construction. --------- Co-authored-by: Jeppe Bundgaard <jb@truckwash.dk>
26 lines
1.7 KiB
PHP
26 lines
1.7 KiB
PHP
<?php
|
|
|
|
it('wires stripe payment intent persistence to prune duplicates and clear reader state safely', function (): void {
|
|
$objectFile = dirname(__DIR__, 3) . '/objects/stripe_payment_intents_o.php';
|
|
$content = file_get_contents($objectFile);
|
|
|
|
expect($content)->not->toBeFalse();
|
|
expect($content)->toContain("new object_property(\$this->table, \$this->id, 'reader_id', 'string', false);");
|
|
expect($content)->toContain('clearOrderPaymentIntents($order_id);');
|
|
expect($content)->toContain('public function getOrderPaymentIntentRows(int $order_id): array');
|
|
expect($content)->toContain('usort($rows');
|
|
expect($content)->toContain('public function clearOrderPaymentIntents(int $order_id, ?int $keepId = null): void');
|
|
expect($content)->toContain("self::delete_object(\$this->getTable(), \$rowId);");
|
|
expect($content)->toContain('public function updateStoredPaymentIntent(mixed $paymentIntent): void');
|
|
expect($content)->toContain('public function setReaderId(?string $readerId): void');
|
|
expect($content)->toContain('sendCancelPaymentIntent($readerId);');
|
|
expect($content)->toContain('payment_intents->cancel($paymentIntentId);');
|
|
expect($content)->toContain('public function delete(): bool');
|
|
expect($content)->toContain('if (!$this->cancelPaymentIntent())');
|
|
expect($content)->toContain('public function cancelPaymentIntent(): bool');
|
|
expect($content)->toContain("if (\$status === 'succeeded')");
|
|
expect($content)->toContain('return false;');
|
|
expect(substr_count($content, 'payment_intents->get($paymentIntentId)'))->toBeGreaterThanOrEqual(2);
|
|
expect($content)->toContain('deleteDuplicateOrderPaymentIntents(int $order_id, int $keepId): void');
|
|
});
|