Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
88.24% covered (success)
88.24%
15 / 17
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
FiscalQr
88.24% covered (success)
88.24%
15 / 17
0.00% covered (danger)
0.00%
0 / 2
9.13
0.00% covered (danger)
0.00%
0 / 1
 buildUrl
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
5.02
 payloadGet
83.33% covered (success)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
4.07
1<?php
2
3declare(strict_types=1);
4
5namespace UppServices\verifactu;
6
7/**
8 * VeriFactu AEAT ValidarQR URL (parity with client VeriFactu QR helpers).
9 *
10 * Domain helper: does not touch TICKETDATA.registry. Callers persist qr after generation.
11 */
12class FiscalQr
13{
14    private const SANDBOX_BASE = 'https://prewww2.aeat.es/wlpl/TIKE-CONT/ValidarQR';
15    private const PROD_BASE = 'https://www2.agenciatributaria.gob.es/wlpl/TIKE-CONT/ValidarQR';
16
17    /**
18     * Builds the AEAT QR validation URL from a VeriFactu payload shape.
19     *
20     * @param array<string, mixed> $payload
21     */
22    public static function buildUrl(array $payload, ?bool $sandbox = null): string
23    {
24        if ($sandbox === null) {
25            $sandbox = class_exists(\AppConstants::class, false) && method_exists(\AppConstants::class, 'VFT_Sandbox')
26                ? (bool) \AppConstants::VFT_Sandbox()
27                : true;
28        }
29
30        $base = $sandbox ? self::SANDBOX_BASE : self::PROD_BASE;
31
32        $query = [];
33        $query[] = 'nif='.rawurlencode((string) self::payloadGet($payload, ['issuer', 'taxId'], ''));
34        $query[] = 'numserie='.rawurlencode((string) self::payloadGet($payload, ['invoice', 'number'], ''));
35        $query[] = 'fecha='.rawurlencode((string) self::payloadGet($payload, ['invoice', 'date'], ''));
36        $query[] = 'importe='.rawurlencode((string) self::payloadGet($payload, ['invoice', 'total'], '0.00'));
37
38        return $base.'?'.implode('&', $query);
39    }
40
41    /**
42     * @param array<string, mixed> $source
43     * @param list<string|int> $path
44     */
45    private static function payloadGet(array $source, array $path, mixed $default = null): mixed
46    {
47        $cursor = $source;
48        foreach ($path as $key) {
49            if (!is_array($cursor) || !array_key_exists($key, $cursor)) {
50                return $default;
51            }
52            $cursor = $cursor[$key];
53        }
54        return $cursor;
55    }
56}