105000KAllgemeinKSkiKWeihnachten'; $result = $this->parse(MessageType::VoucherTemplates, $xml); self::assertInstanceOf(VoucherTemplates::class, $result); self::assertSame(10, $result->minAmount); self::assertSame(5000, $result->maxAmount); self::assertSame([3, 4, 5], array_map(static fn ($template) => $template->id, $result->templates)); self::assertSame(['Allgemein', 'Ski', 'Weihnachten'], array_map(static fn ($template) => $template->label, $result->templates)); self::assertSame('K', $result->templates[1]->type); self::assertNull($result->templates[1]->note); self::assertSame('Ski', $result->find(4)?->label); self::assertNull($result->find(999)); } public function testTemplatesWithoutAmountBoundsAreRejected(): void { $xml = 'KAllgemein'; $this->expectException(ResponseParserException::class); $this->expectExceptionMessage('amount bounds'); $this->parse(MessageType::VoucherTemplates, $xml); } public function testPaymentMethods(): void { $xml = ''; $result = $this->parse(MessageType::VoucherPaymentMethods, $xml); self::assertEquals([new VoucherPaymentMethod(2, 'Überweisung', 'UEBERW')], $result); self::assertTrue($result[0]->isTransfer()); } public function testPreviewIsDecodedFromItsDataUri(): void { $pdf = "%PDF-1.4\nprobe"; $xml = 'data:application/pdf;base64,'.base64_encode($pdf).''; $result = $this->parse(MessageType::VoucherPreview, $xml); self::assertInstanceOf(VoucherPreview::class, $result); self::assertSame($pdf, $result->pdf); } public function testPreviewWithoutAPdfDataUriIsRejected(): void { $xml = ''; $this->expectException(ResponseParserException::class); $this->parse(MessageType::VoucherPreview, $xml); } public function testTheDryRunAnswersPossibleAndIssuesNothing(): void { $xml = 'möglich'; $result = $this->parse(MessageType::VoucherSale, $xml); self::assertInstanceOf(VoucherSaleResponse::class, $result); self::assertTrue($result->isInquiryValid()); self::assertFalse($result->isSaleSuccessful()); self::assertNull($result->voucherNumber); self::assertNull($result->redemptionCode); } public function testACompletedPurchase(): void { $xml = 'erfolgt4711K-001234ABCD1234111,00R-998877kaeufer@example.invalid'; $result = $this->parse(MessageType::VoucherSale, $xml); self::assertInstanceOf(VoucherSaleResponse::class, $result); self::assertTrue($result->isSaleSuccessful()); self::assertSame(4711, $result->voucherId); self::assertSame('K-001234', $result->voucherNumber); self::assertSame('ABCD1234', $result->redemptionCode); self::assertSame(111.0, $result->price); self::assertSame('R-998877', $result->invoiceNumber); self::assertSame('kaeufer@example.invalid', $result->email); } /** * Missing address data comes as 650, the code Notification::isError() treats as success. * The caller must take any HINWEIS as a rejection on this path. */ public function testAMissingAddressFieldIsAHinweis650(): void { $xml = '650Fehlende Adress Daten. Bitte folgende Eingabefelder prüfen: Strasse'; $result = $this->parse(MessageType::VoucherSale, $xml); self::assertInstanceOf(Notification::class, $result); self::assertSame(650, $result->code); } private function parse(MessageType $type, string $xml): mixed { return (new ApiResponseParser($this->createStub(TravelParser::class)))->parseXmlString($type, $xml); } }