mirror of
https://github.com/vr-payment/shopware-6.git
synced 2026-06-05 11:36:37 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 05c83b1ac6 | |||
| 93f24a2cac | |||
| dbd0b6808c | |||
| 3f3bf866dd | |||
| 4fbae35058 | |||
| 922f66e784 | |||
| 089555e77f | |||
| 55bdb5c640 | |||
| d1a78fedcf | |||
| d7ea44d506 |
@@ -1,3 +1,54 @@
|
|||||||
|
# 7.3.4
|
||||||
|
- Shopware 6.7.10.0 compatible
|
||||||
|
- Fix for cart being lost when changing payment methods
|
||||||
|
- Fixed for incorrectly reference function (getState())
|
||||||
|
- Fix cache not being cleaned after payment errors
|
||||||
|
- Fix missing shipping costs
|
||||||
|
- Minor fix for Customer Id null inconsistency
|
||||||
|
|
||||||
|
# 7.3.3
|
||||||
|
- Shopware 6.7.9.0 compatible
|
||||||
|
- Fix for only showing 25 sales channels in selector
|
||||||
|
- Fixed bug with discount occasionally blocking payment methods
|
||||||
|
|
||||||
|
# 7.3.2
|
||||||
|
- Fix for partial refunds using standard refund option
|
||||||
|
- Fixed issue with undefined array key
|
||||||
|
- Fixed issue with recurring payments
|
||||||
|
- Fixed concrete class reference; used interface instead
|
||||||
|
|
||||||
|
# 7.3.1
|
||||||
|
- Shopware 6.7.7.0 compatibility
|
||||||
|
- Fix for missing payment icons
|
||||||
|
- Fixed issue with 'Entire Set' coupons returning error due to totals mismatch
|
||||||
|
- Fixed issue with orphaned order_transactions causing an admin UI failure
|
||||||
|
- Fixed error thrown when customers who aren't logged in attempt to download an invoice
|
||||||
|
|
||||||
|
# 7.3.0
|
||||||
|
- Headless storefront support
|
||||||
|
|
||||||
|
# 7.2.0
|
||||||
|
- Renamed database table to avoid a naming conflict with legacy plugins
|
||||||
|
- Fixed issue with refunds failing for payments using Invoice
|
||||||
|
- Fix to respect sort order of payment methods
|
||||||
|
|
||||||
|
# 7.1.6
|
||||||
|
- Improved rounding handling to force 2 decimal places
|
||||||
|
- Removed references to unused classes
|
||||||
|
- Fixed some type errors
|
||||||
|
- Further improved 0 amount transaction; state transitions
|
||||||
|
|
||||||
|
# 7.1.5
|
||||||
|
- Improved analytics
|
||||||
|
- Improved error handling for refunding amount 0 and too many items
|
||||||
|
|
||||||
|
# 7.1.4
|
||||||
|
- Updated SDK to 4.8.1
|
||||||
|
|
||||||
|
# 7.1.3
|
||||||
|
- Fixed issue with radio button/switch settings not saving
|
||||||
|
- Fixed issue with error screen sporadically happening after failed payments
|
||||||
|
|
||||||
# 7.1.2
|
# 7.1.2
|
||||||
- Support the ability to have different spaces for differet sales channels
|
- Support the ability to have different spaces for differet sales channels
|
||||||
- Fixed issue where Twint would not appear sometimes
|
- Fixed issue where Twint would not appear sometimes
|
||||||
|
|||||||
@@ -1,3 +1,52 @@
|
|||||||
|
# 7.3.4
|
||||||
|
- Kompatibel mit Shopware 6.7.10.0
|
||||||
|
- Problem behoben, bei dem der Warenkorb beim Ändern der Zahlungsmethode verloren ging
|
||||||
|
- Fehlerhafte Referenzierung der Funktion getState() behoben
|
||||||
|
- Problem behoben, bei dem der Cache nach Zahlungsfehlern nicht geleert wurde
|
||||||
|
- Fehlende Versandkosten behoben
|
||||||
|
- Kleinere Korrektur eines Fehlers mit der Kunden-ID (null)
|
||||||
|
|
||||||
|
# 7.3.3
|
||||||
|
- Kompatibel mit Shopware 6.7.9.0
|
||||||
|
- Problem behoben, dass im Selektor nur 25 Vertriebskanäle angezeigt wurden
|
||||||
|
- Fehler behoben, der gelegentlich dazu führte, dass Rabatte Zahlungsmethoden blockierten
|
||||||
|
|
||||||
|
# 7.3.2
|
||||||
|
- Problem mit Teilrückerstattungen über die Standardrückerstattungsoption behoben
|
||||||
|
- Problem mit undefiniertem Array-Schlüssel behoben
|
||||||
|
- Problem mit einziehenden Zahlungen behoben
|
||||||
|
- Konkrete Klasse durch Verwendung einer Schnittstelle korrigiert
|
||||||
|
# 7.3.1
|
||||||
|
- Kompatibilität mit Shopware 6.7.7.0
|
||||||
|
- Fehlende Zahlungssymbole behoben
|
||||||
|
- Problem mit „Gesamtes Set“-Gutscheinen aufgrund von Summenabweichungen behoben
|
||||||
|
- Problem mit verwaisten Bestelltransaktionen behoben, die zu einem Fehler in der Admin-Oberfläche führten
|
||||||
|
- Ein Fehler wurde behoben, der auftrat, wenn nicht angemeldete Kunden versuchten, eine Rechnung herunterzuladen.
|
||||||
|
|
||||||
|
# 7.3.0
|
||||||
|
- Headless Storefront unterstützung
|
||||||
|
|
||||||
|
# 7.2.0
|
||||||
|
- Datenbanktabelle umbenannt, um Namenskonflikte mit älteren Plugins zu vermeiden.
|
||||||
|
- Problem mit fehlgeschlagenen Rückerstattungen bei Zahlungen mit Rechnungen behoben.
|
||||||
|
|
||||||
|
# 7.1.6
|
||||||
|
- Verbesserte Rundungsbehandlung für zwei Dezimalstellen
|
||||||
|
- Entfernte Verweise auf ungenutzte Klassen
|
||||||
|
- Behebung einiger Typfehler
|
||||||
|
- Weitere Verbesserung von Transaktionen mit dem Betrag 0 und Zustandsübergängen
|
||||||
|
|
||||||
|
# 7.1.5
|
||||||
|
- Verbesserte Analysefunktionen
|
||||||
|
- Verbesserte Fehlerbehandlung bei Rückerstattungen von 0 Beträgen und zu vielen Artikeln
|
||||||
|
|
||||||
|
# 7.1.4
|
||||||
|
- Aktualisiertes SDK - 4.8.1
|
||||||
|
|
||||||
|
# 7.1.3
|
||||||
|
– Problem behoben, bei dem die Einstellungen von Optionsfeldern/Schaltern nicht gespeichert wurden.
|
||||||
|
– Problem behoben, bei dem nach fehlgeschlagenen Zahlungen sporadisch ein Fehlerbildschirm angezeigt wurde.
|
||||||
|
|
||||||
# 7.1.2
|
# 7.1.2
|
||||||
- Unterstützung der Möglichkeit, unterschiedliche Bereiche für verschiedene Vertriebskanäle zu nutzen.
|
- Unterstützung der Möglichkeit, unterschiedliche Bereiche für verschiedene Vertriebskanäle zu nutzen.
|
||||||
- Problem behoben, bei dem Twint manchmal nicht angezeigt wurde.
|
- Problem behoben, bei dem Twint manchmal nicht angezeigt wurde.
|
||||||
|
|||||||
+1
-1
@@ -186,7 +186,7 @@
|
|||||||
same "printed page" as the copyright notice for easier
|
same "printed page" as the copyright notice for easier
|
||||||
identification within third-party archives.
|
identification within third-party archives.
|
||||||
|
|
||||||
Copyright 2025 VR Payment GmbH
|
Copyright 2026 VR Payment GmbH
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ Please note that this plugin is for versions 6.5, 6.6 or 6.7. For the 6.4 plugin
|
|||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
- For English documentation click [here](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.1.2/docs/en/documentation.html)
|
- For English documentation click [here](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.3.4/docs/en/documentation.html)
|
||||||
- Für die deutsche Dokumentation klicken Sie [hier](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.1.2/docs/de/documentation.html)
|
- Für die deutsche Dokumentation klicken Sie [hier](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.3.4/docs/de/documentation.html)
|
||||||
- Pour la documentation Française, cliquez [ici](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.1.2/docs/fr/documentation.html)
|
- Pour la documentation Française, cliquez [ici](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.3.4/docs/fr/documentation.html)
|
||||||
- Per la documentazione in tedesco, clicca [qui](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.1.2/docs/it/documentation.html)
|
- Per la documentazione in tedesco, clicca [qui](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.3.4/docs/it/documentation.html)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -42,12 +42,37 @@ bin/console plugin:refresh
|
|||||||
bin/console plugin:install --activate --clearCache VRPayment
|
bin/console plugin:install --activate --clearCache VRPayment
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Update
|
||||||
|
|
||||||
|
### Via Administration
|
||||||
|
1. Go to Shopware Admin > Extensions > My extensions.
|
||||||
|
2. Find VRPaymentPayment.
|
||||||
|
3. Click Update.
|
||||||
|
|
||||||
|
### Via CLI
|
||||||
|
1. Deploy the new plugin files (replace the folder in custom/plugins/VRPaymentPayment or upload/install a new ZIP).
|
||||||
|
2. Run:
|
||||||
|
```bash
|
||||||
|
bin/console plugin:refresh
|
||||||
|
bin/console plugin:update --clearCache VRPaymentPayment
|
||||||
|
bin/console cache:clear
|
||||||
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
### API Credentials
|
### API Credentials
|
||||||
|
|
||||||
1. Navigate to Shopware Admin > Settings > VRPayment.
|
1. Navigate to Shopware Admin > Settings > VRPayment.
|
||||||
2. Enter your Space ID, User ID, and API Key (obtained from the [VR Payment Portal](https://gateway.vr-payment.de/)).
|
2. Enter your Space ID, User ID, and API Key (obtained from the [VR Payment Portal](https://gateway.vr-payment.de/)).
|
||||||
|
|
||||||
|
### VRPayment does not appear in Settings
|
||||||
|
|
||||||
|
1. You should run the following commmand
|
||||||
|
|
||||||
|
```bash
|
||||||
|
Copy
|
||||||
|
bin/build-administration.sh
|
||||||
|
```
|
||||||
|
|
||||||
### Payment Methods
|
### Payment Methods
|
||||||
|
|
||||||
Configure supported methods (e.g., credit cards, Apple Pay) via the [VR Payment Portal](https://gateway.vr-payment.de/).
|
Configure supported methods (e.g., credit cards, Apple Pay) via the [VR Payment Portal](https://gateway.vr-payment.de/).
|
||||||
@@ -69,7 +94,7 @@ ________________________________________________________________________________
|
|||||||
| Shopware 6 version | Plugin major version | Supported until |
|
| Shopware 6 version | Plugin major version | Supported until |
|
||||||
|-------------------------------|------------------------|------------------------|
|
|-------------------------------|------------------------|------------------------|
|
||||||
| Shopware 6.7.x | 7.x | Further notice |
|
| Shopware 6.7.x | 7.x | Further notice |
|
||||||
| Shopware 6.6.x | 6.x | December 2025 |
|
| Shopware 6.6.x | 6.x | March 2026 |
|
||||||
| Shopware 6.5.x | 5.x | October 2024 |
|
| Shopware 6.5.x | 5.x | October 2024 |
|
||||||
-----------------------------------------------------------------------------------
|
-----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -59,5 +59,5 @@
|
|||||||
"vrpayment/sdk": "^4.0.0"
|
"vrpayment/sdk": "^4.0.0"
|
||||||
},
|
},
|
||||||
"type": "shopware-platform-plugin",
|
"type": "shopware-platform-plugin",
|
||||||
"version": "7.1.2"
|
"version": "7.3.4"
|
||||||
}
|
}
|
||||||
|
|||||||
+133
-49
@@ -23,7 +23,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://github.com/vr-payment/shopware-6/releases/tag/7.1.2/">
|
<a href="https://github.com/vr-payment/shopware-6/releases/tag/7.3.4/">
|
||||||
Source
|
Source
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -183,11 +183,73 @@ php bin/console plugin:install --activate --clearCache VRPayment</code></pre>
|
|||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</div> </div>
|
</div> </div>
|
||||||
|
</div> <div class="section" id="_vrpayment_erscheint_nicht_in_den_einstellungen">
|
||||||
|
<div class="section-title">
|
||||||
|
<h2>
|
||||||
|
<span class="title-number">4.4</span>VRPayment erscheint nicht in den Einstellungen </h2>
|
||||||
|
</div>
|
||||||
|
<div class="section-body">
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Folgender Befehl muss ausgeführt werden</p>
|
||||||
|
</div><div class="listingblock">
|
||||||
|
<div class="content">
|
||||||
|
<pre class="highlight"><code class="language-bash" data-lang="bash">bin/build-administration.sh</code></pre>
|
||||||
|
</div>
|
||||||
|
</div> </div>
|
||||||
|
</div> </div>
|
||||||
|
</div> <div class="chapter" id="_update">
|
||||||
|
<div class="chapter-title">
|
||||||
|
<h1>
|
||||||
|
<span class="title-number">5</span>Update </h1>
|
||||||
|
</div>
|
||||||
|
<div class="chapter-body">
|
||||||
|
<div class="section" id="_via_administration">
|
||||||
|
<div class="section-title">
|
||||||
|
<h2>
|
||||||
|
<span class="title-number">5.1</span>Via Administration </h2>
|
||||||
|
</div>
|
||||||
|
<div class="section-body">
|
||||||
|
<div class="olist arabic">
|
||||||
|
<ol class="arabic">
|
||||||
|
<li>
|
||||||
|
<p>Gehe zu Shopware Admin > Erweiterungen > Meine Erweiterungen.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Suche nach VRPaymentPayment.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Klicke auf Aktualisieren.</p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div> </div>
|
||||||
|
</div> <div class="section" id="_via_cli">
|
||||||
|
<div class="section-title">
|
||||||
|
<h2>
|
||||||
|
<span class="title-number">5.2</span>Via CLI </h2>
|
||||||
|
</div>
|
||||||
|
<div class="section-body">
|
||||||
|
<div class="olist arabic">
|
||||||
|
<ol class="arabic">
|
||||||
|
<li>
|
||||||
|
<p>Stelle die neuen Plugin-Dateien bereit (ersetze den Ordner <code>custom/plugins/VRPaymentPayment</code> oder lade ein neues ZIP hoch/installiere es).</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Führe aus:</p>
|
||||||
|
<div class="listingblock">
|
||||||
|
<div class="content">
|
||||||
|
<pre class="highlight"><code class="language-bash" data-lang="bash">bin/console plugin:refresh
|
||||||
|
bin/console plugin:update --clearCache VRPaymentPayment
|
||||||
|
bin/console cache:clear</code></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div> </div>
|
||||||
</div> </div>
|
</div> </div>
|
||||||
</div> <div class="chapter" id="portal-startup-guide">
|
</div> <div class="chapter" id="portal-startup-guide">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">5</span>Portal-Startanleitung </h1>
|
<span class="title-number">6</span>Portal-Startanleitung </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -206,7 +268,7 @@ Wählen Sie das passende Abo aus – es sollte E-Commerce-Transaktionen unterst
|
|||||||
</div> <div class="section" id="_erstellen_sie_den_api_schlüssel">
|
</div> <div class="section" id="_erstellen_sie_den_api_schlüssel">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">5.1</span>Erstellen Sie den API-Schlüssel: </h2>
|
<span class="title-number">6.1</span>Erstellen Sie den API-Schlüssel: </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="olist arabic">
|
<div class="olist arabic">
|
||||||
@@ -292,7 +354,7 @@ Bitte beachten Sie, dass das Laden der Rollen einige Sekunden dauern kann.
|
|||||||
</div> <div class="section" id="_zahlungsmethoden_einrichten">
|
</div> <div class="section" id="_zahlungsmethoden_einrichten">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">5.2</span>Zahlungsmethoden einrichten </h2>
|
<span class="title-number">6.2</span>Zahlungsmethoden einrichten </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -354,7 +416,7 @@ Bitte beachten Sie, dass die Konnektoren doppelt erscheinen, da einer für Zahlu
|
|||||||
</div> <div class="chapter" id="_shop_startanleitung">
|
</div> <div class="chapter" id="_shop_startanleitung">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">6</span>Shop-Startanleitung </h1>
|
<span class="title-number">7</span>Shop-Startanleitung </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="olist arabic">
|
<div class="olist arabic">
|
||||||
@@ -493,7 +555,7 @@ Bitte beachten Sie, dass diese Option leer bleiben sollte, wenn Sie die Space Vi
|
|||||||
</div> <div class="chapter" id="_transaktionszustandsdiagramm">
|
</div> <div class="chapter" id="_transaktionszustandsdiagramm">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">7</span>Transaktionszustandsdiagramm </h1>
|
<span class="title-number">8</span>Transaktionszustandsdiagramm </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -503,7 +565,7 @@ Bitte beachten Sie, dass diese Option leer bleiben sollte, wenn Sie die Space Vi
|
|||||||
</div> <div class="section" id="_zustandsabbildung_von_shopware_bestellungen">
|
</div> <div class="section" id="_zustandsabbildung_von_shopware_bestellungen">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">7.1</span>Zustandsabbildung von Shopware-Bestellungen </h2>
|
<span class="title-number">8.1</span>Zustandsabbildung von Shopware-Bestellungen </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -511,7 +573,7 @@ Bitte beachten Sie, dass diese Option leer bleiben sollte, wenn Sie die Space Vi
|
|||||||
</div> <div class="section" id="_allgemeine_anmerkungen_zu_bestellstatus">
|
</div> <div class="section" id="_allgemeine_anmerkungen_zu_bestellstatus">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h3>
|
<h3>
|
||||||
<span class="title-number">7.1.1</span>Allgemeine Anmerkungen zu Bestellstatus </h3>
|
<span class="title-number">8.1.1</span>Allgemeine Anmerkungen zu Bestellstatus </h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -521,7 +583,7 @@ Bitte beachten Sie, dass diese Option leer bleiben sollte, wenn Sie die Space Vi
|
|||||||
</div> <div class="section" id="_zustandsabbildung_des_shopware_zahlungsstatus">
|
</div> <div class="section" id="_zustandsabbildung_des_shopware_zahlungsstatus">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">7.2</span>Zustandsabbildung des Shopware-Zahlungsstatus </h2>
|
<span class="title-number">8.2</span>Zustandsabbildung des Shopware-Zahlungsstatus </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -548,7 +610,7 @@ Bitte beachten Sie, dass diese Option leer bleiben sollte, wenn Sie die Space Vi
|
|||||||
</div> <div class="section" id="_allgemeine_anmerkungen_zu_zahlungsstatus">
|
</div> <div class="section" id="_allgemeine_anmerkungen_zu_zahlungsstatus">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h3>
|
<h3>
|
||||||
<span class="title-number">7.2.1</span>Allgemeine Anmerkungen zu Zahlungsstatus </h3>
|
<span class="title-number">8.2.1</span>Allgemeine Anmerkungen zu Zahlungsstatus </h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -558,7 +620,7 @@ Bitte beachten Sie, dass diese Option leer bleiben sollte, wenn Sie die Space Vi
|
|||||||
</div> <div class="section" id="_zustandsabbildung_des_shopware_lieferstatus">
|
</div> <div class="section" id="_zustandsabbildung_des_shopware_lieferstatus">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">7.3</span>Zustandsabbildung des Shopware-Lieferstatus </h2>
|
<span class="title-number">8.3</span>Zustandsabbildung des Shopware-Lieferstatus </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -584,7 +646,7 @@ Bitte beachten Sie, dass diese Option leer bleiben sollte, wenn Sie die Space Vi
|
|||||||
</div> <div class="chapter" id="_transaktionsverwaltung">
|
</div> <div class="chapter" id="_transaktionsverwaltung">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">8</span>Transaktionsverwaltung </h1>
|
<span class="title-number">9</span>Transaktionsverwaltung </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -592,7 +654,7 @@ Bitte beachten Sie, dass diese Option leer bleiben sollte, wenn Sie die Space Vi
|
|||||||
</div> <div class="section" id="_bestellung_abschließen_erfassen">
|
</div> <div class="section" id="_bestellung_abschließen_erfassen">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.1</span>Bestellung abschließen (erfassen) </h2>
|
<span class="title-number">9.1</span>Bestellung abschließen (erfassen) </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -620,7 +682,7 @@ Wenn der Abschluss in VR Payment ausstehend ist, bleibt die Bestellung im Status
|
|||||||
</div> <div class="section" id="_transaktion_stornieren">
|
</div> <div class="section" id="_transaktion_stornieren">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.2</span>Transaktion stornieren </h2>
|
<span class="title-number">9.2</span>Transaktion stornieren </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -644,7 +706,7 @@ Sie können nur Transaktionen stornieren, die noch nicht abgeschlossen sind.
|
|||||||
</div> <div class="section" id="_rückerstattung_einer_transaktion">
|
</div> <div class="section" id="_rückerstattung_einer_transaktion">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.3</span>Rückerstattung einer Transaktion </h2>
|
<span class="title-number">9.3</span>Rückerstattung einer Transaktion </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -670,7 +732,7 @@ Es kann einige Zeit dauern, bis Sie die Rückerstattung in Shopware sehen. Rück
|
|||||||
</div> <div class="section" id="_bestellungen_auf_on_hold">
|
</div> <div class="section" id="_bestellungen_auf_on_hold">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.4</span>Bestellungen auf On Hold </h2>
|
<span class="title-number">9.4</span>Bestellungen auf On Hold </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -692,7 +754,7 @@ Es kann einige Zeit dauern, bis Sie die Rückerstattung in Shopware sehen. Rück
|
|||||||
</div> <div class="section" id="_einschränkungen_der_synchronisierung_zwischen_whitelabelname_und_shopware">
|
</div> <div class="section" id="_einschränkungen_der_synchronisierung_zwischen_whitelabelname_und_shopware">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.5</span>Einschränkungen der Synchronisierung zwischen VR Payment und Shopware </h2>
|
<span class="title-number">9.5</span>Einschränkungen der Synchronisierung zwischen VR Payment und Shopware </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -703,7 +765,7 @@ Es kann einige Zeit dauern, bis Sie die Rückerstattung in Shopware sehen. Rück
|
|||||||
</div> <div class="section" id="_tokenisierung">
|
</div> <div class="section" id="_tokenisierung">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.6</span>Tokenisierung </h2>
|
<span class="title-number">9.6</span>Tokenisierung </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -723,7 +785,7 @@ Die Tokenisierung ist für Gast-Checkouts nicht verfügbar
|
|||||||
</div> <div class="section" id="_wiederkehrende_zahlungen">
|
</div> <div class="section" id="_wiederkehrende_zahlungen">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.7</span>Wiederkehrende Zahlungen </h2>
|
<span class="title-number">9.7</span>Wiederkehrende Zahlungen </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -733,7 +795,7 @@ Tokenisierung unterstützt, kann er für Abonnements verwendet werden. Die wiede
|
|||||||
</div> <div class="section" id="_hauptfunktionen">
|
</div> <div class="section" id="_hauptfunktionen">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.8</span>Hauptfunktionen </h2>
|
<span class="title-number">9.8</span>Hauptfunktionen </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="ulist">
|
<div class="ulist">
|
||||||
@@ -758,7 +820,7 @@ Tokenisierung unterstützt, kann er für Abonnements verwendet werden. Die wiede
|
|||||||
</div> <div class="section" id="_fehlerbehebung">
|
</div> <div class="section" id="_fehlerbehebung">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.9</span>Fehlerbehebung </h2>
|
<span class="title-number">9.9</span>Fehlerbehebung </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="ulist">
|
<div class="ulist">
|
||||||
@@ -790,7 +852,7 @@ tail -f var/log/whitelabelname*.log</code></pre>
|
|||||||
</div> <div class="section" id="_faqs">
|
</div> <div class="section" id="_faqs">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.10</span>FAQs </h2>
|
<span class="title-number">9.10</span>FAQs </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -819,7 +881,7 @@ A: Ja, das Plugin unterstützt Wallets wie Apple Pay.</p>
|
|||||||
</div> <div class="chapter" id="_änderungsprotokoll">
|
</div> <div class="chapter" id="_änderungsprotokoll">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">9</span>Änderungsprotokoll </h1>
|
<span class="title-number">10</span>Änderungsprotokoll </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -828,7 +890,7 @@ A: Ja, das Plugin unterstützt Wallets wie Apple Pay.</p>
|
|||||||
</div> <div class="chapter" id="_mitwirken">
|
</div> <div class="chapter" id="_mitwirken">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">10</span>Mitwirken </h1>
|
<span class="title-number">11</span>Mitwirken </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -839,7 +901,7 @@ A: Ja, das Plugin unterstützt Wallets wie Apple Pay.</p>
|
|||||||
</div> <div class="chapter" id="_support">
|
</div> <div class="chapter" id="_support">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">11</span>Support </h1>
|
<span class="title-number">12</span>Support </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -888,135 +950,157 @@ A: Ja, das Plugin unterstützt Wallets wie Apple Pay.</p>
|
|||||||
<span class="item-number">4.3</span>
|
<span class="item-number">4.3</span>
|
||||||
<span class="item-title">Via Composer (Recommended)</span>
|
<span class="item-title">Via Composer (Recommended)</span>
|
||||||
</a>
|
</a>
|
||||||
|
</li> <li class="nav-level-2">
|
||||||
|
<a href="#_vrpayment_erscheint_nicht_in_den_einstellungen">
|
||||||
|
<span class="item-number">4.4</span>
|
||||||
|
<span class="item-title">VRPayment erscheint nicht in den Einstellungen</span>
|
||||||
|
</a>
|
||||||
|
</li> </ul>
|
||||||
|
</li> <li class="nav-level-1">
|
||||||
|
<a href="#_update">
|
||||||
|
<span class="item-number">5</span>
|
||||||
|
<span class="item-title">Update</span>
|
||||||
|
</a>
|
||||||
|
<ul class="nav">
|
||||||
|
<li class="nav-level-2">
|
||||||
|
<a href="#_via_administration">
|
||||||
|
<span class="item-number">5.1</span>
|
||||||
|
<span class="item-title">Via Administration</span>
|
||||||
|
</a>
|
||||||
|
</li> <li class="nav-level-2">
|
||||||
|
<a href="#_via_cli">
|
||||||
|
<span class="item-number">5.2</span>
|
||||||
|
<span class="item-title">Via CLI</span>
|
||||||
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#portal-startup-guide">
|
<a href="#portal-startup-guide">
|
||||||
<span class="item-number">5</span>
|
<span class="item-number">6</span>
|
||||||
<span class="item-title">Portal-Startanleitung</span>
|
<span class="item-title">Portal-Startanleitung</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="nav-level-2">
|
<li class="nav-level-2">
|
||||||
<a href="#_erstellen_sie_den_api_schlüssel">
|
<a href="#_erstellen_sie_den_api_schlüssel">
|
||||||
<span class="item-number">5.1</span>
|
<span class="item-number">6.1</span>
|
||||||
<span class="item-title">Erstellen Sie den API-Schlüssel:</span>
|
<span class="item-title">Erstellen Sie den API-Schlüssel:</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_zahlungsmethoden_einrichten">
|
<a href="#_zahlungsmethoden_einrichten">
|
||||||
<span class="item-number">5.2</span>
|
<span class="item-number">6.2</span>
|
||||||
<span class="item-title">Zahlungsmethoden einrichten</span>
|
<span class="item-title">Zahlungsmethoden einrichten</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_shop_startanleitung">
|
<a href="#_shop_startanleitung">
|
||||||
<span class="item-number">6</span>
|
<span class="item-number">7</span>
|
||||||
<span class="item-title">Shop-Startanleitung</span>
|
<span class="item-title">Shop-Startanleitung</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_transaktionszustandsdiagramm">
|
<a href="#_transaktionszustandsdiagramm">
|
||||||
<span class="item-number">7</span>
|
<span class="item-number">8</span>
|
||||||
<span class="item-title">Transaktionszustandsdiagramm</span>
|
<span class="item-title">Transaktionszustandsdiagramm</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="nav-level-2">
|
<li class="nav-level-2">
|
||||||
<a href="#_zustandsabbildung_von_shopware_bestellungen">
|
<a href="#_zustandsabbildung_von_shopware_bestellungen">
|
||||||
<span class="item-number">7.1</span>
|
<span class="item-number">8.1</span>
|
||||||
<span class="item-title">Zustandsabbildung von Shopware-Bestellungen</span>
|
<span class="item-title">Zustandsabbildung von Shopware-Bestellungen</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="nav-level-3">
|
<li class="nav-level-3">
|
||||||
<a href="#_allgemeine_anmerkungen_zu_bestellstatus">
|
<a href="#_allgemeine_anmerkungen_zu_bestellstatus">
|
||||||
<span class="item-number">7.1.1</span>
|
<span class="item-number">8.1.1</span>
|
||||||
<span class="item-title">Allgemeine Anmerkungen zu Bestellstatus</span>
|
<span class="item-title">Allgemeine Anmerkungen zu Bestellstatus</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_zustandsabbildung_des_shopware_zahlungsstatus">
|
<a href="#_zustandsabbildung_des_shopware_zahlungsstatus">
|
||||||
<span class="item-number">7.2</span>
|
<span class="item-number">8.2</span>
|
||||||
<span class="item-title">Zustandsabbildung des Shopware-Zahlungsstatus</span>
|
<span class="item-title">Zustandsabbildung des Shopware-Zahlungsstatus</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="nav-level-3">
|
<li class="nav-level-3">
|
||||||
<a href="#_allgemeine_anmerkungen_zu_zahlungsstatus">
|
<a href="#_allgemeine_anmerkungen_zu_zahlungsstatus">
|
||||||
<span class="item-number">7.2.1</span>
|
<span class="item-number">8.2.1</span>
|
||||||
<span class="item-title">Allgemeine Anmerkungen zu Zahlungsstatus</span>
|
<span class="item-title">Allgemeine Anmerkungen zu Zahlungsstatus</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_zustandsabbildung_des_shopware_lieferstatus">
|
<a href="#_zustandsabbildung_des_shopware_lieferstatus">
|
||||||
<span class="item-number">7.3</span>
|
<span class="item-number">8.3</span>
|
||||||
<span class="item-title">Zustandsabbildung des Shopware-Lieferstatus</span>
|
<span class="item-title">Zustandsabbildung des Shopware-Lieferstatus</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_transaktionsverwaltung">
|
<a href="#_transaktionsverwaltung">
|
||||||
<span class="item-number">8</span>
|
<span class="item-number">9</span>
|
||||||
<span class="item-title">Transaktionsverwaltung</span>
|
<span class="item-title">Transaktionsverwaltung</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="nav-level-2">
|
<li class="nav-level-2">
|
||||||
<a href="#_bestellung_abschließen_erfassen">
|
<a href="#_bestellung_abschließen_erfassen">
|
||||||
<span class="item-number">8.1</span>
|
<span class="item-number">9.1</span>
|
||||||
<span class="item-title">Bestellung abschließen (erfassen)</span>
|
<span class="item-title">Bestellung abschließen (erfassen)</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_transaktion_stornieren">
|
<a href="#_transaktion_stornieren">
|
||||||
<span class="item-number">8.2</span>
|
<span class="item-number">9.2</span>
|
||||||
<span class="item-title">Transaktion stornieren</span>
|
<span class="item-title">Transaktion stornieren</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_rückerstattung_einer_transaktion">
|
<a href="#_rückerstattung_einer_transaktion">
|
||||||
<span class="item-number">8.3</span>
|
<span class="item-number">9.3</span>
|
||||||
<span class="item-title">Rückerstattung einer Transaktion</span>
|
<span class="item-title">Rückerstattung einer Transaktion</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_bestellungen_auf_on_hold">
|
<a href="#_bestellungen_auf_on_hold">
|
||||||
<span class="item-number">8.4</span>
|
<span class="item-number">9.4</span>
|
||||||
<span class="item-title">Bestellungen auf On Hold</span>
|
<span class="item-title">Bestellungen auf On Hold</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_einschränkungen_der_synchronisierung_zwischen_whitelabelname_und_shopware">
|
<a href="#_einschränkungen_der_synchronisierung_zwischen_whitelabelname_und_shopware">
|
||||||
<span class="item-number">8.5</span>
|
<span class="item-number">9.5</span>
|
||||||
<span class="item-title">Einschränkungen der Synchronisierung zwischen VR Payment und Shopware</span>
|
<span class="item-title">Einschränkungen der Synchronisierung zwischen VR Payment und Shopware</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_tokenisierung">
|
<a href="#_tokenisierung">
|
||||||
<span class="item-number">8.6</span>
|
<span class="item-number">9.6</span>
|
||||||
<span class="item-title">Tokenisierung</span>
|
<span class="item-title">Tokenisierung</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_wiederkehrende_zahlungen">
|
<a href="#_wiederkehrende_zahlungen">
|
||||||
<span class="item-number">8.7</span>
|
<span class="item-number">9.7</span>
|
||||||
<span class="item-title">Wiederkehrende Zahlungen</span>
|
<span class="item-title">Wiederkehrende Zahlungen</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_hauptfunktionen">
|
<a href="#_hauptfunktionen">
|
||||||
<span class="item-number">8.8</span>
|
<span class="item-number">9.8</span>
|
||||||
<span class="item-title">Hauptfunktionen</span>
|
<span class="item-title">Hauptfunktionen</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_fehlerbehebung">
|
<a href="#_fehlerbehebung">
|
||||||
<span class="item-number">8.9</span>
|
<span class="item-number">9.9</span>
|
||||||
<span class="item-title">Fehlerbehebung</span>
|
<span class="item-title">Fehlerbehebung</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_faqs">
|
<a href="#_faqs">
|
||||||
<span class="item-number">8.10</span>
|
<span class="item-number">9.10</span>
|
||||||
<span class="item-title">FAQs</span>
|
<span class="item-title">FAQs</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_änderungsprotokoll">
|
<a href="#_änderungsprotokoll">
|
||||||
<span class="item-number">9</span>
|
<span class="item-number">10</span>
|
||||||
<span class="item-title">Änderungsprotokoll</span>
|
<span class="item-title">Änderungsprotokoll</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_mitwirken">
|
<a href="#_mitwirken">
|
||||||
<span class="item-number">10</span>
|
<span class="item-number">11</span>
|
||||||
<span class="item-title">Mitwirken</span>
|
<span class="item-title">Mitwirken</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_support">
|
<a href="#_support">
|
||||||
<span class="item-number">11</span>
|
<span class="item-number">12</span>
|
||||||
<span class="item-title">Support</span>
|
<span class="item-title">Support</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
|
|||||||
+133
-49
@@ -23,7 +23,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://github.com/vr-payment/shopware-6/releases/tag/7.1.2/">
|
<a href="https://github.com/vr-payment/shopware-6/releases/tag/7.3.4/">
|
||||||
Source
|
Source
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -179,11 +179,73 @@ php bin/console plugin:install --activate --clearCache VRPayment</code></pre>
|
|||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</div> </div>
|
</div> </div>
|
||||||
|
</div> <div class="section" id="_vrpayment_does_not_appear_in_settings">
|
||||||
|
<div class="section-title">
|
||||||
|
<h2>
|
||||||
|
<span class="title-number">4.4</span>VRPayment does not appear in Settings </h2>
|
||||||
|
</div>
|
||||||
|
<div class="section-body">
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>You should run the following commmand</p>
|
||||||
|
</div><div class="listingblock">
|
||||||
|
<div class="content">
|
||||||
|
<pre class="highlight"><code class="language-bash" data-lang="bash">bin/build-administration.sh</code></pre>
|
||||||
|
</div>
|
||||||
|
</div> </div>
|
||||||
|
</div> </div>
|
||||||
|
</div> <div class="chapter" id="_update">
|
||||||
|
<div class="chapter-title">
|
||||||
|
<h1>
|
||||||
|
<span class="title-number">5</span>Update </h1>
|
||||||
|
</div>
|
||||||
|
<div class="chapter-body">
|
||||||
|
<div class="section" id="_via_administration">
|
||||||
|
<div class="section-title">
|
||||||
|
<h2>
|
||||||
|
<span class="title-number">5.1</span>Via Administration </h2>
|
||||||
|
</div>
|
||||||
|
<div class="section-body">
|
||||||
|
<div class="olist arabic">
|
||||||
|
<ol class="arabic">
|
||||||
|
<li>
|
||||||
|
<p>Go to Shopware Admin > Extensions > My extensions.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Find VRPaymentPayment.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Click Update.</p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div> </div>
|
||||||
|
</div> <div class="section" id="_via_cli">
|
||||||
|
<div class="section-title">
|
||||||
|
<h2>
|
||||||
|
<span class="title-number">5.2</span>Via CLI </h2>
|
||||||
|
</div>
|
||||||
|
<div class="section-body">
|
||||||
|
<div class="olist arabic">
|
||||||
|
<ol class="arabic">
|
||||||
|
<li>
|
||||||
|
<p>Deploy the new plugin files (replace the folder in <code>custom/plugins/VRPaymentPayment</code> or upload/install a new ZIP).</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Run:</p>
|
||||||
|
<div class="listingblock">
|
||||||
|
<div class="content">
|
||||||
|
<pre class="highlight"><code class="language-bash" data-lang="bash">bin/console plugin:refresh
|
||||||
|
bin/console plugin:update --clearCache VRPaymentPayment
|
||||||
|
bin/console cache:clear</code></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div> </div>
|
||||||
</div> </div>
|
</div> </div>
|
||||||
</div> <div class="chapter" id="portal-startup-guide">
|
</div> <div class="chapter" id="portal-startup-guide">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">5</span>Portal Startup Guide </h1>
|
<span class="title-number">6</span>Portal Startup Guide </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -202,7 +264,7 @@ Please select the proper subscription plan - it should support ecommerce transac
|
|||||||
</div> <div class="section" id="_create_the_api_key">
|
</div> <div class="section" id="_create_the_api_key">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">5.1</span>Create the API key: </h2>
|
<span class="title-number">6.1</span>Create the API key: </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="olist arabic">
|
<div class="olist arabic">
|
||||||
@@ -288,7 +350,7 @@ Please note that Roles might be loading for few seconds
|
|||||||
</div> <div class="section" id="_setup_payment_methods">
|
</div> <div class="section" id="_setup_payment_methods">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">5.2</span>Setup Payment Methods </h2>
|
<span class="title-number">6.2</span>Setup Payment Methods </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -350,7 +412,7 @@ Please note that the connectors seems duplicated but it because one is for Physi
|
|||||||
</div> <div class="chapter" id="_shop_startup_guide">
|
</div> <div class="chapter" id="_shop_startup_guide">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">6</span>Shop Startup Guide </h1>
|
<span class="title-number">7</span>Shop Startup Guide </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="olist arabic">
|
<div class="olist arabic">
|
||||||
@@ -489,7 +551,7 @@ Please note that if you do not use the Space View Id; this option should stay em
|
|||||||
</div> <div class="chapter" id="_transaction_state_graph">
|
</div> <div class="chapter" id="_transaction_state_graph">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">7</span>Transaction State graph </h1>
|
<span class="title-number">8</span>Transaction State graph </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -501,7 +563,7 @@ can be found in the <a href="https://gateway.vr-payment.de/en-us/doc/payment/tra
|
|||||||
</div> <div class="section" id="_state_mapping_of_shopware_orders">
|
</div> <div class="section" id="_state_mapping_of_shopware_orders">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">7.1</span>State mapping of Shopware orders </h2>
|
<span class="title-number">8.1</span>State mapping of Shopware orders </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -509,7 +571,7 @@ can be found in the <a href="https://gateway.vr-payment.de/en-us/doc/payment/tra
|
|||||||
</div> <div class="section" id="_general_remarks_regarding_order_statuses">
|
</div> <div class="section" id="_general_remarks_regarding_order_statuses">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h3>
|
<h3>
|
||||||
<span class="title-number">7.1.1</span>General remarks regarding order statuses </h3>
|
<span class="title-number">8.1.1</span>General remarks regarding order statuses </h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -519,7 +581,7 @@ can be found in the <a href="https://gateway.vr-payment.de/en-us/doc/payment/tra
|
|||||||
</div> <div class="section" id="_state_mapping_of_shopware_payment_status">
|
</div> <div class="section" id="_state_mapping_of_shopware_payment_status">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">7.2</span>State mapping of Shopware payment status </h2>
|
<span class="title-number">8.2</span>State mapping of Shopware payment status </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -546,7 +608,7 @@ can be found in the <a href="https://gateway.vr-payment.de/en-us/doc/payment/tra
|
|||||||
</div> <div class="section" id="_general_remarks_regarding_payment_statuses">
|
</div> <div class="section" id="_general_remarks_regarding_payment_statuses">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h3>
|
<h3>
|
||||||
<span class="title-number">7.2.1</span>General remarks regarding payment statuses </h3>
|
<span class="title-number">8.2.1</span>General remarks regarding payment statuses </h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -556,7 +618,7 @@ can be found in the <a href="https://gateway.vr-payment.de/en-us/doc/payment/tra
|
|||||||
</div> <div class="section" id="_state_mapping_of_shopware_delivery_status">
|
</div> <div class="section" id="_state_mapping_of_shopware_delivery_status">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">7.3</span>State mapping of Shopware delivery status </h2>
|
<span class="title-number">8.3</span>State mapping of Shopware delivery status </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -582,7 +644,7 @@ can be found in the <a href="https://gateway.vr-payment.de/en-us/doc/payment/tra
|
|||||||
</div> <div class="chapter" id="_transaction_management">
|
</div> <div class="chapter" id="_transaction_management">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">8</span>Transaction management </h1>
|
<span class="title-number">9</span>Transaction management </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -592,7 +654,7 @@ Shopware. However, there are some limitations (see below).</p>
|
|||||||
</div> <div class="section" id="_complete_capture_an_order">
|
</div> <div class="section" id="_complete_capture_an_order">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.1</span>Complete (capture) an order </h2>
|
<span class="title-number">9.1</span>Complete (capture) an order </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -626,7 +688,7 @@ the fulfill state is reached. Initially the transaction will be in the <code>Aut
|
|||||||
</div> <div class="section" id="_void_a_transaction">
|
</div> <div class="section" id="_void_a_transaction">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.2</span>Void a transaction </h2>
|
<span class="title-number">9.2</span>Void a transaction </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -650,7 +712,7 @@ You can only void transactions that are not yet completed.
|
|||||||
</div> <div class="section" id="_refund_of_a_transaction">
|
</div> <div class="section" id="_refund_of_a_transaction">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.3</span>Refund of a transaction </h2>
|
<span class="title-number">9.3</span>Refund of a transaction </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -677,7 +739,7 @@ It can take some time until you see the refund in Shopware. Refunds will only be
|
|||||||
</div> <div class="section" id="_on_hold_orders">
|
</div> <div class="section" id="_on_hold_orders">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.4</span>On hold orders </h2>
|
<span class="title-number">9.4</span>On hold orders </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -701,7 +763,7 @@ within the defined time frame, VR Payment will generate a manual task which you
|
|||||||
</div> <div class="section" id="_limitations_of_the_synchronization_between_whitelabelname_and_shopware">
|
</div> <div class="section" id="_limitations_of_the_synchronization_between_whitelabelname_and_shopware">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.5</span>Limitations of the synchronization between VR Payment and Shopware </h2>
|
<span class="title-number">9.5</span>Limitations of the synchronization between VR Payment and Shopware </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -717,7 +779,7 @@ your Shopware backend.</p>
|
|||||||
</div> <div class="section" id="_tokenization">
|
</div> <div class="section" id="_tokenization">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.6</span>Tokenization </h2>
|
<span class="title-number">9.6</span>Tokenization </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -738,7 +800,7 @@ Tokenization is not available for guest checkouts.
|
|||||||
</div> <div class="section" id="_recurring_payments">
|
</div> <div class="section" id="_recurring_payments">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.7</span>Recurring payments </h2>
|
<span class="title-number">9.7</span>Recurring payments </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -748,7 +810,7 @@ tokenization, it can be used for subscriptions. The recurring payment is fully m
|
|||||||
</div> <div class="section" id="_key_features">
|
</div> <div class="section" id="_key_features">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.8</span>Key Features </h2>
|
<span class="title-number">9.8</span>Key Features </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="ulist">
|
<div class="ulist">
|
||||||
@@ -773,7 +835,7 @@ tokenization, it can be used for subscriptions. The recurring payment is fully m
|
|||||||
</div> <div class="section" id="_troubleshooting">
|
</div> <div class="section" id="_troubleshooting">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.9</span>Troubleshooting </h2>
|
<span class="title-number">9.9</span>Troubleshooting </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="ulist">
|
<div class="ulist">
|
||||||
@@ -804,7 +866,7 @@ tokenization, it can be used for subscriptions. The recurring payment is fully m
|
|||||||
</div> <div class="section" id="_faqs">
|
</div> <div class="section" id="_faqs">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.10</span>FAQs </h2>
|
<span class="title-number">9.10</span>FAQs </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -836,7 +898,7 @@ tokenization, it can be used for subscriptions. The recurring payment is fully m
|
|||||||
</div> <div class="chapter" id="_changelog">
|
</div> <div class="chapter" id="_changelog">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">9</span>Changelog </h1>
|
<span class="title-number">10</span>Changelog </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -845,7 +907,7 @@ tokenization, it can be used for subscriptions. The recurring payment is fully m
|
|||||||
</div> <div class="chapter" id="_contributing">
|
</div> <div class="chapter" id="_contributing">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">10</span>Contributing </h1>
|
<span class="title-number">11</span>Contributing </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -856,7 +918,7 @@ tokenization, it can be used for subscriptions. The recurring payment is fully m
|
|||||||
</div> <div class="chapter" id="_support">
|
</div> <div class="chapter" id="_support">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">11</span>Support </h1>
|
<span class="title-number">12</span>Support </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -905,135 +967,157 @@ tokenization, it can be used for subscriptions. The recurring payment is fully m
|
|||||||
<span class="item-number">4.3</span>
|
<span class="item-number">4.3</span>
|
||||||
<span class="item-title">Manual Installation</span>
|
<span class="item-title">Manual Installation</span>
|
||||||
</a>
|
</a>
|
||||||
|
</li> <li class="nav-level-2">
|
||||||
|
<a href="#_vrpayment_does_not_appear_in_settings">
|
||||||
|
<span class="item-number">4.4</span>
|
||||||
|
<span class="item-title">VRPayment does not appear in Settings</span>
|
||||||
|
</a>
|
||||||
|
</li> </ul>
|
||||||
|
</li> <li class="nav-level-1">
|
||||||
|
<a href="#_update">
|
||||||
|
<span class="item-number">5</span>
|
||||||
|
<span class="item-title">Update</span>
|
||||||
|
</a>
|
||||||
|
<ul class="nav">
|
||||||
|
<li class="nav-level-2">
|
||||||
|
<a href="#_via_administration">
|
||||||
|
<span class="item-number">5.1</span>
|
||||||
|
<span class="item-title">Via Administration</span>
|
||||||
|
</a>
|
||||||
|
</li> <li class="nav-level-2">
|
||||||
|
<a href="#_via_cli">
|
||||||
|
<span class="item-number">5.2</span>
|
||||||
|
<span class="item-title">Via CLI</span>
|
||||||
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#portal-startup-guide">
|
<a href="#portal-startup-guide">
|
||||||
<span class="item-number">5</span>
|
<span class="item-number">6</span>
|
||||||
<span class="item-title">Portal Startup Guide</span>
|
<span class="item-title">Portal Startup Guide</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="nav-level-2">
|
<li class="nav-level-2">
|
||||||
<a href="#_create_the_api_key">
|
<a href="#_create_the_api_key">
|
||||||
<span class="item-number">5.1</span>
|
<span class="item-number">6.1</span>
|
||||||
<span class="item-title">Create the API key:</span>
|
<span class="item-title">Create the API key:</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_setup_payment_methods">
|
<a href="#_setup_payment_methods">
|
||||||
<span class="item-number">5.2</span>
|
<span class="item-number">6.2</span>
|
||||||
<span class="item-title">Setup Payment Methods</span>
|
<span class="item-title">Setup Payment Methods</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_shop_startup_guide">
|
<a href="#_shop_startup_guide">
|
||||||
<span class="item-number">6</span>
|
<span class="item-number">7</span>
|
||||||
<span class="item-title">Shop Startup Guide</span>
|
<span class="item-title">Shop Startup Guide</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_transaction_state_graph">
|
<a href="#_transaction_state_graph">
|
||||||
<span class="item-number">7</span>
|
<span class="item-number">8</span>
|
||||||
<span class="item-title">Transaction State graph</span>
|
<span class="item-title">Transaction State graph</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="nav-level-2">
|
<li class="nav-level-2">
|
||||||
<a href="#_state_mapping_of_shopware_orders">
|
<a href="#_state_mapping_of_shopware_orders">
|
||||||
<span class="item-number">7.1</span>
|
<span class="item-number">8.1</span>
|
||||||
<span class="item-title">State mapping of Shopware orders</span>
|
<span class="item-title">State mapping of Shopware orders</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="nav-level-3">
|
<li class="nav-level-3">
|
||||||
<a href="#_general_remarks_regarding_order_statuses">
|
<a href="#_general_remarks_regarding_order_statuses">
|
||||||
<span class="item-number">7.1.1</span>
|
<span class="item-number">8.1.1</span>
|
||||||
<span class="item-title">General remarks regarding order statuses</span>
|
<span class="item-title">General remarks regarding order statuses</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_state_mapping_of_shopware_payment_status">
|
<a href="#_state_mapping_of_shopware_payment_status">
|
||||||
<span class="item-number">7.2</span>
|
<span class="item-number">8.2</span>
|
||||||
<span class="item-title">State mapping of Shopware payment status</span>
|
<span class="item-title">State mapping of Shopware payment status</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="nav-level-3">
|
<li class="nav-level-3">
|
||||||
<a href="#_general_remarks_regarding_payment_statuses">
|
<a href="#_general_remarks_regarding_payment_statuses">
|
||||||
<span class="item-number">7.2.1</span>
|
<span class="item-number">8.2.1</span>
|
||||||
<span class="item-title">General remarks regarding payment statuses</span>
|
<span class="item-title">General remarks regarding payment statuses</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_state_mapping_of_shopware_delivery_status">
|
<a href="#_state_mapping_of_shopware_delivery_status">
|
||||||
<span class="item-number">7.3</span>
|
<span class="item-number">8.3</span>
|
||||||
<span class="item-title">State mapping of Shopware delivery status</span>
|
<span class="item-title">State mapping of Shopware delivery status</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_transaction_management">
|
<a href="#_transaction_management">
|
||||||
<span class="item-number">8</span>
|
<span class="item-number">9</span>
|
||||||
<span class="item-title">Transaction management</span>
|
<span class="item-title">Transaction management</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="nav-level-2">
|
<li class="nav-level-2">
|
||||||
<a href="#_complete_capture_an_order">
|
<a href="#_complete_capture_an_order">
|
||||||
<span class="item-number">8.1</span>
|
<span class="item-number">9.1</span>
|
||||||
<span class="item-title">Complete (capture) an order</span>
|
<span class="item-title">Complete (capture) an order</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_void_a_transaction">
|
<a href="#_void_a_transaction">
|
||||||
<span class="item-number">8.2</span>
|
<span class="item-number">9.2</span>
|
||||||
<span class="item-title">Void a transaction</span>
|
<span class="item-title">Void a transaction</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_refund_of_a_transaction">
|
<a href="#_refund_of_a_transaction">
|
||||||
<span class="item-number">8.3</span>
|
<span class="item-number">9.3</span>
|
||||||
<span class="item-title">Refund of a transaction</span>
|
<span class="item-title">Refund of a transaction</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_on_hold_orders">
|
<a href="#_on_hold_orders">
|
||||||
<span class="item-number">8.4</span>
|
<span class="item-number">9.4</span>
|
||||||
<span class="item-title">On hold orders</span>
|
<span class="item-title">On hold orders</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_limitations_of_the_synchronization_between_whitelabelname_and_shopware">
|
<a href="#_limitations_of_the_synchronization_between_whitelabelname_and_shopware">
|
||||||
<span class="item-number">8.5</span>
|
<span class="item-number">9.5</span>
|
||||||
<span class="item-title">Limitations of the synchronization between VR Payment and Shopware</span>
|
<span class="item-title">Limitations of the synchronization between VR Payment and Shopware</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_tokenization">
|
<a href="#_tokenization">
|
||||||
<span class="item-number">8.6</span>
|
<span class="item-number">9.6</span>
|
||||||
<span class="item-title">Tokenization</span>
|
<span class="item-title">Tokenization</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_recurring_payments">
|
<a href="#_recurring_payments">
|
||||||
<span class="item-number">8.7</span>
|
<span class="item-number">9.7</span>
|
||||||
<span class="item-title">Recurring payments</span>
|
<span class="item-title">Recurring payments</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_key_features">
|
<a href="#_key_features">
|
||||||
<span class="item-number">8.8</span>
|
<span class="item-number">9.8</span>
|
||||||
<span class="item-title">Key Features</span>
|
<span class="item-title">Key Features</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_troubleshooting">
|
<a href="#_troubleshooting">
|
||||||
<span class="item-number">8.9</span>
|
<span class="item-number">9.9</span>
|
||||||
<span class="item-title">Troubleshooting</span>
|
<span class="item-title">Troubleshooting</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_faqs">
|
<a href="#_faqs">
|
||||||
<span class="item-number">8.10</span>
|
<span class="item-number">9.10</span>
|
||||||
<span class="item-title">FAQs</span>
|
<span class="item-title">FAQs</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_changelog">
|
<a href="#_changelog">
|
||||||
<span class="item-number">9</span>
|
<span class="item-number">10</span>
|
||||||
<span class="item-title">Changelog</span>
|
<span class="item-title">Changelog</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_contributing">
|
<a href="#_contributing">
|
||||||
<span class="item-number">10</span>
|
<span class="item-number">11</span>
|
||||||
<span class="item-title">Contributing</span>
|
<span class="item-title">Contributing</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_support">
|
<a href="#_support">
|
||||||
<span class="item-number">11</span>
|
<span class="item-number">12</span>
|
||||||
<span class="item-title">Support</span>
|
<span class="item-title">Support</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
|
|||||||
+133
-49
@@ -23,7 +23,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://github.com/vr-payment/shopware-6/releases/tag/7.1.2/">
|
<a href="https://github.com/vr-payment/shopware-6/releases/tag/7.3.4/">
|
||||||
Source
|
Source
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -170,11 +170,73 @@ php bin/console plugin:install --activate --clearCache VRPayment</code></pre>
|
|||||||
php bin/console plugin:install --activate --clearCache VRPayment</code></pre>
|
php bin/console plugin:install --activate --clearCache VRPayment</code></pre>
|
||||||
</div>
|
</div>
|
||||||
</div> </div>
|
</div> </div>
|
||||||
|
</div> <div class="section" id="_si_vrpayment_n_apparaît_pas_dans_les_paramètres">
|
||||||
|
<div class="section-title">
|
||||||
|
<h2>
|
||||||
|
<span class="title-number">4.4</span>Si VRPayment n’apparaît pas dans les paramètres </h2>
|
||||||
|
</div>
|
||||||
|
<div class="section-body">
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Vous devez exécuter la commande suivante</p>
|
||||||
|
</div><div class="listingblock">
|
||||||
|
<div class="content">
|
||||||
|
<pre class="highlight"><code class="language-bash" data-lang="bash">bin/build-administration.sh</code></pre>
|
||||||
|
</div>
|
||||||
|
</div> </div>
|
||||||
|
</div> </div>
|
||||||
|
</div> <div class="chapter" id="_mise_à_jour">
|
||||||
|
<div class="chapter-title">
|
||||||
|
<h1>
|
||||||
|
<span class="title-number">5</span>Mise à jour </h1>
|
||||||
|
</div>
|
||||||
|
<div class="chapter-body">
|
||||||
|
<div class="section" id="_via_l_administration">
|
||||||
|
<div class="section-title">
|
||||||
|
<h2>
|
||||||
|
<span class="title-number">5.1</span>Via l’administration </h2>
|
||||||
|
</div>
|
||||||
|
<div class="section-body">
|
||||||
|
<div class="olist arabic">
|
||||||
|
<ol class="arabic">
|
||||||
|
<li>
|
||||||
|
<p>Allez dans Shopware Admin > Extensions > Mes extensions.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Recherchez VRPaymentPayment.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Cliquez sur Mettre à jour.</p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div> </div>
|
||||||
|
</div> <div class="section" id="_via_la_cli">
|
||||||
|
<div class="section-title">
|
||||||
|
<h2>
|
||||||
|
<span class="title-number">5.2</span>Via la CLI </h2>
|
||||||
|
</div>
|
||||||
|
<div class="section-body">
|
||||||
|
<div class="olist arabic">
|
||||||
|
<ol class="arabic">
|
||||||
|
<li>
|
||||||
|
<p>Déployez les nouveaux fichiers du plugin (remplacez le dossier <code>custom/plugins/VRPaymentPayment</code> ou téléversez/installez un nouveau ZIP).</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Exécutez :</p>
|
||||||
|
<div class="listingblock">
|
||||||
|
<div class="content">
|
||||||
|
<pre class="highlight"><code class="language-bash" data-lang="bash">bin/console plugin:refresh
|
||||||
|
bin/console plugin:update --clearCache VRPaymentPayment
|
||||||
|
bin/console cache:clear</code></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div> </div>
|
||||||
</div> </div>
|
</div> </div>
|
||||||
</div> <div class="chapter" id="portal-startup-guide">
|
</div> <div class="chapter" id="portal-startup-guide">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">5</span>Guide de démarrage pour le Portail </h1>
|
<span class="title-number">6</span>Guide de démarrage pour le Portail </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -193,7 +255,7 @@ Veuillez sélectionner le plan d’abonnement approprié - il doit prendre e
|
|||||||
</div> <div class="section" id="_créez_la_clé_api">
|
</div> <div class="section" id="_créez_la_clé_api">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">5.1</span>Créez la clé API: </h2>
|
<span class="title-number">6.1</span>Créez la clé API: </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="olist arabic">
|
<div class="olist arabic">
|
||||||
@@ -279,7 +341,7 @@ Veuillez noter que le chargement des rôles peut durer quelques secondes.
|
|||||||
</div> <div class="section" id="_configurer_les_modes_de_paiement">
|
</div> <div class="section" id="_configurer_les_modes_de_paiement">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">5.2</span>Configurer les modes de paiement </h2>
|
<span class="title-number">6.2</span>Configurer les modes de paiement </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -341,7 +403,7 @@ Veuillez noter que les connecteurs semblent faire double emploi, mais c’es
|
|||||||
</div> <div class="chapter" id="_guide_de_démarrage_pour_shopware">
|
</div> <div class="chapter" id="_guide_de_démarrage_pour_shopware">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">6</span>Guide de démarrage pour Shopware </h1>
|
<span class="title-number">7</span>Guide de démarrage pour Shopware </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="olist arabic">
|
<div class="olist arabic">
|
||||||
@@ -480,7 +542,7 @@ Veuillez noter que si vous n’utilisez pas Space View Id, cette option doit
|
|||||||
</div> <div class="chapter" id="_différents_etats_pour_une_transaction">
|
</div> <div class="chapter" id="_différents_etats_pour_une_transaction">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">7</span>Différents Etats pour une Transaction </h1>
|
<span class="title-number">8</span>Différents Etats pour une Transaction </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -490,7 +552,7 @@ Veuillez noter que si vous n’utilisez pas Space View Id, cette option doit
|
|||||||
</div> <div class="section" id="_cartographie_des_différents_états_d_une_commande_de_shopware">
|
</div> <div class="section" id="_cartographie_des_différents_états_d_une_commande_de_shopware">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">7.1</span>Cartographie des différents états d’une commande de Shopware </h2>
|
<span class="title-number">8.1</span>Cartographie des différents états d’une commande de Shopware </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -498,7 +560,7 @@ Veuillez noter que si vous n’utilisez pas Space View Id, cette option doit
|
|||||||
</div> <div class="section" id="_remarque_générales_concernant_les_status_des_commandes">
|
</div> <div class="section" id="_remarque_générales_concernant_les_status_des_commandes">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h3>
|
<h3>
|
||||||
<span class="title-number">7.1.1</span>Remarque générales concernant les status des commandes </h3>
|
<span class="title-number">8.1.1</span>Remarque générales concernant les status des commandes </h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -508,7 +570,7 @@ Veuillez noter que si vous n’utilisez pas Space View Id, cette option doit
|
|||||||
</div> <div class="section" id="_cartographie_des_différents_états_du_paiement_de_shopware">
|
</div> <div class="section" id="_cartographie_des_différents_états_du_paiement_de_shopware">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">7.2</span>Cartographie des différents états du paiement de Shopware </h2>
|
<span class="title-number">8.2</span>Cartographie des différents états du paiement de Shopware </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -535,7 +597,7 @@ Veuillez noter que si vous n’utilisez pas Space View Id, cette option doit
|
|||||||
</div> <div class="section" id="_remarques_générales_concernant_les_différents_status_pour_les_paiements">
|
</div> <div class="section" id="_remarques_générales_concernant_les_différents_status_pour_les_paiements">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h3>
|
<h3>
|
||||||
<span class="title-number">7.2.1</span>Remarques générales concernant les différents status pour les paiements </h3>
|
<span class="title-number">8.2.1</span>Remarques générales concernant les différents status pour les paiements </h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -545,7 +607,7 @@ Veuillez noter que si vous n’utilisez pas Space View Id, cette option doit
|
|||||||
</div> <div class="section" id="_carthographie_des_différents_états_de_livraison_chez_shopware">
|
</div> <div class="section" id="_carthographie_des_différents_états_de_livraison_chez_shopware">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">7.3</span>Carthographie des différents états de livraison chez Shopware </h2>
|
<span class="title-number">8.3</span>Carthographie des différents états de livraison chez Shopware </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -571,7 +633,7 @@ Veuillez noter que si vous n’utilisez pas Space View Id, cette option doit
|
|||||||
</div> <div class="chapter" id="_gestion_des_transactions">
|
</div> <div class="chapter" id="_gestion_des_transactions">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">8</span>Gestion des Transactions </h1>
|
<span class="title-number">9</span>Gestion des Transactions </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -579,7 +641,7 @@ Veuillez noter que si vous n’utilisez pas Space View Id, cette option doit
|
|||||||
</div> <div class="section" id="_complete_capture_an_order">
|
</div> <div class="section" id="_complete_capture_an_order">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.1</span>Complete (capture) an order </h2>
|
<span class="title-number">9.1</span>Complete (capture) an order </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -611,7 +673,7 @@ Lorsque le paiement est en attente dans VR Payment, la commande reste en attente
|
|||||||
</div> <div class="section" id="_annuler_une_transaction">
|
</div> <div class="section" id="_annuler_une_transaction">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.2</span>Annuler une transaction </h2>
|
<span class="title-number">9.2</span>Annuler une transaction </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -635,7 +697,7 @@ Vous ne pouvez annuler que les transactions qui ne sont pas encore complétée..
|
|||||||
</div> <div class="section" id="_remboursement_d_une_transaction">
|
</div> <div class="section" id="_remboursement_d_une_transaction">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.3</span>Remboursement d’une transaction </h2>
|
<span class="title-number">9.3</span>Remboursement d’une transaction </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -661,7 +723,7 @@ Il peut s’écouler un certain temps avant que vous ne voyiez le remboursem
|
|||||||
</div> <div class="section" id="_commandes_en_attente">
|
</div> <div class="section" id="_commandes_en_attente">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.4</span>Commandes en attente </h2>
|
<span class="title-number">9.4</span>Commandes en attente </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -683,7 +745,7 @@ Il peut s’écouler un certain temps avant que vous ne voyiez le remboursem
|
|||||||
</div> <div class="section" id="_limites_de_la_synchronisation_entre_whitelabelname_et_shopware">
|
</div> <div class="section" id="_limites_de_la_synchronisation_entre_whitelabelname_et_shopware">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.5</span>Limites de la synchronisation entre VR Payment et Shopware </h2>
|
<span class="title-number">9.5</span>Limites de la synchronisation entre VR Payment et Shopware </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -694,7 +756,7 @@ Il peut s’écouler un certain temps avant que vous ne voyiez le remboursem
|
|||||||
</div> <div class="section" id="_tokenisation">
|
</div> <div class="section" id="_tokenisation">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.6</span>Tokenisation </h2>
|
<span class="title-number">9.6</span>Tokenisation </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -714,7 +776,7 @@ La tokenisation n’est pas disponible pour les paiements par les invités.
|
|||||||
</div> <div class="section" id="_paiements_récurrents">
|
</div> <div class="section" id="_paiements_récurrents">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.7</span>Paiements récurrents </h2>
|
<span class="title-number">9.7</span>Paiements récurrents </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -723,7 +785,7 @@ La tokenisation n’est pas disponible pour les paiements par les invités.
|
|||||||
</div> <div class="section" id="_caractéristiques_pricinpales">
|
</div> <div class="section" id="_caractéristiques_pricinpales">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.8</span>Caractéristiques Pricinpales </h2>
|
<span class="title-number">9.8</span>Caractéristiques Pricinpales </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="ulist">
|
<div class="ulist">
|
||||||
@@ -748,7 +810,7 @@ La tokenisation n’est pas disponible pour les paiements par les invités.
|
|||||||
</div> <div class="section" id="_troubleshooting">
|
</div> <div class="section" id="_troubleshooting">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.9</span>Troubleshooting </h2>
|
<span class="title-number">9.9</span>Troubleshooting </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="ulist">
|
<div class="ulist">
|
||||||
@@ -779,7 +841,7 @@ La tokenisation n’est pas disponible pour les paiements par les invités.
|
|||||||
</div> <div class="section" id="_faqs">
|
</div> <div class="section" id="_faqs">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.10</span>FAQs </h2>
|
<span class="title-number">9.10</span>FAQs </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -807,7 +869,7 @@ A: Oui, le plugin prend en charge les portefeuilles comme Apple Pay.</p>
|
|||||||
</div> <div class="chapter" id="_changelog">
|
</div> <div class="chapter" id="_changelog">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">9</span>Changelog </h1>
|
<span class="title-number">10</span>Changelog </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -816,7 +878,7 @@ A: Oui, le plugin prend en charge les portefeuilles comme Apple Pay.</p>
|
|||||||
</div> <div class="chapter" id="_contribuer">
|
</div> <div class="chapter" id="_contribuer">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">10</span>Contribuer </h1>
|
<span class="title-number">11</span>Contribuer </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -827,7 +889,7 @@ A: Oui, le plugin prend en charge les portefeuilles comme Apple Pay.</p>
|
|||||||
</div> <div class="chapter" id="_support">
|
</div> <div class="chapter" id="_support">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">11</span>Support </h1>
|
<span class="title-number">12</span>Support </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -876,135 +938,157 @@ A: Oui, le plugin prend en charge les portefeuilles comme Apple Pay.</p>
|
|||||||
<span class="item-number">4.3</span>
|
<span class="item-number">4.3</span>
|
||||||
<span class="item-title">Via Composer (Recommended)</span>
|
<span class="item-title">Via Composer (Recommended)</span>
|
||||||
</a>
|
</a>
|
||||||
|
</li> <li class="nav-level-2">
|
||||||
|
<a href="#_si_vrpayment_n_apparaît_pas_dans_les_paramètres">
|
||||||
|
<span class="item-number">4.4</span>
|
||||||
|
<span class="item-title">Si VRPayment n&#8217;apparaît pas dans les paramètres</span>
|
||||||
|
</a>
|
||||||
|
</li> </ul>
|
||||||
|
</li> <li class="nav-level-1">
|
||||||
|
<a href="#_mise_à_jour">
|
||||||
|
<span class="item-number">5</span>
|
||||||
|
<span class="item-title">Mise à jour</span>
|
||||||
|
</a>
|
||||||
|
<ul class="nav">
|
||||||
|
<li class="nav-level-2">
|
||||||
|
<a href="#_via_l_administration">
|
||||||
|
<span class="item-number">5.1</span>
|
||||||
|
<span class="item-title">Via l’administration</span>
|
||||||
|
</a>
|
||||||
|
</li> <li class="nav-level-2">
|
||||||
|
<a href="#_via_la_cli">
|
||||||
|
<span class="item-number">5.2</span>
|
||||||
|
<span class="item-title">Via la CLI</span>
|
||||||
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#portal-startup-guide">
|
<a href="#portal-startup-guide">
|
||||||
<span class="item-number">5</span>
|
<span class="item-number">6</span>
|
||||||
<span class="item-title">Guide de démarrage pour le Portail</span>
|
<span class="item-title">Guide de démarrage pour le Portail</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="nav-level-2">
|
<li class="nav-level-2">
|
||||||
<a href="#_créez_la_clé_api">
|
<a href="#_créez_la_clé_api">
|
||||||
<span class="item-number">5.1</span>
|
<span class="item-number">6.1</span>
|
||||||
<span class="item-title">Créez la clé API:</span>
|
<span class="item-title">Créez la clé API:</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_configurer_les_modes_de_paiement">
|
<a href="#_configurer_les_modes_de_paiement">
|
||||||
<span class="item-number">5.2</span>
|
<span class="item-number">6.2</span>
|
||||||
<span class="item-title">Configurer les modes de paiement</span>
|
<span class="item-title">Configurer les modes de paiement</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_guide_de_démarrage_pour_shopware">
|
<a href="#_guide_de_démarrage_pour_shopware">
|
||||||
<span class="item-number">6</span>
|
<span class="item-number">7</span>
|
||||||
<span class="item-title">Guide de démarrage pour Shopware</span>
|
<span class="item-title">Guide de démarrage pour Shopware</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_différents_etats_pour_une_transaction">
|
<a href="#_différents_etats_pour_une_transaction">
|
||||||
<span class="item-number">7</span>
|
<span class="item-number">8</span>
|
||||||
<span class="item-title">Différents Etats pour une Transaction</span>
|
<span class="item-title">Différents Etats pour une Transaction</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="nav-level-2">
|
<li class="nav-level-2">
|
||||||
<a href="#_cartographie_des_différents_états_d_une_commande_de_shopware">
|
<a href="#_cartographie_des_différents_états_d_une_commande_de_shopware">
|
||||||
<span class="item-number">7.1</span>
|
<span class="item-number">8.1</span>
|
||||||
<span class="item-title">Cartographie des différents états d’une commande de Shopware</span>
|
<span class="item-title">Cartographie des différents états d’une commande de Shopware</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="nav-level-3">
|
<li class="nav-level-3">
|
||||||
<a href="#_remarque_générales_concernant_les_status_des_commandes">
|
<a href="#_remarque_générales_concernant_les_status_des_commandes">
|
||||||
<span class="item-number">7.1.1</span>
|
<span class="item-number">8.1.1</span>
|
||||||
<span class="item-title">Remarque générales concernant les status des commandes</span>
|
<span class="item-title">Remarque générales concernant les status des commandes</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_cartographie_des_différents_états_du_paiement_de_shopware">
|
<a href="#_cartographie_des_différents_états_du_paiement_de_shopware">
|
||||||
<span class="item-number">7.2</span>
|
<span class="item-number">8.2</span>
|
||||||
<span class="item-title">Cartographie des différents états du paiement de Shopware</span>
|
<span class="item-title">Cartographie des différents états du paiement de Shopware</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="nav-level-3">
|
<li class="nav-level-3">
|
||||||
<a href="#_remarques_générales_concernant_les_différents_status_pour_les_paiements">
|
<a href="#_remarques_générales_concernant_les_différents_status_pour_les_paiements">
|
||||||
<span class="item-number">7.2.1</span>
|
<span class="item-number">8.2.1</span>
|
||||||
<span class="item-title">Remarques générales concernant les différents status pour les paiements</span>
|
<span class="item-title">Remarques générales concernant les différents status pour les paiements</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_carthographie_des_différents_états_de_livraison_chez_shopware">
|
<a href="#_carthographie_des_différents_états_de_livraison_chez_shopware">
|
||||||
<span class="item-number">7.3</span>
|
<span class="item-number">8.3</span>
|
||||||
<span class="item-title">Carthographie des différents états de livraison chez Shopware</span>
|
<span class="item-title">Carthographie des différents états de livraison chez Shopware</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_gestion_des_transactions">
|
<a href="#_gestion_des_transactions">
|
||||||
<span class="item-number">8</span>
|
<span class="item-number">9</span>
|
||||||
<span class="item-title">Gestion des Transactions</span>
|
<span class="item-title">Gestion des Transactions</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="nav-level-2">
|
<li class="nav-level-2">
|
||||||
<a href="#_complete_capture_an_order">
|
<a href="#_complete_capture_an_order">
|
||||||
<span class="item-number">8.1</span>
|
<span class="item-number">9.1</span>
|
||||||
<span class="item-title">Complete (capture) an order</span>
|
<span class="item-title">Complete (capture) an order</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_annuler_une_transaction">
|
<a href="#_annuler_une_transaction">
|
||||||
<span class="item-number">8.2</span>
|
<span class="item-number">9.2</span>
|
||||||
<span class="item-title">Annuler une transaction</span>
|
<span class="item-title">Annuler une transaction</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_remboursement_d_une_transaction">
|
<a href="#_remboursement_d_une_transaction">
|
||||||
<span class="item-number">8.3</span>
|
<span class="item-number">9.3</span>
|
||||||
<span class="item-title">Remboursement d&#8217;une transaction</span>
|
<span class="item-title">Remboursement d&#8217;une transaction</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_commandes_en_attente">
|
<a href="#_commandes_en_attente">
|
||||||
<span class="item-number">8.4</span>
|
<span class="item-number">9.4</span>
|
||||||
<span class="item-title">Commandes en attente</span>
|
<span class="item-title">Commandes en attente</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_limites_de_la_synchronisation_entre_whitelabelname_et_shopware">
|
<a href="#_limites_de_la_synchronisation_entre_whitelabelname_et_shopware">
|
||||||
<span class="item-number">8.5</span>
|
<span class="item-number">9.5</span>
|
||||||
<span class="item-title">Limites de la synchronisation entre VR Payment et Shopware</span>
|
<span class="item-title">Limites de la synchronisation entre VR Payment et Shopware</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_tokenisation">
|
<a href="#_tokenisation">
|
||||||
<span class="item-number">8.6</span>
|
<span class="item-number">9.6</span>
|
||||||
<span class="item-title">Tokenisation</span>
|
<span class="item-title">Tokenisation</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_paiements_récurrents">
|
<a href="#_paiements_récurrents">
|
||||||
<span class="item-number">8.7</span>
|
<span class="item-number">9.7</span>
|
||||||
<span class="item-title">Paiements récurrents</span>
|
<span class="item-title">Paiements récurrents</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_caractéristiques_pricinpales">
|
<a href="#_caractéristiques_pricinpales">
|
||||||
<span class="item-number">8.8</span>
|
<span class="item-number">9.8</span>
|
||||||
<span class="item-title">Caractéristiques Pricinpales</span>
|
<span class="item-title">Caractéristiques Pricinpales</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_troubleshooting">
|
<a href="#_troubleshooting">
|
||||||
<span class="item-number">8.9</span>
|
<span class="item-number">9.9</span>
|
||||||
<span class="item-title">Troubleshooting</span>
|
<span class="item-title">Troubleshooting</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_faqs">
|
<a href="#_faqs">
|
||||||
<span class="item-number">8.10</span>
|
<span class="item-number">9.10</span>
|
||||||
<span class="item-title">FAQs</span>
|
<span class="item-title">FAQs</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_changelog">
|
<a href="#_changelog">
|
||||||
<span class="item-number">9</span>
|
<span class="item-number">10</span>
|
||||||
<span class="item-title">Changelog</span>
|
<span class="item-title">Changelog</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_contribuer">
|
<a href="#_contribuer">
|
||||||
<span class="item-number">10</span>
|
<span class="item-number">11</span>
|
||||||
<span class="item-title">Contribuer</span>
|
<span class="item-title">Contribuer</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_support">
|
<a href="#_support">
|
||||||
<span class="item-number">11</span>
|
<span class="item-number">12</span>
|
||||||
<span class="item-title">Support</span>
|
<span class="item-title">Support</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
|
|||||||
+133
-49
@@ -23,7 +23,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://github.com/vr-payment/shopware-6/releases/tag/7.1.2/">
|
<a href="https://github.com/vr-payment/shopware-6/releases/tag/7.3.4/">
|
||||||
Source
|
Source
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -177,11 +177,73 @@ php bin/console plugin:install --activate --clearCache VRPayment</code></pre>
|
|||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</div> </div>
|
</div> </div>
|
||||||
|
</div> <div class="section" id="_vrpayment_non_appare_nelle_impostazioni">
|
||||||
|
<div class="section-title">
|
||||||
|
<h2>
|
||||||
|
<span class="title-number">4.4</span>VRPayment non appare nelle impostazioni </h2>
|
||||||
|
</div>
|
||||||
|
<div class="section-body">
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Dovrebbe eseguire il seguente comando</p>
|
||||||
|
</div><div class="listingblock">
|
||||||
|
<div class="content">
|
||||||
|
<pre class="highlight"><code class="language-bash" data-lang="bash">bin/build-administration.sh</code></pre>
|
||||||
|
</div>
|
||||||
|
</div> </div>
|
||||||
|
</div> </div>
|
||||||
|
</div> <div class="chapter" id="_aggiornamento">
|
||||||
|
<div class="chapter-title">
|
||||||
|
<h1>
|
||||||
|
<span class="title-number">5</span>Aggiornamento </h1>
|
||||||
|
</div>
|
||||||
|
<div class="chapter-body">
|
||||||
|
<div class="section" id="_tramite_amministrazione">
|
||||||
|
<div class="section-title">
|
||||||
|
<h2>
|
||||||
|
<span class="title-number">5.1</span>Tramite Amministrazione </h2>
|
||||||
|
</div>
|
||||||
|
<div class="section-body">
|
||||||
|
<div class="olist arabic">
|
||||||
|
<ol class="arabic">
|
||||||
|
<li>
|
||||||
|
<p>Vai su Shopware Admin > Estensioni > Le mie estensioni.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Trova VRPaymentPayment.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Clicca su Aggiorna.</p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div> </div>
|
||||||
|
</div> <div class="section" id="_tramite_cli">
|
||||||
|
<div class="section-title">
|
||||||
|
<h2>
|
||||||
|
<span class="title-number">5.2</span>Tramite CLI </h2>
|
||||||
|
</div>
|
||||||
|
<div class="section-body">
|
||||||
|
<div class="olist arabic">
|
||||||
|
<ol class="arabic">
|
||||||
|
<li>
|
||||||
|
<p>Distribuisci i nuovi file del plugin (sostituisci la cartella <code>custom/plugins/VRPaymentPayment</code> oppure carica/installa un nuovo ZIP).</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Esegui:</p>
|
||||||
|
<div class="listingblock">
|
||||||
|
<div class="content">
|
||||||
|
<pre class="highlight"><code class="language-bash" data-lang="bash">bin/console plugin:refresh
|
||||||
|
bin/console plugin:update --clearCache VRPaymentPayment
|
||||||
|
bin/console cache:clear</code></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div> </div>
|
||||||
</div> </div>
|
</div> </div>
|
||||||
</div> <div class="chapter" id="portal-startup-guide">
|
</div> <div class="chapter" id="portal-startup-guide">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">5</span>Guida Rapida al Portale </h1>
|
<span class="title-number">6</span>Guida Rapida al Portale </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -200,7 +262,7 @@ Selezionate il piano di abbonamento appropriato: dovrebbe supportare le transazi
|
|||||||
</div> <div class="section" id="_create_la_chiave_api">
|
</div> <div class="section" id="_create_la_chiave_api">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">5.1</span>Create la chiave API: </h2>
|
<span class="title-number">6.1</span>Create la chiave API: </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="olist arabic">
|
<div class="olist arabic">
|
||||||
@@ -286,7 +348,7 @@ Si prega di notare che il caricamento dei ruoli potrebbe richiedere alcuni secon
|
|||||||
</div> <div class="section" id="_configurate_i_metodi_di_pagamento">
|
</div> <div class="section" id="_configurate_i_metodi_di_pagamento">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">5.2</span>Configurate i Metodi di Pagamento </h2>
|
<span class="title-number">6.2</span>Configurate i Metodi di Pagamento </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -348,7 +410,7 @@ Si prega di notare che i connettori sembrano duplicati, ma è perché uno è per
|
|||||||
</div> <div class="chapter" id="_guida_rapida_al_shop">
|
</div> <div class="chapter" id="_guida_rapida_al_shop">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">6</span>Guida Rapida al Shop </h1>
|
<span class="title-number">7</span>Guida Rapida al Shop </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="olist arabic">
|
<div class="olist arabic">
|
||||||
@@ -487,7 +549,7 @@ Si prega di notare che se non utilizzate lo Space View Id, questa opzione dovreb
|
|||||||
</div> <div class="chapter" id="_grafico_dello_stato_della_transazione">
|
</div> <div class="chapter" id="_grafico_dello_stato_della_transazione">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">7</span>Grafico dello Stato della Transazione </h1>
|
<span class="title-number">8</span>Grafico dello Stato della Transazione </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -497,7 +559,7 @@ Si prega di notare che se non utilizzate lo Space View Id, questa opzione dovreb
|
|||||||
</div> <div class="section" id="_mappatura_degli_stati_degli_ordini_di_shopware">
|
</div> <div class="section" id="_mappatura_degli_stati_degli_ordini_di_shopware">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">7.1</span>Mappatura degli Stati degli Ordini di Shopware </h2>
|
<span class="title-number">8.1</span>Mappatura degli Stati degli Ordini di Shopware </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -505,7 +567,7 @@ Si prega di notare che se non utilizzate lo Space View Id, questa opzione dovreb
|
|||||||
</div> <div class="section" id="_osservazioni_generali_riguardo_agli_stati_degli_ordini">
|
</div> <div class="section" id="_osservazioni_generali_riguardo_agli_stati_degli_ordini">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h3>
|
<h3>
|
||||||
<span class="title-number">7.1.1</span>Osservazioni Generali Riguardo agli Stati degli Ordini </h3>
|
<span class="title-number">8.1.1</span>Osservazioni Generali Riguardo agli Stati degli Ordini </h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -515,7 +577,7 @@ Si prega di notare che se non utilizzate lo Space View Id, questa opzione dovreb
|
|||||||
</div> <div class="section" id="_mappatura_dello_stato_di_pagamento_di_shopware">
|
</div> <div class="section" id="_mappatura_dello_stato_di_pagamento_di_shopware">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">7.2</span>Mappatura dello Stato di Pagamento di Shopware </h2>
|
<span class="title-number">8.2</span>Mappatura dello Stato di Pagamento di Shopware </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -542,7 +604,7 @@ Si prega di notare che se non utilizzate lo Space View Id, questa opzione dovreb
|
|||||||
</div> <div class="section" id="_osservazioni_generali_riguardo_agli_stati_di_pagamento">
|
</div> <div class="section" id="_osservazioni_generali_riguardo_agli_stati_di_pagamento">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h3>
|
<h3>
|
||||||
<span class="title-number">7.2.1</span>Osservazioni Generali Riguardo agli Stati di Pagamento </h3>
|
<span class="title-number">8.2.1</span>Osservazioni Generali Riguardo agli Stati di Pagamento </h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -552,7 +614,7 @@ Si prega di notare che se non utilizzate lo Space View Id, questa opzione dovreb
|
|||||||
</div> <div class="section" id="_mappatura_dello_stato_di_spedizione_di_shopware">
|
</div> <div class="section" id="_mappatura_dello_stato_di_spedizione_di_shopware">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">7.3</span>Mappatura dello Stato di Spedizione di Shopware </h2>
|
<span class="title-number">8.3</span>Mappatura dello Stato di Spedizione di Shopware </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -578,7 +640,7 @@ Si prega di notare che se non utilizzate lo Space View Id, questa opzione dovreb
|
|||||||
</div> <div class="chapter" id="_gestione_delle_transazioni">
|
</div> <div class="chapter" id="_gestione_delle_transazioni">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">8</span>Gestione delle Transazioni </h1>
|
<span class="title-number">9</span>Gestione delle Transazioni </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -586,7 +648,7 @@ Si prega di notare che se non utilizzate lo Space View Id, questa opzione dovreb
|
|||||||
</div> <div class="section" id="_completare_acquisire_un_ordine">
|
</div> <div class="section" id="_completare_acquisire_un_ordine">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.1</span>Completare (Acquisire) un Ordine </h2>
|
<span class="title-number">9.1</span>Completare (Acquisire) un Ordine </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -618,7 +680,7 @@ Quando il completamento è in sospeso in VR Payment, l’ordine rimarrà nel
|
|||||||
</div> <div class="section" id="_annullare_una_transazione">
|
</div> <div class="section" id="_annullare_una_transazione">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.2</span>Annullare una transazione </h2>
|
<span class="title-number">9.2</span>Annullare una transazione </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -642,7 +704,7 @@ Puoi annullare solo le transazioni che non sono ancora state completate
|
|||||||
</div> <div class="section" id="_rimborso_di_una_transazione">
|
</div> <div class="section" id="_rimborso_di_una_transazione">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.3</span>Rimborso di una Transazione </h2>
|
<span class="title-number">9.3</span>Rimborso di una Transazione </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -668,7 +730,7 @@ Potrebbe volerci un po' di tempo prima che vediate il rimborso in Shopware. I ri
|
|||||||
</div> <div class="section" id="_ordini_in_attesa">
|
</div> <div class="section" id="_ordini_in_attesa">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.4</span>Ordini in Attesa </h2>
|
<span class="title-number">9.4</span>Ordini in Attesa </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -690,7 +752,7 @@ Potrebbe volerci un po' di tempo prima che vediate il rimborso in Shopware. I ri
|
|||||||
</div> <div class="section" id="_limitazioni_della_sincronizzazione_tra_whitelabelname_e_shopware">
|
</div> <div class="section" id="_limitazioni_della_sincronizzazione_tra_whitelabelname_e_shopware">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.5</span>Limitazioni della Sincronizzazione tra VR Payment e Shopware </h2>
|
<span class="title-number">9.5</span>Limitazioni della Sincronizzazione tra VR Payment e Shopware </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -701,7 +763,7 @@ Potrebbe volerci un po' di tempo prima che vediate il rimborso in Shopware. I ri
|
|||||||
</div> <div class="section" id="_tokenization">
|
</div> <div class="section" id="_tokenization">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.6</span>Tokenization </h2>
|
<span class="title-number">9.6</span>Tokenization </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -721,7 +783,7 @@ La tokenizzazione non è disponibile per i checkout degli ospiti.
|
|||||||
</div> <div class="section" id="_pagamenti_ricorrenti">
|
</div> <div class="section" id="_pagamenti_ricorrenti">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.7</span>Pagamenti ricorrenti </h2>
|
<span class="title-number">9.7</span>Pagamenti ricorrenti </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -730,7 +792,7 @@ La tokenizzazione non è disponibile per i checkout degli ospiti.
|
|||||||
</div> <div class="section" id="_key_features">
|
</div> <div class="section" id="_key_features">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.8</span>Key Features </h2>
|
<span class="title-number">9.8</span>Key Features </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="ulist">
|
<div class="ulist">
|
||||||
@@ -755,7 +817,7 @@ La tokenizzazione non è disponibile per i checkout degli ospiti.
|
|||||||
</div> <div class="section" id="_risoluzione_dei_problemi">
|
</div> <div class="section" id="_risoluzione_dei_problemi">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.9</span>Risoluzione dei Problemi </h2>
|
<span class="title-number">9.9</span>Risoluzione dei Problemi </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="ulist">
|
<div class="ulist">
|
||||||
@@ -786,7 +848,7 @@ La tokenizzazione non è disponibile per i checkout degli ospiti.
|
|||||||
</div> <div class="section" id="_faqs">
|
</div> <div class="section" id="_faqs">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="title-number">8.10</span>FAQs </h2>
|
<span class="title-number">9.10</span>FAQs </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -818,7 +880,7 @@ La tokenizzazione non è disponibile per i checkout degli ospiti.
|
|||||||
</div> <div class="chapter" id="_changelog">
|
</div> <div class="chapter" id="_changelog">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">9</span>Changelog </h1>
|
<span class="title-number">10</span>Changelog </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -827,7 +889,7 @@ La tokenizzazione non è disponibile per i checkout degli ospiti.
|
|||||||
</div> <div class="chapter" id="_contribuzione">
|
</div> <div class="chapter" id="_contribuzione">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">10</span>Contribuzione </h1>
|
<span class="title-number">11</span>Contribuzione </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -838,7 +900,7 @@ La tokenizzazione non è disponibile per i checkout degli ospiti.
|
|||||||
</div> <div class="chapter" id="_support">
|
</div> <div class="chapter" id="_support">
|
||||||
<div class="chapter-title">
|
<div class="chapter-title">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="title-number">11</span>Support </h1>
|
<span class="title-number">12</span>Support </h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="chapter-body">
|
<div class="chapter-body">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -887,135 +949,157 @@ La tokenizzazione non è disponibile per i checkout degli ospiti.
|
|||||||
<span class="item-number">4.3</span>
|
<span class="item-number">4.3</span>
|
||||||
<span class="item-title">Installazione manuale</span>
|
<span class="item-title">Installazione manuale</span>
|
||||||
</a>
|
</a>
|
||||||
|
</li> <li class="nav-level-2">
|
||||||
|
<a href="#_vrpayment_non_appare_nelle_impostazioni">
|
||||||
|
<span class="item-number">4.4</span>
|
||||||
|
<span class="item-title">VRPayment non appare nelle impostazioni</span>
|
||||||
|
</a>
|
||||||
|
</li> </ul>
|
||||||
|
</li> <li class="nav-level-1">
|
||||||
|
<a href="#_aggiornamento">
|
||||||
|
<span class="item-number">5</span>
|
||||||
|
<span class="item-title">Aggiornamento</span>
|
||||||
|
</a>
|
||||||
|
<ul class="nav">
|
||||||
|
<li class="nav-level-2">
|
||||||
|
<a href="#_tramite_amministrazione">
|
||||||
|
<span class="item-number">5.1</span>
|
||||||
|
<span class="item-title">Tramite Amministrazione</span>
|
||||||
|
</a>
|
||||||
|
</li> <li class="nav-level-2">
|
||||||
|
<a href="#_tramite_cli">
|
||||||
|
<span class="item-number">5.2</span>
|
||||||
|
<span class="item-title">Tramite CLI</span>
|
||||||
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#portal-startup-guide">
|
<a href="#portal-startup-guide">
|
||||||
<span class="item-number">5</span>
|
<span class="item-number">6</span>
|
||||||
<span class="item-title">Guida Rapida al Portale</span>
|
<span class="item-title">Guida Rapida al Portale</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="nav-level-2">
|
<li class="nav-level-2">
|
||||||
<a href="#_create_la_chiave_api">
|
<a href="#_create_la_chiave_api">
|
||||||
<span class="item-number">5.1</span>
|
<span class="item-number">6.1</span>
|
||||||
<span class="item-title">Create la chiave API:</span>
|
<span class="item-title">Create la chiave API:</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_configurate_i_metodi_di_pagamento">
|
<a href="#_configurate_i_metodi_di_pagamento">
|
||||||
<span class="item-number">5.2</span>
|
<span class="item-number">6.2</span>
|
||||||
<span class="item-title">Configurate i Metodi di Pagamento</span>
|
<span class="item-title">Configurate i Metodi di Pagamento</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_guida_rapida_al_shop">
|
<a href="#_guida_rapida_al_shop">
|
||||||
<span class="item-number">6</span>
|
<span class="item-number">7</span>
|
||||||
<span class="item-title">Guida Rapida al Shop</span>
|
<span class="item-title">Guida Rapida al Shop</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_grafico_dello_stato_della_transazione">
|
<a href="#_grafico_dello_stato_della_transazione">
|
||||||
<span class="item-number">7</span>
|
<span class="item-number">8</span>
|
||||||
<span class="item-title">Grafico dello Stato della Transazione</span>
|
<span class="item-title">Grafico dello Stato della Transazione</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="nav-level-2">
|
<li class="nav-level-2">
|
||||||
<a href="#_mappatura_degli_stati_degli_ordini_di_shopware">
|
<a href="#_mappatura_degli_stati_degli_ordini_di_shopware">
|
||||||
<span class="item-number">7.1</span>
|
<span class="item-number">8.1</span>
|
||||||
<span class="item-title">Mappatura degli Stati degli Ordini di Shopware</span>
|
<span class="item-title">Mappatura degli Stati degli Ordini di Shopware</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="nav-level-3">
|
<li class="nav-level-3">
|
||||||
<a href="#_osservazioni_generali_riguardo_agli_stati_degli_ordini">
|
<a href="#_osservazioni_generali_riguardo_agli_stati_degli_ordini">
|
||||||
<span class="item-number">7.1.1</span>
|
<span class="item-number">8.1.1</span>
|
||||||
<span class="item-title">Osservazioni Generali Riguardo agli Stati degli Ordini</span>
|
<span class="item-title">Osservazioni Generali Riguardo agli Stati degli Ordini</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_mappatura_dello_stato_di_pagamento_di_shopware">
|
<a href="#_mappatura_dello_stato_di_pagamento_di_shopware">
|
||||||
<span class="item-number">7.2</span>
|
<span class="item-number">8.2</span>
|
||||||
<span class="item-title">Mappatura dello Stato di Pagamento di Shopware</span>
|
<span class="item-title">Mappatura dello Stato di Pagamento di Shopware</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="nav-level-3">
|
<li class="nav-level-3">
|
||||||
<a href="#_osservazioni_generali_riguardo_agli_stati_di_pagamento">
|
<a href="#_osservazioni_generali_riguardo_agli_stati_di_pagamento">
|
||||||
<span class="item-number">7.2.1</span>
|
<span class="item-number">8.2.1</span>
|
||||||
<span class="item-title">Osservazioni Generali Riguardo agli Stati di Pagamento</span>
|
<span class="item-title">Osservazioni Generali Riguardo agli Stati di Pagamento</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_mappatura_dello_stato_di_spedizione_di_shopware">
|
<a href="#_mappatura_dello_stato_di_spedizione_di_shopware">
|
||||||
<span class="item-number">7.3</span>
|
<span class="item-number">8.3</span>
|
||||||
<span class="item-title">Mappatura dello Stato di Spedizione di Shopware</span>
|
<span class="item-title">Mappatura dello Stato di Spedizione di Shopware</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_gestione_delle_transazioni">
|
<a href="#_gestione_delle_transazioni">
|
||||||
<span class="item-number">8</span>
|
<span class="item-number">9</span>
|
||||||
<span class="item-title">Gestione delle Transazioni</span>
|
<span class="item-title">Gestione delle Transazioni</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="nav-level-2">
|
<li class="nav-level-2">
|
||||||
<a href="#_completare_acquisire_un_ordine">
|
<a href="#_completare_acquisire_un_ordine">
|
||||||
<span class="item-number">8.1</span>
|
<span class="item-number">9.1</span>
|
||||||
<span class="item-title">Completare (Acquisire) un Ordine</span>
|
<span class="item-title">Completare (Acquisire) un Ordine</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_annullare_una_transazione">
|
<a href="#_annullare_una_transazione">
|
||||||
<span class="item-number">8.2</span>
|
<span class="item-number">9.2</span>
|
||||||
<span class="item-title">Annullare una transazione</span>
|
<span class="item-title">Annullare una transazione</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_rimborso_di_una_transazione">
|
<a href="#_rimborso_di_una_transazione">
|
||||||
<span class="item-number">8.3</span>
|
<span class="item-number">9.3</span>
|
||||||
<span class="item-title">Rimborso di una Transazione</span>
|
<span class="item-title">Rimborso di una Transazione</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_ordini_in_attesa">
|
<a href="#_ordini_in_attesa">
|
||||||
<span class="item-number">8.4</span>
|
<span class="item-number">9.4</span>
|
||||||
<span class="item-title">Ordini in Attesa</span>
|
<span class="item-title">Ordini in Attesa</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_limitazioni_della_sincronizzazione_tra_whitelabelname_e_shopware">
|
<a href="#_limitazioni_della_sincronizzazione_tra_whitelabelname_e_shopware">
|
||||||
<span class="item-number">8.5</span>
|
<span class="item-number">9.5</span>
|
||||||
<span class="item-title">Limitazioni della Sincronizzazione tra VR Payment e Shopware</span>
|
<span class="item-title">Limitazioni della Sincronizzazione tra VR Payment e Shopware</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_tokenization">
|
<a href="#_tokenization">
|
||||||
<span class="item-number">8.6</span>
|
<span class="item-number">9.6</span>
|
||||||
<span class="item-title">Tokenization</span>
|
<span class="item-title">Tokenization</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_pagamenti_ricorrenti">
|
<a href="#_pagamenti_ricorrenti">
|
||||||
<span class="item-number">8.7</span>
|
<span class="item-number">9.7</span>
|
||||||
<span class="item-title">Pagamenti ricorrenti</span>
|
<span class="item-title">Pagamenti ricorrenti</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_key_features">
|
<a href="#_key_features">
|
||||||
<span class="item-number">8.8</span>
|
<span class="item-number">9.8</span>
|
||||||
<span class="item-title">Key Features</span>
|
<span class="item-title">Key Features</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_risoluzione_dei_problemi">
|
<a href="#_risoluzione_dei_problemi">
|
||||||
<span class="item-number">8.9</span>
|
<span class="item-number">9.9</span>
|
||||||
<span class="item-title">Risoluzione dei Problemi</span>
|
<span class="item-title">Risoluzione dei Problemi</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-2">
|
</li> <li class="nav-level-2">
|
||||||
<a href="#_faqs">
|
<a href="#_faqs">
|
||||||
<span class="item-number">8.10</span>
|
<span class="item-number">9.10</span>
|
||||||
<span class="item-title">FAQs</span>
|
<span class="item-title">FAQs</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_changelog">
|
<a href="#_changelog">
|
||||||
<span class="item-number">9</span>
|
<span class="item-number">10</span>
|
||||||
<span class="item-title">Changelog</span>
|
<span class="item-title">Changelog</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_contribuzione">
|
<a href="#_contribuzione">
|
||||||
<span class="item-number">10</span>
|
<span class="item-number">11</span>
|
||||||
<span class="item-title">Contribuzione</span>
|
<span class="item-title">Contribuzione</span>
|
||||||
</a>
|
</a>
|
||||||
</li> <li class="nav-level-1">
|
</li> <li class="nav-level-1">
|
||||||
<a href="#_support">
|
<a href="#_support">
|
||||||
<span class="item-number">11</span>
|
<span class="item-number">12</span>
|
||||||
<span class="item-title">Support</span>
|
<span class="item-title">Support</span>
|
||||||
</a>
|
</a>
|
||||||
</li> </ul>
|
</li> </ul>
|
||||||
|
|||||||
@@ -661,6 +661,13 @@ class PaymentMethodConfigurationService {
|
|||||||
], $context);
|
], $context);
|
||||||
|
|
||||||
// Media insert/update
|
// Media insert/update
|
||||||
|
|
||||||
|
// detect if collision, return existing id
|
||||||
|
$existingId = $this->checkMediaAlreadyExists($paymentMethodConfiguration->getResolvedImageUrl(), $context);
|
||||||
|
if ($existingId) {
|
||||||
|
return $existingId;
|
||||||
|
}
|
||||||
|
|
||||||
$mediaDefinition = $this->container->get(MediaDefinition::class);
|
$mediaDefinition = $this->container->get(MediaDefinition::class);
|
||||||
$this->mediaSerializer->setRegistry($this->serializerRegistry);
|
$this->mediaSerializer->setRegistry($this->serializerRegistry);
|
||||||
|
|
||||||
@@ -681,6 +688,22 @@ class PaymentMethodConfigurationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function checkMediaAlreadyExists($paymentMethodUrl, $context) {
|
||||||
|
// detect if collision, return existing id
|
||||||
|
if (preg_match('#/([^/]+)\.[^/.]+$#', $paymentMethodUrl, $matches)) {
|
||||||
|
$filename = $matches[1];
|
||||||
|
}
|
||||||
|
$criteria = new Criteria();
|
||||||
|
$criteria->addFilter(new EqualsFilter('fileName', $filename));
|
||||||
|
|
||||||
|
$existing = $this->mediaRepository->search($criteria, $context)->first();
|
||||||
|
|
||||||
|
if ($existing) {
|
||||||
|
return $existing->getId();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves media default folder for a given payment method configuration.
|
* Retrieves media default folder for a given payment method configuration.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ use Symfony\Component\{
|
|||||||
};
|
};
|
||||||
use VRPaymentPayment\Core\{
|
use VRPaymentPayment\Core\{
|
||||||
Api\Refund\Service\RefundService,
|
Api\Refund\Service\RefundService,
|
||||||
Settings\Service\SettingsService
|
Api\Transaction\Service\TransactionService,
|
||||||
|
Settings\Service\SettingsService,
|
||||||
|
Util\Exception\RefundNotSupportedException
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,16 +43,23 @@ class RefundController extends AbstractController
|
|||||||
*/
|
*/
|
||||||
protected $logger;
|
protected $logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService
|
||||||
|
*/
|
||||||
|
protected $transactionService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RefundController constructor.
|
* RefundController constructor.
|
||||||
*
|
*
|
||||||
* @param \VRPaymentPayment\Core\Api\Refund\Service\RefundService $refundService
|
* @param \VRPaymentPayment\Core\Api\Refund\Service\RefundService $refundService
|
||||||
* @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService
|
* @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService
|
||||||
|
* @param \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService $transactionService
|
||||||
*/
|
*/
|
||||||
public function __construct(RefundService $refundService, SettingsService $settingsService)
|
public function __construct(RefundService $refundService, SettingsService $settingsService, TransactionService $transactionService)
|
||||||
{
|
{
|
||||||
$this->settingsService = $settingsService;
|
$this->settingsService = $settingsService;
|
||||||
$this->refundService = $refundService;
|
$this->refundService = $refundService;
|
||||||
|
$this->transactionService = $transactionService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,11 +91,28 @@ class RefundController extends AbstractController
|
|||||||
$quantity = (int)$request->request->get('quantity');
|
$quantity = (int)$request->request->get('quantity');
|
||||||
$lineItemId = $request->request->get('lineItemId');
|
$lineItemId = $request->request->get('lineItemId');
|
||||||
|
|
||||||
|
if ($quantity === null || $quantity <= 0) {
|
||||||
|
return new Response('refundQuantityZero', Response::HTTP_BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
$settings = $this->settingsService->getSettings($salesChannelId);
|
$settings = $this->settingsService->getSettings($salesChannelId);
|
||||||
$apiClient = $settings->getApiClient();
|
$apiClient = $settings->getApiClient();
|
||||||
|
|
||||||
$transaction = $apiClient->getTransactionService()->read($settings->getSpaceId(), $transactionId);
|
$transaction = $apiClient->getTransactionService()->read($settings->getSpaceId(), $transactionId);
|
||||||
|
|
||||||
|
$maxQuantity = $this->refundService->getMaxRefundableQuantity($transaction, $context, $lineItemId);
|
||||||
|
|
||||||
|
if ($quantity > $maxQuantity) {
|
||||||
|
return new Response('refundExceedsQuantity', Response::HTTP_BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
$refund = $this->refundService->create($transaction, $context, $lineItemId, $quantity);
|
$refund = $this->refundService->create($transaction, $context, $lineItemId, $quantity);
|
||||||
|
} catch (RefundNotSupportedException $exception) {
|
||||||
|
$this->logger->info('Payment method does not support online refunds for transaction: ' . $transactionId);
|
||||||
|
return new Response('methodDoesNotSupportRefund', Response::HTTP_BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
if ($refund === null) {
|
if ($refund === null) {
|
||||||
return new Response('Refund was not created. Please check the refund amound or if the item was not refunded before', Response::HTTP_BAD_REQUEST);
|
return new Response('Refund was not created. Please check the refund amound or if the item was not refunded before', Response::HTTP_BAD_REQUEST);
|
||||||
}
|
}
|
||||||
@@ -111,11 +137,29 @@ class RefundController extends AbstractController
|
|||||||
$transactionId = $request->request->get('transactionId');
|
$transactionId = $request->request->get('transactionId');
|
||||||
$refundableAmount = $request->request->get('refundableAmount');
|
$refundableAmount = $request->request->get('refundableAmount');
|
||||||
|
|
||||||
|
if ($refundableAmount === null || $refundableAmount <= 0.0) {
|
||||||
|
return new Response('refundAmountZero', Response::HTTP_BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
$settings = $this->settingsService->getSettings($salesChannelId);
|
$settings = $this->settingsService->getSettings($salesChannelId);
|
||||||
$apiClient = $settings->getApiClient();
|
$apiClient = $settings->getApiClient();
|
||||||
|
|
||||||
$transaction = $apiClient->getTransactionService()->read($settings->getSpaceId(), $transactionId);
|
$transaction = $apiClient->getTransactionService()->read($settings->getSpaceId(), $transactionId);
|
||||||
|
|
||||||
|
$completed = (float) $transaction->getCompletedAmount();
|
||||||
|
$refunded = (float) $transaction->getRefundedAmount();
|
||||||
|
$maxRefund = round($completed - $refunded, 2);
|
||||||
|
|
||||||
|
if ($refundableAmount > $maxRefund) {
|
||||||
|
return new Response('refundExceedsAmount', Response::HTTP_BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
$refund = $this->refundService->createRefundByAmount($transaction, $refundableAmount, $context);
|
$refund = $this->refundService->createRefundByAmount($transaction, $refundableAmount, $context);
|
||||||
|
} catch (RefundNotSupportedException $exception) {
|
||||||
|
$this->logger->info('Payment method does not support online refunds for transaction: ' . $transactionId);
|
||||||
|
return new Response('methodDoesNotSupportRefund', Response::HTTP_BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
if ($refund === null) {
|
if ($refund === null) {
|
||||||
return new Response('refundExceedsAmount', Response::HTTP_BAD_REQUEST);
|
return new Response('refundExceedsAmount', Response::HTTP_BAD_REQUEST);
|
||||||
@@ -146,7 +190,13 @@ class RefundController extends AbstractController
|
|||||||
$apiClient = $settings->getApiClient();
|
$apiClient = $settings->getApiClient();
|
||||||
|
|
||||||
$transaction = $apiClient->getTransactionService()->read($settings->getSpaceId(), $transactionId);
|
$transaction = $apiClient->getTransactionService()->read($settings->getSpaceId(), $transactionId);
|
||||||
$this->refundService->createPartialRefund($transaction, $context, $lineItemId, $refundableAmount);
|
|
||||||
|
try {
|
||||||
|
$refund = $this->refundService->createPartialRefund($transaction, $context, $lineItemId, $refundableAmount);
|
||||||
|
} catch (RefundNotSupportedException $exception) {
|
||||||
|
$this->logger->info('Payment method does not support online refunds for transaction: ' . $transactionId);
|
||||||
|
return new Response('methodDoesNotSupportRefund', Response::HTTP_BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
return new Response(null, Response::HTTP_NO_CONTENT);
|
return new Response(null, Response::HTTP_NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,14 +12,20 @@ use Shopware\Core\{
|
|||||||
};
|
};
|
||||||
use VRPayment\Sdk\{
|
use VRPayment\Sdk\{
|
||||||
Model\Refund,
|
Model\Refund,
|
||||||
Model\Transaction
|
Model\Transaction,
|
||||||
|
Model\CriteriaOperator,
|
||||||
|
Model\EntityQueryFilter,
|
||||||
|
Model\EntityQueryFilterType,
|
||||||
|
Model\EntityQuery,
|
||||||
|
ApiException
|
||||||
};
|
};
|
||||||
use VRPaymentPayment\Core\{
|
use VRPaymentPayment\Core\{
|
||||||
Api\Refund\Entity\RefundEntity,
|
Api\Refund\Entity\RefundEntity,
|
||||||
Api\Transaction\Entity\TransactionEntity,
|
Api\Transaction\Entity\TransactionEntity,
|
||||||
Api\Transaction\Entity\TransactionEntityDefinition,
|
Api\Transaction\Entity\TransactionEntityDefinition,
|
||||||
Settings\Service\SettingsService,
|
Settings\Service\SettingsService,
|
||||||
Util\Payload\RefundPayload
|
Util\Payload\RefundPayload,
|
||||||
|
Util\Exception\RefundNotSupportedException
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,6 +105,12 @@ class RefundService
|
|||||||
$this->upsert($refund, $context);
|
$this->upsert($refund, $context);
|
||||||
return $refund;
|
return $refund;
|
||||||
}
|
}
|
||||||
|
} catch (ApiException $exception) {
|
||||||
|
$message = $exception->getMessage();
|
||||||
|
$this->logger->critical($message);
|
||||||
|
if ($exception->getCode() === 442 && str_contains($message, 'does not support online refunds')) {
|
||||||
|
throw new RefundNotSupportedException($message, 0, $exception);
|
||||||
|
}
|
||||||
} catch (\Exception $exception) {
|
} catch (\Exception $exception) {
|
||||||
$this->logger->critical($exception->getMessage());
|
$this->logger->critical($exception->getMessage());
|
||||||
}
|
}
|
||||||
@@ -134,6 +146,12 @@ class RefundService
|
|||||||
$this->upsert($refund, $context);
|
$this->upsert($refund, $context);
|
||||||
return $refund;
|
return $refund;
|
||||||
}
|
}
|
||||||
|
} catch (ApiException $exception) {
|
||||||
|
$message = $exception->getMessage();
|
||||||
|
$this->logger->critical($message);
|
||||||
|
if ($exception->getCode() === 442 && str_contains($message, 'does not support online refunds')) {
|
||||||
|
throw new RefundNotSupportedException($message, 0, $exception);
|
||||||
|
}
|
||||||
} catch (\Exception $exception) {
|
} catch (\Exception $exception) {
|
||||||
$this->logger->critical($exception->getMessage());
|
$this->logger->critical($exception->getMessage());
|
||||||
}
|
}
|
||||||
@@ -170,6 +188,12 @@ class RefundService
|
|||||||
$this->upsert($refund, $context);
|
$this->upsert($refund, $context);
|
||||||
return $refund;
|
return $refund;
|
||||||
}
|
}
|
||||||
|
} catch (ApiException $exception) {
|
||||||
|
$message = $exception->getMessage();
|
||||||
|
$this->logger->critical($message);
|
||||||
|
if ($exception->getCode() === 442 && str_contains($message, 'does not support online refunds')) {
|
||||||
|
throw new RefundNotSupportedException($message, 0, $exception);
|
||||||
|
}
|
||||||
} catch (\Exception $exception) {
|
} catch (\Exception $exception) {
|
||||||
$this->logger->critical($exception->getMessage());
|
$this->logger->critical($exception->getMessage());
|
||||||
}
|
}
|
||||||
@@ -241,4 +265,67 @@ class RefundService
|
|||||||
->first();
|
->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get total refunded quantity for transaction's line item by lineItemId.
|
||||||
|
*
|
||||||
|
* @param \VRPayment\Sdk\Model\Transaction $transaction
|
||||||
|
* @param \Shopware\Core\Framework\Context $context
|
||||||
|
* @param string $lineItemId
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getRefundedQuantity(Transaction $transaction, Context $context, string $lineItemId): int {
|
||||||
|
$transactionEntity = $this->getTransactionEntityByTransactionId($transaction->getId(), $context);
|
||||||
|
$settings = $this->settingsService->getSettings($transactionEntity->getSalesChannel()->getId());
|
||||||
|
$apiClient = $settings->getApiClient();
|
||||||
|
|
||||||
|
$entityQueryFilter = (new EntityQueryFilter())
|
||||||
|
->setType(EntityQueryFilterType::LEAF)
|
||||||
|
->setOperator(CriteriaOperator::EQUALS)
|
||||||
|
->setFieldName('transaction.id')
|
||||||
|
->setValue($transaction->getId());
|
||||||
|
|
||||||
|
$query = (new EntityQuery())->setFilter($entityQueryFilter);
|
||||||
|
|
||||||
|
$refunds = $apiClient->getRefundService()->search($settings->getSpaceId(), $query);
|
||||||
|
|
||||||
|
$refundedQuantity = 0;
|
||||||
|
|
||||||
|
foreach ($refunds as $refund) {
|
||||||
|
foreach ($refund->getReductions() as $reduction) {
|
||||||
|
if ($reduction->getLineItemUniqueId() === $lineItemId) {
|
||||||
|
$refundedQuantity += (int) $reduction->getQuantityReduction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $refundedQuantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get maximum quantity of available items to refund for line item.
|
||||||
|
*
|
||||||
|
* @param \VRPayment\Sdk\Model\Transaction $transaction
|
||||||
|
* @param \Shopware\Core\Framework\Context $context
|
||||||
|
* @param string $lineItemId
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getMaxRefundableQuantity(Transaction $transaction, Context $context, string $lineItemId): int {
|
||||||
|
|
||||||
|
$originalQuantity = 0;
|
||||||
|
|
||||||
|
foreach ($transaction->getLineItems() as $lineItem) {
|
||||||
|
if ($lineItem->getUniqueId() === $lineItemId) {
|
||||||
|
$originalQuantity = (int) $lineItem->getQuantity();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$refundedQuantity = $this->getRefundedQuantity($transaction, $context, $lineItemId);
|
||||||
|
|
||||||
|
$maxQuantity = $originalQuantity - $refundedQuantity;
|
||||||
|
|
||||||
|
return $maxQuantity;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ use VRPaymentPayment\Core\Api\Refund\Entity\RefundEntityDefinition;
|
|||||||
*/
|
*/
|
||||||
class TransactionEntityDefinition extends EntityDefinition {
|
class TransactionEntityDefinition extends EntityDefinition {
|
||||||
|
|
||||||
public const ENTITY_NAME = 'vrpayment_transaction';
|
public const ENTITY_NAME = 'vrpayment_transaction_data';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace VRPaymentPayment\Core\Api\Transaction\Service;
|
namespace VRPaymentPayment\Core\Api\Transaction\Service;
|
||||||
|
|
||||||
|
use Psr\Cache\CacheItemPoolInterface;
|
||||||
use Psr\Container\ContainerInterface;
|
use Psr\Container\ContainerInterface;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Shopware\Core\{
|
use Shopware\Core\{
|
||||||
@@ -29,6 +32,7 @@ use VRPayment\Sdk\Model\{
|
|||||||
LineItemAttributeCreate,
|
LineItemAttributeCreate,
|
||||||
LineItemCreate,
|
LineItemCreate,
|
||||||
LineItemType,
|
LineItemType,
|
||||||
|
TaxCreate,
|
||||||
TokenizationMode,
|
TokenizationMode,
|
||||||
Transaction,
|
Transaction,
|
||||||
TransactionCreate,
|
TransactionCreate,
|
||||||
@@ -45,7 +49,8 @@ use VRPaymentPayment\Core\{
|
|||||||
Settings\Service\SettingsService,
|
Settings\Service\SettingsService,
|
||||||
Util\LocaleCodeProvider,
|
Util\LocaleCodeProvider,
|
||||||
Util\Payload\CustomProducts\CustomProductsLineItemTypes,
|
Util\Payload\CustomProducts\CustomProductsLineItemTypes,
|
||||||
Util\Payload\TransactionPayload
|
Util\Payload\TransactionPayload,
|
||||||
|
Util\Analytics\Analytics
|
||||||
};
|
};
|
||||||
use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity;
|
use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity;
|
||||||
use Shopware\Core\Framework\Struct\ArrayEntity;
|
use Shopware\Core\Framework\Struct\ArrayEntity;
|
||||||
@@ -78,6 +83,12 @@ class TransactionService
|
|||||||
*/
|
*/
|
||||||
private $settingsService;
|
private $settingsService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache for storing pending transaction IDs across headless requests.
|
||||||
|
* @var CacheItemPoolInterface
|
||||||
|
*/
|
||||||
|
private CacheItemPoolInterface $cache;
|
||||||
|
|
||||||
const CARD_HOLDER_KEY = '1456765000789';
|
const CARD_HOLDER_KEY = '1456765000789';
|
||||||
const PSEUDO_CODE_KEY = '1485172176673';
|
const PSEUDO_CODE_KEY = '1485172176673';
|
||||||
const CARD_VALIDITY_KEY = '1456765711187';
|
const CARD_VALIDITY_KEY = '1456765711187';
|
||||||
@@ -90,16 +101,18 @@ class TransactionService
|
|||||||
* @param \Psr\Container\ContainerInterface $container
|
* @param \Psr\Container\ContainerInterface $container
|
||||||
* @param \VRPaymentPayment\Core\Util\LocaleCodeProvider $localeCodeProvider
|
* @param \VRPaymentPayment\Core\Util\LocaleCodeProvider $localeCodeProvider
|
||||||
* @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService
|
* @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService
|
||||||
|
* @param CacheItemPoolInterface $cache Cache for headless transaction persistence
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ContainerInterface $container,
|
ContainerInterface $container,
|
||||||
LocaleCodeProvider $localeCodeProvider,
|
LocaleCodeProvider $localeCodeProvider,
|
||||||
SettingsService $settingsService
|
SettingsService $settingsService,
|
||||||
)
|
CacheItemPoolInterface $cache
|
||||||
{
|
) {
|
||||||
$this->container = $container;
|
$this->container = $container;
|
||||||
$this->localeCodeProvider = $localeCodeProvider;
|
$this->localeCodeProvider = $localeCodeProvider;
|
||||||
$this->settingsService = $settingsService;
|
$this->settingsService = $settingsService;
|
||||||
|
$this->cache = $cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -131,8 +144,7 @@ class TransactionService
|
|||||||
public function create(
|
public function create(
|
||||||
PaymentTransactionStruct $transaction,
|
PaymentTransactionStruct $transaction,
|
||||||
SalesChannelContext $salesChannelContext
|
SalesChannelContext $salesChannelContext
|
||||||
): string
|
): string {
|
||||||
{
|
|
||||||
$criteria = new Criteria([$transaction->getOrderTransactionId()]);
|
$criteria = new Criteria([$transaction->getOrderTransactionId()]);
|
||||||
$criteria->addAssociation('order');
|
$criteria->addAssociation('order');
|
||||||
$orderTransaction = $this->container->get('order_transaction.repository')->search($criteria, $salesChannelContext->getContext())->first();
|
$orderTransaction = $this->container->get('order_transaction.repository')->search($criteria, $salesChannelContext->getContext())->first();
|
||||||
@@ -141,13 +153,28 @@ class TransactionService
|
|||||||
$settings = $this->settingsService->getSettings($salesChannelId);
|
$settings = $this->settingsService->getSettings($salesChannelId);
|
||||||
$apiClient = $settings->getApiClient();
|
$apiClient = $settings->getApiClient();
|
||||||
|
|
||||||
$transactionId = $_SESSION['transactionId'] ?? null;
|
// Get transaction ID from cache (headless) or session (storefront).
|
||||||
|
$transactionId = $this->getTransactionIdFromContext($salesChannelContext);
|
||||||
|
$pendingTransaction = null;
|
||||||
|
|
||||||
|
// Try to read the pending transaction if we have an ID stored.
|
||||||
if ($transactionId !== null) {
|
if ($transactionId !== null) {
|
||||||
$pendingTransaction = $this->read($_SESSION['transactionId'], $salesChannelId);
|
try {
|
||||||
|
$pendingTransaction = $this->read($transactionId, $salesChannelId);
|
||||||
|
// Verify it's still in PENDING state - otherwise we can't reuse it.
|
||||||
|
if ($pendingTransaction != null && $pendingTransaction->getState() !== TransactionState::PENDING) {
|
||||||
|
$pendingTransaction = null;
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Transaction may have been deleted, expired, or is invalid - we'll create a new one.
|
||||||
|
$this->logger?->debug('Could not read pending transaction, will create new one: ' . $e->getMessage());
|
||||||
|
$pendingTransaction = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($transactionId === null || $pendingTransaction === null || $pendingTransaction->getState() !== TransactionState::PENDING) {
|
// Create a new transaction if we don't have a valid pending one.
|
||||||
unset($_SESSION['transactionId']);
|
if ($pendingTransaction === null) {
|
||||||
|
$this->clearTransactionIdFromContext($salesChannelContext);
|
||||||
$pendingTransactionId = $this->createPendingTransaction($salesChannelContext);
|
$pendingTransactionId = $this->createPendingTransaction($salesChannelContext);
|
||||||
$pendingTransaction = $this->read($pendingTransactionId, $salesChannelId);
|
$pendingTransaction = $this->read($pendingTransactionId, $salesChannelId);
|
||||||
}
|
}
|
||||||
@@ -160,6 +187,7 @@ class TransactionService
|
|||||||
$transaction
|
$transaction
|
||||||
));
|
));
|
||||||
$transactionPayloadClass->setLogger($this->logger);
|
$transactionPayloadClass->setLogger($this->logger);
|
||||||
|
$transactionPayloadClass->setTransactionId($pendingTransaction->getId());
|
||||||
$transactionPayload = $transactionPayloadClass->get($pendingTransaction->getVersion());
|
$transactionPayload = $transactionPayloadClass->get($pendingTransaction->getVersion());
|
||||||
|
|
||||||
$createdTransaction = $apiClient->getTransactionService()
|
$createdTransaction = $apiClient->getTransactionService()
|
||||||
@@ -169,7 +197,8 @@ class TransactionService
|
|||||||
$transaction,
|
$transaction,
|
||||||
$salesChannelContext->getContext(),
|
$salesChannelContext->getContext(),
|
||||||
$createdTransaction->getId(),
|
$createdTransaction->getId(),
|
||||||
$settings->getSpaceId()
|
$settings->getSpaceId(),
|
||||||
|
$salesChannelContext->getToken()
|
||||||
);
|
);
|
||||||
|
|
||||||
$redirectUrl = $this->container->get('router')->generate(
|
$redirectUrl = $this->container->get('router')->generate(
|
||||||
@@ -178,6 +207,16 @@ class TransactionService
|
|||||||
UrlGeneratorInterface::ABSOLUTE_URL
|
UrlGeneratorInterface::ABSOLUTE_URL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// If the request comes from the Store API (headless), we should not redirect to a Storefront Twig page.
|
||||||
|
// Instead, we return the returnUrl so the headless client can handle the next steps (e.g. rendering the iframe).
|
||||||
|
$request = $this->container->get('request_stack')->getCurrentRequest();
|
||||||
|
if ($request) {
|
||||||
|
$routeScope = $request->attributes->get('_route_scope', []);
|
||||||
|
if (in_array('store-api', $routeScope, true)) {
|
||||||
|
$redirectUrl = $transaction->getReturnUrl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($settings->getIntegration() == Integration::PAYMENT_PAGE) {
|
if ($settings->getIntegration() == Integration::PAYMENT_PAGE) {
|
||||||
$redirectUrl = $apiClient->getTransactionPaymentPageService()
|
$redirectUrl = $apiClient->getTransactionPaymentPageService()
|
||||||
->paymentPageUrl($settings->getSpaceId(), $createdTransaction->getId());
|
->paymentPageUrl($settings->getSpaceId(), $createdTransaction->getId());
|
||||||
@@ -215,15 +254,21 @@ class TransactionService
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function createRecurringTransaction(TransactionCreate $sdkTransactionCreate, string $spaceId = ""): Transaction {
|
public function createRecurringTransaction(TransactionCreate $sdkTransactionCreate, string $spaceId = ""): Transaction
|
||||||
|
{
|
||||||
$settings = $this->settingsService->getSettings();
|
$settings = $this->settingsService->getSettings();
|
||||||
if (empty($spaceId)) {
|
if (empty($spaceId)) {
|
||||||
$spaceId = $settings->getSpaceId();
|
$spaceId = $settings->getSpaceId();
|
||||||
}
|
}
|
||||||
|
|
||||||
$sdkTransaction = $settings->getApiClient()->getTransactionService()->create($spaceId, $sdkTransactionCreate);
|
$apiClient = $settings->getApiClient();
|
||||||
|
Analytics::addHeaders($apiClient, [
|
||||||
|
Analytics::SUBSCRIPTION_TRANSACTION => true
|
||||||
|
]);
|
||||||
|
|
||||||
|
$sdkTransaction = $apiClient->getTransactionService()->create($spaceId, $sdkTransactionCreate);
|
||||||
if ($sdkTransaction->valid()) {
|
if ($sdkTransaction->valid()) {
|
||||||
return $settings->getApiClient()->getTransactionService()->processWithoutUserInteraction($spaceId, $sdkTransaction->getId());
|
return $apiClient->getTransactionService()->processWithoutUserInteraction($spaceId, $sdkTransaction->getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new \Exception("The transacion is not valid and could not be created.");
|
throw new \Exception("The transacion is not valid and could not be created.");
|
||||||
@@ -239,15 +284,21 @@ class TransactionService
|
|||||||
PaymentTransactionStruct $transaction,
|
PaymentTransactionStruct $transaction,
|
||||||
Context $context,
|
Context $context,
|
||||||
int $vrpaymentTransactionId,
|
int $vrpaymentTransactionId,
|
||||||
int $spaceId
|
int $spaceId,
|
||||||
): void
|
?string $token = null
|
||||||
{
|
): void {
|
||||||
$data = [
|
$customFields = [
|
||||||
'id' => $transaction->getOrderTransactionId(),
|
|
||||||
'customFields' => [
|
|
||||||
TransactionPayload::ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_TRANSACTION_ID => $vrpaymentTransactionId,
|
TransactionPayload::ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_TRANSACTION_ID => $vrpaymentTransactionId,
|
||||||
TransactionPayload::ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_SPACE_ID => $spaceId,
|
TransactionPayload::ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_SPACE_ID => $spaceId,
|
||||||
],
|
];
|
||||||
|
|
||||||
|
if ($token) {
|
||||||
|
$customFields[TransactionPayload::ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_TOKEN] = $token;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'id' => $transaction->getOrderTransactionId(),
|
||||||
|
'customFields' => $customFields,
|
||||||
];
|
];
|
||||||
$this->container->get('order_transaction.repository')->update([$data], $context);
|
$this->container->get('order_transaction.repository')->update([$data], $context);
|
||||||
}
|
}
|
||||||
@@ -263,10 +314,9 @@ class TransactionService
|
|||||||
public function upsert(
|
public function upsert(
|
||||||
Transaction $transaction,
|
Transaction $transaction,
|
||||||
Context $context,
|
Context $context,
|
||||||
string $paymentMethodId = null,
|
?string $paymentMethodId = null,
|
||||||
string $salesChannelId = null
|
?string $salesChannelId = null
|
||||||
): void
|
): void {
|
||||||
{
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
$transactionId = $transaction->getId();
|
$transactionId = $transaction->getId();
|
||||||
@@ -279,6 +329,13 @@ class TransactionService
|
|||||||
$orderId = $transactionMetaData[TransactionPayload::VRPAYMENT_METADATA_ORDER_ID];
|
$orderId = $transactionMetaData[TransactionPayload::VRPAYMENT_METADATA_ORDER_ID];
|
||||||
$orderTransactionId = $transactionMetaData[TransactionPayload::VRPAYMENT_METADATA_ORDER_TRANSACTION_ID];
|
$orderTransactionId = $transactionMetaData[TransactionPayload::VRPAYMENT_METADATA_ORDER_TRANSACTION_ID];
|
||||||
|
|
||||||
|
if (!$paymentMethodId) {
|
||||||
|
$criteria = new Criteria([$orderTransactionId]);
|
||||||
|
$criteria->addAssociation('order');
|
||||||
|
$orderTransaction = $this->container->get('order_transaction.repository')->search($criteria, $context)->first();
|
||||||
|
$paymentMethodId = $orderTransaction->getPaymentMethodId();
|
||||||
|
}
|
||||||
|
|
||||||
$dataParamValue = json_decode(strval($transaction), true);
|
$dataParamValue = json_decode(strval($transaction), true);
|
||||||
$brandName = '';
|
$brandName = '';
|
||||||
if (isset($dataParamValue['paymentConnectorConfiguration'])) {
|
if (isset($dataParamValue['paymentConnectorConfiguration'])) {
|
||||||
@@ -345,7 +402,6 @@ class TransactionService
|
|||||||
|
|
||||||
$data = array_filter($data);
|
$data = array_filter($data);
|
||||||
$this->container->get(TransactionEntityDefinition::ENTITY_NAME . '.repository')->upsert([$data], $context);
|
$this->container->get(TransactionEntityDefinition::ENTITY_NAME . '.repository')->upsert([$data], $context);
|
||||||
|
|
||||||
} catch (\Exception $exception) {
|
} catch (\Exception $exception) {
|
||||||
$this->logger->critical(__CLASS__ . ' : ' . __FUNCTION__ . ' : ' . $exception->getMessage());
|
$this->logger->critical(__CLASS__ . ' : ' . __FUNCTION__ . ' : ' . $exception->getMessage());
|
||||||
}
|
}
|
||||||
@@ -381,7 +437,7 @@ class TransactionService
|
|||||||
*
|
*
|
||||||
* @return \Shopware\Core\Checkout\Order\OrderEntity
|
* @return \Shopware\Core\Checkout\Order\OrderEntity
|
||||||
*/
|
*/
|
||||||
protected function getOrderEntity(string $orderId, Context $context): OrderEntity
|
public function getOrderEntity(string $orderId, Context $context): OrderEntity
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$criteria = (new Criteria([$orderId]))->addAssociations(['deliveries']);
|
$criteria = (new Criteria([$orderId]))->addAssociations(['deliveries']);
|
||||||
@@ -396,7 +452,6 @@ class TransactionService
|
|||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
throw CartException::orderNotFound($orderId);
|
throw CartException::orderNotFound($orderId);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -444,7 +499,8 @@ class TransactionService
|
|||||||
return $this->container->get(TransactionEntityDefinition::ENTITY_NAME . '.repository')
|
return $this->container->get(TransactionEntityDefinition::ENTITY_NAME . '.repository')
|
||||||
->search(
|
->search(
|
||||||
(new Criteria())->addFilter(new EqualsFilter('transactionId', $transactionId))
|
(new Criteria())->addFilter(new EqualsFilter('transactionId', $transactionId))
|
||||||
->addAssociations(['refunds']), $context
|
->addAssociations(['refunds']),
|
||||||
|
$context
|
||||||
)
|
)
|
||||||
->first();
|
->first();
|
||||||
}
|
}
|
||||||
@@ -462,7 +518,8 @@ class TransactionService
|
|||||||
return $this->container->get(TransactionEntityDefinition::ENTITY_NAME . '.repository')
|
return $this->container->get(TransactionEntityDefinition::ENTITY_NAME . '.repository')
|
||||||
->search(
|
->search(
|
||||||
(new Criteria())->addFilter(new EqualsFilter('orderTransactionId', $orderTransactionId))
|
(new Criteria())->addFilter(new EqualsFilter('orderTransactionId', $orderTransactionId))
|
||||||
->addAssociations(['refunds']), $context
|
->addAssociations(['refunds']),
|
||||||
|
$context
|
||||||
)
|
)
|
||||||
->first();
|
->first();
|
||||||
}
|
}
|
||||||
@@ -479,7 +536,8 @@ class TransactionService
|
|||||||
{
|
{
|
||||||
return $this->container->get(RefundEntityDefinition::ENTITY_NAME . '.repository')
|
return $this->container->get(RefundEntityDefinition::ENTITY_NAME . '.repository')
|
||||||
->search(
|
->search(
|
||||||
(new Criteria())->addFilter(new EqualsFilter('transactionId', $transactionId)), $context
|
(new Criteria())->addFilter(new EqualsFilter('transactionId', $transactionId)),
|
||||||
|
$context
|
||||||
)
|
)
|
||||||
->getEntities();
|
->getEntities();
|
||||||
}
|
}
|
||||||
@@ -522,43 +580,44 @@ class TransactionService
|
|||||||
public function createPendingTransaction(SalesChannelContext $salesChannelContext, $event = null): int
|
public function createPendingTransaction(SalesChannelContext $salesChannelContext, $event = null): int
|
||||||
{
|
{
|
||||||
$expiredTransaction = true;
|
$expiredTransaction = true;
|
||||||
$transactionId = $_SESSION['transactionId'] ?? null;
|
// Get transaction ID from cache (headless) or session (storefront).
|
||||||
|
$transactionId = $this->getTransactionIdFromContext($salesChannelContext);
|
||||||
$settings = $this->settingsService->getValidSettings($salesChannelContext->getSalesChannel()->getId());
|
$settings = $this->settingsService->getValidSettings($salesChannelContext->getSalesChannel()->getId());
|
||||||
if (!$settings) {
|
if (!$settings) {
|
||||||
throw new \Exception('Space settings not configured');
|
throw new \Exception('Space settings not configured');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($transactionId) {
|
if ($transactionId) {
|
||||||
|
try {
|
||||||
$transactionService = $settings->getApiClient()->getTransactionService();
|
$transactionService = $settings->getApiClient()->getTransactionService();
|
||||||
$pendingTransaction = $transactionService->read($settings->getSpaceId(), $transactionId);
|
$pendingTransaction = $transactionService->read($settings->getSpaceId(), $transactionId);
|
||||||
if ($pendingTransaction->getState() === TransactionState::PENDING) {
|
if ($pendingTransaction->getState() === TransactionState::PENDING) {
|
||||||
$expiredTransaction = false;
|
$expiredTransaction = false;
|
||||||
}
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Transaction may have been deleted, expired, or is invalid - treat as expired.
|
||||||
|
$expiredTransaction = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$transactionId || $expiredTransaction) {
|
if (!$transactionId || $expiredTransaction) {
|
||||||
$settings = $this->settingsService->getValidSettings($salesChannelContext->getSalesChannel()->getId());
|
$settings = $this->settingsService->getValidSettings($salesChannelContext->getSalesChannel()->getId());
|
||||||
|
|
||||||
$customer = $salesChannelContext->getCustomer();
|
$customer = $salesChannelContext->getCustomer();
|
||||||
$lineItems = [];
|
if ($customer === null) {
|
||||||
if ($event) {
|
throw new \Exception('Customer is required to create a transaction');
|
||||||
if ($event instanceof CheckoutConfirmPageLoadedEvent) {
|
|
||||||
$cartLineItems = $event->getPage()->getCart()->getLineItems()->getElements();
|
|
||||||
foreach ($cartLineItems as $cartLineItem) {
|
|
||||||
if ($cartLineItem->getType() === CustomProductsLineItemTypes::LINE_ITEM_TYPE_CUSTOMIZED_PRODUCTS) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$lineItems[] = $this->createTempLineItem($cartLineItem);
|
|
||||||
}
|
|
||||||
} elseif ($event instanceof AccountEditOrderPageLoadedEvent) {
|
|
||||||
$order = $event->getPage()->getOrder();
|
|
||||||
foreach ($order->getLineItems() as $orderLineItem) {
|
|
||||||
$lineItems[] = $this->createTempLineItem($orderLineItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
$lineItems = $this->extractLineItems(
|
||||||
|
$event,
|
||||||
|
$salesChannelContext,
|
||||||
|
);
|
||||||
|
|
||||||
$customerId = "";
|
/*
|
||||||
|
* For guest checkouts, the customer ID is set to null rather than an empty string.
|
||||||
|
* This ensures consistency with TransactionPayload which also uses null for guests,
|
||||||
|
* preventing the Portal from treating the difference as an update/change in customer details.
|
||||||
|
*/
|
||||||
|
$customerId = null;
|
||||||
if ($customer->getGuest() === false) {
|
if ($customer->getGuest() === false) {
|
||||||
$customerId = $customer->getCustomerNumber();
|
$customerId = $customer->getCustomerNumber();
|
||||||
}
|
}
|
||||||
@@ -574,11 +633,14 @@ class TransactionService
|
|||||||
throw new \Exception('Space settings not configured');
|
throw new \Exception('Space settings not configured');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$language = $this->localeCodeProvider->getLocaleCodeFromContext($salesChannelContext->getContext());
|
||||||
|
|
||||||
$transactionPayload = (new TransactionCreate())
|
$transactionPayload = (new TransactionCreate())
|
||||||
->setBillingAddress($billingAddress)
|
->setBillingAddress($billingAddress)
|
||||||
->setShippingAddress($shippingAddress)
|
->setShippingAddress($shippingAddress)
|
||||||
->setLineItems($lineItems)
|
->setLineItems($lineItems)
|
||||||
->setCurrency($currency)
|
->setCurrency($currency)
|
||||||
|
->setLanguage($language)
|
||||||
->setSpaceViewId($settings->getSpaceViewId())
|
->setSpaceViewId($settings->getSpaceViewId())
|
||||||
->setAutoConfirmationEnabled(false)
|
->setAutoConfirmationEnabled(false)
|
||||||
->setChargeRetryEnabled(false)
|
->setChargeRetryEnabled(false)
|
||||||
@@ -587,14 +649,16 @@ class TransactionService
|
|||||||
->setSuccessUrl($homeUrl . '?success')
|
->setSuccessUrl($homeUrl . '?success')
|
||||||
->setFailedUrl($homeUrl . '?fail');
|
->setFailedUrl($homeUrl . '?fail');
|
||||||
|
|
||||||
if($this->isSubscription($salesChannelContext)) {
|
if ($this->isSubscription($salesChannelContext)) {
|
||||||
$transactionPayload->setTokenizationMode(TokenizationMode::FORCE_CREATION);
|
$transactionPayload->setTokenizationMode(TokenizationMode::FORCE_CREATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
$transactionService = $settings->getApiClient()->getTransactionService();
|
$transactionService = $settings->getApiClient()->getTransactionService();
|
||||||
$transaction = $transactionService->create($settings->getSpaceId(), $transactionPayload);
|
$transaction = $transactionService->create($settings->getSpaceId(), $transactionPayload);
|
||||||
$transactionId = $transaction->getId();
|
$transactionId = $transaction->getId();
|
||||||
$_SESSION['transactionId'] = $transactionId;
|
|
||||||
|
// Store in cache and session for transaction reuse.
|
||||||
|
$this->storeTransactionIdInContext($salesChannelContext, $transactionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $transactionId;
|
return $transactionId;
|
||||||
@@ -605,7 +669,7 @@ class TransactionService
|
|||||||
* @param int $transactionId
|
* @param int $transactionId
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function updateTempTransaction(SalesChannelContext $salesChannelContext, int $transactionId): void
|
public function updateTempTransaction(SalesChannelContext $salesChannelContext, int $transactionId, array $lineItems = []): void
|
||||||
{
|
{
|
||||||
$pendingTransaction = new TransactionPending();
|
$pendingTransaction = new TransactionPending();
|
||||||
$pendingTransaction->setId($transactionId);
|
$pendingTransaction->setId($transactionId);
|
||||||
@@ -616,17 +680,95 @@ class TransactionService
|
|||||||
|
|
||||||
$currency = $salesChannelContext->getCurrency()->getIsoCode();
|
$currency = $salesChannelContext->getCurrency()->getIsoCode();
|
||||||
|
|
||||||
|
$language = $this->localeCodeProvider->getLocaleCodeFromContext($salesChannelContext->getContext());
|
||||||
|
|
||||||
$pendingTransaction->setCurrency($currency);
|
$pendingTransaction->setCurrency($currency);
|
||||||
|
$pendingTransaction->setLanguage($language);
|
||||||
$billingAddress = $this->buildAddress($salesChannelContext, $salesChannelContext->getCustomer()->getActiveBillingAddress());
|
$billingAddress = $this->buildAddress($salesChannelContext, $salesChannelContext->getCustomer()->getActiveBillingAddress());
|
||||||
$shippingAddress = $this->buildAddress($salesChannelContext, $salesChannelContext->getCustomer()->getActiveShippingAddress());
|
$shippingAddress = $this->buildAddress($salesChannelContext, $salesChannelContext->getCustomer()->getActiveShippingAddress());
|
||||||
|
|
||||||
$pendingTransaction->setBillingAddress($billingAddress);
|
$pendingTransaction->setBillingAddress($billingAddress);
|
||||||
$pendingTransaction->setShippingAddress($shippingAddress);
|
$pendingTransaction->setShippingAddress($shippingAddress);
|
||||||
|
|
||||||
|
if (!empty($lineItems)) {
|
||||||
|
$pendingTransaction->setLineItems($lineItems);
|
||||||
|
}
|
||||||
|
|
||||||
$settings->getApiClient()->getTransactionService()
|
$settings->getApiClient()->getTransactionService()
|
||||||
->update($settings->getSpaceId(), $pendingTransaction);
|
->update($settings->getSpaceId(), $pendingTransaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts line items from the given source (Event or Cart) and appends shipping costs.
|
||||||
|
*
|
||||||
|
* @param mixed $source
|
||||||
|
* @param SalesChannelContext|null $salesChannelContext
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function extractLineItems(
|
||||||
|
$source,
|
||||||
|
?SalesChannelContext $salesChannelContext = null,
|
||||||
|
): array {
|
||||||
|
$lineItems = [];
|
||||||
|
if ($source) {
|
||||||
|
if ($source instanceof CheckoutConfirmPageLoadedEvent) {
|
||||||
|
$cartLineItems = $source->getPage()->getCart()->getLineItems()->getElements();
|
||||||
|
foreach ($cartLineItems as $cartLineItem) {
|
||||||
|
if ($cartLineItem->getType() === CustomProductsLineItemTypes::LINE_ITEM_TYPE_CUSTOMIZED_PRODUCTS) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$lineItems[] = $this->createTempLineItem($cartLineItem);
|
||||||
|
}
|
||||||
|
} elseif ($source instanceof AccountEditOrderPageLoadedEvent) {
|
||||||
|
$order = $source->getPage()->getOrder();
|
||||||
|
foreach ($order->getLineItems() as $orderLineItem) {
|
||||||
|
$lineItems[] = $this->createTempLineItem($orderLineItem);
|
||||||
|
}
|
||||||
|
} elseif ($source instanceof \Shopware\Core\Checkout\Cart\Cart) {
|
||||||
|
$cartLineItems = $source->getLineItems()->getElements();
|
||||||
|
foreach ($cartLineItems as $cartLineItem) {
|
||||||
|
if ($cartLineItem->getType() === CustomProductsLineItemTypes::LINE_ITEM_TYPE_CUSTOMIZED_PRODUCTS) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$lineItems[] = $this->createTempLineItem($cartLineItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract and append shipping costs as a line item if applicable.
|
||||||
|
$shippingCosts = null;
|
||||||
|
$taxStatus = 'gross';
|
||||||
|
|
||||||
|
if ($source instanceof CheckoutConfirmPageLoadedEvent) {
|
||||||
|
$cart = $source->getPage()->getCart();
|
||||||
|
$shippingCosts = $cart->getDeliveries()->getShippingCosts();
|
||||||
|
if ($salesChannelContext !== null) {
|
||||||
|
$taxStatus = $salesChannelContext->getTaxState();
|
||||||
|
}
|
||||||
|
} elseif ($source instanceof AccountEditOrderPageLoadedEvent) {
|
||||||
|
$order = $source->getPage()->getOrder();
|
||||||
|
$shippingCosts = $order->getShippingCosts();
|
||||||
|
$taxStatus = $order->getTaxStatus();
|
||||||
|
} elseif ($source instanceof \Shopware\Core\Checkout\Cart\Cart) {
|
||||||
|
$shippingCosts = $source->getDeliveries()->getShippingCosts();
|
||||||
|
if ($salesChannelContext !== null) {
|
||||||
|
$taxStatus = $salesChannelContext->getTaxState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($shippingCosts !== null) {
|
||||||
|
$shippingLineItem = $this->extractShippingLineItem(
|
||||||
|
$shippingCosts,
|
||||||
|
$taxStatus,
|
||||||
|
$salesChannelContext,
|
||||||
|
);
|
||||||
|
if ($shippingLineItem !== null) {
|
||||||
|
$lineItems[] = $shippingLineItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $lineItems;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ChargeAttempt|null $chargeAttempt
|
* @param ChargeAttempt|null $chargeAttempt
|
||||||
* @param string $descriptorKey
|
* @param string $descriptorKey
|
||||||
@@ -715,18 +857,26 @@ class TransactionService
|
|||||||
{
|
{
|
||||||
$lineItem = new LineItemCreate();
|
$lineItem = new LineItemCreate();
|
||||||
|
|
||||||
|
$price = $productData->getPrice();
|
||||||
|
$unit = $price->getUnitPrice();
|
||||||
|
|
||||||
|
// Expects discounts as separate items, avoid negative prices
|
||||||
|
if ($unit < 0) {
|
||||||
|
return $this->mapDiscountLineItem($productData);
|
||||||
|
}
|
||||||
|
|
||||||
if ($productData instanceof LineItem) {
|
if ($productData instanceof LineItem) {
|
||||||
$lineItem->setName($productData->getLabel());
|
$lineItem->setName($productData->getLabel());
|
||||||
$lineItem->setUniqueId($productData->getId());
|
$lineItem->setUniqueId($productData->getId());
|
||||||
$lineItem->setSku($productData->getReferencedId() ?? $productData->getId());
|
$lineItem->setSku($productData->getReferencedId() ?? $productData->getId());
|
||||||
$lineItem->setQuantity($productData->getQuantity());
|
$lineItem->setQuantity($productData->getQuantity());
|
||||||
$lineItem->setAmountIncludingTax($productData->getPrice()->getUnitPrice());
|
$lineItem->setAmountIncludingTax($this->round($unit));
|
||||||
} elseif ($productData instanceof OrderLineItemEntity) {
|
} elseif ($productData instanceof OrderLineItemEntity) {
|
||||||
$lineItem->setName($productData->getLabel());
|
$lineItem->setName($productData->getLabel());
|
||||||
$lineItem->setUniqueId($productData->getId());
|
$lineItem->setUniqueId($productData->getId());
|
||||||
$lineItem->setSku($productData->getProductId() ?? $productData->getIdentifier() ?? $productData->getId());
|
$lineItem->setSku($productData->getProductId() ?? $productData->getIdentifier() ?? $productData->getId());
|
||||||
$lineItem->setQuantity($productData->getQuantity());
|
$lineItem->setQuantity($productData->getQuantity());
|
||||||
$lineItem->setAmountIncludingTax($productData->getUnitPrice());
|
$lineItem->setAmountIncludingTax($this->round($unit));
|
||||||
} else {
|
} else {
|
||||||
throw new \InvalidArgumentException('Unsupported line item type: ' . get_class($productData));
|
throw new \InvalidArgumentException('Unsupported line item type: ' . get_class($productData));
|
||||||
}
|
}
|
||||||
@@ -756,6 +906,7 @@ class TransactionService
|
|||||||
$address->setOrganizationName($addressEntity->getCompany());
|
$address->setOrganizationName($addressEntity->getCompany());
|
||||||
$address->setPhoneNumber($addressEntity->getPhoneNumber());
|
$address->setPhoneNumber($addressEntity->getPhoneNumber());
|
||||||
$address->setCountry($addressEntity->getCountry()->getIso());
|
$address->setCountry($addressEntity->getCountry()->getIso());
|
||||||
|
$address->setCity($addressEntity->getCity() ?: '');
|
||||||
|
|
||||||
$postalState = $addressEntity?->getCountryState()?->getName()
|
$postalState = $addressEntity?->getCountryState()?->getName()
|
||||||
?: $addressEntity?->getCountryState()?->getShortCode()
|
?: $addressEntity?->getCountryState()?->getShortCode()
|
||||||
@@ -790,7 +941,8 @@ class TransactionService
|
|||||||
* @param \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext
|
* @param \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
private function isSubscription(SalesChannelContext $salesChannelContext): bool {
|
private function isSubscription(SalesChannelContext $salesChannelContext): bool
|
||||||
|
{
|
||||||
$extensionName = 'subscription';
|
$extensionName = 'subscription';
|
||||||
if (class_exists(\Shopware\Commercial\Subscription\Framework\Struct\SubscriptionContextStruct::class)) {
|
if (class_exists(\Shopware\Commercial\Subscription\Framework\Struct\SubscriptionContextStruct::class)) {
|
||||||
$extensionName = SubscriptionContextStruct::SUBSCRIPTION_EXTENSION;
|
$extensionName = SubscriptionContextStruct::SUBSCRIPTION_EXTENSION;
|
||||||
@@ -800,4 +952,197 @@ class TransactionService
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $amount
|
||||||
|
* @param int $precision
|
||||||
|
*
|
||||||
|
* @return float
|
||||||
|
*/
|
||||||
|
private function round($value, $precision = 2): float
|
||||||
|
{
|
||||||
|
return \round($value, $precision);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a cache key for the pending transaction ID.
|
||||||
|
* Uses customer ID for authenticated users, which works for both headless and storefront.
|
||||||
|
*
|
||||||
|
* @param SalesChannelContext $salesChannelContext
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
private function getPendingTransactionCacheKey(SalesChannelContext $salesChannelContext): ?string
|
||||||
|
{
|
||||||
|
$customer = $salesChannelContext->getCustomer();
|
||||||
|
if ($customer) {
|
||||||
|
return 'vrpn_pending_transaction_id_customer_' . $customer->getId();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the stored pending transaction ID from cache or session.
|
||||||
|
* Uses customer ID as cache key for headless (stateless) support.
|
||||||
|
* Falls back to session for Storefront (stateful) compatibility.
|
||||||
|
*
|
||||||
|
* @param SalesChannelContext $salesChannelContext
|
||||||
|
* @return int|null The transaction ID if found, otherwise null.
|
||||||
|
*/
|
||||||
|
private function getTransactionIdFromContext(SalesChannelContext $salesChannelContext): ?int
|
||||||
|
{
|
||||||
|
// Try cache first (for headless/API where session might not persist or be shared).
|
||||||
|
$cacheKey = $this->getPendingTransactionCacheKey($salesChannelContext);
|
||||||
|
if ($cacheKey) {
|
||||||
|
$item = $this->cache->getItem($cacheKey);
|
||||||
|
if ($item->isHit()) {
|
||||||
|
return (int) $item->get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to PHP session for traditional Storefront compatibility.
|
||||||
|
if (isset($_SESSION['transactionId'])) {
|
||||||
|
return (int) $_SESSION['transactionId'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the pending transaction ID from cache and session.
|
||||||
|
*
|
||||||
|
* @param SalesChannelContext $salesChannelContext
|
||||||
|
*/
|
||||||
|
public function clearTransactionIdFromContext(SalesChannelContext $salesChannelContext): void
|
||||||
|
{
|
||||||
|
// Clear from cache key.
|
||||||
|
$cacheKey = $this->getPendingTransactionCacheKey($salesChannelContext);
|
||||||
|
if ($cacheKey) {
|
||||||
|
$this->cache->deleteItem($cacheKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear from session.
|
||||||
|
if (isset($_SESSION['transactionId'])) {
|
||||||
|
unset($_SESSION['transactionId']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the pending transaction ID in cache and session.
|
||||||
|
* This persists in the database (via cache) and works across all request types (Storefront & headless).
|
||||||
|
*
|
||||||
|
* @param SalesChannelContext $salesChannelContext
|
||||||
|
* @param int $transactionId
|
||||||
|
*/
|
||||||
|
private function storeTransactionIdInContext(SalesChannelContext $salesChannelContext, int $transactionId): void
|
||||||
|
{
|
||||||
|
// Store in cache for headless.
|
||||||
|
$cacheKey = $this->getPendingTransactionCacheKey($salesChannelContext);
|
||||||
|
if ($cacheKey) {
|
||||||
|
$item = $this->cache->getItem($cacheKey);
|
||||||
|
$item->set($transactionId);
|
||||||
|
// Expire after 2 hours to avoid stale data (matching typical cart lifetime).
|
||||||
|
$item->expiresAfter(7200);
|
||||||
|
$this->cache->save($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store in session for Storefront.
|
||||||
|
$_SESSION['transactionId'] = $transactionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a discount line item for negative-priced cart entries.
|
||||||
|
*
|
||||||
|
* @param $productData
|
||||||
|
* @return LineItemCreate
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function mapDiscountLineItem($productData): LineItemCreate
|
||||||
|
{
|
||||||
|
$price = $productData->getPrice();
|
||||||
|
|
||||||
|
$lineItem = new LineItemCreate();
|
||||||
|
|
||||||
|
$amount = abs($price->getTotalPrice());
|
||||||
|
|
||||||
|
$lineItem->setName($productData->getLabel() ?: 'Discount');
|
||||||
|
$lineItem->setUniqueId('discount-' . $productData->getId());
|
||||||
|
$lineItem->setSku('discount');
|
||||||
|
$lineItem->setQuantity(1);
|
||||||
|
$lineItem->setAmountIncludingTax($this->round($amount));
|
||||||
|
$lineItem->setType(LineItemType::DISCOUNT);
|
||||||
|
|
||||||
|
return $lineItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts shipping line item from cart/order shipping costs.
|
||||||
|
*
|
||||||
|
* @param mixed $shippingCosts
|
||||||
|
* @param string $taxStatus
|
||||||
|
* @param SalesChannelContext|null $salesChannelContext
|
||||||
|
* @return LineItemCreate|null
|
||||||
|
*/
|
||||||
|
private function extractShippingLineItem(
|
||||||
|
$shippingCosts,
|
||||||
|
string $taxStatus,
|
||||||
|
?SalesChannelContext $salesChannelContext = null,
|
||||||
|
): ?LineItemCreate {
|
||||||
|
// When shipping costs are extracted from a Cart, they are returned as a PriceCollection
|
||||||
|
// containing multiple CalculatedPrice items. We must sum them to get a single aggregated price.
|
||||||
|
if ($shippingCosts instanceof \Shopware\Core\Checkout\Cart\Price\Struct\PriceCollection) {
|
||||||
|
$shippingCosts = $shippingCosts->sum();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$shippingCosts instanceof \Shopware\Core\Checkout\Cart\Price\Struct\CalculatedPrice) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($shippingCosts->getTotalPrice() <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$amount = $shippingCosts->getTotalPrice();
|
||||||
|
if ($taxStatus === 'net') {
|
||||||
|
$amount += $shippingCosts->getCalculatedTaxes()->getAmount();
|
||||||
|
}
|
||||||
|
$roundedAmount = $this->round($amount);
|
||||||
|
|
||||||
|
$shippingMethodName = $salesChannelContext?->getShippingMethod()?->getName();
|
||||||
|
$translator = $this->container->has('translator') ? $this->container->get('translator') : null;
|
||||||
|
$fallbackName = $translator ? $translator->trans('vrpayment.payload.shipping.name') : 'Shipping';
|
||||||
|
$shippingName = $shippingMethodName ?? $fallbackName;
|
||||||
|
|
||||||
|
$shippingLineItem = new LineItemCreate();
|
||||||
|
$shippingLineItem->setAmountIncludingTax($roundedAmount)
|
||||||
|
->setName($this->fixLength($shippingName . ' ' . ($translator ? $translator->trans('vrpayment.payload.shipping.lineItem') : 'Shipping'), 150))
|
||||||
|
->setQuantity($shippingCosts->getQuantity() ?? 1)
|
||||||
|
->setSku($this->fixLength($shippingName . '-Shipping', 200))
|
||||||
|
->setType(LineItemType::SHIPPING)
|
||||||
|
->setUniqueId($this->fixLength($shippingName . '-Shipping', 200));
|
||||||
|
|
||||||
|
if ($taxStatus !== 'tax-free') {
|
||||||
|
$taxes = [];
|
||||||
|
foreach ($shippingCosts->getCalculatedTaxes() as $calculatedTax) {
|
||||||
|
$tax = (new TaxCreate())
|
||||||
|
->setRate($calculatedTax->getTaxRate())
|
||||||
|
->setTitle($this->fixLength($shippingName . ' : ' . $calculatedTax->getTaxRate(), 40));
|
||||||
|
$taxes[] = $tax;
|
||||||
|
}
|
||||||
|
$shippingLineItem->setTaxes($taxes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $shippingLineItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fix string length to specific length.
|
||||||
|
*
|
||||||
|
* @param string $string
|
||||||
|
* @param int $maxLength
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function fixLength(string $string, int $maxLength): string
|
||||||
|
{
|
||||||
|
return \mb_substr($string, 0, $maxLength, 'UTF-8');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -486,7 +486,7 @@ class WebHookController extends AbstractController {
|
|||||||
$transaction = $this->settings->getApiClient()
|
$transaction = $this->settings->getApiClient()
|
||||||
->getTransactionService()
|
->getTransactionService()
|
||||||
->read($callBackData->getSpaceId(), $callBackData->getEntityId());
|
->read($callBackData->getSpaceId(), $callBackData->getEntityId());
|
||||||
$orderId = $transaction->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_ID];
|
$orderId = $transaction->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_ID] ?? null;
|
||||||
if(!empty($orderId) && !$transaction->getParent()) {
|
if(!empty($orderId) && !$transaction->getParent()) {
|
||||||
$this->executeLocked($orderId, $context, function () use ($orderId, $transaction, $context, $callBackData) {
|
$this->executeLocked($orderId, $context, function () use ($orderId, $transaction, $context, $callBackData) {
|
||||||
$this->transactionService->upsert($transaction, $context);
|
$this->transactionService->upsert($transaction, $context);
|
||||||
@@ -700,16 +700,6 @@ class WebHookController extends AbstractController {
|
|||||||
private function unholdAndCancelDelivery(string $orderId, Context $context): void
|
private function unholdAndCancelDelivery(string $orderId, Context $context): void
|
||||||
{
|
{
|
||||||
$order = $this->getOrderEntity($orderId, $context);
|
$order = $this->getOrderEntity($orderId, $context);
|
||||||
try {
|
|
||||||
$this->orderService->orderStateTransition(
|
|
||||||
$order->getId(),
|
|
||||||
StateMachineTransitionActions::ACTION_CANCEL,
|
|
||||||
new ParameterBag(),
|
|
||||||
$context
|
|
||||||
);
|
|
||||||
} catch (\Exception $exception) {
|
|
||||||
$this->logger->info($exception->getMessage(), $exception->getTrace());
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class WebHookRefundStrategy extends WebHookStrategyBase implements WebhookStrate
|
|||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
public function getOrderIdByTransaction($transaction): string
|
public function getOrderIdByTransaction($transaction): string|null
|
||||||
{
|
{
|
||||||
/** @var \VRPayment\Sdk\Model\Refund $transaction */
|
/** @var \VRPayment\Sdk\Model\Refund $transaction */
|
||||||
return $transaction->getTransaction()
|
return $transaction->getTransaction()
|
||||||
|
|||||||
@@ -378,16 +378,6 @@ abstract class WebHookStrategyBase implements WebHookStrategyInterface {
|
|||||||
protected function unholdAndCancelDelivery(string $orderId, Context $context): void
|
protected function unholdAndCancelDelivery(string $orderId, Context $context): void
|
||||||
{
|
{
|
||||||
$order = $this->getOrderEntity($orderId, $context);
|
$order = $this->getOrderEntity($orderId, $context);
|
||||||
try {
|
|
||||||
$this->orderService->orderStateTransition(
|
|
||||||
$order->getId(),
|
|
||||||
StateMachineTransitionActions::ACTION_CANCEL,
|
|
||||||
new ParameterBag(),
|
|
||||||
$context
|
|
||||||
);
|
|
||||||
} catch (\Exception $exception) {
|
|
||||||
$this->logger->info($exception->getMessage(), $exception->getTrace());
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
|||||||
@@ -63,10 +63,10 @@ class WebHookTransactionInvoiceStrategy extends WebHookStrategyBase implements W
|
|||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
public function getOrderIdByTransaction($transaction): string
|
public function getOrderIdByTransaction($transactionInvoice): string|null
|
||||||
{
|
{
|
||||||
/** @var \VRPayment\Sdk\Model\TransactionInvoice $transaction */
|
/** @var \VRPayment\Sdk\Model\TransactionInvoice $transaction */
|
||||||
return $transaction->getCompletion()
|
return $transactionInvoice->getCompletion()
|
||||||
->getLineItemVersion()
|
->getLineItemVersion()
|
||||||
->getTransaction()
|
->getTransaction()
|
||||||
->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_ID];
|
->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_ID];
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class WebHookTransactionStrategy extends WebHookStrategyBase implements WebhookS
|
|||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
public function getOrderIdByTransaction(Transaction $transaction): string
|
public function getOrderIdByTransaction(Transaction $transaction): string|null
|
||||||
{
|
{
|
||||||
/** @var \VRPayment\Sdk\Model\Transaction $transaction */
|
/** @var \VRPayment\Sdk\Model\Transaction $transaction */
|
||||||
return $transaction->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_ID];
|
return $transaction->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_ID];
|
||||||
@@ -116,10 +116,12 @@ class WebHookTransactionStrategy extends WebHookStrategyBase implements WebhookS
|
|||||||
/** @var \Shopware\Core\Checkout\Order\OrderEntity $order */
|
/** @var \Shopware\Core\Checkout\Order\OrderEntity $order */
|
||||||
$transaction = $this->getTransaction($request);
|
$transaction = $this->getTransaction($request);
|
||||||
$token = $transaction->getToken();
|
$token = $transaction->getToken();
|
||||||
$orderId = $transaction->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_ID];
|
$orderId = $transaction->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_ID] ?? null;
|
||||||
if (!empty($orderId) && !$transaction->getParent()) {
|
if (!empty($orderId) && !$transaction->getParent()) {
|
||||||
$this->executeLocked($orderId, $context, function () use ($orderId, $transaction, $context, $request, $token) {
|
$this->executeLocked($orderId, $context, function () use ($orderId, $transaction, $context, $request, $token) {
|
||||||
|
if ($this->allowUpsert($transaction, $orderId, $context)) {
|
||||||
$this->transactionService->upsert($transaction, $context);
|
$this->transactionService->upsert($transaction, $context);
|
||||||
|
}
|
||||||
$orderTransactionId = $transaction->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_TRANSACTION_ID];
|
$orderTransactionId = $transaction->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_TRANSACTION_ID];
|
||||||
$orderTransaction = $this->getOrderTransaction($orderId, $context);
|
$orderTransaction = $this->getOrderTransaction($orderId, $context);
|
||||||
$this->logger->info("OrderId: {orderId} Current state: {state}", [
|
$this->logger->info("OrderId: {orderId} Current state: {state}", [
|
||||||
@@ -178,4 +180,26 @@ class WebHookTransactionStrategy extends WebHookStrategyBase implements WebhookS
|
|||||||
|
|
||||||
return new JsonResponse(['data' => $request->jsonSerialize()], $status);
|
return new JsonResponse(['data' => $request->jsonSerialize()], $status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the incoming transaction ID against the current transaction ID of the order.
|
||||||
|
* If they don’t match, the saved transaction in the database remains unchanged.
|
||||||
|
*
|
||||||
|
* @param Transaction $transaction The transaction data retrieved from the portal.
|
||||||
|
* @param string $orderId The order ID of the current transaction.
|
||||||
|
* @param Context $context The operational context providing settings and environment for transaction processing.
|
||||||
|
* @return bool Returns a value that determines whether to upsert the transaction into the database.
|
||||||
|
*/
|
||||||
|
private function allowUpsert(Transaction $transaction, string $orderId, Context $context): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$transactionEntity = $this->transactionService->getByOrderId($orderId, $context);
|
||||||
|
if ($transactionEntity->getTransactionId() !== $transaction->getId()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,5 +57,5 @@ interface WebhookStrategyActionsInterface {
|
|||||||
* @param Transaction|TransactionInvoiceState|Refund|mixed $transaction The transaction object from which the order ID should be extracted.
|
* @param Transaction|TransactionInvoiceState|Refund|mixed $transaction The transaction object from which the order ID should be extracted.
|
||||||
* @return string The order ID as a string.
|
* @return string The order ID as a string.
|
||||||
*/
|
*/
|
||||||
public function getOrderIdByTransaction(Transaction $transaction): string;
|
public function getOrderIdByTransaction(Transaction $transaction): string|null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace VRPaymentPayment\Core\Checkout\PaymentHandler;
|
namespace VRPaymentPayment\Core\Checkout\PaymentHandler;
|
||||||
|
|
||||||
@@ -23,7 +25,7 @@ use Shopware\Core\{
|
|||||||
Framework\Struct\Struct,
|
Framework\Struct\Struct,
|
||||||
Framework\Validation\DataBag\RequestDataBag,
|
Framework\Validation\DataBag\RequestDataBag,
|
||||||
System\StateMachine\Aggregation\StateMachineState\StateMachineStateEntity,
|
System\StateMachine\Aggregation\StateMachineState\StateMachineStateEntity,
|
||||||
System\SalesChannel\Context\SalesChannelContextService,
|
System\SalesChannel\Context\SalesChannelContextServiceInterface,
|
||||||
System\SalesChannel\Context\SalesChannelContextServiceParameters
|
System\SalesChannel\Context\SalesChannelContextServiceParameters
|
||||||
};
|
};
|
||||||
use Shopware\Core\Framework\Util\Random;
|
use Shopware\Core\Framework\Util\Random;
|
||||||
@@ -69,7 +71,7 @@ class VRPaymentPaymentHandler extends AbstractPaymentHandler
|
|||||||
*/
|
*/
|
||||||
private $orderTransactionStateHandler;
|
private $orderTransactionStateHandler;
|
||||||
|
|
||||||
protected SalesChannelContextService $salesChannelContextService;
|
protected SalesChannelContextServiceInterface $salesChannelContextService;
|
||||||
|
|
||||||
protected EntityRepository $orderTransactionRepository;
|
protected EntityRepository $orderTransactionRepository;
|
||||||
|
|
||||||
@@ -82,7 +84,7 @@ class VRPaymentPaymentHandler extends AbstractPaymentHandler
|
|||||||
CustomCartPersister $cartPersister,
|
CustomCartPersister $cartPersister,
|
||||||
PluginTransactionService $pluginTransactionService,
|
PluginTransactionService $pluginTransactionService,
|
||||||
OrderTransactionStateHandler $orderTransactionStateHandler,
|
OrderTransactionStateHandler $orderTransactionStateHandler,
|
||||||
SalesChannelContextService $salesChannelContextService,
|
SalesChannelContextServiceInterface $salesChannelContextService,
|
||||||
EntityRepository $orderTransactionRepository,
|
EntityRepository $orderTransactionRepository,
|
||||||
?EntityRepository $subscriptionRepository,
|
?EntityRepository $subscriptionRepository,
|
||||||
) {
|
) {
|
||||||
@@ -119,13 +121,14 @@ class VRPaymentPaymentHandler extends AbstractPaymentHandler
|
|||||||
PaymentTransactionStruct $transaction,
|
PaymentTransactionStruct $transaction,
|
||||||
Context $context,
|
Context $context,
|
||||||
?Struct $validateStruct
|
?Struct $validateStruct
|
||||||
): RedirectResponse
|
): RedirectResponse {
|
||||||
{
|
|
||||||
try {
|
try {
|
||||||
$orderTransactionId = $transaction->getOrderTransactionId();
|
$orderTransactionId = $transaction->getOrderTransactionId();
|
||||||
$orderTransaction = $this->orderTransactionRepository->search(
|
$orderTransaction = $this->orderTransactionRepository->search(
|
||||||
(new Criteria([$orderTransactionId]))
|
(new Criteria([$orderTransactionId]))
|
||||||
->addAssociation('order'), $context
|
->addAssociation('order')
|
||||||
|
->addAssociation('stateMachineState'),
|
||||||
|
$context
|
||||||
)->getEntities()->first();
|
)->getEntities()->first();
|
||||||
|
|
||||||
$contextSource = $context->getSource();
|
$contextSource = $context->getSource();
|
||||||
@@ -133,7 +136,9 @@ class VRPaymentPaymentHandler extends AbstractPaymentHandler
|
|||||||
$salesChannelContextId = $contextSource->getSalesChannelId();
|
$salesChannelContextId = $contextSource->getSalesChannelId();
|
||||||
}
|
}
|
||||||
|
|
||||||
$parameters = new SalesChannelContextServiceParameters($salesChannelContextId, $request->getSession()->get("sw-context-token", Random::getAlphanumericString(32)), originalContext: $context);
|
$contextToken = $this->getContextToken($request);
|
||||||
|
$parameters = new SalesChannelContextServiceParameters($salesChannelContextId, $contextToken, languageId: $context->getLanguageId(), originalContext: $context);
|
||||||
|
|
||||||
$salesChannelContext = $this->salesChannelContextService->get($parameters);
|
$salesChannelContext = $this->salesChannelContextService->get($parameters);
|
||||||
$redirectUrl = $transaction->getReturnUrl();
|
$redirectUrl = $transaction->getReturnUrl();
|
||||||
|
|
||||||
@@ -146,10 +151,21 @@ class VRPaymentPaymentHandler extends AbstractPaymentHandler
|
|||||||
}
|
}
|
||||||
return new RedirectResponse($redirectUrl);
|
return new RedirectResponse($redirectUrl);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
|
// Clear the transaction ID from the cache or session context depending on whether the SalesChannelContext was initialized
|
||||||
|
// to prevent subsequent checkout attempts from reusing a failed/invalid transaction ID.
|
||||||
|
if (isset($salesChannelContext)) {
|
||||||
|
$this->pluginTransactionService->clearTransactionIdFromContext($salesChannelContext);
|
||||||
|
} else {
|
||||||
$request->getSession()->remove('transactionId');
|
$request->getSession()->remove('transactionId');
|
||||||
|
}
|
||||||
$errorMessage = 'An error occurred during the communication with external payment gateway : ' . $e->getMessage();
|
$errorMessage = 'An error occurred during the communication with external payment gateway : ' . $e->getMessage();
|
||||||
$this->logger->critical($errorMessage);
|
$this->logger->critical($errorMessage);
|
||||||
throw PaymentException::customerCanceled($transaction->getOrderTransaction()->getId(), $errorMessage);
|
// If the transaction has already been marked as cancelled (e.g. via webhook or concurrent request),
|
||||||
|
// throw an interrupted exception to signal that payment processing cannot be finalized normally.
|
||||||
|
if ($orderTransaction->getStateMachineState()?->getTechnicalName() === OrderTransactionStates::STATE_CANCELLED) {
|
||||||
|
throw PaymentException::asyncFinalizeInterrupted($orderTransaction->getOrder()->getId(), $errorMessage);
|
||||||
|
}
|
||||||
|
throw PaymentException::customerCanceled($transaction->getOrderTransactionId(), $errorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,12 +184,13 @@ class VRPaymentPaymentHandler extends AbstractPaymentHandler
|
|||||||
Request $request,
|
Request $request,
|
||||||
PaymentTransactionStruct $transaction,
|
PaymentTransactionStruct $transaction,
|
||||||
Context $context
|
Context $context
|
||||||
): void
|
): void {
|
||||||
{
|
|
||||||
$orderTransactionId = $transaction->getOrderTransactionId();
|
$orderTransactionId = $transaction->getOrderTransactionId();
|
||||||
$orderTransaction = $this->orderTransactionRepository->search(
|
$orderTransaction = $this->orderTransactionRepository->search(
|
||||||
(new Criteria([$orderTransactionId]))
|
(new Criteria([$orderTransactionId]))
|
||||||
->addAssociation('order'), $context
|
->addAssociation('order')
|
||||||
|
->addAssociation('stateMachineState'),
|
||||||
|
$context
|
||||||
)->getEntities()->first();
|
)->getEntities()->first();
|
||||||
|
|
||||||
if ($orderTransaction->getOrder()->getAmountTotal() > 0) {
|
if ($orderTransaction->getOrder()->getAmountTotal() > 0) {
|
||||||
@@ -187,28 +204,65 @@ class VRPaymentPaymentHandler extends AbstractPaymentHandler
|
|||||||
$transactionEntity->getSalesChannelId()
|
$transactionEntity->getSalesChannelId()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (in_array($vRPaymentTransaction->getState(), [TransactionState::FAILED])) {
|
if (in_array($vRPaymentTransaction->getState(), [TransactionState::FAILED, TransactionState::DECLINE, TransactionState::VOIDED,])) {
|
||||||
$errorMessage = strtr('Customer canceled payment for :orderId on SalesChannel :salesChannelName', [
|
$errorMessage = strtr('Customer canceled payment for :orderId on SalesChannel :salesChannelName', [
|
||||||
':orderId' => $orderTransaction->getOrder()->getId(),
|
':orderId' => $orderTransaction->getOrder()->getId(),
|
||||||
':salesChannelName' => $transactionEntity->getSalesChannelId(),
|
':salesChannelName' => $transactionEntity->getSalesChannelId(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Retrieve the sales channel context parameters to clear the transaction ID from cache or session context,
|
||||||
|
// ensuring that a failed transaction is not reused when the customer retries payment.
|
||||||
|
$token = $this->getContextToken($request);
|
||||||
|
if ($token) {
|
||||||
|
$parameters = new SalesChannelContextServiceParameters(
|
||||||
|
$transactionEntity->getSalesChannelId(),
|
||||||
|
$token,
|
||||||
|
originalContext: $context,
|
||||||
|
);
|
||||||
|
$salesChannelContext = $this->salesChannelContextService->get($parameters);
|
||||||
|
$this->pluginTransactionService->clearTransactionIdFromContext($salesChannelContext);
|
||||||
|
} else {
|
||||||
$request->getSession()->remove('transactionId');
|
$request->getSession()->remove('transactionId');
|
||||||
|
}
|
||||||
|
|
||||||
$this->logger->info($errorMessage);
|
$this->logger->info($errorMessage);
|
||||||
throw PaymentException::customerCanceled($transaction->getOrderTransaction()->getId(), $errorMessage);
|
throw PaymentException::customerCanceled($orderTransactionId, $errorMessage);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->orderTransactionStateHandler->paid($orderTransaction->getId(), $context);
|
$this->orderTransactionStateHandler->paid($orderTransaction->getId(), $context);
|
||||||
}
|
}
|
||||||
|
|
||||||
$token = $request->getSession()->get('sw-context-token');
|
$token = $this->getContextToken($request);
|
||||||
if ($token) {
|
if ($token) {
|
||||||
$salesChannelId = $transactionEntity->getSalesChannelId();
|
$orderEntity = $this->pluginTransactionService->getOrderEntity(
|
||||||
|
$orderTransaction->getOrder()->getId(),
|
||||||
|
$context
|
||||||
|
);
|
||||||
|
$salesChannelId = $orderEntity->getSalesChannelId();
|
||||||
$parameters = new SalesChannelContextServiceParameters($salesChannelId, $token, originalContext: $context);
|
$parameters = new SalesChannelContextServiceParameters($salesChannelId, $token, originalContext: $context);
|
||||||
$salesChannelContext = $this->salesChannelContextService->get($parameters);
|
$salesChannelContext = $this->salesChannelContextService->get($parameters);
|
||||||
|
|
||||||
$salesChannelContext->getContext()->addState('do-cart-delete');
|
$salesChannelContext->getContext()->addState('do-cart-delete');
|
||||||
$this->logger->info('Clearing cart with token: ' . $token);
|
$this->logger->info('Clearing cart with token: ' . $token);
|
||||||
$this->cartPersister->delete($salesChannelContext->getToken(), $salesChannelContext);
|
$this->cartPersister->delete($salesChannelContext->getToken(), $salesChannelContext);
|
||||||
|
} else {
|
||||||
|
// Fallback: Try to get token from transaction custom fields (for Headless redirects)
|
||||||
|
$customFields = $orderTransaction->getCustomFields() ?? [];
|
||||||
|
$storedToken = $customFields[TransactionPayload::ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_TOKEN] ?? null;
|
||||||
|
|
||||||
|
if ($storedToken) {
|
||||||
|
$this->logger->info('Clearing cart with stored token: ' . $storedToken);
|
||||||
|
$orderEntity = $this->pluginTransactionService->getOrderEntity(
|
||||||
|
$orderTransaction->getOrder()->getId(),
|
||||||
|
$context
|
||||||
|
);
|
||||||
|
$salesChannelId = $orderEntity->getSalesChannelId();
|
||||||
|
$parameters = new SalesChannelContextServiceParameters($salesChannelId, $storedToken, originalContext: $context);
|
||||||
|
$salesChannelContext = $this->salesChannelContextService->get($parameters);
|
||||||
|
|
||||||
|
$salesChannelContext->getContext()->addState('do-cart-delete');
|
||||||
|
$this->cartPersister->delete($salesChannelContext->getToken(), $salesChannelContext);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,7 +325,7 @@ class VRPaymentPaymentHandler extends AbstractPaymentHandler
|
|||||||
throw PaymentException::recurringInterrupted($newTransactionId, 'No orders found associated with the subscription.');
|
throw PaymentException::recurringInterrupted($newTransactionId, 'No orders found associated with the subscription.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$orders->sort(fn (OrderEntity $a, OrderEntity $b) => $a->getCreatedAt() <=> $b->getCreatedAt());
|
$orders->sort(fn(OrderEntity $a, OrderEntity $b) => $a->getCreatedAt() <=> $b->getCreatedAt());
|
||||||
/** @var OrderEntity|null $originalOrder */
|
/** @var OrderEntity|null $originalOrder */
|
||||||
$originalOrder = $orders->first();
|
$originalOrder = $orders->first();
|
||||||
|
|
||||||
@@ -283,7 +337,7 @@ class VRPaymentPaymentHandler extends AbstractPaymentHandler
|
|||||||
|
|
||||||
/** @var OrderTransactionEntity|null $originalTransaction */
|
/** @var OrderTransactionEntity|null $originalTransaction */
|
||||||
$originalTransaction = $originalTransactions->filter(
|
$originalTransaction = $originalTransactions->filter(
|
||||||
fn (OrderTransactionEntity $t) => $t->getStateMachineState()?->getTechnicalName() === OrderTransactionStates::STATE_PAID
|
fn(OrderTransactionEntity $t) => $t->getStateMachineState()?->getTechnicalName() === OrderTransactionStates::STATE_PAID
|
||||||
)->first();
|
)->first();
|
||||||
|
|
||||||
if ($originalTransaction === null) {
|
if ($originalTransaction === null) {
|
||||||
@@ -292,7 +346,8 @@ class VRPaymentPaymentHandler extends AbstractPaymentHandler
|
|||||||
|
|
||||||
$newOrderTransaction = $this->orderTransactionRepository->search(
|
$newOrderTransaction = $this->orderTransactionRepository->search(
|
||||||
(new Criteria([$newTransactionId]))
|
(new Criteria([$newTransactionId]))
|
||||||
->addAssociation('order'), $context
|
->addAssociation('order'),
|
||||||
|
$context
|
||||||
)->getEntities()->first();
|
)->getEntities()->first();
|
||||||
$orderNumber = $newOrderTransaction->getOrder()->getOrderNumber();
|
$orderNumber = $newOrderTransaction->getOrder()->getOrderNumber();
|
||||||
|
|
||||||
@@ -365,8 +420,7 @@ class VRPaymentPaymentHandler extends AbstractPaymentHandler
|
|||||||
// Update the new order transaction with the new transaction details
|
// Update the new order transaction with the new transaction details
|
||||||
$this->orderTransactionRepository->update([$data], $context);
|
$this->orderTransactionRepository->update([$data], $context);
|
||||||
$this->pluginTransactionService->upsert($newSdkTransaction, $context);
|
$this->pluginTransactionService->upsert($newSdkTransaction, $context);
|
||||||
}
|
} catch (\Throwable $e) {
|
||||||
catch (\Throwable $e) {
|
|
||||||
$errorMessage = 'An error occurred during the communication with external payment gateway : ' . $e->getMessage();
|
$errorMessage = 'An error occurred during the communication with external payment gateway : ' . $e->getMessage();
|
||||||
$this->logger->critical($errorMessage);
|
$this->logger->critical($errorMessage);
|
||||||
throw PaymentException::recurringInterrupted($transaction->getOrderTransactionId(), $errorMessage);
|
throw PaymentException::recurringInterrupted($transaction->getOrderTransactionId(), $errorMessage);
|
||||||
@@ -379,7 +433,8 @@ class VRPaymentPaymentHandler extends AbstractPaymentHandler
|
|||||||
* @param \VRPayment\Sdk\Model\Address $address The address model from the SDK.
|
* @param \VRPayment\Sdk\Model\Address $address The address model from the SDK.
|
||||||
* @return \VRPayment\Sdk\Model\AddressCreate The newly created AddressCreate instance.
|
* @return \VRPayment\Sdk\Model\AddressCreate The newly created AddressCreate instance.
|
||||||
*/
|
*/
|
||||||
private function addressCreateFromSdk(\VRPayment\Sdk\Model\Address $address): \VRPayment\Sdk\Model\AddressCreate {
|
private function addressCreateFromSdk(\VRPayment\Sdk\Model\Address $address): \VRPayment\Sdk\Model\AddressCreate
|
||||||
|
{
|
||||||
$addressCreate = new \VRPayment\Sdk\Model\AddressCreate;
|
$addressCreate = new \VRPayment\Sdk\Model\AddressCreate;
|
||||||
|
|
||||||
$addressCreate->setCity($address->getCity());
|
$addressCreate->setCity($address->getCity());
|
||||||
@@ -419,7 +474,9 @@ class VRPaymentPaymentHandler extends AbstractPaymentHandler
|
|||||||
{
|
{
|
||||||
$lineItemCreate = new \VRPayment\Sdk\Model\LineItemCreate();
|
$lineItemCreate = new \VRPayment\Sdk\Model\LineItemCreate();
|
||||||
|
|
||||||
$lineItemCreate->setAmountIncludingTax($lineItem->getAmountIncludingTax());
|
$roundedPrice = $this->round($lineItem->getAmountIncludingTax());
|
||||||
|
|
||||||
|
$lineItemCreate->setAmountIncludingTax($roundedPrice);
|
||||||
|
|
||||||
$attributes = $lineItem->getAttributes();
|
$attributes = $lineItem->getAttributes();
|
||||||
$attributesCreate = [];
|
$attributesCreate = [];
|
||||||
@@ -456,4 +513,30 @@ class VRPaymentPaymentHandler extends AbstractPaymentHandler
|
|||||||
|
|
||||||
return $lineItemCreate;
|
return $lineItemCreate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $amount
|
||||||
|
* @param int $precision
|
||||||
|
*
|
||||||
|
* @return float
|
||||||
|
*/
|
||||||
|
private function round($value, $precision = 2): float
|
||||||
|
{
|
||||||
|
return \round($value, $precision);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getContextToken(Request $request): ?string
|
||||||
|
{
|
||||||
|
$headerContextToken = $request->headers->get('sw-context-token');
|
||||||
|
if ($headerContextToken) {
|
||||||
|
return $headerContextToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sessionContextToken = $request->getSession()->get("sw-context-token");
|
||||||
|
if ($sessionContextToken) {
|
||||||
|
return $sessionContextToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace VRPaymentPayment\Core\Checkout\PaymentMethod\SalesChannel;
|
||||||
|
|
||||||
|
use Shopware\Core\Checkout\Payment\PaymentMethodDefinition;
|
||||||
|
use Shopware\Core\Checkout\Payment\SalesChannel\AbstractPaymentMethodRoute;
|
||||||
|
use Shopware\Core\Checkout\Payment\SalesChannel\PaymentMethodRouteResponse;
|
||||||
|
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
|
||||||
|
use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult;
|
||||||
|
use Shopware\Core\Framework\Log\Package;
|
||||||
|
use Shopware\Core\System\SalesChannel\SalesChannelContext;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
use VRPaymentPayment\Core\Checkout\Service\PaymentMethodFilterService;
|
||||||
|
|
||||||
|
#[Package('checkout')]
|
||||||
|
/**
|
||||||
|
* This decorator intercepts the Store API payment method route to apply WhitelabelMachineName-specific filtering.
|
||||||
|
* It ensures that only payment methods valid for the current transaction (given currency, amount, etc.) are shown.
|
||||||
|
*/
|
||||||
|
class PaymentMethodRouteDecorator extends AbstractPaymentMethodRoute
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var AbstractPaymentMethodRoute
|
||||||
|
* The original route being decorated.
|
||||||
|
*/
|
||||||
|
private AbstractPaymentMethodRoute $decorated;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var PaymentMethodFilterService
|
||||||
|
* Service used to filter the payment methods based on WhitelabelMachineName API availability.
|
||||||
|
*/
|
||||||
|
private PaymentMethodFilterService $paymentMethodFilterService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param AbstractPaymentMethodRoute $decorated
|
||||||
|
* @param PaymentMethodFilterService $paymentMethodFilterService
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
AbstractPaymentMethodRoute $decorated,
|
||||||
|
PaymentMethodFilterService $paymentMethodFilterService
|
||||||
|
) {
|
||||||
|
$this->decorated = $decorated;
|
||||||
|
$this->paymentMethodFilterService = $paymentMethodFilterService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the decorated service.
|
||||||
|
*
|
||||||
|
* @return AbstractPaymentMethodRoute
|
||||||
|
*/
|
||||||
|
public function getDecorated(): AbstractPaymentMethodRoute
|
||||||
|
{
|
||||||
|
return $this->decorated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the payment methods and applies the WhitelabelMachineName filter to the result.
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param SalesChannelContext $context
|
||||||
|
* @param Criteria $criteria
|
||||||
|
* @return PaymentMethodRouteResponse
|
||||||
|
*/
|
||||||
|
#[Route(
|
||||||
|
path: '/store-api/payment-method',
|
||||||
|
name: 'store-api.payment.method',
|
||||||
|
methods: ['GET', 'POST'],
|
||||||
|
defaults: ['_entity' => 'payment_method']
|
||||||
|
)]
|
||||||
|
public function load(Request $request, SalesChannelContext $context, Criteria $criteria): PaymentMethodRouteResponse
|
||||||
|
{
|
||||||
|
// Fetch the initial list of payment methods from the decorated service.
|
||||||
|
$response = $this->decorated->load($request, $context, $criteria);
|
||||||
|
|
||||||
|
$currentRoute = $request->attributes->get('_route');
|
||||||
|
if ($currentRoute === 'frontend.checkout.finish.page' || !$this->allowFilterPaymentMethods($request)) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
$paymentMethods = $response->getPaymentMethods();
|
||||||
|
|
||||||
|
// Apply WhitelabelMachineName-specific filtering logic via the dedicated service.
|
||||||
|
$filteredCollection = $this->paymentMethodFilterService->filterPaymentMethods(
|
||||||
|
$paymentMethods,
|
||||||
|
$context
|
||||||
|
);
|
||||||
|
|
||||||
|
// Return the filtered results as a new response.
|
||||||
|
return new PaymentMethodRouteResponse(
|
||||||
|
new EntitySearchResult(
|
||||||
|
'payment_method',
|
||||||
|
(int)$filteredCollection->count(),
|
||||||
|
$filteredCollection,
|
||||||
|
null,
|
||||||
|
$criteria,
|
||||||
|
$context->getContext()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We prevent filtering methods unless onlyAvailable is true.
|
||||||
|
* This is because the filterPaymentMethods() function creates unnecessary pending transactions in the
|
||||||
|
* portal when logged-in users navigate between pages.
|
||||||
|
* The onlyAvailable flag applies rule-based filtering of payment methods and is usually true on checkout pages,
|
||||||
|
* so we apply filterPaymentMethods() only when relevant.
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function allowFilterPaymentMethods(Request $request): bool
|
||||||
|
{
|
||||||
|
if ($request->query->getBoolean('onlyAvailable') || $request->request->getBoolean('onlyAvailable')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,298 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace VRPaymentPayment\Core\Checkout\Service;
|
||||||
|
|
||||||
|
use Shopware\Core\Checkout\Cart\Cart;
|
||||||
|
use Shopware\Core\Checkout\Cart\LineItemFactoryRegistry;
|
||||||
|
use Shopware\Core\Checkout\Cart\SalesChannel\CartService;
|
||||||
|
use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemCollection;
|
||||||
|
use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity;
|
||||||
|
use Shopware\Core\Checkout\Order\OrderEntity;
|
||||||
|
use Shopware\Core\Framework\Context;
|
||||||
|
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
|
||||||
|
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
|
||||||
|
use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
|
||||||
|
use Shopware\Core\System\SalesChannel\SalesChannelContext;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use VRPaymentPayment\Core\Util\Payload\CustomProducts\CustomProductsLineItemTypes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This service handles the reconstruction of a Shopware cart from an existing order.
|
||||||
|
* It ensures that line items, including those from complex plugins like 'Customized Products',
|
||||||
|
* are correctly re-added to a fresh cart.
|
||||||
|
*/
|
||||||
|
class CartRecoveryService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var CartService
|
||||||
|
* Shopware service for cart-related operations.
|
||||||
|
*/
|
||||||
|
private CartService $cartService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var LineItemFactoryRegistry
|
||||||
|
* Registry to create Shopware LineItems from raw data.
|
||||||
|
*/
|
||||||
|
private LineItemFactoryRegistry $lineItemFactoryRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var EntityRepository
|
||||||
|
* Repository for accessing order data.
|
||||||
|
*/
|
||||||
|
private EntityRepository $orderRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var object|null
|
||||||
|
* Optional route service for adding customized products to the cart.
|
||||||
|
*/
|
||||||
|
private ?object $addCustomizedProductsRoute;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param CartService $cartService
|
||||||
|
* @param LineItemFactoryRegistry $lineItemFactoryRegistry
|
||||||
|
* @param EntityRepository $orderRepository
|
||||||
|
* @param object|null $addCustomizedProductsRoute
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
CartService $cartService,
|
||||||
|
LineItemFactoryRegistry $lineItemFactoryRegistry,
|
||||||
|
EntityRepository $orderRepository,
|
||||||
|
?object $addCustomizedProductsRoute = null
|
||||||
|
) {
|
||||||
|
$this->cartService = $cartService;
|
||||||
|
$this->lineItemFactoryRegistry = $lineItemFactoryRegistry;
|
||||||
|
$this->orderRepository = $orderRepository;
|
||||||
|
$this->addCustomizedProductsRoute = $addCustomizedProductsRoute;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recreates a cart based on the items in an existing order.
|
||||||
|
*
|
||||||
|
* @param OrderEntity $order The source order.
|
||||||
|
* @param SalesChannelContext $salesChannelContext The current sales channel context.
|
||||||
|
* @return Cart The newly created and populated cart.
|
||||||
|
*/
|
||||||
|
public function recreateCartFromOrder(OrderEntity $order, SalesChannelContext $salesChannelContext): Cart
|
||||||
|
{
|
||||||
|
// Start with a clean slate by deleting any existing cart for the current session.
|
||||||
|
$this->cartService->deleteCart($salesChannelContext);
|
||||||
|
$cart = $this->cartService->createNew($salesChannelContext->getToken());
|
||||||
|
|
||||||
|
$orderItems = $order->getLineItems();
|
||||||
|
|
||||||
|
if ($orderItems === null) {
|
||||||
|
return $cart;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special handling for Customized Products if the plugin logic is available.
|
||||||
|
if ($this->hasCustomProducts($orderItems) && $this->addCustomizedProductsRoute) {
|
||||||
|
$cart = $this->addCustomProducts($orderItems, $salesChannelContext, $cart);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var OrderLineItemEntity $orderLineItemEntity */
|
||||||
|
foreach ($orderItems as $orderLineItemEntity) {
|
||||||
|
$type = (string)$orderLineItemEntity->getType();
|
||||||
|
|
||||||
|
// Skip child items and complex types that should have been handled by specialized logic.
|
||||||
|
if ($type !== CustomProductsLineItemTypes::LINE_ITEM_TYPE_PRODUCT || $orderLineItemEntity->getParentId() !== null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a standard product line item.
|
||||||
|
$lineItem = $this->lineItemFactoryRegistry->create([
|
||||||
|
'id' => $orderLineItemEntity->getId(),
|
||||||
|
'quantity' => (int)$orderLineItemEntity->getQuantity(),
|
||||||
|
'referencedId' => (string)$orderLineItemEntity->getReferencedId(),
|
||||||
|
'type' => $type,
|
||||||
|
], $salesChannelContext);
|
||||||
|
|
||||||
|
// Preserve payload data to ensure product options and other metadata are carried over.
|
||||||
|
$lineItemPayload = $orderLineItemEntity->getPayload();
|
||||||
|
if (!empty($lineItemPayload)) {
|
||||||
|
$lineItem->setPayload($lineItemPayload);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the item to the cart.
|
||||||
|
$cart = $this->cartService->add($cart, $lineItem, $salesChannelContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the order contains any line items belonging to the Customized Products plugin.
|
||||||
|
*
|
||||||
|
* @param OrderLineItemCollection $orderItems The items in the order.
|
||||||
|
* @return bool True if customized products are present.
|
||||||
|
*/
|
||||||
|
private function hasCustomProducts(OrderLineItemCollection $orderItems): bool
|
||||||
|
{
|
||||||
|
/** @var OrderLineItemEntity $orderItem */
|
||||||
|
foreach ($orderItems as $orderItem) {
|
||||||
|
if ($orderItem->getType() === CustomProductsLineItemTypes::LINE_ITEM_TYPE_CUSTOMIZED_PRODUCTS) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specialized logic to re-add customized products to the cart via the plugin's own route.
|
||||||
|
*
|
||||||
|
* @param OrderLineItemCollection $orderItems All order items.
|
||||||
|
* @param SalesChannelContext $salesChannelContext Context.
|
||||||
|
* @param Cart $cart Current cart.
|
||||||
|
* @return Cart Updated cart.
|
||||||
|
*/
|
||||||
|
private function addCustomProducts(OrderLineItemCollection $orderItems, SalesChannelContext $salesChannelContext, Cart $cart): Cart
|
||||||
|
{
|
||||||
|
if (!$this->addCustomizedProductsRoute) {
|
||||||
|
return $cart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var OrderLineItemEntity $orderItem */
|
||||||
|
foreach ($orderItems as $orderItem) {
|
||||||
|
if ($orderItem->getType() !== CustomProductsLineItemTypes::LINE_ITEM_TYPE_CUSTOMIZED_PRODUCTS) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the main product associated with this customized product container.
|
||||||
|
$product = $this->getCustomProduct($orderItems, (string)$orderItem->getId());
|
||||||
|
if (!$product) continue;
|
||||||
|
|
||||||
|
// Gather the chosen options and their values.
|
||||||
|
$productOptions = $this->getCustomProductOptions($orderItems, (string)$orderItem->getId());
|
||||||
|
$optionValues = $this->getOptionValues($productOptions);
|
||||||
|
|
||||||
|
// Prepare the data bag for the specialized add-to-cart route.
|
||||||
|
$params = new RequestDataBag([
|
||||||
|
'customized-products-template' => new RequestDataBag([
|
||||||
|
'id' => (string)$orderItem->getReferencedId(),
|
||||||
|
'options' => new RequestDataBag($optionValues),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$request = new Request([], [
|
||||||
|
'lineItems' => [
|
||||||
|
(string)$product->getReferencedId() => [
|
||||||
|
'quantity' => (int)$orderItem->getQuantity(),
|
||||||
|
'id' => (string)$product->getReferencedId(),
|
||||||
|
'type' => CustomProductsLineItemTypes::LINE_ITEM_TYPE_PRODUCT,
|
||||||
|
'referencedId' => (string)$product->getReferencedId(),
|
||||||
|
'stackable' => (bool)$orderItem->getStackable(),
|
||||||
|
'removable' => (bool)$orderItem->getRemovable(),
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Call the Customized Products plugin's internal logic to add the item with its complex configuration.
|
||||||
|
$this->addCustomizedProductsRoute->add($params, $request, $salesChannelContext, $cart);
|
||||||
|
|
||||||
|
// Re-fetch the cart to reflect changes made by the plugin route.
|
||||||
|
$cart = $this->cartService->getCart($salesChannelContext->getToken(), $salesChannelContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the main product item within a customized product structure.
|
||||||
|
*
|
||||||
|
* @param OrderLineItemCollection $orderItems All order items.
|
||||||
|
* @param string $parentId The ID of the customized product container.
|
||||||
|
* @return OrderLineItemEntity|null The product line item entity.
|
||||||
|
*/
|
||||||
|
private function getCustomProduct(OrderLineItemCollection $orderItems, string $parentId): ?OrderLineItemEntity
|
||||||
|
{
|
||||||
|
/** @var OrderLineItemEntity $orderItem */
|
||||||
|
foreach ($orderItems as $orderItem) {
|
||||||
|
if ($orderItem->getType() === CustomProductsLineItemTypes::LINE_ITEM_TYPE_PRODUCT && $orderItem->getParentId() === $parentId) {
|
||||||
|
return $orderItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gathers all option items for a customized product.
|
||||||
|
*
|
||||||
|
* @param OrderLineItemCollection $orderItems All items.
|
||||||
|
* @param string $parentId The ID of the customized product container.
|
||||||
|
* @return OrderLineItemEntity[] List of option entities.
|
||||||
|
*/
|
||||||
|
private function getCustomProductOptions(OrderLineItemCollection $orderItems, string $parentId): array
|
||||||
|
{
|
||||||
|
$options = [];
|
||||||
|
/** @var OrderLineItemEntity $orderItem */
|
||||||
|
foreach ($orderItems as $orderItem) {
|
||||||
|
if ($orderItem->getType() === CustomProductsLineItemTypes::LINE_ITEM_TYPE_CUSTOMIZED_PRODUCTS_OPTION && $orderItem->getParentId() === $parentId) {
|
||||||
|
$options[] = $orderItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a list of options into a data structure suitable for the Customized Products logic.
|
||||||
|
*
|
||||||
|
* @param OrderLineItemEntity[] $productOptions List of option entities.
|
||||||
|
* @return array Formatted option values.
|
||||||
|
*/
|
||||||
|
private function getOptionValues(array $productOptions): array
|
||||||
|
{
|
||||||
|
$optionValues = [];
|
||||||
|
foreach ($productOptions as $productOption) {
|
||||||
|
$payload = (array)$productOption->getPayload();
|
||||||
|
$optionType = (string)($payload['type'] ?? '');
|
||||||
|
|
||||||
|
switch ($optionType) {
|
||||||
|
case CustomProductsLineItemTypes::PRODUCT_OPTION_TYPE_IMAGE_UPLOAD:
|
||||||
|
case CustomProductsLineItemTypes::PRODUCT_OPTION_TYPE_FILE_UPLOAD:
|
||||||
|
$media = (array)($payload['media'] ?? []);
|
||||||
|
foreach ($media as $mediaItem) {
|
||||||
|
$optionValues[(string)$productOption->getReferencedId()] = new RequestDataBag([
|
||||||
|
'media' => new RequestDataBag([
|
||||||
|
(string)$mediaItem['filename'] => new RequestDataBag([
|
||||||
|
'id' => (string)$mediaItem['mediaId'],
|
||||||
|
'filename' => (string)$mediaItem['filename'],
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$optionValues[(string)$productOption->getReferencedId()] = new RequestDataBag([
|
||||||
|
'value' => (string)($payload['value'] ?? ''),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $optionValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches a full order entity with necessary associations for recovery or display.
|
||||||
|
*
|
||||||
|
* @param string $orderId The ID of the order.
|
||||||
|
* @param Context $context The current system context.
|
||||||
|
* @return OrderEntity The order entity.
|
||||||
|
* @throws \Exception If the order is not found.
|
||||||
|
*/
|
||||||
|
public function getOrderEntity(string $orderId, Context $context): OrderEntity
|
||||||
|
{
|
||||||
|
$criteria = (new Criteria([$orderId]))
|
||||||
|
->addAssociation('lineItems.cover')
|
||||||
|
->addAssociation('transactions.paymentMethod')
|
||||||
|
->addAssociation('deliveries.shippingMethod');
|
||||||
|
|
||||||
|
/** @var OrderEntity|null $order */
|
||||||
|
$order = $this->orderRepository->search($criteria, $context)->get($orderId);
|
||||||
|
|
||||||
|
if (!$order) {
|
||||||
|
throw new \Exception('Order not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $order;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace VRPaymentPayment\Core\Checkout\Service;
|
||||||
|
|
||||||
|
use Shopware\Core\System\SalesChannel\SalesChannelContext;
|
||||||
|
use VRPaymentPayment\Core\Api\Transaction\Service\TransactionService;
|
||||||
|
use VRPaymentPayment\Core\Settings\Service\SettingsService;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This service provides methods for retrieving invoice documents associated with WhitelabelMachineName transactions.
|
||||||
|
* It abstracts the API calls needed to fetch invoice data from the WhitelabelMachineName platform.
|
||||||
|
*/
|
||||||
|
class InvoiceService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var SettingsService
|
||||||
|
* The service used to access sales channel specific settings.
|
||||||
|
*/
|
||||||
|
protected SettingsService $settingsService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var TransactionService
|
||||||
|
* The service used to handle transaction-specific operations.
|
||||||
|
*/
|
||||||
|
protected TransactionService $transactionService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param SettingsService $settingsService
|
||||||
|
* @param TransactionService $transactionService
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
SettingsService $settingsService,
|
||||||
|
TransactionService $transactionService
|
||||||
|
) {
|
||||||
|
$this->settingsService = $settingsService;
|
||||||
|
$this->transactionService = $transactionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the invoice document metadata for a given order.
|
||||||
|
*
|
||||||
|
* @param string $orderId The Shopware order ID.
|
||||||
|
* @param SalesChannelContext $salesChannelContext The current context.
|
||||||
|
*
|
||||||
|
* @return object The invoice document metadata (instance of \VRPayment\Sdk\Model\RenderedDocument).
|
||||||
|
*/
|
||||||
|
public function getInvoiceDocument(string $orderId, SalesChannelContext $salesChannelContext): object
|
||||||
|
{
|
||||||
|
// Retrieve valid settings for the current sales channel.
|
||||||
|
$settings = $this->settingsService->getSettings($salesChannelContext->getSalesChannel()->getId());
|
||||||
|
|
||||||
|
// Fetch the local transaction entity associated with the order.
|
||||||
|
$transactionEntity = $this->transactionService->getByOrderId($orderId, $salesChannelContext->getContext());
|
||||||
|
|
||||||
|
// Perform the API call to WhitelabelMachineName to get the invoice document metadata.
|
||||||
|
return $settings->getApiClient()->getTransactionService()->getInvoiceDocument(
|
||||||
|
(int)$settings->getSpaceId(),
|
||||||
|
(int)$transactionEntity->getTransactionId()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace VRPaymentPayment\Core\Checkout\Service;
|
||||||
|
|
||||||
|
use Shopware\Core\Framework\Uuid\Uuid;
|
||||||
|
use Shopware\Core\System\SalesChannel\SalesChannelContext;
|
||||||
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||||
|
use Symfony\Component\Routing\RouterInterface;
|
||||||
|
use VRPaymentPayment\Core\Api\Transaction\Service\TransactionService;
|
||||||
|
use VRPaymentPayment\Core\Settings\Options\Integration;
|
||||||
|
use VRPaymentPayment\Core\Settings\Service\SettingsService;
|
||||||
|
use VRPaymentPayment\Core\Checkout\Struct\PaymentConfigStruct;
|
||||||
|
use VRPaymentPayment\Core\Util\LocaleCodeProvider;
|
||||||
|
use VRPayment\Sdk\Model\TransactionState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This service provides the consolidated payment configuration needed for the frontend.
|
||||||
|
* it handles the generation of JS URLs and provides integration parameters for both Storefront and headless clients.
|
||||||
|
*/
|
||||||
|
class PaymentIntegrationService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var TransactionService
|
||||||
|
* Service to handle transaction-specific API calls.
|
||||||
|
*/
|
||||||
|
private TransactionService $transactionService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var SettingsService
|
||||||
|
* Service to retrieve sales channel specific settings.
|
||||||
|
*/
|
||||||
|
private SettingsService $settingsService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var TransactionManagementService
|
||||||
|
* Service to manage transaction state.
|
||||||
|
*/
|
||||||
|
private TransactionManagementService $transactionManagementService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var RouterInterface
|
||||||
|
* Shopware router for generating callback and redirect URLs.
|
||||||
|
*/
|
||||||
|
private RouterInterface $router;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var LocaleCodeProvider
|
||||||
|
*/
|
||||||
|
private LocaleCodeProvider $localeCodeProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TransactionService $transactionService
|
||||||
|
* @param SettingsService $settingsService
|
||||||
|
* @param TransactionManagementService $transactionManagementService
|
||||||
|
* @param RouterInterface $router
|
||||||
|
* @param LocaleCodeProvider $localeCodeProvider
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
TransactionService $transactionService,
|
||||||
|
SettingsService $settingsService,
|
||||||
|
TransactionManagementService $transactionManagementService,
|
||||||
|
RouterInterface $router,
|
||||||
|
LocaleCodeProvider $localeCodeProvider
|
||||||
|
) {
|
||||||
|
$this->transactionService = $transactionService;
|
||||||
|
$this->settingsService = $settingsService;
|
||||||
|
$this->transactionManagementService = $transactionManagementService;
|
||||||
|
$this->router = $router;
|
||||||
|
$this->localeCodeProvider = $localeCodeProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the payment configuration for a given transaction ID.
|
||||||
|
* This is used on the checkout confirm page before the order is created.
|
||||||
|
*
|
||||||
|
* @param int $transactionId The WhitelabelMachineName transaction ID.
|
||||||
|
* @param SalesChannelContext $salesChannelContext The context.
|
||||||
|
* @return PaymentConfigStruct The consolidated integration data.
|
||||||
|
*/
|
||||||
|
public function getConfigForTransaction(
|
||||||
|
int $transactionId,
|
||||||
|
SalesChannelContext $salesChannelContext
|
||||||
|
): PaymentConfigStruct {
|
||||||
|
$settings = $this->settingsService->getSettings($salesChannelContext->getSalesChannel()->getId());
|
||||||
|
|
||||||
|
// Fetch the transaction details from WhitelabelMachineName API.
|
||||||
|
$vrpaymentTransaction = $settings->getApiClient()->getTransactionService()->read(
|
||||||
|
$settings->getSpaceId(),
|
||||||
|
$transactionId
|
||||||
|
);
|
||||||
|
|
||||||
|
$localeCode = $this->localeCodeProvider->getLocaleCodeFromContext($salesChannelContext->getContext());
|
||||||
|
$paymentPageLocale = $this->localeCodeProvider->mapToPaymentPageLocale($localeCode);
|
||||||
|
$javascriptUrl = $this->getTransactionJavaScriptUrl($settings, $transactionId, $paymentPageLocale);
|
||||||
|
|
||||||
|
$possiblePaymentMethods = $settings->getApiClient()
|
||||||
|
->getTransactionService()
|
||||||
|
->fetchPaymentMethods(
|
||||||
|
$settings->getSpaceId(),
|
||||||
|
$transactionId,
|
||||||
|
$settings->getIntegration()
|
||||||
|
);
|
||||||
|
|
||||||
|
$cartRecreateUrl = $this->router->generate(
|
||||||
|
'frontend.checkout.cart.page',
|
||||||
|
[],
|
||||||
|
UrlGeneratorInterface::ABSOLUTE_URL
|
||||||
|
);
|
||||||
|
|
||||||
|
$checkoutUrl = $this->router->generate(
|
||||||
|
'frontend.checkout.confirm.page',
|
||||||
|
[],
|
||||||
|
UrlGeneratorInterface::ABSOLUTE_URL
|
||||||
|
);
|
||||||
|
|
||||||
|
return (new PaymentConfigStruct())
|
||||||
|
->setIntegration((string)$settings->getIntegration())
|
||||||
|
->setJavascriptUrl($javascriptUrl)
|
||||||
|
->setDeviceJavascriptUrl($this->getDeviceJavascriptUrl((int)$settings->getSpaceId()))
|
||||||
|
->setTransactionPossiblePaymentMethods($possiblePaymentMethods)
|
||||||
|
->setTransactionId($transactionId)
|
||||||
|
->setSpaceId((int)$settings->getSpaceId())
|
||||||
|
->setCartRecreateUrl($cartRecreateUrl)
|
||||||
|
->setCheckoutUrl($checkoutUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the payment configuration for a given order.
|
||||||
|
*
|
||||||
|
* @param string $orderId The Shopware order ID.
|
||||||
|
* @param SalesChannelContext $salesChannelContext The context.
|
||||||
|
* @param string|null $cartRecreateUrl Optional override for the cart recreation URL.
|
||||||
|
* @param string|null $checkoutUrl Optional override for the checkout confirmation URL.
|
||||||
|
* @return PaymentConfigStruct The consolidated integration data.
|
||||||
|
*/
|
||||||
|
public function getPaymentConfig(
|
||||||
|
string $orderId,
|
||||||
|
SalesChannelContext $salesChannelContext,
|
||||||
|
?string $cartRecreateUrl = null,
|
||||||
|
?string $checkoutUrl = null
|
||||||
|
): PaymentConfigStruct {
|
||||||
|
// Retrieve settings and the transaction entity for the order.
|
||||||
|
$settings = $this->settingsService->getSettings($salesChannelContext->getSalesChannel()->getId());
|
||||||
|
$transactionEntity = $this->transactionService->getByOrderId($orderId, $salesChannelContext->getContext());
|
||||||
|
|
||||||
|
// Default to storefront URLs if no overrides are provided.
|
||||||
|
if ($cartRecreateUrl === null) {
|
||||||
|
$cartRecreateUrl = $this->router->generate(
|
||||||
|
'frontend.vrpayment.checkout.recreate-cart',
|
||||||
|
['orderId' => $orderId],
|
||||||
|
UrlGeneratorInterface::ABSOLUTE_URL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($checkoutUrl === null) {
|
||||||
|
$checkoutUrl = $this->router->generate(
|
||||||
|
'frontend.checkout.confirm.page',
|
||||||
|
[],
|
||||||
|
UrlGeneratorInterface::ABSOLUTE_URL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->getConfigForTransaction((int)$transactionEntity->getTransactionId(), $salesChannelContext)
|
||||||
|
->setCartRecreateUrl($cartRecreateUrl)
|
||||||
|
->setCheckoutUrl($checkoutUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the JavaScript URL for the WhitelabelMachineName integration.
|
||||||
|
*
|
||||||
|
* @param mixed $settings The plugin settings.
|
||||||
|
* @param int $transactionId The transaction ID.
|
||||||
|
* @param string $paymentPageLocale The payment page locale.
|
||||||
|
* @return string The absolute URL to the JavaScript component.
|
||||||
|
*/
|
||||||
|
private function getTransactionJavaScriptUrl($settings, int $transactionId, string $paymentPageLocale = ''): string
|
||||||
|
{
|
||||||
|
$javascriptUrl = '';
|
||||||
|
switch ($settings->getIntegration()) {
|
||||||
|
case Integration::IFRAME:
|
||||||
|
$javascriptUrl = $settings->getApiClient()->getTransactionIframeService()
|
||||||
|
->javascriptUrl($settings->getSpaceId(), $transactionId);
|
||||||
|
break;
|
||||||
|
case Integration::LIGHTBOX:
|
||||||
|
$javascriptUrl = $settings->getApiClient()->getTransactionLightboxService()
|
||||||
|
->javascriptUrl($settings->getSpaceId(), $transactionId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($javascriptUrl && $paymentPageLocale) {
|
||||||
|
$separator = str_contains($javascriptUrl, '?') ? '&' : '?';
|
||||||
|
$javascriptUrl .= $separator . 'language=' . $paymentPageLocale;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $javascriptUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the device tracking JavaScript URL.
|
||||||
|
*
|
||||||
|
* @param int $spaceId The WhitelabelMachineName space ID.
|
||||||
|
* @return string The tracking URL.
|
||||||
|
*/
|
||||||
|
private function getDeviceJavascriptUrl(int $spaceId): string
|
||||||
|
{
|
||||||
|
return 'https://gateway.vr-payment.de/s/' . $spaceId . '/payment/device.js?session=' . Uuid::randomHex();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,260 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace VRPaymentPayment\Core\Checkout\Service;
|
||||||
|
|
||||||
|
use Shopware\Core\Checkout\Payment\PaymentMethodCollection;
|
||||||
|
use Shopware\Core\System\SalesChannel\SalesChannelContext;
|
||||||
|
use Shopware\Core\Checkout\Cart\SalesChannel\CartService;
|
||||||
|
use VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Service\PaymentMethodConfigurationService;
|
||||||
|
use VRPaymentPayment\Core\Api\Transaction\Service\TransactionService;
|
||||||
|
use VRPaymentPayment\Core\Checkout\PaymentHandler\VRPaymentPaymentHandler;
|
||||||
|
use VRPaymentPayment\Core\Settings\Service\SettingsService;
|
||||||
|
use VRPaymentPayment\Core\Util\PaymentMethodUtil;
|
||||||
|
use Shopware\Core\Framework\Struct\ArrayEntity;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This service centralizes the logic for filtering WhitelabelMachineName payment methods.
|
||||||
|
* It ensures that only valid and available payment methods are displayed to the customer,
|
||||||
|
* based on the current transaction state and configured settings.
|
||||||
|
*/
|
||||||
|
class PaymentMethodFilterService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var PaymentMethodConfigurationService
|
||||||
|
* Service to handle WhitelabelMachineName payment method configurations.
|
||||||
|
*/
|
||||||
|
private PaymentMethodConfigurationService $paymentMethodConfigurationService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var TransactionService
|
||||||
|
* Service to manage WhitelabelMachineName transactions via API.
|
||||||
|
*/
|
||||||
|
private TransactionService $transactionService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var SettingsService
|
||||||
|
* Service to retrieve plugin settings.
|
||||||
|
*/
|
||||||
|
private SettingsService $settingsService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var PaymentMethodUtil
|
||||||
|
* Utility for payment method operations.
|
||||||
|
*/
|
||||||
|
private PaymentMethodUtil $paymentMethodUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var LoggerInterface
|
||||||
|
*/
|
||||||
|
private LoggerInterface $logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var TransactionManagementService
|
||||||
|
* Service to manage transaction state consistency.
|
||||||
|
*/
|
||||||
|
private TransactionManagementService $transactionManagementService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CartService
|
||||||
|
*/
|
||||||
|
private CartService $cartService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param SettingsService $settingsService
|
||||||
|
* @param TransactionService $transactionService
|
||||||
|
* @param PaymentMethodConfigurationService $paymentMethodConfigurationService
|
||||||
|
* @param PaymentMethodUtil $paymentMethodUtil
|
||||||
|
* @param TransactionManagementService $transactionManagementService
|
||||||
|
* @param CartService $cartService
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
SettingsService $settingsService,
|
||||||
|
TransactionService $transactionService,
|
||||||
|
PaymentMethodConfigurationService $paymentMethodConfigurationService,
|
||||||
|
PaymentMethodUtil $paymentMethodUtil,
|
||||||
|
TransactionManagementService $transactionManagementService,
|
||||||
|
CartService $cartService
|
||||||
|
) {
|
||||||
|
$this->settingsService = $settingsService;
|
||||||
|
$this->transactionService = $transactionService;
|
||||||
|
$this->paymentMethodConfigurationService = $paymentMethodConfigurationService;
|
||||||
|
$this->paymentMethodUtil = $paymentMethodUtil;
|
||||||
|
$this->transactionManagementService = $transactionManagementService;
|
||||||
|
$this->cartService = $cartService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param LoggerInterface $logger
|
||||||
|
*/
|
||||||
|
public function setLogger(LoggerInterface $logger): void
|
||||||
|
{
|
||||||
|
$this->logger = $logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters the given collection of payment methods based on WhitelabelMachineName's availability logic.
|
||||||
|
*
|
||||||
|
* @param PaymentMethodCollection $paymentMethodCollection The initial collection of payment methods.
|
||||||
|
* @param SalesChannelContext $salesChannelContext The current sales channel context.
|
||||||
|
* @param mixed $event Optional event that triggered the filtering.
|
||||||
|
* @return PaymentMethodCollection The filtered collection of payment methods.
|
||||||
|
*/
|
||||||
|
public function filterPaymentMethods(
|
||||||
|
PaymentMethodCollection $paymentMethodCollection,
|
||||||
|
SalesChannelContext $salesChannelContext,
|
||||||
|
$event = null
|
||||||
|
): PaymentMethodCollection {
|
||||||
|
// Fetch valid settings for the current sales channel.
|
||||||
|
$settings = $this->settingsService->getValidSettings($salesChannelContext->getSalesChannel()->getId());
|
||||||
|
|
||||||
|
// If settings are missing, remove all WhitelabelMachineName payment methods to prevent incorrect behavior.
|
||||||
|
if (is_null($settings)) {
|
||||||
|
return $this->removeVRPaymentPaymentMethods($paymentMethodCollection, $salesChannelContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is no customer, we cannot create a transaction or perform API-based filtering.
|
||||||
|
// This typically happens on non-checkout pages like the frontpage footer.
|
||||||
|
if ($salesChannelContext->getCustomer() === null) {
|
||||||
|
return $paymentMethodCollection;
|
||||||
|
}
|
||||||
|
|
||||||
|
$source = $event;
|
||||||
|
if ($source === null) {
|
||||||
|
// In headless (Store API) flow, event is null. We explicitly fetch the cart to get line items.
|
||||||
|
$source = $this->cartService->getCart($salesChannelContext->getToken(), $salesChannelContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure a pending transaction exists in WhitelabelMachineName for correct filtering,
|
||||||
|
// using the transaction management service for state consistency.
|
||||||
|
$createdTransactionId = $this->transactionManagementService->getOrCreatePendingTransaction($salesChannelContext, $source);
|
||||||
|
|
||||||
|
// Update the temporary transaction if customer data has changed.
|
||||||
|
$this->transactionManagementService->updateTempTransactionIfNeeded($salesChannelContext, $createdTransactionId, $source);
|
||||||
|
|
||||||
|
// Fetch available payment method IDs from WhitelabelMachineName API for this transaction.
|
||||||
|
$allowedIds = $this->fetchAvailablePaymentMethodIds($settings, $createdTransactionId, $salesChannelContext);
|
||||||
|
|
||||||
|
// Return a new collection containing only allowed methods.
|
||||||
|
return $this->buildFilteredCollection($paymentMethodCollection, $allowedIds, $settings->getSpaceId(), $salesChannelContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all WhitelabelMachineName-related payment methods from the collection.
|
||||||
|
*
|
||||||
|
* @param PaymentMethodCollection $paymentMethodCollection The collection to clean.
|
||||||
|
* @param SalesChannelContext $salesChannelContext The context.
|
||||||
|
* @return PaymentMethodCollection The cleaned collection.
|
||||||
|
*/
|
||||||
|
private function removeVRPaymentPaymentMethods(
|
||||||
|
PaymentMethodCollection $paymentMethodCollection,
|
||||||
|
SalesChannelContext $salesChannelContext
|
||||||
|
): PaymentMethodCollection {
|
||||||
|
$paymentMethodIds = $this->paymentMethodUtil->getVRPaymentPaymentMethodIds($salesChannelContext->getContext());
|
||||||
|
foreach ($paymentMethodIds as $paymentMethodId) {
|
||||||
|
$paymentMethodCollection->remove($paymentMethodId);
|
||||||
|
}
|
||||||
|
return $paymentMethodCollection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the list of allowed payment method IDs from the WhitelabelMachineName API.
|
||||||
|
*
|
||||||
|
* @param mixed $settings The plugin settings.
|
||||||
|
* @param int $createdTransactionId The WhitelabelMachineName transaction ID.
|
||||||
|
* @param SalesChannelContext $salesChannelContext The context.
|
||||||
|
* @return string[] Array of allowed payment method configuration IDs.
|
||||||
|
*/
|
||||||
|
private function fetchAvailablePaymentMethodIds(
|
||||||
|
$settings,
|
||||||
|
int $createdTransactionId,
|
||||||
|
SalesChannelContext $salesChannelContext
|
||||||
|
): array {
|
||||||
|
$transactionService = $settings->getApiClient()->getTransactionService();
|
||||||
|
$possiblePaymentMethods = $transactionService->fetchPaymentMethods(
|
||||||
|
$settings->getSpaceId(),
|
||||||
|
$createdTransactionId,
|
||||||
|
$settings->getIntegration()
|
||||||
|
);
|
||||||
|
|
||||||
|
$arrayOfPossibleMethods = [];
|
||||||
|
foreach ($possiblePaymentMethods as $possiblePaymentMethod) {
|
||||||
|
$arrayOfPossibleMethods[] = (string) $possiblePaymentMethod->getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the allowed IDs in context extension for later use.
|
||||||
|
$salesChannelContext->getContext()->addExtension(
|
||||||
|
'possibleMethods',
|
||||||
|
new ArrayEntity(['ids' => $arrayOfPossibleMethods])
|
||||||
|
);
|
||||||
|
|
||||||
|
return $arrayOfPossibleMethods;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a filtered PaymentMethodCollection based on allowed IDs.
|
||||||
|
*
|
||||||
|
* Filters the original collection (which already has Shopware's availability rules applied)
|
||||||
|
* to only include WhitelabelMachineName methods that are also allowed by the API.
|
||||||
|
* Non-WhitelabelMachineName methods are kept as-is.
|
||||||
|
*
|
||||||
|
* @param PaymentMethodCollection $paymentMethodCollection Original collection (already rule-filtered by Shopware).
|
||||||
|
* @param string[] $allowedIds List of allowed configuration IDs from the WhitelabelMachineName API.
|
||||||
|
* @param int $spaceId WhitelabelMachineName space ID.
|
||||||
|
* @param SalesChannelContext $salesChannelContext The context.
|
||||||
|
* @return PaymentMethodCollection The final collection.
|
||||||
|
*/
|
||||||
|
private function buildFilteredCollection(
|
||||||
|
PaymentMethodCollection $paymentMethodCollection,
|
||||||
|
array $allowedIds,
|
||||||
|
int $spaceId,
|
||||||
|
SalesChannelContext $salesChannelContext
|
||||||
|
): PaymentMethodCollection {
|
||||||
|
// Fetch all WhitelabelMachineName payment method configurations for the space.
|
||||||
|
$paymentMethodConfigurations = $this->paymentMethodConfigurationService
|
||||||
|
->getAllPaymentMethodConfigurations($spaceId, $salesChannelContext->getContext());
|
||||||
|
|
||||||
|
// Build a map of Shopware payment method ID => configuration for methods allowed by the API.
|
||||||
|
$allowedWLConfigByPmId = [];
|
||||||
|
foreach ($paymentMethodConfigurations as $paymentMethodConfiguration) {
|
||||||
|
if ($paymentMethodConfiguration->getPaymentMethod() === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pmId = $paymentMethodConfiguration->getPaymentMethod()->getId();
|
||||||
|
$pmConfigId = (string) $paymentMethodConfiguration->getPaymentMethodConfigurationId();
|
||||||
|
|
||||||
|
if (
|
||||||
|
$paymentMethodConfiguration->getSpaceId() === $spaceId
|
||||||
|
&& \in_array($pmConfigId, $allowedIds, true)
|
||||||
|
) {
|
||||||
|
$allowedWLConfigByPmId[$pmId] = $paymentMethodConfiguration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter the original collection to preserve Shopware's availability rule filtering.
|
||||||
|
// Non-WLM methods pass through unchanged; WLM methods are kept only if allowed by the API.
|
||||||
|
$collection = new PaymentMethodCollection();
|
||||||
|
foreach ($paymentMethodCollection as $method) {
|
||||||
|
$isVRPaymentPM = VRPaymentPaymentHandler::class === $method->getHandlerIdentifier();
|
||||||
|
|
||||||
|
if (!$isVRPaymentPM) {
|
||||||
|
$collection->add($method);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($allowedWLConfigByPmId[$method->getId()])) {
|
||||||
|
$method->addExtension('vrpayment_config', $allowedWLConfigByPmId[$method->getId()]);
|
||||||
|
$collection->add($method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$collection->sort(function ($a, $b) {
|
||||||
|
return ($a->getPosition() ?? 0) <=> ($b->getPosition() ?? 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
return $collection;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace VRPaymentPayment\Core\Checkout\Service;
|
||||||
|
|
||||||
|
use Psr\Cache\CacheItemPoolInterface;
|
||||||
|
use Shopware\Core\Framework\Struct\ArrayEntity;
|
||||||
|
use Shopware\Core\System\SalesChannel\SalesChannelContext;
|
||||||
|
use VRPaymentPayment\Core\Api\Transaction\Service\TransactionService;
|
||||||
|
use VRPaymentPayment\Core\Settings\Service\SettingsService;
|
||||||
|
use VRPayment\Sdk\Model\TransactionState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This service manages the lifecycle of WhitelabelMachineName transactions and their state within the Shopware context.
|
||||||
|
* It provides methods to retrieve, create, and update transactions while ensuring state consistency.
|
||||||
|
*/
|
||||||
|
class TransactionManagementService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var TransactionService
|
||||||
|
* The service used to interact with the WhitelabelMachineName API for transaction operations.
|
||||||
|
*/
|
||||||
|
private TransactionService $transactionService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var SettingsService
|
||||||
|
* The service used to retrieve configuration settings for the current sales channel.
|
||||||
|
*/
|
||||||
|
private SettingsService $settingsService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CacheItemPoolInterface
|
||||||
|
* Cache for headless transaction persistence
|
||||||
|
*/
|
||||||
|
private CacheItemPoolInterface $cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TransactionService $transactionService
|
||||||
|
* @param SettingsService $settingsService
|
||||||
|
* @param CacheItemPoolInterface $cache
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
TransactionService $transactionService,
|
||||||
|
SettingsService $settingsService,
|
||||||
|
CacheItemPoolInterface $cache
|
||||||
|
) {
|
||||||
|
$this->transactionService = $transactionService;
|
||||||
|
$this->settingsService = $settingsService;
|
||||||
|
$this->cache = $cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves an existing pending transaction ID from the context or creates a new one if necessary.
|
||||||
|
*
|
||||||
|
* @param SalesChannelContext $salesChannelContext The current sales channel context.
|
||||||
|
* @param mixed $event Optional event context.
|
||||||
|
* @return int The WhitelabelMachineName transaction ID.
|
||||||
|
* @throws \Exception If settings are not configured.
|
||||||
|
*/
|
||||||
|
public function getOrCreatePendingTransaction(SalesChannelContext $salesChannelContext, $event = null): int
|
||||||
|
{
|
||||||
|
// Try to get the transaction ID from the current context state.
|
||||||
|
$transactionId = $this->getTransactionIdFromContext($salesChannelContext);
|
||||||
|
$settings = $this->settingsService->getValidSettings($salesChannelContext->getSalesChannel()->getId());
|
||||||
|
|
||||||
|
if (!$settings) {
|
||||||
|
throw new \Exception('Space settings not configured');
|
||||||
|
}
|
||||||
|
|
||||||
|
$expiredTransaction = true;
|
||||||
|
if ($transactionId) {
|
||||||
|
try {
|
||||||
|
// Verify if the transaction still exists and is in a PENDING state.
|
||||||
|
$pendingTransaction = $this->transactionService->read($transactionId, (string)$salesChannelContext->getSalesChannel()->getId());
|
||||||
|
if ($pendingTransaction->getState() === TransactionState::PENDING) {
|
||||||
|
$expiredTransaction = false;
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// If the transaction cannot be read, we treat it as expired or invalid.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new transaction if none exists or the existing one is no longer valid.
|
||||||
|
if (!$transactionId || $expiredTransaction) {
|
||||||
|
$transactionId = (int)$this->transactionService->createPendingTransaction($salesChannelContext, $event);
|
||||||
|
$this->storeTransactionIdInContext($salesChannelContext, $transactionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $transactionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the WhitelabelMachineName transaction if the customer context (address or currency) or line items have changed.
|
||||||
|
*
|
||||||
|
* @param SalesChannelContext $salesChannelContext The current context.
|
||||||
|
* @param int $transactionId The WhitelabelMachineName transaction ID to update.
|
||||||
|
* @param mixed $event The event that triggered this update (optional).
|
||||||
|
*/
|
||||||
|
public function updateTempTransactionIfNeeded(SalesChannelContext $salesChannelContext, int $transactionId, $event = null): void
|
||||||
|
{
|
||||||
|
$ctx = $salesChannelContext->getContext();
|
||||||
|
|
||||||
|
/** @var ArrayEntity|null $ext */
|
||||||
|
$ext = $ctx->getExtension('checkoutState');
|
||||||
|
|
||||||
|
$oldAddressHash = $ext instanceof ArrayEntity ? (string)$ext->get('addressHash') : null;
|
||||||
|
$oldCurrency = $ext instanceof ArrayEntity ? (string)$ext->get('currency') : null;
|
||||||
|
$oldLineItemHash = $ext instanceof ArrayEntity ? (string)$ext->get('lineItemHash') : null;
|
||||||
|
|
||||||
|
$customer = $salesChannelContext->getCustomer();
|
||||||
|
$addressHash = $customer ? md5(json_encode((array) $customer)) : null;
|
||||||
|
$currency = (string)$salesChannelContext->getCurrency()->getIsoCode();
|
||||||
|
|
||||||
|
$lineItems = $this->transactionService->extractLineItems(
|
||||||
|
$event,
|
||||||
|
$salesChannelContext,
|
||||||
|
);
|
||||||
|
$lineItemHash = !empty($lineItems) ? md5(json_encode($lineItems)) : $oldLineItemHash;
|
||||||
|
|
||||||
|
$needsUpdate = ($oldAddressHash !== $addressHash)
|
||||||
|
|| ($oldCurrency !== $currency)
|
||||||
|
|| ($oldLineItemHash !== $lineItemHash);
|
||||||
|
|
||||||
|
if ($needsUpdate) {
|
||||||
|
// Update the transaction in WhitelabelMachineName to reflect current cart and customer data.
|
||||||
|
if ($transactionId) {
|
||||||
|
$this->transactionService->updateTempTransaction($salesChannelContext, $transactionId, $lineItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear payment method cache as options might have changed due to address/currency change.
|
||||||
|
$ctx->addExtension('possibleMethods', new ArrayEntity(['ids' => []]));
|
||||||
|
|
||||||
|
// Persist the new state hash in the context.
|
||||||
|
$ctx->addExtension(
|
||||||
|
'checkoutState',
|
||||||
|
new ArrayEntity([
|
||||||
|
'transactionId' => $transactionId,
|
||||||
|
'addressHash' => $addressHash,
|
||||||
|
'currency' => $currency,
|
||||||
|
'lineItemHash' => $lineItemHash,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the stored WhitelabelMachineName transaction ID from the context, cache, or session.
|
||||||
|
*
|
||||||
|
* @param SalesChannelContext $salesChannelContext The context.
|
||||||
|
* @return int|null The transaction ID if found, otherwise null.
|
||||||
|
*/
|
||||||
|
public function getTransactionIdFromContext(SalesChannelContext $salesChannelContext): ?int
|
||||||
|
{
|
||||||
|
/** @var ArrayEntity|null $ext */
|
||||||
|
$ext = $salesChannelContext->getContext()->getExtension('checkoutState');
|
||||||
|
if ($ext instanceof ArrayEntity && $ext->get('transactionId')) {
|
||||||
|
return (int) $ext->get('transactionId');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to get from cache (headless support).
|
||||||
|
$customer = $salesChannelContext->getCustomer();
|
||||||
|
if ($customer) {
|
||||||
|
$cacheKey = 'vrpn_pending_transaction_id_customer_' . $customer->getId();
|
||||||
|
$item = $this->cache->getItem($cacheKey);
|
||||||
|
if ($item->isHit()) {
|
||||||
|
return (int) $item->get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to PHP session for traditional Storefront compatibility.
|
||||||
|
if (isset($_SESSION['transactionId'])) {
|
||||||
|
return (int) $_SESSION['transactionId'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persists the transaction ID in the context state, cache, and session.
|
||||||
|
*
|
||||||
|
* @param SalesChannelContext $salesChannelContext The context.
|
||||||
|
* @param int $transactionId The transaction ID to store.
|
||||||
|
*/
|
||||||
|
private function storeTransactionIdInContext(SalesChannelContext $salesChannelContext, int $transactionId): void
|
||||||
|
{
|
||||||
|
$ctx = $salesChannelContext->getContext();
|
||||||
|
/** @var ArrayEntity|null $ext */
|
||||||
|
$ext = $ctx->getExtension('checkoutState');
|
||||||
|
|
||||||
|
$data = $ext instanceof ArrayEntity ? $ext->all() : [];
|
||||||
|
$data['transactionId'] = $transactionId;
|
||||||
|
|
||||||
|
// Store in context extension for stateless (headless) support within the request.
|
||||||
|
$ctx->addExtension('checkoutState', new ArrayEntity($data));
|
||||||
|
|
||||||
|
// Store in cache for persistent headless support.
|
||||||
|
$customer = $salesChannelContext->getCustomer();
|
||||||
|
if ($customer) {
|
||||||
|
$cacheKey = 'vrpn_pending_transaction_id_customer_' . $customer->getId();
|
||||||
|
$item = $this->cache->getItem($cacheKey);
|
||||||
|
$item->set($transactionId);
|
||||||
|
$item->expiresAfter(7200);
|
||||||
|
$this->cache->save($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync with PHP session for stateful Storefront support.
|
||||||
|
$_SESSION['transactionId'] = $transactionId;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace VRPaymentPayment\Core\Checkout\StoreApi\Route;
|
||||||
|
|
||||||
|
use Shopware\Core\Framework\Log\Package;
|
||||||
|
use Shopware\Core\System\SalesChannel\SalesChannelContext;
|
||||||
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
use VRPaymentPayment\Core\Api\Transaction\Service\TransactionService;
|
||||||
|
use VRPaymentPayment\Core\Checkout\Service\CartRecoveryService;
|
||||||
|
|
||||||
|
#[Package('checkout')]
|
||||||
|
#[Route(defaults: ['_routeScope' => ['store-api']])]
|
||||||
|
/**
|
||||||
|
* This Store API route allows headless clients to recreate a cart from an existing order.
|
||||||
|
* This is particularly useful for 'Try Again' scenarios if a payment fails.
|
||||||
|
*/
|
||||||
|
class CartRecoveryRoute
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var CartRecoveryService
|
||||||
|
* Service to handle the logic of reconstructing a cart from order items.
|
||||||
|
*/
|
||||||
|
private CartRecoveryService $cartRecoveryService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var TransactionService
|
||||||
|
* Service to clear and manage transactions.
|
||||||
|
*/
|
||||||
|
private TransactionService $transactionService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param CartRecoveryService $cartRecoveryService
|
||||||
|
* @param TransactionService $transactionService
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
CartRecoveryService $cartRecoveryService,
|
||||||
|
TransactionService $transactionService,
|
||||||
|
) {
|
||||||
|
$this->cartRecoveryService = $cartRecoveryService;
|
||||||
|
$this->transactionService = $transactionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recreates a cart based on the provided order ID.
|
||||||
|
*
|
||||||
|
* @param string $orderId The ID of the order to recover the cart from.
|
||||||
|
* @param Request $request The incoming request.
|
||||||
|
* @param SalesChannelContext $context The current sales channel context.
|
||||||
|
* @return JsonResponse A JSON response containing either the new cart data or an error message.
|
||||||
|
*/
|
||||||
|
#[Route(
|
||||||
|
path: '/store-api/vrpayment/checkout/recreate-cart/{orderId}',
|
||||||
|
name: 'store-api.vrpayment.checkout.recreate-cart',
|
||||||
|
methods: ['POST']
|
||||||
|
)]
|
||||||
|
public function recreate(string $orderId, Request $request, SalesChannelContext $context): JsonResponse
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Fetch the order entity.
|
||||||
|
$order = $this->cartRecoveryService->getOrderEntity($orderId, $context->getContext());
|
||||||
|
|
||||||
|
// Security check: ensure the order belongs to the current sales channel.
|
||||||
|
if ($order->getSalesChannelId() !== $context->getSalesChannelId()) {
|
||||||
|
return new JsonResponse(['error' => 'Sales channel mismatch'], 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the transaction ID from cache and session to prevent the subsequent
|
||||||
|
// checkout attempt from reusing a stale/failed transaction.
|
||||||
|
$this->transactionService->clearTransactionIdFromContext($context);
|
||||||
|
|
||||||
|
// Perform the cart reconstruction.
|
||||||
|
$cart = $this->cartRecoveryService->recreateCartFromOrder($order, $context);
|
||||||
|
|
||||||
|
// Return the reconstructed cart data.
|
||||||
|
return new JsonResponse($cart);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Handle any exceptions during the process.
|
||||||
|
return new JsonResponse(['error' => $e->getMessage()], 400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace VRPaymentPayment\Core\Checkout\StoreApi\Route;
|
||||||
|
|
||||||
|
use Shopware\Core\Framework\Log\Package;
|
||||||
|
use Shopware\Core\System\SalesChannel\SalesChannelContext;
|
||||||
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
use VRPaymentPayment\Core\Checkout\Service\InvoiceService;
|
||||||
|
|
||||||
|
#[Package('checkout')]
|
||||||
|
#[Route(defaults: ['_routeScope' => ['store-api']])]
|
||||||
|
/**
|
||||||
|
* This Store API route provides access to invoice documents for headless clients.
|
||||||
|
*/
|
||||||
|
class InvoiceRoute
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var InvoiceService
|
||||||
|
* Service to handle the retrieval of invoice documents from WhitelabelMachineName.
|
||||||
|
*/
|
||||||
|
private InvoiceService $invoiceService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param InvoiceService $invoiceService
|
||||||
|
*/
|
||||||
|
public function __construct(InvoiceService $invoiceService)
|
||||||
|
{
|
||||||
|
$this->invoiceService = $invoiceService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the invoice document metadata and content for a given order.
|
||||||
|
*
|
||||||
|
* @param string $orderId The ID of the order.
|
||||||
|
* @param Request $request The incoming request.
|
||||||
|
* @param SalesChannelContext $context The current sales channel context.
|
||||||
|
* @return JsonResponse A JSON response containing the invoice title, MIME type, and base64-encoded data.
|
||||||
|
*/
|
||||||
|
#[Route(
|
||||||
|
path: '/store-api/vrpayment/account/order/invoice/{orderId}',
|
||||||
|
name: 'store-api.vrpayment.account.order.invoice',
|
||||||
|
methods: ['GET']
|
||||||
|
)]
|
||||||
|
public function load(string $orderId, Request $request, SalesChannelContext $context): JsonResponse
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Retrieve the invoice document via the dedicated service.
|
||||||
|
$invoice = $this->invoiceService->getInvoiceDocument($orderId, $context);
|
||||||
|
|
||||||
|
// Structure the response for headless consumption.
|
||||||
|
return new JsonResponse([
|
||||||
|
'title' => (string)$invoice->getTitle(),
|
||||||
|
'mimeType' => (string)$invoice->getMimeType(),
|
||||||
|
'data' => (string)$invoice->getData(), // Base64 encoded
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Handle errors (e.g., order not found or API failure).
|
||||||
|
return new JsonResponse(['error' => $e->getMessage()], 400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace VRPaymentPayment\Core\Checkout\StoreApi\Route;
|
||||||
|
|
||||||
|
use Shopware\Core\Framework\Log\Package;
|
||||||
|
use Shopware\Core\System\SalesChannel\SalesChannelContext;
|
||||||
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
use VRPaymentPayment\Core\Checkout\Service\PaymentIntegrationService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This route provides transaction-specific configuration for a given order.
|
||||||
|
* It is primarily used by headless clients to initialize the WhitelabelMachineName payment iframe or lightbox.
|
||||||
|
*/
|
||||||
|
#[Package('checkout')]
|
||||||
|
#[Route(defaults: ['_routeScope' => ['store-api']])]
|
||||||
|
class WhitelabelMachineNameTransactionInfoRoute
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var PaymentIntegrationService
|
||||||
|
* The service responsible for generating the unified payment configuration.
|
||||||
|
*/
|
||||||
|
private PaymentIntegrationService $paymentIntegrationService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param PaymentIntegrationService $paymentIntegrationService
|
||||||
|
*/
|
||||||
|
public function __construct(PaymentIntegrationService $paymentIntegrationService)
|
||||||
|
{
|
||||||
|
$this->paymentIntegrationService = $paymentIntegrationService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the payment configuration for a specific order.
|
||||||
|
*
|
||||||
|
* @param string $orderId The Shopware order ID.
|
||||||
|
* @param Request $request The incoming request object.
|
||||||
|
* @param SalesChannelContext $context The current sales channel context.
|
||||||
|
* @return JsonResponse JSON response containing the PaymentConfigStruct data.
|
||||||
|
*/
|
||||||
|
#[Route(
|
||||||
|
path: '/store-api/vrpayment/transaction/info/{orderId}',
|
||||||
|
name: 'store-api.vrpayment.transaction.info',
|
||||||
|
methods: ['GET']
|
||||||
|
)]
|
||||||
|
public function load(string $orderId, Request $request, SalesChannelContext $context): JsonResponse
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Retrieve the payment configuration using the common service.
|
||||||
|
// This ensures logic parity between Storefront and Store API.
|
||||||
|
$config = $this->paymentIntegrationService->getPaymentConfig($orderId, $context);
|
||||||
|
|
||||||
|
// Return the configuration as a JSON response for the headless client.
|
||||||
|
return new JsonResponse($config);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// In case of error, return a 400 response with the error message.
|
||||||
|
return new JsonResponse(['error' => $e->getMessage()], 400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,205 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace VRPaymentPayment\Core\Checkout\Struct;
|
||||||
|
|
||||||
|
use Shopware\Core\Framework\Struct\Struct;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This struct encapsulates all the configuration data required to initialize a WhitelabelMachineName payment integration.
|
||||||
|
*/
|
||||||
|
class PaymentConfigStruct extends Struct
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* The type of integration (e.g., 'iframe' or 'lightbox').
|
||||||
|
*/
|
||||||
|
protected string $integration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* The absolute URL to the WhitelabelMachineName JavaScript component for transaction handling.
|
||||||
|
*/
|
||||||
|
protected string $javascriptUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* The absolute URL to the WhitelabelMachineName JavaScript component for device tracking.
|
||||||
|
*/
|
||||||
|
protected string $deviceJavascriptUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
* A list of possible payment methods for the current transaction.
|
||||||
|
*/
|
||||||
|
protected array $transactionPossiblePaymentMethods;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
* The unique ID of the WhitelabelMachineName transaction.
|
||||||
|
*/
|
||||||
|
protected int $transactionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
* The unique ID of the WhitelabelMachineName space.
|
||||||
|
*/
|
||||||
|
protected int $spaceId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string|null
|
||||||
|
* The URL to redirect to if the cart needs to be recreated (e.g., after an error).
|
||||||
|
*/
|
||||||
|
protected ?string $cartRecreateUrl = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string|null
|
||||||
|
* The URL to the checkout confirmation page.
|
||||||
|
*/
|
||||||
|
protected ?string $checkoutUrl = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getIntegration(): string
|
||||||
|
{
|
||||||
|
return $this->integration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $integration
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setIntegration(string $integration): self
|
||||||
|
{
|
||||||
|
$this->integration = $integration;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getJavascriptUrl(): string
|
||||||
|
{
|
||||||
|
return $this->javascriptUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $javascriptUrl
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setJavascriptUrl(string $javascriptUrl): self
|
||||||
|
{
|
||||||
|
$this->javascriptUrl = $javascriptUrl;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDeviceJavascriptUrl(): string
|
||||||
|
{
|
||||||
|
return $this->deviceJavascriptUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $deviceJavascriptUrl
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setDeviceJavascriptUrl(string $deviceJavascriptUrl): self
|
||||||
|
{
|
||||||
|
$this->deviceJavascriptUrl = $deviceJavascriptUrl;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getTransactionPossiblePaymentMethods(): array
|
||||||
|
{
|
||||||
|
return $this->transactionPossiblePaymentMethods;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $transactionPossiblePaymentMethods
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setTransactionPossiblePaymentMethods(array $transactionPossiblePaymentMethods): self
|
||||||
|
{
|
||||||
|
$this->transactionPossiblePaymentMethods = $transactionPossiblePaymentMethods;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getTransactionId(): int
|
||||||
|
{
|
||||||
|
return $this->transactionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $transactionId
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setTransactionId(int $transactionId): self
|
||||||
|
{
|
||||||
|
$this->transactionId = $transactionId;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getSpaceId(): int
|
||||||
|
{
|
||||||
|
return $this->spaceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $spaceId
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setSpaceId(int $spaceId): self
|
||||||
|
{
|
||||||
|
$this->spaceId = $spaceId;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getCartRecreateUrl(): ?string
|
||||||
|
{
|
||||||
|
return $this->cartRecreateUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $cartRecreateUrl
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setCartRecreateUrl(?string $cartRecreateUrl): self
|
||||||
|
{
|
||||||
|
$this->cartRecreateUrl = $cartRecreateUrl;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getCheckoutUrl(): ?string
|
||||||
|
{
|
||||||
|
return $this->checkoutUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $checkoutUrl
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setCheckoutUrl(?string $checkoutUrl): self
|
||||||
|
{
|
||||||
|
$this->checkoutUrl = $checkoutUrl;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace VRPaymentPayment\Core\Storefront\Account\Controller;
|
namespace VRPaymentPayment\Core\Storefront\Account\Controller;
|
||||||
|
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Shopware\Core\{
|
use Shopware\Core\{
|
||||||
Checkout\Cart\Exception\CustomerNotLoggedInException,
|
Checkout\Cart\CartException,
|
||||||
Checkout\Customer\CustomerEntity,
|
Checkout\Customer\CustomerEntity,
|
||||||
PlatformRequest,
|
PlatformRequest,
|
||||||
System\SalesChannel\SalesChannelContext
|
System\SalesChannel\SalesChannelContext
|
||||||
@@ -20,51 +22,57 @@ use Symfony\Component\{
|
|||||||
};
|
};
|
||||||
use VRPaymentPayment\Core\{
|
use VRPaymentPayment\Core\{
|
||||||
Api\Transaction\Service\TransactionService,
|
Api\Transaction\Service\TransactionService,
|
||||||
Settings\Service\SettingsService
|
Checkout\Service\InvoiceService
|
||||||
};
|
};
|
||||||
|
|
||||||
#[Package('storefront')]
|
#[Package('storefront')]
|
||||||
#[Route(defaults: ['_routeScope' => ['storefront']])]
|
#[Route(defaults: ['_routeScope' => ['storefront']])]
|
||||||
|
/**
|
||||||
|
* This controller handles account-related actions for orders, specifically
|
||||||
|
* allowing customers to download their invoice documents.
|
||||||
|
*/
|
||||||
class AccountOrderController extends StorefrontController
|
class AccountOrderController extends StorefrontController
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var \VRPaymentPayment\Core\Settings\Service\SettingsService
|
* @var LoggerInterface
|
||||||
*/
|
*/
|
||||||
protected $settingsService;
|
protected LoggerInterface $logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Psr\Log\LoggerInterface
|
* @var TransactionService
|
||||||
|
* Local transaction service for order data retrieval.
|
||||||
*/
|
*/
|
||||||
protected $logger;
|
protected TransactionService $transactionService;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService
|
|
||||||
*/
|
|
||||||
protected $transactionService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var RequestStack
|
* @var RequestStack
|
||||||
|
* Symfony service to access the current request context.
|
||||||
*/
|
*/
|
||||||
protected $requestStack;
|
protected RequestStack $requestStack;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AccountOrderController constructor.
|
* @var InvoiceService
|
||||||
* @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService
|
* Service to fetch invoice documents from WhitelabelMachineName.
|
||||||
* @param \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService $transactionService
|
|
||||||
* @param RequestStack $requestStack
|
|
||||||
*/
|
*/
|
||||||
public function __construct(SettingsService $settingsService, TransactionService $transactionService, RequestStack $requestStack)
|
private InvoiceService $invoiceService;
|
||||||
{
|
|
||||||
$this->settingsService = $settingsService;
|
/**
|
||||||
|
* @param TransactionService $transactionService
|
||||||
|
* @param RequestStack $requestStack
|
||||||
|
* @param InvoiceService $invoiceService
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
TransactionService $transactionService,
|
||||||
|
RequestStack $requestStack,
|
||||||
|
InvoiceService $invoiceService
|
||||||
|
) {
|
||||||
$this->transactionService = $transactionService;
|
$this->transactionService = $transactionService;
|
||||||
$this->requestStack = $requestStack;
|
$this->requestStack = $requestStack;
|
||||||
|
$this->invoiceService = $invoiceService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \Psr\Log\LoggerInterface $logger
|
* @param LoggerInterface $logger
|
||||||
* @internal
|
|
||||||
* @required
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public function setLogger(LoggerInterface $logger): void
|
public function setLogger(LoggerInterface $logger): void
|
||||||
{
|
{
|
||||||
@@ -72,59 +80,76 @@ class AccountOrderController extends StorefrontController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Download invoice document
|
* Downloads an invoice document for a specific order.
|
||||||
*
|
*
|
||||||
* @param string $orderId
|
* @param string $orderId The ID of the order.
|
||||||
* @param \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext
|
* @param SalesChannelContext $salesChannelContext The context.
|
||||||
* @return \Symfony\Component\HttpFoundation\Response
|
* @return Response The PDF document as a download response.
|
||||||
*
|
|
||||||
* @throws \VRPayment\Sdk\ApiException
|
|
||||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
|
||||||
* @throws \VRPayment\Sdk\VersioningException
|
|
||||||
*/
|
*/
|
||||||
#[Route("/vrpayment/account/order/download/invoice/document/{orderId}",
|
#[Route(
|
||||||
|
path: "/vrpayment/account/order/download/invoice/document/{orderId}",
|
||||||
name: "frontend.vrpayment.account.order.download.invoice.document",
|
name: "frontend.vrpayment.account.order.download.invoice.document",
|
||||||
methods: ['GET'])]
|
methods: ['GET']
|
||||||
|
)]
|
||||||
public function downloadInvoiceDocument(string $orderId, SalesChannelContext $salesChannelContext): Response
|
public function downloadInvoiceDocument(string $orderId, SalesChannelContext $salesChannelContext): Response
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
|
// Ensure the user is logged in.
|
||||||
$customer = $this->getLoggedInCustomer();
|
$customer = $this->getLoggedInCustomer();
|
||||||
$settings = $this->settingsService->getSettings($salesChannelContext->getSalesChannel()->getId());
|
|
||||||
|
// Fetch the transaction entity to verify ownership.
|
||||||
$transactionEntity = $this->transactionService->getByOrderId($orderId, $salesChannelContext->getContext());
|
$transactionEntity = $this->transactionService->getByOrderId($orderId, $salesChannelContext->getContext());
|
||||||
if (strcasecmp($customer->getCustomerNumber(), $transactionEntity->getData()['customerId']) != 0) {
|
|
||||||
|
// Security check: ensure the document belongs to the logged-in customer.
|
||||||
|
if (strcasecmp((string)$customer->getCustomerNumber(), (string)$transactionEntity->getData()['customerId']) != 0) {
|
||||||
throw new AccessDeniedException();
|
throw new AccessDeniedException();
|
||||||
}
|
}
|
||||||
$invoiceDocument = $settings->getApiClient()->getTransactionService()->getInvoiceDocument($settings->getSpaceId(), $transactionEntity->getTransactionId());
|
|
||||||
$forceDownload = true;
|
// Retrieve the invoice document metadata and content.
|
||||||
$filename = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '_', $invoiceDocument->getTitle()) . '.pdf';
|
/** @var object $invoiceDocument */
|
||||||
|
$invoiceDocument = $this->invoiceService->getInvoiceDocument($orderId, $salesChannelContext);
|
||||||
|
|
||||||
|
// Sanitize the filename for the download.
|
||||||
|
$filename = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '_', (string)$invoiceDocument->getTitle()) . '.pdf';
|
||||||
$disposition = HeaderUtils::makeDisposition(
|
$disposition = HeaderUtils::makeDisposition(
|
||||||
$forceDownload ? HeaderUtils::DISPOSITION_ATTACHMENT : HeaderUtils::DISPOSITION_INLINE,
|
HeaderUtils::DISPOSITION_ATTACHMENT,
|
||||||
$filename,
|
$filename,
|
||||||
$filename
|
$filename
|
||||||
);
|
);
|
||||||
$response = new Response(base64_decode($invoiceDocument->getData()));
|
|
||||||
$response->headers->set('Content-Type', $invoiceDocument->getMimeType());
|
// Create the response with the PDF content (base64 decoded).
|
||||||
|
$response = new Response(base64_decode((string)$invoiceDocument->getData()));
|
||||||
|
$response->headers->set('Content-Type', (string)$invoiceDocument->getMimeType());
|
||||||
$response->headers->set('Content-Disposition', $disposition);
|
$response->headers->set('Content-Disposition', $disposition);
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->logger->error($e->getMessage());
|
||||||
|
return $this->redirectToRoute('frontend.home.page');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Helper to retrieve the currently logged-in customer.
|
||||||
|
*
|
||||||
* @return CustomerEntity
|
* @return CustomerEntity
|
||||||
|
* @throws CartException
|
||||||
*/
|
*/
|
||||||
protected function getLoggedInCustomer(): CustomerEntity
|
protected function getLoggedInCustomer(): CustomerEntity
|
||||||
{
|
{
|
||||||
$request = $this->requestStack->getCurrentRequest();
|
$request = $this->requestStack->getCurrentRequest();
|
||||||
|
|
||||||
if (!$request) {
|
if (!$request) {
|
||||||
throw new CustomerNotLoggedInException();
|
throw CartException::customerNotLoggedIn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @var SalesChannelContext|null $context */
|
||||||
$context = $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT);
|
$context = $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT);
|
||||||
|
|
||||||
if ($context && $context->getCustomer() && $context->getCustomer()->getGuest() === false) {
|
if ($context && $context->getCustomer() && $context->getCustomer()->getGuest() === false) {
|
||||||
return $context->getCustomer();
|
return $context->getCustomer();
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new CustomerNotLoggedInException();
|
throw CartException::customerNotLoggedIn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,18 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace VRPaymentPayment\Core\Storefront\Checkout\Controller;
|
namespace VRPaymentPayment\Core\Storefront\Checkout\Controller;
|
||||||
|
|
||||||
|
use VRPayment\Sdk\Model\TransactionState as SdkTransactionState;
|
||||||
|
use VRPaymentPayment\Core\Api\Transaction\Service\TransactionService;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Shopware\Core\{
|
use Shopware\Core\{
|
||||||
Checkout\Cart\Cart,
|
|
||||||
Checkout\Cart\CartException,
|
Checkout\Cart\CartException,
|
||||||
Checkout\Cart\LineItemFactoryRegistry,
|
|
||||||
Checkout\Cart\SalesChannel\CartService,
|
|
||||||
Checkout\Order\Aggregate\OrderLineItem\OrderLineItemCollection,
|
|
||||||
Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity,
|
|
||||||
Checkout\Order\OrderEntity,
|
|
||||||
Checkout\Order\SalesChannel\AbstractOrderRoute,
|
Checkout\Order\SalesChannel\AbstractOrderRoute,
|
||||||
Framework\Context,
|
|
||||||
Framework\DataAbstractionLayer\Search\Criteria,
|
|
||||||
Framework\DataAbstractionLayer\Search\Filter\EqualsFilter,
|
|
||||||
Framework\DataAbstractionLayer\Search\Sorting\FieldSorting,
|
|
||||||
Framework\Log\Package,
|
Framework\Log\Package,
|
||||||
Framework\Routing\Exception\MissingRequestParameterException,
|
Framework\Routing\RoutingException,
|
||||||
Framework\Uuid\Uuid,
|
Framework\DataAbstractionLayer\Search\Criteria,
|
||||||
Framework\Uuid\Exception\InvalidUuidException,
|
|
||||||
Framework\Validation\DataBag\RequestDataBag,
|
|
||||||
System\SalesChannel\SalesChannelContext
|
System\SalesChannel\SalesChannelContext
|
||||||
};
|
};
|
||||||
use Shopware\Storefront\{
|
use Shopware\Storefront\{
|
||||||
@@ -31,105 +23,90 @@ use Shopware\Storefront\{
|
|||||||
use Symfony\Component\{
|
use Symfony\Component\{
|
||||||
HttpFoundation\Request,
|
HttpFoundation\Request,
|
||||||
HttpFoundation\Response,
|
HttpFoundation\Response,
|
||||||
Routing\Attribute\Route,
|
Routing\Attribute\Route
|
||||||
Routing\Generator\UrlGeneratorInterface
|
|
||||||
};
|
|
||||||
use VRPayment\Sdk\{
|
|
||||||
Model\Transaction,
|
|
||||||
Model\TransactionState
|
|
||||||
};
|
};
|
||||||
use VRPaymentPayment\Core\{
|
use VRPaymentPayment\Core\{
|
||||||
Api\Transaction\Service\TransactionService,
|
Checkout\Service\CartRecoveryService,
|
||||||
Settings\Options\Integration,
|
Checkout\Service\PaymentIntegrationService,
|
||||||
Settings\Service\SettingsService,
|
Settings\Service\SettingsService
|
||||||
Storefront\Checkout\Struct\CheckoutPageData,
|
|
||||||
Util\Payload\CustomProducts\CustomProductsLineItemTypes
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class CheckoutController
|
|
||||||
*
|
|
||||||
* @package VRPaymentPayment\Core\Storefront\Checkout\Controller
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#[Package('checkout')]
|
#[Package('checkout')]
|
||||||
#[Route(defaults: ['_routeScope' => ['storefront']])]
|
#[Route(defaults: ['_routeScope' => ['storefront']])]
|
||||||
class CheckoutController extends StorefrontController {
|
/**
|
||||||
|
* This controller handles Storefront-specific actions for the WhitelabelMachineName integration,
|
||||||
/**
|
* such as rendering the payment page and recreating a cart from a failed order.
|
||||||
* @var \Shopware\Storefront\Page\GenericPageLoader
|
|
||||||
*/
|
*/
|
||||||
protected $genericLoader;
|
class CheckoutController extends StorefrontController
|
||||||
|
{
|
||||||
/**
|
/**
|
||||||
* @var \Shopware\Core\Checkout\Cart\SalesChannel\CartService
|
* @var GenericPageLoaderInterface
|
||||||
|
* Loader for basic Shopware page data.
|
||||||
*/
|
*/
|
||||||
protected $cartService;
|
protected GenericPageLoaderInterface $genericLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \VRPaymentPayment\Core\Settings\Service\SettingsService
|
* @var SettingsService
|
||||||
|
* Plugin settings service.
|
||||||
*/
|
*/
|
||||||
protected $settingsService;
|
protected SettingsService $settingsService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \VRPaymentPayment\Core\Settings\Struct\Settings
|
* @var LoggerInterface|null
|
||||||
|
* Logger for recording errors and important information.
|
||||||
*/
|
*/
|
||||||
protected $settings;
|
private ?LoggerInterface $logger = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService
|
* @var AbstractOrderRoute
|
||||||
|
* Shopware service for order retrieval.
|
||||||
*/
|
*/
|
||||||
protected $transactionService;
|
private AbstractOrderRoute $orderRoute;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Psr\Log\LoggerInterface
|
* @var CartRecoveryService
|
||||||
|
* Service to help customers recover their cart from a past order.
|
||||||
*/
|
*/
|
||||||
private $logger;
|
private CartRecoveryService $cartRecoveryService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Shopware\Core\Checkout\Cart\LineItemFactoryRegistry
|
* @var PaymentIntegrationService
|
||||||
|
* Service to provide the integration parameters (JS URL, transaction ID, etc.).
|
||||||
*/
|
*/
|
||||||
private $lineItemFactoryRegistry;
|
private PaymentIntegrationService $paymentIntegrationService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Shopware\Core\Checkout\Order\SalesChannel\AbstractOrderRoute
|
* @var TransactionService
|
||||||
|
* Service to check transaction details.
|
||||||
*/
|
*/
|
||||||
private $orderRoute;
|
private TransactionService $transactionService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PaymentController constructor.
|
* @param SettingsService $settingsService
|
||||||
*
|
* @param GenericPageLoaderInterface $genericLoader
|
||||||
* @param \Shopware\Core\Checkout\Cart\LineItemFactoryRegistry $lineItemFactoryRegistry
|
* @param AbstractOrderRoute $orderRoute
|
||||||
* @param \Shopware\Core\Checkout\Cart\SalesChannel\CartService $cartService
|
* @param CartRecoveryService $cartRecoveryService
|
||||||
* @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService
|
* @param PaymentIntegrationService $paymentIntegrationService
|
||||||
* @param \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService $transactionService
|
|
||||||
* @param \Shopware\Storefront\Page\GenericPageLoaderInterface $genericLoader
|
|
||||||
* @param \Shopware\Core\Checkout\Order\SalesChannel\AbstractOrderRoute $orderRoute
|
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
LineItemFactoryRegistry $lineItemFactoryRegistry,
|
|
||||||
CartService $cartService,
|
|
||||||
SettingsService $settingsService,
|
SettingsService $settingsService,
|
||||||
TransactionService $transactionService,
|
|
||||||
GenericPageLoaderInterface $genericLoader,
|
GenericPageLoaderInterface $genericLoader,
|
||||||
AbstractOrderRoute $orderRoute
|
AbstractOrderRoute $orderRoute,
|
||||||
)
|
CartRecoveryService $cartRecoveryService,
|
||||||
{
|
PaymentIntegrationService $paymentIntegrationService,
|
||||||
$this->cartService = $cartService;
|
TransactionService $transactionService
|
||||||
|
) {
|
||||||
$this->genericLoader = $genericLoader;
|
$this->genericLoader = $genericLoader;
|
||||||
$this->settingsService = $settingsService;
|
$this->settingsService = $settingsService;
|
||||||
$this->transactionService = $transactionService;
|
|
||||||
$this->lineItemFactoryRegistry = $lineItemFactoryRegistry;
|
|
||||||
$this->orderRoute = $orderRoute;
|
$this->orderRoute = $orderRoute;
|
||||||
|
$this->cartRecoveryService = $cartRecoveryService;
|
||||||
|
$this->paymentIntegrationService = $paymentIntegrationService;
|
||||||
|
$this->transactionService = $transactionService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \Psr\Log\LoggerInterface $logger
|
* @param LoggerInterface $logger
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
* @required
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public function setLogger(LoggerInterface $logger): void
|
public function setLogger(LoggerInterface $logger): void
|
||||||
{
|
{
|
||||||
@@ -137,14 +114,11 @@ class CheckoutController extends StorefrontController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext
|
* Renders the WhitelabelMachineName payment page (usually contains the iframe or lightbox script).
|
||||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
|
||||||
*
|
|
||||||
* @return \Symfony\Component\HttpFoundation\Response
|
|
||||||
* @throws \VRPayment\Sdk\ApiException
|
|
||||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
|
||||||
* @throws \VRPayment\Sdk\VersioningException
|
|
||||||
*
|
*
|
||||||
|
* @param SalesChannelContext $salesChannelContext The current context.
|
||||||
|
* @param Request $request The incoming request.
|
||||||
|
* @return Response The rendered payment page.
|
||||||
*/
|
*/
|
||||||
#[Route(
|
#[Route(
|
||||||
path: "/vrpayment/checkout/pay",
|
path: "/vrpayment/checkout/pay",
|
||||||
@@ -154,191 +128,60 @@ class CheckoutController extends StorefrontController {
|
|||||||
)]
|
)]
|
||||||
public function pay(SalesChannelContext $salesChannelContext, Request $request): Response
|
public function pay(SalesChannelContext $salesChannelContext, Request $request): Response
|
||||||
{
|
{
|
||||||
$orderId = $request->query->get('orderId');
|
$orderId = (string)$request->query->get('orderId');
|
||||||
|
|
||||||
if (empty($orderId)) {
|
if (empty($orderId)) {
|
||||||
throw new MissingRequestParameterException('orderId');
|
throw RoutingException::missingRequestParameter('orderId');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configuration
|
try {
|
||||||
$this->settings = $this->settingsService->getSettings($salesChannelContext->getSalesChannel()->getId());
|
// Load the order with necessary associations for the product table and addresses.
|
||||||
|
$criteria = new Criteria([$orderId]);
|
||||||
|
$criteria->addAssociation('lineItems.product')
|
||||||
|
->addAssociation('deliveries.shippingOrderAddress.country')
|
||||||
|
->addAssociation('orderCustomer.customer')
|
||||||
|
->addAssociation('transactions.paymentMethod');
|
||||||
|
|
||||||
$transaction = $this->getTransaction($orderId, $salesChannelContext->getContext());
|
$order = $this->orderRoute->load(new Request(), $salesChannelContext, $criteria)->getOrders()->first();
|
||||||
$recreateCartUrl = $this->generateUrl(
|
|
||||||
'frontend.vrpayment.checkout.recreate-cart',
|
|
||||||
['orderId' => $orderId,],
|
|
||||||
UrlGeneratorInterface::ABSOLUTE_URL
|
|
||||||
);
|
|
||||||
|
|
||||||
if (in_array(
|
if (!$order) {
|
||||||
$transaction->getState(),
|
throw RoutingException::missingRequestParameter('orderId');
|
||||||
[
|
|
||||||
TransactionState::AUTHORIZED,
|
|
||||||
TransactionState::COMPLETED,
|
|
||||||
TransactionState::FULFILL,
|
|
||||||
]
|
|
||||||
)) {
|
|
||||||
return $this->redirect($transaction->getSuccessUrl(), Response::HTTP_MOVED_PERMANENTLY);
|
|
||||||
} else {
|
|
||||||
if (in_array(
|
|
||||||
$transaction->getState(),
|
|
||||||
[
|
|
||||||
TransactionState::DECLINE,
|
|
||||||
TransactionState::FAILED,
|
|
||||||
TransactionState::VOIDED,
|
|
||||||
]
|
|
||||||
)) {
|
|
||||||
return $this->redirect($transaction->getFailedUrl(), Response::HTTP_MOVED_PERMANENTLY);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$possiblePaymentMethods = $this->settings->getApiClient()
|
// Fetch the configuration required for the frontend integration.
|
||||||
->getTransactionService()
|
$paymentConfig = $this->paymentIntegrationService->getPaymentConfig($orderId, $salesChannelContext);
|
||||||
->fetchPaymentMethods(
|
|
||||||
$this->settings->getSpaceId(),
|
|
||||||
$transaction->getId(),
|
|
||||||
$this->settings->getIntegration()
|
|
||||||
);
|
|
||||||
|
|
||||||
if (empty($possiblePaymentMethods)) {
|
// Load a generic Shopware page to have layout headers/footers.
|
||||||
$this->addFlash('danger', $this->trans('vrpayment.paymentMethod.notAvailable'));
|
$page = $this->genericLoader->load($request, $salesChannelContext);
|
||||||
return $this->redirect($recreateCartUrl, Response::HTTP_MOVED_PERMANENTLY);
|
$page->addExtension('vRPaymentData', $paymentConfig);
|
||||||
}
|
|
||||||
|
|
||||||
$javascriptUrl = $this->getTransactionJavaScriptUrl($transaction->getId());
|
// Assign the order to the page so the templates can access page.order.
|
||||||
|
$page->assign(['order' => $order]);
|
||||||
// Set Checkout Page Data
|
|
||||||
$checkoutPageData = (new CheckoutPageData())
|
|
||||||
->setIntegration($this->settings->getIntegration())
|
|
||||||
->setJavascriptUrl($javascriptUrl)
|
|
||||||
->setDeviceJavascriptUrl($this->settings->getSpaceId(), Uuid::randomHex())
|
|
||||||
->setTransactionPossiblePaymentMethods($possiblePaymentMethods)
|
|
||||||
->setCheckoutUrl($this->generateUrl(
|
|
||||||
'frontend.vrpayment.checkout.pay',
|
|
||||||
['orderId' => $orderId,],
|
|
||||||
UrlGeneratorInterface::ABSOLUTE_URL
|
|
||||||
))
|
|
||||||
->setCartRecreateUrl($recreateCartUrl);
|
|
||||||
$page = $this->load($request, $salesChannelContext);
|
|
||||||
$page->addExtension('vRPaymentData', $checkoutPageData);
|
|
||||||
|
|
||||||
|
// Render the specialized Twig template for WhitelabelMachineName.
|
||||||
return $this->renderStorefront(
|
return $this->renderStorefront(
|
||||||
'@VRPaymentPayment/storefront/page/checkout/order/vrpayment.html.twig',
|
'@VRPaymentPayment/storefront/page/checkout/order/vrpayment.html.twig',
|
||||||
['page' => $page]
|
['page' => $page]
|
||||||
);
|
);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
if ($this->logger) {
|
||||||
|
$this->logger->error($e->getMessage());
|
||||||
|
}
|
||||||
|
// Clear the transaction ID from the cache/session context on error to ensure
|
||||||
|
// subsequent payment attempts will create/retrieve a clean transaction.
|
||||||
|
$this->transactionService->clearTransactionIdFromContext($salesChannelContext);
|
||||||
|
$this->addFlash('danger', $this->trans('vrpayment.paymentMethod.notAvailable'));
|
||||||
|
return $this->redirectToRoute('frontend.home.page');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get transaction Javascript URL
|
* Redirects the user to a route that recreates their cart from an existing order.
|
||||||
*
|
* This is useful for allowing users to try payment again with different details.
|
||||||
* @param int $transactionId
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
* @throws \VRPayment\Sdk\ApiException
|
|
||||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
|
||||||
* @throws \VRPayment\Sdk\VersioningException
|
|
||||||
*/
|
|
||||||
private function getTransactionJavaScriptUrl(int $transactionId): string
|
|
||||||
{
|
|
||||||
$javascriptUrl = '';
|
|
||||||
switch ($this->settings->getIntegration()) {
|
|
||||||
case Integration::IFRAME:
|
|
||||||
$javascriptUrl = $this->settings->getApiClient()->getTransactionIframeService()
|
|
||||||
->javascriptUrl($this->settings->getSpaceId(), $transactionId);
|
|
||||||
break;
|
|
||||||
case Integration::LIGHTBOX:
|
|
||||||
$javascriptUrl = $this->settings->getApiClient()->getTransactionLightboxService()
|
|
||||||
->javascriptUrl($this->settings->getSpaceId(), $transactionId);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$this->logger->critical(strtr('invalid integration : :integration', [':integration' => $this->settings->getIntegration()]));
|
|
||||||
|
|
||||||
}
|
|
||||||
return $javascriptUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $orderId
|
|
||||||
* @param \Shopware\Core\Framework\Context $context
|
|
||||||
*
|
|
||||||
* @return \VRPayment\Sdk\Model\Transaction
|
|
||||||
* @throws \VRPayment\Sdk\ApiException
|
|
||||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
|
||||||
* @throws \VRPayment\Sdk\VersioningException
|
|
||||||
*/
|
|
||||||
private function getTransaction($orderId, Context $context): Transaction
|
|
||||||
{
|
|
||||||
$transactionEntity = $this->transactionService->getByOrderId($orderId, $context);
|
|
||||||
return $this->settings->getApiClient()->getTransactionService()->read($this->settings->getSpaceId(), $transactionEntity->getTransactionId());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
|
||||||
* @param \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext
|
|
||||||
*
|
|
||||||
* @return \Shopware\Storefront\Page\Checkout\Finish\CheckoutFinishPage
|
|
||||||
*/
|
|
||||||
protected function load(Request $request, SalesChannelContext $salesChannelContext): CheckoutFinishPage
|
|
||||||
{
|
|
||||||
$page = CheckoutFinishPage::createFrom($this->genericLoader->load($request, $salesChannelContext));
|
|
||||||
$page->setOrder($this->getOrder($request, $salesChannelContext));
|
|
||||||
|
|
||||||
return $page;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
|
||||||
* @param \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext
|
|
||||||
*
|
|
||||||
* @return \Shopware\Core\Checkout\Order\OrderEntity
|
|
||||||
*/
|
|
||||||
private function getOrder(Request $request, SalesChannelContext $salesChannelContext): OrderEntity
|
|
||||||
{
|
|
||||||
|
|
||||||
$orderId = $request->get('orderId');
|
|
||||||
if (!$orderId) {
|
|
||||||
throw new MissingRequestParameterException('orderId', '/orderId');
|
|
||||||
}
|
|
||||||
|
|
||||||
$criteria = (new Criteria([$orderId]))
|
|
||||||
->addAssociation('lineItems.cover')
|
|
||||||
->addAssociation('transactions.paymentMethod')
|
|
||||||
->addAssociation('deliveries.shippingMethod');
|
|
||||||
|
|
||||||
$customer = $salesChannelContext->getCustomer();
|
|
||||||
if ($customer !== null) {
|
|
||||||
$criteria = $criteria->addFilter(new EqualsFilter('order.orderCustomer.customerId', $customer->getId()));
|
|
||||||
}
|
|
||||||
|
|
||||||
$criteria->getAssociation('transactions')->addSorting(new FieldSorting('createdAt'));
|
|
||||||
|
|
||||||
try {
|
|
||||||
$searchResult = $this->orderRoute
|
|
||||||
->load(new Request(), $salesChannelContext, $criteria)
|
|
||||||
->getOrders();
|
|
||||||
} catch (InvalidUuidException $e) {
|
|
||||||
throw CartException::orderNotFound($orderId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var OrderEntity|null $order */
|
|
||||||
$order = $searchResult->get($orderId);
|
|
||||||
|
|
||||||
if (!$order) {
|
|
||||||
throw CartException::orderNotFound($orderId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $order;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recreate Cart
|
|
||||||
*
|
|
||||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
|
||||||
* @param \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext
|
|
||||||
*
|
|
||||||
* @return \Symfony\Component\HttpFoundation\Response
|
|
||||||
*
|
*
|
||||||
|
* @param Request $request The incoming request.
|
||||||
|
* @param SalesChannelContext $salesChannelContext The context.
|
||||||
|
* @return Response Redirect to the checkout confirmation page.
|
||||||
*/
|
*/
|
||||||
#[Route(
|
#[Route(
|
||||||
path: "/vrpayment/checkout/recreate-cart",
|
path: "/vrpayment/checkout/recreate-cart",
|
||||||
@@ -346,223 +189,53 @@ class CheckoutController extends StorefrontController {
|
|||||||
options: ["seo" => false],
|
options: ["seo" => false],
|
||||||
methods: ["GET"],
|
methods: ["GET"],
|
||||||
)]
|
)]
|
||||||
public function recreateCart(Request $request, SalesChannelContext $salesChannelContext)
|
public function recreateCart(Request $request, SalesChannelContext $salesChannelContext): Response
|
||||||
{
|
{
|
||||||
$orderId = $request->query->get('orderId');
|
$orderId = (string)$request->query->get('orderId');
|
||||||
|
|
||||||
if (empty($orderId)) {
|
if (empty($orderId)) {
|
||||||
throw new MissingRequestParameterException('orderId');
|
throw RoutingException::missingRequestParameter('orderId');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adoption for Headless Storefronts
|
|
||||||
$orderRepo = $this->container->get('order.repository');
|
|
||||||
$criteria = new Criteria([$orderId]);
|
|
||||||
|
|
||||||
$orderEntity = $orderRepo->search($criteria, $salesChannelContext->getContext())->first();
|
|
||||||
|
|
||||||
if($orderEntity->getSalesChannelId() !== $salesChannelContext->getSalesChannelId()) {
|
|
||||||
$this->settings = $this->settingsService->getSettings($orderEntity->getSalesChannelId());
|
|
||||||
$trans = $this->getTransaction($orderId, $salesChannelContext->getContext());
|
|
||||||
return $this->redirect($trans->getSuccessUrl());
|
|
||||||
}
|
|
||||||
// End Adoption for Headless Storefronts
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->cartService->deleteCart($salesChannelContext);
|
// Find the order that should be recovered.
|
||||||
$cart = $this->cartService->createNew($salesChannelContext->getToken());
|
$order = $this->cartRecoveryService->getOrderEntity($orderId, $salesChannelContext->getContext());
|
||||||
|
|
||||||
// Configuration
|
// Security: Order must belong to the active sales channel.
|
||||||
$this->settings = $this->settingsService->getSettings($salesChannelContext->getSalesChannel()->getId());
|
if ($order->getSalesChannelId() !== $salesChannelContext->getSalesChannelId()) {
|
||||||
$orderEntity = $this->getOrder($request, $salesChannelContext);
|
|
||||||
$lastTransaction = $orderEntity->getTransactions()->last();
|
|
||||||
if ($lastTransaction && !$lastTransaction->getPaymentMethod()->getAfterOrderEnabled()) {
|
|
||||||
return $this->redirectToRoute('frontend.home.page');
|
return $this->redirectToRoute('frontend.home.page');
|
||||||
}
|
}
|
||||||
|
|
||||||
$transaction = $this->getTransaction($orderId, $salesChannelContext->getContext());
|
// Perform the recovery process.
|
||||||
if (!empty($transaction->getUserFailureMessage())) {
|
$transactionEntity = $this->transactionService->getByOrderId($order->getId(), $salesChannelContext->getContext());
|
||||||
|
if ($transactionEntity) {
|
||||||
|
$transaction = $this->transactionService->read($transactionEntity->getTransactionId(), $salesChannelContext->getSalesChannelId());
|
||||||
|
if (in_array($transaction->getState(), [
|
||||||
|
SdkTransactionState::AUTHORIZED,
|
||||||
|
SdkTransactionState::CONFIRMED,
|
||||||
|
SdkTransactionState::FULFILL
|
||||||
|
])) {
|
||||||
|
return $this->redirectToRoute('frontend.checkout.finish.page', ['orderId' => $orderId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($transaction->getUserFailureMessage()) {
|
||||||
$this->addFlash('danger', $transaction->getUserFailureMessage());
|
$this->addFlash('danger', $transaction->getUserFailureMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
$orderItems = $orderEntity->getLineItems();
|
|
||||||
$hasCustomProducts = $this->hasCustomProducts($orderItems);
|
|
||||||
|
|
||||||
if ($hasCustomProducts === true) {
|
|
||||||
$cart = $this->addCustomProducts($orderItems, $request, $salesChannelContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($orderItems as $orderLineItemEntity) {
|
|
||||||
$type = $orderLineItemEntity->getType();
|
|
||||||
|
|
||||||
if ($type !== CustomProductsLineItemTypes::LINE_ITEM_TYPE_PRODUCT || $orderLineItemEntity->getParentid() !== null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$lineItem = $this->lineItemFactoryRegistry->create([
|
|
||||||
'id' => $orderLineItemEntity->getId(),
|
|
||||||
'quantity' => $orderLineItemEntity->getQuantity(),
|
|
||||||
'referencedId' => $orderLineItemEntity->getReferencedId(),
|
|
||||||
'type' => $type,
|
|
||||||
], $salesChannelContext);
|
|
||||||
|
|
||||||
$lineItemPayload = $orderLineItemEntity->getPayload();
|
|
||||||
if (!empty($lineItemPayload)) {
|
|
||||||
$lineItem->setPayload($lineItemPayload);
|
|
||||||
}
|
|
||||||
|
|
||||||
$cart = $this->cartService->add($cart, $lineItem, $salesChannelContext);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// Clear the transaction ID from cache and session to prevent the subsequent
|
||||||
|
// checkout attempt from reusing a stale/failed transaction.
|
||||||
|
$this->transactionService->clearTransactionIdFromContext($salesChannelContext);
|
||||||
|
|
||||||
|
$this->cartRecoveryService->recreateCartFromOrder($order, $salesChannelContext);
|
||||||
} catch (\Exception $exception) {
|
} catch (\Exception $exception) {
|
||||||
$this->addFlash('danger', $this->trans('error.addToCartError'));
|
$this->addFlash('danger', $this->trans('error.addToCartError'));
|
||||||
|
if ($this->logger) {
|
||||||
$this->logger->critical($exception->getMessage());
|
$this->logger->critical($exception->getMessage());
|
||||||
|
}
|
||||||
return $this->redirectToRoute('frontend.home.page');
|
return $this->redirectToRoute('frontend.home.page');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send the user back to the checkout confirm page with their items restored.
|
||||||
return $this->redirectToRoute('frontend.checkout.confirm.page');
|
return $this->redirectToRoute('frontend.checkout.confirm.page');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param OrderLineItemCollection $orderItems
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function hasCustomProducts(OrderLineItemCollection $orderItems): bool
|
|
||||||
{
|
|
||||||
foreach ($orderItems as $orderItem) {
|
|
||||||
if ($orderItem->getType() === CustomProductsLineItemTypes::LINE_ITEM_TYPE_CUSTOMIZED_PRODUCTS) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param OrderLineItemCollection $orderItems
|
|
||||||
* @param string $parentId
|
|
||||||
*
|
|
||||||
* @return OrderLineItemEntity|null
|
|
||||||
*/
|
|
||||||
private function getCustomProduct(OrderLineItemCollection $orderItems, string $parentId): ?OrderLineItemEntity
|
|
||||||
{
|
|
||||||
foreach ($orderItems as $orderItem) {
|
|
||||||
if ($orderItem->getType() === CustomProductsLineItemTypes::LINE_ITEM_TYPE_PRODUCT && $orderItem->getParentId() === $parentId) {
|
|
||||||
return $orderItem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param OrderLineItemCollection $orderItems
|
|
||||||
* @param string $parentId
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getCustomProductOptions(OrderLineItemCollection $orderItems, string $parentId): array
|
|
||||||
{
|
|
||||||
$options = [];
|
|
||||||
foreach ($orderItems as $orderItem) {
|
|
||||||
if ($orderItem->getType() === CustomProductsLineItemTypes::LINE_ITEM_TYPE_CUSTOMIZED_PRODUCTS_OPTION && $orderItem->getParentId() === $parentId) {
|
|
||||||
$options[] = $orderItem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $options;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $orderItems
|
|
||||||
* @param $request
|
|
||||||
* @param $salesChannelContext
|
|
||||||
*
|
|
||||||
* @return Cart
|
|
||||||
*/
|
|
||||||
private function addCustomProducts(OrderLineItemCollection $orderItems, Request $request, SalesChannelContext $salesChannelContext): Cart
|
|
||||||
{
|
|
||||||
|
|
||||||
$cart = $this->cartService->getCart($salesChannelContext->getToken(), $salesChannelContext);
|
|
||||||
if (!\class_exists('Swag\\CustomizedProducts\\Core\\Checkout\\Cart\\Route\\AddCustomizedProductsToCartRoute')) {
|
|
||||||
return $cart;
|
|
||||||
}
|
|
||||||
|
|
||||||
$customProductsService = $this->get('Swag\CustomizedProducts\Core\Checkout\Cart\Route\AddCustomizedProductsToCartRoute');
|
|
||||||
|
|
||||||
foreach ($orderItems as $orderItem) {
|
|
||||||
if ($orderItem->getType() !== CustomProductsLineItemTypes::LINE_ITEM_TYPE_CUSTOMIZED_PRODUCTS) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$product = $this->getCustomProduct($orderItems, $orderItem->getId());
|
|
||||||
$productOptions = $this->getCustomProductOptions($orderItems, $orderItem->getId());
|
|
||||||
$optionValues = $this->getOptionValues($productOptions);
|
|
||||||
|
|
||||||
$params = new RequestDataBag([
|
|
||||||
'customized-products-template' => new RequestDataBag([
|
|
||||||
'id' => $orderItem->getReferencedId(),
|
|
||||||
'options' => new RequestDataBag($optionValues),
|
|
||||||
]),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$request->request->add(
|
|
||||||
[
|
|
||||||
'lineItems' =>
|
|
||||||
[
|
|
||||||
$product->getProductId() =>
|
|
||||||
[
|
|
||||||
'quantity' => $orderItem->getQuantity(),
|
|
||||||
'id' => $product->getProductId(),
|
|
||||||
'type' => CustomProductsLineItemTypes::LINE_ITEM_TYPE_PRODUCT,
|
|
||||||
'referencedId' => $product->getReferencedId(),
|
|
||||||
'stackable' => $orderItem->getStackable(),
|
|
||||||
'removable' => $orderItem->getRemovable(),
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
$customProductsService->add($params, $request, $salesChannelContext, $cart);
|
|
||||||
$cart = $this->cartService->getCart($salesChannelContext->getToken(), $salesChannelContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $cart;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $productOptions
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getOptionValues(array $productOptions): array
|
|
||||||
{
|
|
||||||
$optionValues = [];
|
|
||||||
foreach ($productOptions as $productOption) {
|
|
||||||
$optionType = $productOption->getPayload()['type'] ?: '';
|
|
||||||
|
|
||||||
switch ($optionType) {
|
|
||||||
case CustomProductsLineItemTypes::PRODUCT_OPTION_TYPE_IMAGE_UPLOAD:
|
|
||||||
case CustomProductsLineItemTypes::PRODUCT_OPTION_TYPE_FILE_UPLOAD:
|
|
||||||
$media = $productOption->getPayload()['media'] ?: [];
|
|
||||||
foreach ($media as $mediaItem) {
|
|
||||||
$optionValues[$productOption->getReferencedId()] = new RequestDataBag([
|
|
||||||
'media' => new RequestDataBag([
|
|
||||||
$mediaItem['filename'] => new RequestDataBag([
|
|
||||||
'id' => $mediaItem['mediaId'],
|
|
||||||
'filename' => $mediaItem['filename'],
|
|
||||||
]),
|
|
||||||
]),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
$optionValues[$productOption->getReferencedId()] = new RequestDataBag([
|
|
||||||
'value' => $productOption->getPayload()['value'] ?: '',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $optionValues;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,105 +1,70 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace VRPaymentPayment\Core\Storefront\Checkout\Subscriber;
|
namespace VRPaymentPayment\Core\Storefront\Checkout\Subscriber;
|
||||||
|
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Shopware\Core\{Checkout\Order\Aggregate\OrderTransaction\OrderTransactionCollection,
|
use Shopware\Core\{
|
||||||
|
Checkout\Order\Aggregate\OrderTransaction\OrderTransactionCollection,
|
||||||
Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStates,
|
Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStates,
|
||||||
Checkout\Order\OrderEntity,
|
Checkout\Order\OrderEntity,
|
||||||
Content\MailTemplate\Service\Event\MailBeforeValidateEvent};
|
Content\MailTemplate\Service\Event\MailBeforeValidateEvent
|
||||||
|
};
|
||||||
use Shopware\Core\Checkout\Payment\PaymentMethodCollection;
|
use Shopware\Core\Checkout\Payment\PaymentMethodCollection;
|
||||||
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
|
|
||||||
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
|
|
||||||
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
|
|
||||||
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
|
|
||||||
use Shopware\Core\System\SalesChannel\SalesChannelContext;
|
use Shopware\Core\System\SalesChannel\SalesChannelContext;
|
||||||
use Shopware\Storefront\Page\Account\Order\AccountEditOrderPageLoadedEvent;
|
use Shopware\Storefront\Page\Account\Order\AccountEditOrderPageLoadedEvent;
|
||||||
use Shopware\Storefront\Page\Account\PaymentMethod\AccountPaymentMethodPageLoadedEvent;
|
use Shopware\Storefront\Page\Account\PaymentMethod\AccountPaymentMethodPageLoadedEvent;
|
||||||
use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent;
|
use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent;
|
||||||
use Shopware\Storefront\Page\Checkout\Finish\CheckoutFinishPageLoadedEvent;
|
|
||||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
use VRPaymentPayment\Core\{Api\Transaction\Service\TransactionService,
|
use VRPaymentPayment\Core\Checkout\PaymentHandler\VRPaymentPaymentHandler;
|
||||||
Checkout\PaymentHandler\VRPaymentPaymentHandler,
|
use VRPaymentPayment\Core\Settings\Service\SettingsService;
|
||||||
Settings\Service\SettingsService,
|
use VRPaymentPayment\Core\Checkout\Service\PaymentMethodFilterService;
|
||||||
Settings\Struct\Settings,
|
use VRPaymentPayment\Core\Checkout\Service\PaymentIntegrationService;
|
||||||
Util\PaymentMethodUtil};
|
|
||||||
use VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Service\PaymentMethodConfigurationService;
|
|
||||||
use VRPaymentPayment\Sdk\{Model\AddressCreate,
|
|
||||||
Model\ChargeAttempt,
|
|
||||||
Model\CreationEntityState,
|
|
||||||
Model\CriteriaOperator,
|
|
||||||
Model\EntityQuery,
|
|
||||||
Model\EntityQueryFilter,
|
|
||||||
Model\EntityQueryFilterType,
|
|
||||||
Model\LineItemAttributeCreate,
|
|
||||||
Model\LineItemCreate,
|
|
||||||
Model\LineItemType,
|
|
||||||
Model\TaxCreate,
|
|
||||||
Model\Transaction,
|
|
||||||
Model\TransactionCreate,
|
|
||||||
Model\TransactionPending};
|
|
||||||
use Shopware\Core\Framework\Struct\ArrayEntity;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class CheckoutSubscriber
|
* This subscriber listens to page load events in the Storefront to filter out
|
||||||
*
|
* WhitelabelMachineName payment methods that are not applicable for the current cart or customer.
|
||||||
* @package VRPaymentPayment\Storefront\Checkout\Subscriber
|
|
||||||
*/
|
*/
|
||||||
class CheckoutSubscriber implements EventSubscriberInterface
|
class CheckoutSubscriber implements EventSubscriberInterface
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var LoggerInterface
|
||||||
|
*/
|
||||||
|
protected LoggerInterface $logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Psr\Log\LoggerInterface
|
* @var SettingsService
|
||||||
*/
|
*/
|
||||||
protected $logger;
|
private SettingsService $settingsService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Service\PaymentMethodConfigurationService
|
* @var PaymentMethodFilterService
|
||||||
*/
|
*/
|
||||||
private $paymentMethodConfigurationService;
|
private PaymentMethodFilterService $paymentMethodFilterService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService
|
* @var PaymentIntegrationService
|
||||||
*/
|
*/
|
||||||
private $transactionService;
|
private PaymentIntegrationService $paymentIntegrationService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \VRPaymentPayment\Core\Settings\Service\SettingsService
|
* @param SettingsService $settingsService
|
||||||
|
* @param PaymentMethodFilterService $paymentMethodFilterService
|
||||||
|
* @param PaymentIntegrationService $paymentIntegrationService
|
||||||
*/
|
*/
|
||||||
private $settingsService;
|
public function __construct(
|
||||||
|
SettingsService $settingsService,
|
||||||
/**
|
PaymentMethodFilterService $paymentMethodFilterService,
|
||||||
* @var \VRPaymentPayment\Core\Util\PaymentMethodUtil
|
PaymentIntegrationService $paymentIntegrationService
|
||||||
*/
|
) {
|
||||||
private $paymentMethodUtil;
|
|
||||||
|
|
||||||
/** @var EntityRepository */
|
|
||||||
private EntityRepository $paymentMethodRepository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CheckoutSubscriber constructor.
|
|
||||||
*
|
|
||||||
* @param \VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Service\PaymentMethodConfigurationService $paymentMethodConfigurationService
|
|
||||||
* @param \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService $transactionService
|
|
||||||
* @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService
|
|
||||||
* @param \VRPaymentPayment\Core\Util\PaymentMethodUtil $paymentMethodUtil
|
|
||||||
* @param EntityRepository $paymentMethodRepository
|
|
||||||
*/
|
|
||||||
public function __construct(PaymentMethodConfigurationService $paymentMethodConfigurationService, TransactionService $transactionService, SettingsService $settingsService, PaymentMethodUtil $paymentMethodUtil, EntityRepository $paymentMethodRepository)
|
|
||||||
{
|
|
||||||
$this->paymentMethodConfigurationService = $paymentMethodConfigurationService;
|
|
||||||
$this->transactionService = $transactionService;
|
|
||||||
$this->settingsService = $settingsService;
|
$this->settingsService = $settingsService;
|
||||||
$this->paymentMethodUtil = $paymentMethodUtil;
|
$this->paymentMethodFilterService = $paymentMethodFilterService;
|
||||||
$this->paymentMethodRepository = $paymentMethodRepository;
|
$this->paymentIntegrationService = $paymentIntegrationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \Psr\Log\LoggerInterface $logger
|
* @param LoggerInterface $logger
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
* @required
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public function setLogger(LoggerInterface $logger): void
|
public function setLogger(LoggerInterface $logger): void
|
||||||
{
|
{
|
||||||
@@ -107,38 +72,75 @@ class CheckoutSubscriber implements EventSubscriberInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Register events to listen to.
|
||||||
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function getSubscribedEvents(): array
|
public static function getSubscribedEvents(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
CheckoutConfirmPageLoadedEvent::class => 'onCheckoutConfirmLoaded',
|
CheckoutConfirmPageLoadedEvent::class => 'onPageLoaded',
|
||||||
AccountEditOrderPageLoadedEvent::class => 'onAccountOrderEditLoaded',
|
AccountEditOrderPageLoadedEvent::class => 'onPageLoaded',
|
||||||
AccountPaymentMethodPageLoadedEvent::class => 'onAccountPaymentMethodLoaded',
|
AccountPaymentMethodPageLoadedEvent::class => 'onPageLoaded',
|
||||||
"subscription." . CheckoutConfirmPageLoadedEvent::class => ['onConfirmPageLoaded', 1],
|
"subscription." . CheckoutConfirmPageLoadedEvent::class => ['onPageLoaded', 1],
|
||||||
MailBeforeValidateEvent::class => ['onMailBeforeValidate', 1],
|
MailBeforeValidateEvent::class => ['onMailBeforeValidate', 1],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop order emails being sent out
|
* Handles filtering of payment methods when a relevant page is loaded.
|
||||||
*
|
*
|
||||||
* @param \Shopware\Core\Content\MailTemplate\Service\Event\MailBeforeValidateEvent $event
|
* @param mixed $event The page loaded event.
|
||||||
|
*/
|
||||||
|
public function onPageLoaded($event): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$salesChannelContext = $event->getSalesChannelContext();
|
||||||
|
|
||||||
|
// Access the payment methods available for the current page.
|
||||||
|
$paymentMethodCollection = $event->getPage()->getPaymentMethods();
|
||||||
|
|
||||||
|
// Delegate filtering to the centralized service.
|
||||||
|
$filteredCollection = $this->paymentMethodFilterService->filterPaymentMethods(
|
||||||
|
$paymentMethodCollection,
|
||||||
|
$salesChannelContext,
|
||||||
|
$event
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update the page with the filtered list.
|
||||||
|
$event->getPage()->setPaymentMethods($filteredCollection);
|
||||||
|
|
||||||
|
// If we are on a checkout or account page and have a pending transaction, provide integration data.
|
||||||
|
$transactionId = $salesChannelContext->getContext()->getExtension('vrpayment_transaction_id');
|
||||||
|
if ($transactionId) {
|
||||||
|
$paymentConfig = $this->paymentIntegrationService->getConfigForTransaction(
|
||||||
|
(int) $transactionId->getVars()['value'],
|
||||||
|
$salesChannelContext
|
||||||
|
);
|
||||||
|
$event->getPage()->addExtension('vRPaymentData', $paymentConfig);
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->logger->error($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles logic before a mail is validated/sent.
|
||||||
|
*
|
||||||
|
* @param MailBeforeValidateEvent $event
|
||||||
*/
|
*/
|
||||||
public function onMailBeforeValidate(MailBeforeValidateEvent $event): void
|
public function onMailBeforeValidate(MailBeforeValidateEvent $event): void
|
||||||
{
|
{
|
||||||
$templateData = $event->getTemplateData();
|
$templateData = $event->getTemplateData();
|
||||||
|
|
||||||
/**
|
/** @var OrderEntity|null $order */
|
||||||
* @var $order \Shopware\Core\Checkout\Order\OrderEntity
|
|
||||||
*/
|
|
||||||
$order = !empty($templateData['order']) && $templateData['order'] instanceof OrderEntity ? $templateData['order'] : null;
|
$order = !empty($templateData['order']) && $templateData['order'] instanceof OrderEntity ? $templateData['order'] : null;
|
||||||
|
|
||||||
if (!empty($order) && $order->getAmountTotal() > 0) {
|
if (!empty($order) && $order->getAmountTotal() > 0) {
|
||||||
|
// Check if WhitelabelMachineName emails are enabled for this sales channel.
|
||||||
|
$isVRPaymentEmailSettingEnabled = (bool)$this->settingsService->getSettings($order->getSalesChannelId())->isEmailEnabled();
|
||||||
|
|
||||||
$isVRPaymentEmailSettingEnabled = $this->settingsService->getSettings($order->getSalesChannelId())->isEmailEnabled();
|
if (!$isVRPaymentEmailSettingEnabled) {
|
||||||
|
|
||||||
if (!$isVRPaymentEmailSettingEnabled) { //setting is disabled
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,253 +148,30 @@ class CheckoutSubscriber implements EventSubscriberInterface
|
|||||||
if (!($orderTransactions instanceof OrderTransactionCollection)) {
|
if (!($orderTransactions instanceof OrderTransactionCollection)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$orderTransactionLast = $orderTransactions->last();
|
$orderTransactionLast = $orderTransactions->last();
|
||||||
if (empty($orderTransactionLast) || empty($orderTransactionLast->getPaymentMethod())) { // no payment method available
|
if (empty($orderTransactionLast) || empty($orderTransactionLast->getPaymentMethod())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the payment method used belongs to this plugin.
|
||||||
$isVRPaymentPM = VRPaymentPaymentHandler::class == $orderTransactionLast->getPaymentMethod()->getHandlerIdentifier();
|
$isVRPaymentPM = VRPaymentPaymentHandler::class == $orderTransactionLast->getPaymentMethod()->getHandlerIdentifier();
|
||||||
if (!$isVRPaymentPM) { // not our payment method
|
if (!$isVRPaymentPM) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify if the transaction is in a state where an email should be handled.
|
||||||
$isOrderTransactionStateOpen = in_array(
|
$isOrderTransactionStateOpen = in_array(
|
||||||
$orderTransactionLast->getStateMachineState()->getTechnicalName(), [
|
$orderTransactionLast->getStateMachineState()->getTechnicalName(),
|
||||||
|
[
|
||||||
OrderTransactionStates::STATE_OPEN,
|
OrderTransactionStates::STATE_OPEN,
|
||||||
OrderTransactionStates::STATE_IN_PROGRESS,
|
OrderTransactionStates::STATE_IN_PROGRESS,
|
||||||
]);
|
]
|
||||||
|
);
|
||||||
|
|
||||||
if (!$isOrderTransactionStateOpen) { // order payment status is open or in progress
|
if (!$isOrderTransactionStateOpen) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param CheckoutConfirmPageLoadedEvent $event
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function onCheckoutConfirmLoaded(CheckoutConfirmPageLoadedEvent $event): void
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$salesChannelContext = $event->getSalesChannelContext();
|
|
||||||
$settings = $this->settingsService->getValidSettings($salesChannelContext->getSalesChannel()->getId());
|
|
||||||
if (is_null($settings)) {
|
|
||||||
$this->logger->notice('Removing payment methods because settings are invalid');
|
|
||||||
$this->removeVRPaymentPaymentMethodFromConfirmPage($event);
|
|
||||||
}
|
|
||||||
|
|
||||||
$createdTransactionId = $this->transactionService->createPendingTransaction($salesChannelContext, $event);
|
|
||||||
$this->updateTempTransactionIfNeeded($salesChannelContext, $createdTransactionId);
|
|
||||||
|
|
||||||
$this->getAvailablePaymentMethods($settings, $createdTransactionId, $salesChannelContext);
|
|
||||||
$this->setPossiblePaymentMethods($settings->getSpaceId(), $event);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$this->logger->error($e->getMessage());
|
|
||||||
$this->removeVRPaymentPaymentMethodFromConfirmPage($event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param AccountEditOrderPageLoadedEvent $event
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function onAccountOrderEditLoaded(AccountEditOrderPageLoadedEvent $event): void
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$this->handlePaymentMethodFiltering($event);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
$this->logger->error($e->getMessage());
|
|
||||||
$this->removeVRPaymentPaymentMethodFromConfirmPage($event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param AccountPaymentMethodPageLoadedEvent $event
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function onAccountPaymentMethodLoaded(AccountPaymentMethodPageLoadedEvent $event): void
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$this->handlePaymentMethodFiltering($event);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
$this->logger->error($e->getMessage());
|
|
||||||
$this->removeVRPaymentPaymentMethodFromConfirmPage($event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param \Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent $event
|
|
||||||
*/
|
|
||||||
public function onConfirmPageLoaded(CheckoutConfirmPageLoadedEvent $event): void
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$this->handlePaymentMethodFiltering($event);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
$this->logger->error($e->getMessage());
|
|
||||||
$this->removeVRPaymentPaymentMethodFromConfirmPage($event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $event
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function handlePaymentMethodFiltering($event): void
|
|
||||||
{
|
|
||||||
$salesChannelContext = $event->getSalesChannelContext();
|
|
||||||
$settings = $this->settingsService->getValidSettings($salesChannelContext->getSalesChannel()->getId());
|
|
||||||
|
|
||||||
if (is_null($settings)) {
|
|
||||||
$this->logger->notice('Removing payment methods because settings are invalid');
|
|
||||||
$this->removeVRPaymentPaymentMethodFromConfirmPage($event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$createdTransactionId = $this->transactionService->createPendingTransaction($salesChannelContext, $event);
|
|
||||||
$this->updateTempTransactionIfNeeded($salesChannelContext, $createdTransactionId);
|
|
||||||
|
|
||||||
$this->getAvailablePaymentMethods($settings, $createdTransactionId, $salesChannelContext);
|
|
||||||
$this->setPossiblePaymentMethods($settings->getSpaceId(), $event);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $event
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function removeVRPaymentPaymentMethodFromConfirmPage($event): void
|
|
||||||
{
|
|
||||||
$paymentMethodCollection = $event->getPage()->getPaymentMethods();
|
|
||||||
$paymentMethodIds = $this->paymentMethodUtil->getVRPaymentPaymentMethodIds($event->getContext());
|
|
||||||
foreach ($paymentMethodIds as $paymentMethodId) {
|
|
||||||
$paymentMethodCollection->remove($paymentMethodId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Settings $settings
|
|
||||||
* @param int $createdTransactionId
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function getAvailablePaymentMethods(Settings $settings, int $createdTransactionId, SalesChannelContext $salesChannelContext): void
|
|
||||||
{
|
|
||||||
$transactionService = $settings->getApiClient()->getTransactionService();
|
|
||||||
$possiblePaymentMethods = $transactionService->fetchPaymentMethods(
|
|
||||||
$settings->getSpaceId(),
|
|
||||||
$createdTransactionId,
|
|
||||||
$settings->getIntegration()
|
|
||||||
);
|
|
||||||
$arrayOfPossibleMethods = [];
|
|
||||||
foreach ($possiblePaymentMethods as $possiblePaymentMethod) {
|
|
||||||
$arrayOfPossibleMethods[] = $possiblePaymentMethod->getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
$salesChannelContext->getContext()->addExtension(
|
|
||||||
'possibleMethods',
|
|
||||||
new ArrayEntity(['ids' => $arrayOfPossibleMethods])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int $spaceId
|
|
||||||
* @param CheckoutConfirmPageLoadedEvent $event
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function setPossiblePaymentMethods(int $spaceId, $event): void
|
|
||||||
{
|
|
||||||
$paymentIds = [];
|
|
||||||
$paymentMethodCollection = $event->getPage()->getPaymentMethods();
|
|
||||||
|
|
||||||
foreach ($paymentMethodCollection as $paymentMethodCollectionItem) {
|
|
||||||
$isVRPaymentPM = VRPaymentPaymentHandler::class === $paymentMethodCollectionItem->getHandlerIdentifier();
|
|
||||||
if (!$isVRPaymentPM) {
|
|
||||||
$paymentIds[] = $paymentMethodCollectionItem->getId();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$allowedWLMethods = [];
|
|
||||||
$paymentMethodConfigurations = $this->paymentMethodConfigurationService
|
|
||||||
->getAllPaymentMethodConfigurations($spaceId, $event->getSalesChannelContext()->getContext());
|
|
||||||
|
|
||||||
foreach ($paymentMethodConfigurations as $paymentMethodConfiguration) {
|
|
||||||
if ($paymentMethodConfiguration->getPaymentMethod() === null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$pmId = $paymentMethodConfiguration->getPaymentMethod()->getId();
|
|
||||||
$pmConfigId = $paymentMethodConfiguration->getPaymentMethodConfigurationId();
|
|
||||||
$allowedIds = $this->getAllowedPaymentMethodIds($event->getSalesChannelContext());
|
|
||||||
|
|
||||||
if ($paymentMethodConfiguration->getSpaceId() === $spaceId
|
|
||||||
&& \in_array($pmConfigId, $allowedIds, true)) {
|
|
||||||
$allowedWLMethods[] = $pmId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$allPaymentIds = array_unique(array_merge($paymentIds, $allowedWLMethods));
|
|
||||||
$collection = new PaymentMethodCollection();
|
|
||||||
if (!empty($allPaymentIds)) {
|
|
||||||
$criteria = new Criteria($allPaymentIds);
|
|
||||||
$criteria->addFilter(new EqualsFilter('active', true));
|
|
||||||
$criteria->addFilter(
|
|
||||||
new EqualsFilter('salesChannels.id', $event->getSalesChannelContext()->getSalesChannelId())
|
|
||||||
);
|
|
||||||
|
|
||||||
$result = $this->paymentMethodRepository->search($criteria, $event->getContext());
|
|
||||||
foreach ($result->getEntities() as $method) {
|
|
||||||
if (!$collection->has($method->getId())) {
|
|
||||||
$collection->add($method);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$event->getPage()->setPaymentMethods($collection);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param SalesChannelContext $salesChannelContext
|
|
||||||
* @param int $createdTransactionId
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function updateTempTransactionIfNeeded(SalesChannelContext $salesChannelContext, int $createdTransactionId): void
|
|
||||||
{
|
|
||||||
$ctx = $salesChannelContext->getContext();
|
|
||||||
|
|
||||||
/** @var ArrayEntity|null $ext */
|
|
||||||
$ext = $ctx->getExtension('checkoutState');
|
|
||||||
|
|
||||||
$oldAddressHash = $ext instanceof ArrayEntity ? $ext->get('addressHash') : null;
|
|
||||||
$oldCurrency = $ext instanceof ArrayEntity ? $ext->get('currency') : null;
|
|
||||||
|
|
||||||
$customer = $salesChannelContext->getCustomer();
|
|
||||||
$addressHash = md5(json_encode((array) $customer));
|
|
||||||
$currency = $salesChannelContext->getCurrency()->getIsoCode();
|
|
||||||
|
|
||||||
$needsUpdate = ($oldAddressHash !== $addressHash) || ($oldCurrency !== $currency);
|
|
||||||
|
|
||||||
if ($needsUpdate) {
|
|
||||||
if ($createdTransactionId) {
|
|
||||||
$this->transactionService->updateTempTransaction($salesChannelContext, $createdTransactionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
$ctx->addExtension('possibleMethods', new ArrayEntity(['ids' => []]));
|
|
||||||
$ctx->addExtension(
|
|
||||||
'checkoutState',
|
|
||||||
new ArrayEntity([
|
|
||||||
'addressHash' => $addressHash,
|
|
||||||
'currency' => $currency,
|
|
||||||
])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param SalesChannelContext $salesChannelContext
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getAllowedPaymentMethodIds(SalesChannelContext $salesChannelContext): array
|
|
||||||
{
|
|
||||||
$ext = $salesChannelContext->getContext()->getExtension('possibleMethods');
|
|
||||||
return $ext instanceof ArrayEntity ? ($ext->get('ids') ?? []) : [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ class Analytics {
|
|||||||
public const SHOP_SYSTEM_VERSION = 'x-meta-shop-system-version';
|
public const SHOP_SYSTEM_VERSION = 'x-meta-shop-system-version';
|
||||||
public const SHOP_SYSTEM_AND_VERSION = 'x-meta-shop-system-and-version';
|
public const SHOP_SYSTEM_AND_VERSION = 'x-meta-shop-system-and-version';
|
||||||
public const PLUGIN_SYSTEM_VERSION = 'x-meta-plugin-version';
|
public const PLUGIN_SYSTEM_VERSION = 'x-meta-plugin-version';
|
||||||
|
public const SUBSCRIPTION_TRANSACTION = 'x-meta-subscription-transaction';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array
|
* @return array
|
||||||
@@ -25,16 +26,17 @@ class Analytics {
|
|||||||
self::SHOP_SYSTEM => 'shopware',
|
self::SHOP_SYSTEM => 'shopware',
|
||||||
self::SHOP_SYSTEM_VERSION => '6',
|
self::SHOP_SYSTEM_VERSION => '6',
|
||||||
self::SHOP_SYSTEM_AND_VERSION => 'shopware-6',
|
self::SHOP_SYSTEM_AND_VERSION => 'shopware-6',
|
||||||
self::PLUGIN_SYSTEM_VERSION => '7.1.2',
|
self::PLUGIN_SYSTEM_VERSION => '7.3.4',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \VRPayment\Sdk\ApiClient $apiClient
|
* @param \VRPayment\Sdk\ApiClient $apiClient
|
||||||
*/
|
*/
|
||||||
public static function addHeaders(ApiClient &$apiClient)
|
public static function addHeaders(ApiClient &$apiClient, array $additionalHeaders = [])
|
||||||
{
|
{
|
||||||
$data = self::getDefaultData();
|
$data = self::getDefaultData();
|
||||||
|
$data = array_merge($data, $additionalHeaders);
|
||||||
foreach ($data as $key => $value) {
|
foreach ($data as $key => $value) {
|
||||||
$apiClient->addDefaultHeader($key, $value);
|
$apiClient->addDefaultHeader($key, $value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
|
||||||
|
namespace VRPaymentPayment\Core\Util\Exception;
|
||||||
|
|
||||||
|
class RefundNotSupportedException extends \LogicException{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -91,6 +91,26 @@ class LocaleCodeProvider {
|
|||||||
return $language->getLocale() ? $language->getLocale()->getCode() : $defaultLocale;
|
return $language->getLocale() ? $language->getLocale()->getCode() : $defaultLocale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps a locale code to a VRPayment-supported payment page locale by matching the language prefix.
|
||||||
|
* E.g. de-CH -> de-DE, fr-CH -> fr-FR, en-US -> en-GB, it-CH -> it-IT.
|
||||||
|
*
|
||||||
|
* @param string $localeCode
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function mapToPaymentPageLocale(string $localeCode): string
|
||||||
|
{
|
||||||
|
$supportedLocales = [
|
||||||
|
'de' => self::LOCALE_GERMANY_GERMAN,
|
||||||
|
'fr' => self::LOCALE_FRANCE_FRENCH,
|
||||||
|
'it' => self::LOCALE_ITALY_ITALIAN,
|
||||||
|
'en' => self::LOCALE_GREAT_BRITAIN_ENGLISH,
|
||||||
|
];
|
||||||
|
|
||||||
|
$languagePrefix = substr($localeCode, 0, 2);
|
||||||
|
|
||||||
|
return $supportedLocales[$languagePrefix] ?? self::LOCALE_GREAT_BRITAIN_ENGLISH;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \Shopware\Core\Framework\Context $context
|
* @param \Shopware\Core\Framework\Context $context
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace VRPaymentPayment\Core\Util\Payload;
|
namespace VRPaymentPayment\Core\Util\Payload;
|
||||||
|
|
||||||
|
|
||||||
use Psr\Container\ContainerInterface;
|
use Psr\Container\ContainerInterface;
|
||||||
use Shopware\Core\{Checkout\Cart\Tax\Struct\CalculatedTaxCollection,
|
use Shopware\Core\{
|
||||||
|
Checkout\Cart\Tax\Struct\CalculatedTaxCollection,
|
||||||
Checkout\Customer\Aggregate\CustomerAddress\CustomerAddressEntity,
|
Checkout\Customer\Aggregate\CustomerAddress\CustomerAddressEntity,
|
||||||
Checkout\Customer\CustomerEntity,
|
Checkout\Customer\CustomerEntity,
|
||||||
Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity,
|
Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity,
|
||||||
@@ -15,10 +18,12 @@ use Shopware\Core\{Checkout\Cart\Tax\Struct\CalculatedTaxCollection,
|
|||||||
};
|
};
|
||||||
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
|
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
|
||||||
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
|
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
|
||||||
|
use Shopware\Core\Framework\Api\Context\SalesChannelApiSource;
|
||||||
|
|
||||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
use VRPayment\Sdk\{Model\AddressCreate,
|
use VRPayment\Sdk\{
|
||||||
|
Model\AddressCreate,
|
||||||
Model\ChargeAttempt,
|
Model\ChargeAttempt,
|
||||||
Model\CreationEntityState,
|
Model\CreationEntityState,
|
||||||
Model\CriteriaOperator,
|
Model\CriteriaOperator,
|
||||||
@@ -32,7 +37,8 @@ use VRPayment\Sdk\{Model\AddressCreate,
|
|||||||
Model\TransactionCreate,
|
Model\TransactionCreate,
|
||||||
Model\TransactionPending
|
Model\TransactionPending
|
||||||
};
|
};
|
||||||
use VRPaymentPayment\Core\{Api\PaymentMethodConfiguration\Entity\PaymentMethodConfigurationEntity,
|
use VRPaymentPayment\Core\{
|
||||||
|
Api\PaymentMethodConfiguration\Entity\PaymentMethodConfigurationEntity,
|
||||||
Settings\Struct\Settings,
|
Settings\Struct\Settings,
|
||||||
Util\Exception\InvalidPayloadException,
|
Util\Exception\InvalidPayloadException,
|
||||||
Util\LocaleCodeProvider,
|
Util\LocaleCodeProvider,
|
||||||
@@ -98,6 +104,16 @@ class TransactionPayload extends AbstractPayload
|
|||||||
|
|
||||||
protected OrderEntity $order;
|
protected OrderEntity $order;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $transactionId;
|
||||||
|
|
||||||
|
public function setTransactionId(int $transactionId): void
|
||||||
|
{
|
||||||
|
$this->transactionId = $transactionId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TransactionPayload constructor.
|
* TransactionPayload constructor.
|
||||||
*
|
*
|
||||||
@@ -113,8 +129,7 @@ class TransactionPayload extends AbstractPayload
|
|||||||
SalesChannelContext $salesChannelContext,
|
SalesChannelContext $salesChannelContext,
|
||||||
Settings $settings,
|
Settings $settings,
|
||||||
PaymentTransactionStruct $transaction
|
PaymentTransactionStruct $transaction
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
$this->localeCodeProvider = $localeCodeProvider;
|
$this->localeCodeProvider = $localeCodeProvider;
|
||||||
$this->salesChannelContext = $salesChannelContext;
|
$this->salesChannelContext = $salesChannelContext;
|
||||||
$this->settings = $settings;
|
$this->settings = $settings;
|
||||||
@@ -182,7 +197,7 @@ class TransactionPayload extends AbstractPayload
|
|||||||
'merchant_reference' => $this->fixLength($this->order->getOrderNumber(), 100),
|
'merchant_reference' => $this->fixLength($this->order->getOrderNumber(), 100),
|
||||||
'meta_data' => [
|
'meta_data' => [
|
||||||
self::VRPAYMENT_METADATA_ORDER_ID => $this->order->getId(),
|
self::VRPAYMENT_METADATA_ORDER_ID => $this->order->getId(),
|
||||||
self::VRPAYMENT_METADATA_ORDER_TRANSACTION_ID => $this->order->getTransactions()->first()->getId(),
|
self::VRPAYMENT_METADATA_ORDER_TRANSACTION_ID => $this->transaction->getOrderTransactionId(),
|
||||||
self::VRPAYMENT_METADATA_SALES_CHANNEL_ID => $this->salesChannelContext->getSalesChannel()->getId(),
|
self::VRPAYMENT_METADATA_SALES_CHANNEL_ID => $this->salesChannelContext->getSalesChannel()->getId(),
|
||||||
self::VRPAYMENT_METADATA_CUSTOMER_NAME => $customerName,
|
self::VRPAYMENT_METADATA_CUSTOMER_NAME => $customerName,
|
||||||
],
|
],
|
||||||
@@ -218,7 +233,7 @@ class TransactionPayload extends AbstractPayload
|
|||||||
}
|
}
|
||||||
|
|
||||||
$transactionPayload = (new TransactionPending())
|
$transactionPayload = (new TransactionPending())
|
||||||
->setId($_SESSION['transactionId'])
|
->setId($this->transactionId)
|
||||||
->setVersion($version)
|
->setVersion($version)
|
||||||
->setBillingAddress($billingAddress)
|
->setBillingAddress($billingAddress)
|
||||||
->setCurrency($transactionData['currency'])
|
->setCurrency($transactionData['currency'])
|
||||||
@@ -243,7 +258,9 @@ class TransactionPayload extends AbstractPayload
|
|||||||
}
|
}
|
||||||
|
|
||||||
$successUrl = $this->transaction->getReturnUrl() . '&status=paid';
|
$successUrl = $this->transaction->getReturnUrl() . '&status=paid';
|
||||||
$failedUrl = $this->getFailUrl($this->order->getId()) . '&status=fail';
|
// For headless clients, use the returnUrl for failure as well (they handle the status parameter).
|
||||||
|
// For Storefront, use the recreate-cart route.
|
||||||
|
$failedUrl = $this->getFailUrl($this->order->getId(), $this->transaction->getReturnUrl()) . '&status=fail';
|
||||||
$transactionPayload->setSuccessUrl($successUrl)
|
$transactionPayload->setSuccessUrl($successUrl)
|
||||||
->setFailedUrl($failedUrl);
|
->setFailedUrl($failedUrl);
|
||||||
|
|
||||||
@@ -361,7 +378,7 @@ class TransactionPayload extends AbstractPayload
|
|||||||
$rate = $calculatedTax->getTaxRate();
|
$rate = $calculatedTax->getTaxRate();
|
||||||
$amount = $this->calculateDiscountAmount($calculatedTax);
|
$amount = $this->calculateDiscountAmount($calculatedTax);
|
||||||
|
|
||||||
$lineItems[] = $this->createDiscountLineItem($discountName, $amount, $rate);
|
$lineItems[] = $this->createDiscountLineItem($discountName, $amount, $rate, $discount->getId());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$taxRules = $calculatedPrice->getTaxRules();
|
$taxRules = $calculatedPrice->getTaxRules();
|
||||||
@@ -370,12 +387,12 @@ class TransactionPayload extends AbstractPayload
|
|||||||
foreach ($taxRules as $taxRule) {
|
foreach ($taxRules as $taxRule) {
|
||||||
$rate = $taxRule->getTaxRate();
|
$rate = $taxRule->getTaxRate();
|
||||||
$amount = $calculatedPrice->getTotalPrice();
|
$amount = $calculatedPrice->getTotalPrice();
|
||||||
$lineItems[] = $this->createDiscountLineItem($discountName, $amount, $rate);
|
$lineItems[] = $this->createDiscountLineItem($discountName, $amount, $rate, $discount->getId());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$rate = $this->getDefaultTaxRate();
|
$rate = $this->getDefaultTaxRate();
|
||||||
$amount = $calculatedPrice->getTotalPrice();
|
$amount = $calculatedPrice->getTotalPrice();
|
||||||
$lineItems[] = $this->createDiscountLineItem($discountName, $amount, $rate);
|
$lineItems[] = $this->createDiscountLineItem($discountName, $amount, $rate, $discount->getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -386,7 +403,7 @@ class TransactionPayload extends AbstractPayload
|
|||||||
* @param float $rate
|
* @param float $rate
|
||||||
* @return LineItemCreate
|
* @return LineItemCreate
|
||||||
*/
|
*/
|
||||||
private function createDiscountLineItem(string $discountName, float $amount, float $rate): LineItemCreate
|
private function createDiscountLineItem(string $discountName, float $amount, float $rate, string $discountId): LineItemCreate
|
||||||
{
|
{
|
||||||
$lineItem = new LineItemCreate();
|
$lineItem = new LineItemCreate();
|
||||||
|
|
||||||
@@ -397,13 +414,15 @@ class TransactionPayload extends AbstractPayload
|
|||||||
$discountTitle = sprintf('DISCOUNT: %s', $discountName);
|
$discountTitle = sprintf('DISCOUNT: %s', $discountName);
|
||||||
}
|
}
|
||||||
|
|
||||||
$lineItem->setAmountIncludingTax($amount)
|
$roundedAmount = self::round($amount);
|
||||||
|
|
||||||
|
$lineItem->setAmountIncludingTax($roundedAmount)
|
||||||
->setName($discountTitle)
|
->setName($discountTitle)
|
||||||
->setQuantity(1)
|
->setQuantity(1)
|
||||||
->setShippingRequired(false)
|
->setShippingRequired(false)
|
||||||
->setSku($discountSkuName, 200)
|
->setSku($discountSkuName, 200)
|
||||||
->setType(LineItemType::DISCOUNT)
|
->setType(LineItemType::DISCOUNT)
|
||||||
->setUniqueId('coupon-' . $discountSkuName);
|
->setUniqueId('coupon-' . $discountSkuName . '-' . $discountId);
|
||||||
|
|
||||||
$taxRate = new TaxCreate([
|
$taxRate = new TaxCreate([
|
||||||
'title' => 'Discount Tax: ' . $rate,
|
'title' => 'Discount Tax: ' . $rate,
|
||||||
@@ -520,12 +539,14 @@ class TransactionPayload extends AbstractPayload
|
|||||||
$amount = self::round($amount + $shopLineItem->getPrice()->getCalculatedTaxes()->getAmount());
|
$amount = self::round($amount + $shopLineItem->getPrice()->getCalculatedTaxes()->getAmount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$roundedAmount = self::round($amount);
|
||||||
|
|
||||||
$lineItem = (new LineItemCreate())
|
$lineItem = (new LineItemCreate())
|
||||||
->setName($this->fixLength($shopLineItem->getLabel(), 150))
|
->setName($this->fixLength($shopLineItem->getLabel(), 150))
|
||||||
->setUniqueId($uniqueId)
|
->setUniqueId($uniqueId)
|
||||||
->setSku($sku)
|
->setSku($sku)
|
||||||
->setQuantity($shopLineItem->getQuantity() ?? 1)
|
->setQuantity($shopLineItem->getQuantity() ?? 1)
|
||||||
->setAmountIncludingTax($amount);
|
->setAmountIncludingTax($roundedAmount);
|
||||||
|
|
||||||
|
|
||||||
if (!empty($shopLineItem->getType()) && $shopLineItem->getType() == CustomProductsLineItemTypes::LINE_ITEM_TYPE_CUSTOMIZED_PRODUCTS) {
|
if (!empty($shopLineItem->getType()) && $shopLineItem->getType() == CustomProductsLineItemTypes::LINE_ITEM_TYPE_CUSTOMIZED_PRODUCTS) {
|
||||||
@@ -536,7 +557,6 @@ class TransactionPayload extends AbstractPayload
|
|||||||
$this->translator->trans('vrpayment.payload.taxes'),
|
$this->translator->trans('vrpayment.payload.taxes'),
|
||||||
$amount
|
$amount
|
||||||
);
|
);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$productAttributes = $this->getProductAttributes($shopLineItem);
|
$productAttributes = $this->getProductAttributes($shopLineItem);
|
||||||
|
|
||||||
@@ -586,7 +606,7 @@ class TransactionPayload extends AbstractPayload
|
|||||||
throw new InvalidPayloadException('Tax payload invalid:' . json_encode($tax->listInvalidProperties()));
|
throw new InvalidPayloadException('Tax payload invalid:' . json_encode($tax->listInvalidProperties()));
|
||||||
}
|
}
|
||||||
|
|
||||||
$taxes [] = $tax;
|
$taxes[] = $tax;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $taxes;
|
return $taxes;
|
||||||
@@ -644,9 +664,10 @@ class TransactionPayload extends AbstractPayload
|
|||||||
$amount = self::round($amount + $this->order->getShippingCosts()->getCalculatedTaxes()->getAmount());
|
$amount = self::round($amount + $this->order->getShippingCosts()->getCalculatedTaxes()->getAmount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$roundedAmount = self::round($amount);
|
||||||
|
|
||||||
$lineItem = (new LineItemCreate())
|
$lineItem = (new LineItemCreate())
|
||||||
->setAmountIncludingTax($amount)
|
->setAmountIncludingTax($roundedAmount)
|
||||||
->setName($this->fixLength($shippingName . ' ' . $this->translator->trans('vrpayment.payload.shipping.lineItem'), 150))
|
->setName($this->fixLength($shippingName . ' ' . $this->translator->trans('vrpayment.payload.shipping.lineItem'), 150))
|
||||||
->setQuantity($this->order->getShippingCosts()->getQuantity() ?? 1)
|
->setQuantity($this->order->getShippingCosts()->getQuantity() ?? 1)
|
||||||
->setSku($this->fixLength($shippingName . '-Shipping', 200))
|
->setSku($this->fixLength($shippingName . '-Shipping', 200))
|
||||||
@@ -665,7 +686,6 @@ class TransactionPayload extends AbstractPayload
|
|||||||
|
|
||||||
return $lineItem;
|
return $lineItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (\Exception $exception) {
|
} catch (\Exception $exception) {
|
||||||
$this->logger->critical(__CLASS__ . ' : ' . __FUNCTION__ . ' : ' . $exception->getMessage());
|
$this->logger->critical(__CLASS__ . ' : ' . __FUNCTION__ . ' : ' . $exception->getMessage());
|
||||||
}
|
}
|
||||||
@@ -692,11 +712,13 @@ class TransactionPayload extends AbstractPayload
|
|||||||
$taxRate = $taxItem->getTaxRate();
|
$taxRate = $taxItem->getTaxRate();
|
||||||
$tax = (new TaxCreate())
|
$tax = (new TaxCreate())
|
||||||
->setRate($taxRate)
|
->setRate($taxRate)
|
||||||
->setTitle('Tax rate: '.$taxRate);
|
->setTitle('Tax rate: ' . $taxRate);
|
||||||
|
|
||||||
|
$roundedAmount = self::round($amount);
|
||||||
|
|
||||||
$name = $taxRate . '%-' . $shippingName;
|
$name = $taxRate . '%-' . $shippingName;
|
||||||
$lineItem = (new LineItemCreate())
|
$lineItem = (new LineItemCreate())
|
||||||
->setAmountIncludingTax($amount)
|
->setAmountIncludingTax($roundedAmount)
|
||||||
->setName($this->fixLength($name . ' ' . $this->translator->trans('vrpayment.payload.shipping.lineItem'), 150))
|
->setName($this->fixLength($name . ' ' . $this->translator->trans('vrpayment.payload.shipping.lineItem'), 150))
|
||||||
->setQuantity($this->order->getShippingCosts()->getQuantity() ?? 1)
|
->setQuantity($this->order->getShippingCosts()->getQuantity() ?? 1)
|
||||||
->setSku($this->fixLength($name . '-Shipping', 200))
|
->setSku($this->fixLength($name . '-Shipping', 200))
|
||||||
@@ -717,7 +739,6 @@ class TransactionPayload extends AbstractPayload
|
|||||||
}
|
}
|
||||||
return $lineItems;
|
return $lineItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (\Exception $exception) {
|
} catch (\Exception $exception) {
|
||||||
$this->logger->critical(__CLASS__ . ' : ' . __FUNCTION__ . ' : ' . $exception->getMessage());
|
$this->logger->critical(__CLASS__ . ' : ' . __FUNCTION__ . ' : ' . $exception->getMessage());
|
||||||
}
|
}
|
||||||
@@ -760,7 +781,6 @@ class TransactionPayload extends AbstractPayload
|
|||||||
]);
|
]);
|
||||||
$this->logger->critical($error);
|
$this->logger->critical($error);
|
||||||
throw new \Exception($error);
|
throw new \Exception($error);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$lineItem = (new LineItemCreate())
|
$lineItem = (new LineItemCreate())
|
||||||
->setName($this->translator->trans('vrpayment.payload.adjustmentLineItem'))
|
->setName($this->translator->trans('vrpayment.payload.adjustmentLineItem'))
|
||||||
@@ -768,7 +788,8 @@ class TransactionPayload extends AbstractPayload
|
|||||||
->setSku('Adjustment-Line-Item')
|
->setSku('Adjustment-Line-Item')
|
||||||
->setQuantity(1);
|
->setQuantity(1);
|
||||||
/** @noinspection PhpParamsInspection */
|
/** @noinspection PhpParamsInspection */
|
||||||
$lineItem->setAmountIncludingTax($adjustmentPrice)
|
$roundedAdjustmentPrice = self::round($adjustmentPrice);
|
||||||
|
$lineItem->setAmountIncludingTax($roundedAdjustmentPrice)
|
||||||
->setType(($adjustmentPrice > 0) ? LineItemType::FEE : LineItemType::DISCOUNT);
|
->setType(($adjustmentPrice > 0) ? LineItemType::FEE : LineItemType::DISCOUNT);
|
||||||
|
|
||||||
if (!$lineItem->valid()) {
|
if (!$lineItem->valid()) {
|
||||||
@@ -841,7 +862,6 @@ class TransactionPayload extends AbstractPayload
|
|||||||
} else {
|
} else {
|
||||||
if (!empty($customer->getSalutation())) {
|
if (!empty($customer->getSalutation())) {
|
||||||
$salutation = $customer->getSalutation()->getDisplayName();
|
$salutation = $customer->getSalutation()->getDisplayName();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$salutation = !empty($salutation) ? $this->fixLength($salutation, 20) : null;
|
$salutation = !empty($salutation) ? $this->fixLength($salutation, 20) : null;
|
||||||
@@ -923,14 +943,48 @@ class TransactionPayload extends AbstractPayload
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get failure URL
|
* Generates the failure URL for payment transactions.
|
||||||
|
* For headless clients (Store API), returns the client's returnUrl.
|
||||||
|
* For Storefront, returns the recreate-cart route.
|
||||||
*
|
*
|
||||||
* @param string $orderId
|
* @param string $orderId The order ID for the Storefront route.
|
||||||
*
|
* @param string|null $returnUrl The client's return URL (used for headless).
|
||||||
* @return string
|
* @return string The failure URL.
|
||||||
*/
|
*/
|
||||||
protected function getFailUrl(string $orderId): string
|
protected function getFailUrl(string $orderId, ?string $returnUrl = null): string
|
||||||
{
|
{
|
||||||
|
// For headless clients (Store API) or custom integrations, we use the returnUrl so the client can handle the failure.
|
||||||
|
// We detect "Standard Storefront" usage by checking if the returnUrl matches the standard Shopware 'finalize-transaction' route.
|
||||||
|
// If it DOES match, we usually redirect to 'recreate-cart' for better error handling. (Storefront / Edit Order)
|
||||||
|
// If it DOES NOT match (e.g. custom URL, headless URL), we return it as is.
|
||||||
|
|
||||||
|
$isStandardShopwareUrl = strpos($returnUrl ?? '', 'payment/finalize-transaction') !== false;
|
||||||
|
|
||||||
|
$request = $this->container->get('request_stack')->getCurrentRequest();
|
||||||
|
$isJsonRequest = false;
|
||||||
|
if ($request) {
|
||||||
|
// Check for JSON preference
|
||||||
|
$format = $request->getPreferredFormat();
|
||||||
|
$contentTypes = $request->getAcceptableContentTypes();
|
||||||
|
$isJsonRequest = $format === 'json' || in_array('application/json', $contentTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($returnUrl) {
|
||||||
|
// Case 1: Custom URL (Headless detection part 1)
|
||||||
|
// If the URL is custom (not standard Shopware), we always respect it.
|
||||||
|
if (!$isStandardShopwareUrl) {
|
||||||
|
return $returnUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 2: Standard URL + JSON Request (Headless detection part 2)
|
||||||
|
// If URL is standard BUT request prefers JSON, it's likely a headless app (Store API) being proxied.
|
||||||
|
if ($isStandardShopwareUrl && $isJsonRequest) {
|
||||||
|
return $returnUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default: Storefront (SystemSource, SalesChannelSource, or AdminSalesChannelApiSource via Edit Order/HTML)
|
||||||
|
// We generate the recreate-cart route to restore the cart and show the error.
|
||||||
return $this->container->get('router')->generate(
|
return $this->container->get('router')->generate(
|
||||||
'frontend.vrpayment.checkout.recreate-cart',
|
'frontend.vrpayment.checkout.recreate-cart',
|
||||||
['orderId' => $orderId,],
|
['orderId' => $orderId,],
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class Migration1590156974TransactionEntity extends MigrationStep {
|
|||||||
public function update(Connection $connection): void
|
public function update(Connection $connection): void
|
||||||
{
|
{
|
||||||
$connection->executeStatement('
|
$connection->executeStatement('
|
||||||
CREATE TABLE IF NOT EXISTS `vrpayment_transaction` (
|
CREATE TABLE IF NOT EXISTS `vrpayment_transaction_tmp` (
|
||||||
`id` BINARY(16) NOT NULL,
|
`id` BINARY(16) NOT NULL,
|
||||||
`data` JSON NOT NULL,
|
`data` JSON NOT NULL,
|
||||||
`payment_method_id` BINARY(16) NOT NULL,
|
`payment_method_id` BINARY(16) NOT NULL,
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class Migration1590646356RefundEntity extends MigrationStep {
|
|||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
UNIQUE KEY `refund_id_UNIQUE` (`refund_id`),
|
UNIQUE KEY `refund_id_UNIQUE` (`refund_id`),
|
||||||
KEY `fk.vrp_refund.transaction_id` (`transaction_id`),
|
KEY `fk.vrp_refund.transaction_id` (`transaction_id`),
|
||||||
CONSTRAINT `fk.vrp_refund.transaction_id` FOREIGN KEY (`transaction_id`) REFERENCES `vrpayment_transaction` (`transaction_id`) ON DELETE CASCADE
|
CONSTRAINT `fk.vrp_refund.transaction_id` FOREIGN KEY (`transaction_id`) REFERENCES `vrpayment_transaction_tmp` (`transaction_id`) ON DELETE CASCADE
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
');
|
');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class Migration1590646356TransactionEntity extends MigrationStep {
|
|||||||
public function update(Connection $connection): void
|
public function update(Connection $connection): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$connection->executeStatement('ALTER TABLE `vrpayment_transaction` ADD COLUMN `confirmation_email_sent` TINYINT(1) NOT NULL DEFAULT 0 AFTER `id`;');
|
$connection->executeStatement('ALTER TABLE `vrpayment_transaction_tmp` ADD COLUMN `confirmation_email_sent` TINYINT(1) NOT NULL DEFAULT 0 AFTER `id`;');
|
||||||
}catch (\Exception $exception){
|
}catch (\Exception $exception){
|
||||||
// column probably exists
|
// column probably exists
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,19 +33,19 @@ class Migration1605701048TransactionEntity extends MigrationStep
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
$connection->executeStatement('
|
$connection->executeStatement('
|
||||||
ALTER TABLE `vrpayment_transaction`
|
ALTER TABLE `vrpayment_transaction_tmp`
|
||||||
ADD `order_version_id` binary(16) NOT NULL AFTER `transaction_id`;
|
ADD `order_version_id` binary(16) NOT NULL AFTER `transaction_id`;
|
||||||
');
|
');
|
||||||
|
|
||||||
$connection->executeStatement('
|
$connection->executeStatement('
|
||||||
UPDATE `vrpayment_transaction` t1
|
UPDATE `vrpayment_transaction_tmp` t1
|
||||||
INNER JOIN `order` t2
|
INNER JOIN `order` t2
|
||||||
ON t1.order_id = t2.id
|
ON t1.order_id = t2.id
|
||||||
SET t1.order_version_id = t2.version_id;
|
SET t1.order_version_id = t2.version_id;
|
||||||
');
|
');
|
||||||
|
|
||||||
$connection->executeStatement('
|
$connection->executeStatement('
|
||||||
ALTER TABLE `vrpayment_transaction`
|
ALTER TABLE `vrpayment_transaction_tmp`
|
||||||
DROP FOREIGN KEY `fk.vrp_transaction.order_id`,
|
DROP FOREIGN KEY `fk.vrp_transaction.order_id`,
|
||||||
DROP FOREIGN KEY `fk.vrp_transaction.order_transaction_id`,
|
DROP FOREIGN KEY `fk.vrp_transaction.order_transaction_id`,
|
||||||
DROP FOREIGN KEY `fk.vrp_transaction.payment_method_id`,
|
DROP FOREIGN KEY `fk.vrp_transaction.payment_method_id`,
|
||||||
@@ -53,7 +53,7 @@ class Migration1605701048TransactionEntity extends MigrationStep
|
|||||||
');
|
');
|
||||||
|
|
||||||
$connection->executeStatement('
|
$connection->executeStatement('
|
||||||
ALTER TABLE `vrpayment_transaction`
|
ALTER TABLE `vrpayment_transaction_tmp`
|
||||||
ADD CONSTRAINT `fk.vrp_transaction_order_id` FOREIGN KEY (`order_id`, `order_version_id`)
|
ADD CONSTRAINT `fk.vrp_transaction_order_id` FOREIGN KEY (`order_id`, `order_version_id`)
|
||||||
REFERENCES `order` (`id`, `version_id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
REFERENCES `order` (`id`, `version_id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
ADD CONSTRAINT `fk.vrp_transaction_payment_method_id` FOREIGN KEY (`payment_method_id`)
|
ADD CONSTRAINT `fk.vrp_transaction_payment_method_id` FOREIGN KEY (`payment_method_id`)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class Migration1684240994TransactionEntity extends MigrationStep {
|
|||||||
public function update(Connection $connection): void
|
public function update(Connection $connection): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$connection->executeStatement('ALTER TABLE `vrpayment_transaction` ADD COLUMN `erp_merchant_id` VARCHAR(255) DEFAULT NULL AFTER `confirmation_email_sent`;');
|
$connection->executeStatement('ALTER TABLE `vrpayment_transaction_tmp` ADD COLUMN `erp_merchant_id` VARCHAR(255) DEFAULT NULL AFTER `confirmation_email_sent`;');
|
||||||
}catch (\Exception $exception){
|
}catch (\Exception $exception){
|
||||||
// column probably exists
|
// column probably exists
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,323 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace VRPaymentPayment\Migration;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Connection;
|
||||||
|
use Shopware\Core\Framework\Migration\MigrationStep;
|
||||||
|
use Monolog\Logger;
|
||||||
|
use Monolog\Handler\StreamHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Migration1766067106TransactionEntity
|
||||||
|
*
|
||||||
|
* @package VRPaymentPayment\Migration
|
||||||
|
*/
|
||||||
|
class Migration1766067106TransactionEntity extends MigrationStep
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get creation timestamp
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getCreationTimestamp(): int
|
||||||
|
{
|
||||||
|
return 1766067106;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update non-destructive changes
|
||||||
|
*
|
||||||
|
* @param \Doctrine\DBAL\Connection $connection
|
||||||
|
*/
|
||||||
|
public function update(Connection $connection): void
|
||||||
|
{
|
||||||
|
$oldTableName = 'vrpayment_transaction';
|
||||||
|
$tempTableName = 'vrpayment_transaction_tmp';
|
||||||
|
$realTableName = 'vrpayment_transaction_data';
|
||||||
|
$logger = new Logger('vrpayment_migration');
|
||||||
|
$logger->pushHandler(new StreamHandler(dirname(__DIR__, 5) . '/var/log/vrpayment-migration.log'));
|
||||||
|
$logger->info(
|
||||||
|
'Migration start', [
|
||||||
|
'old_table_exists' => $this->tableExists($connection, $oldTableName),
|
||||||
|
'temp_table_exists' => $this->tableExists($connection, $tempTableName),
|
||||||
|
'real_table_exists' => $this->tableExists($connection, $realTableName),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
if ($this->tableExists($connection, $tempTableName)) {
|
||||||
|
// If _temp table exists, it means that this is a fresh installation.
|
||||||
|
$logger->info('Fresh installation detected.');
|
||||||
|
$connection->executeStatement(
|
||||||
|
sprintf('RENAME TABLE `%s` TO `%s`', $tempTableName, $realTableName)
|
||||||
|
);
|
||||||
|
$logger->info('Fresh installation finished.');
|
||||||
|
} else {
|
||||||
|
// If _temp does not exist, it means that this could be a version upgrade.
|
||||||
|
$logger->info('Possible plugin upgrade detected.');
|
||||||
|
if ($this->tableExists($connection, $oldTableName) && !$this->isOldPluginTable($connection, $oldTableName)) {
|
||||||
|
$logger->info('Old vrpayment_transaction table detected.');
|
||||||
|
// If vrpayment_transaction already exists and does not belong to old plugin,
|
||||||
|
// it means that this is indeed a version update.
|
||||||
|
$this->syncTransactionTable($connection, $oldTableName);
|
||||||
|
$logger->info('Old vrpayment_transaction table sync finished.');
|
||||||
|
$this->syncRefundTable($connection, $oldTableName);
|
||||||
|
$logger->info('Old vrpayment_refund table sync finished.');
|
||||||
|
$connection->executeStatement(
|
||||||
|
sprintf('RENAME TABLE `%s` TO `%s`', $oldTableName, $realTableName)
|
||||||
|
);
|
||||||
|
$logger->info('Old vrpayment_transaction table renaming completed.');
|
||||||
|
}
|
||||||
|
$logger->info('Possible plugin upgrade finished.');
|
||||||
|
// If vrpayment_transaction exists and it does belong to old plugin,
|
||||||
|
// it means we must run it in parallel.
|
||||||
|
}
|
||||||
|
$logger->info('Migration finished.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if table exists.
|
||||||
|
*
|
||||||
|
* @param \Doctrine\DBAL\Connection $connection
|
||||||
|
* @param string $table
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function tableExists(Connection $connection, string $table): bool {
|
||||||
|
$result = $connection->fetchOne('SHOW TABLES LIKE :table', ['table' => $table]);
|
||||||
|
return $result !== false && $result !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if table belongs to old plugin.
|
||||||
|
*
|
||||||
|
* @param \Doctrine\DBAL\Connection $connection
|
||||||
|
* @param string $table
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isOldPluginTable(Connection $connection, string $table): bool {
|
||||||
|
$oldTableExclusiveColumns = [
|
||||||
|
'finalized_at' => 'datetime',
|
||||||
|
'refunded_at' => 'datetime',
|
||||||
|
'initial_transaction_mode' => 'varchar',
|
||||||
|
'manual_capture' => 'tinyint',
|
||||||
|
'partial_refunded_at' => 'datetime',
|
||||||
|
'refunded_amount' => 'double',
|
||||||
|
'amount_to_refund' => 'double',
|
||||||
|
];
|
||||||
|
$resultColumns = $connection->fetchAllAssociative(
|
||||||
|
'SELECT LOWER(COLUMN_NAME) AS column_name, LOWER(DATA_TYPE) AS data_type
|
||||||
|
FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = DATABASE()
|
||||||
|
AND TABLE_NAME = :table',
|
||||||
|
['table' => $table]
|
||||||
|
);
|
||||||
|
$dbColumns = [];
|
||||||
|
foreach($resultColumns as $column) {
|
||||||
|
$dbColumns[$column['column_name']] = $column['data_type'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$oldPluginTable = true;
|
||||||
|
foreach($oldTableExclusiveColumns as $columnName => $columnType) {
|
||||||
|
if(!isset($dbColumns[$columnName])) {
|
||||||
|
$oldPluginTable = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ($dbColumns[$columnName] !== $columnType) {
|
||||||
|
$oldPluginTable = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $oldPluginTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronizes the transaction table with the current/latest version.
|
||||||
|
*
|
||||||
|
* @param \Doctrine\DBAL\Connection $connection
|
||||||
|
* @param string $table
|
||||||
|
*/
|
||||||
|
private function syncTransactionTable(Connection $connection, string $table): void {
|
||||||
|
$this->addColumnIfMissing($connection, $table, 'confirmation_email_sent', "TINYINT(1) NOT NULL DEFAULT 0 AFTER `id`");
|
||||||
|
$this->addColumnIfMissing($connection, $table, 'erp_merchant_id', "VARCHAR(255) DEFAULT NULL AFTER `confirmation_email_sent`");
|
||||||
|
$this->addColumnIfMissing($connection, $table, 'data', "LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL CHECK (json_valid(`data`)) AFTER `erp_merchant_id`");
|
||||||
|
$this->addColumnIfMissing($connection, $table, 'payment_method_id', "BINARY(16) NOT NULL");
|
||||||
|
$this->addColumnIfMissing($connection, $table, 'order_id', "BINARY(16) NOT NULL");
|
||||||
|
$this->addColumnIfMissing($connection, $table, 'order_transaction_id', "BINARY(16) NOT NULL");
|
||||||
|
$this->addColumnIfMissing($connection, $table, 'space_id', "INT(10) UNSIGNED NOT NULL");
|
||||||
|
$this->addColumnIfMissing($connection, $table, 'state', "VARCHAR(255) NOT NULL");
|
||||||
|
$this->addColumnIfMissing($connection, $table, 'sales_channel_id', "BINARY(16) NOT NULL");
|
||||||
|
$this->addColumnIfMissing($connection, $table, 'transaction_id', "INT(10) UNSIGNED NOT NULL");
|
||||||
|
$this->addColumnIfMissing($connection, $table, 'order_version_id', "BINARY(16) NOT NULL AFTER `transaction_id`");
|
||||||
|
|
||||||
|
$this->addColumnIfMissing($connection, $table, 'created_at', "DATETIME(3) NOT NULL");
|
||||||
|
$this->addColumnIfMissing($connection, $table, 'updated_at', "DATETIME(3) DEFAULT NULL");
|
||||||
|
|
||||||
|
$this->ensureIndexBySql($connection, $table, 'fk.vrp_transaction.order_id', "KEY `fk.vrp_transaction.order_id` (`order_id`)");
|
||||||
|
$this->ensureIndexBySql($connection, $table, 'fk.vrp_transaction.order_transaction_id', "KEY `fk.vrp_transaction.order_transaction_id` (`order_transaction_id`)");
|
||||||
|
$this->ensureIndexBySql($connection, $table, 'fk.vrp_transaction.payment_method_id', "KEY `fk.vrp_transaction.payment_method_id` (`payment_method_id`)");
|
||||||
|
$this->ensureIndexBySql($connection, $table, 'fk.vrp_transaction.sales_channel_id', "KEY `fk.vrp_transaction.sales_channel_id` (`sales_channel_id`)");
|
||||||
|
$this->ensureIndexBySql($connection, $table, 'fk.vrp_transaction', "KEY `fk.vrp_transaction` (`order_id`,`order_version_id`)");
|
||||||
|
|
||||||
|
$this->ensureForeignKey(
|
||||||
|
$connection,
|
||||||
|
$table,
|
||||||
|
'fk.vrp_transaction_order_id',
|
||||||
|
['order_id', 'order_version_id'],
|
||||||
|
'order',
|
||||||
|
['id', 'version_id'],
|
||||||
|
'CASCADE',
|
||||||
|
'CASCADE'
|
||||||
|
);
|
||||||
|
$this->ensureForeignKey(
|
||||||
|
$connection,
|
||||||
|
$table,
|
||||||
|
'fk.vrp_transaction_payment_method_id',
|
||||||
|
['payment_method_id'],
|
||||||
|
'payment_method',
|
||||||
|
['id'],
|
||||||
|
'RESTRICT',
|
||||||
|
'CASCADE'
|
||||||
|
);
|
||||||
|
$this->ensureForeignKey(
|
||||||
|
$connection,
|
||||||
|
$table,
|
||||||
|
'fk.vrp_transaction_sales_channel_id',
|
||||||
|
['sales_channel_id'],
|
||||||
|
'sales_channel',
|
||||||
|
['id'],
|
||||||
|
'RESTRICT',
|
||||||
|
'CASCADE'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronizes the parts of the refund table related to transactions with the current/latest version.
|
||||||
|
*
|
||||||
|
* @param \Doctrine\DBAL\Connection $connection
|
||||||
|
* @param string $table
|
||||||
|
*/
|
||||||
|
private function syncRefundTable(Connection $connection, string $table): void {
|
||||||
|
$refundTable = 'vrpayment_refund';
|
||||||
|
$this->ensureIndexBySql($connection, $refundTable, 'fk.vrp_refund.transaction_id', "KEY `fk.vrp_refund.transaction_id` (`transaction_id`)");
|
||||||
|
$this->ensureForeignKey(
|
||||||
|
$connection,
|
||||||
|
$refundTable,
|
||||||
|
'fk.vrp_refund.transaction_id',
|
||||||
|
['transaction_id'],
|
||||||
|
$table,
|
||||||
|
['transaction_id'],
|
||||||
|
'CASCADE',
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds column to the table if it's missing.
|
||||||
|
*
|
||||||
|
* @param \Doctrine\DBAL\Connection $connection
|
||||||
|
* @param string $table
|
||||||
|
* @param string $column
|
||||||
|
* @param string $sqlFragment
|
||||||
|
*/
|
||||||
|
private function addColumnIfMissing(Connection $connection, string $table, string $column, string $sqlFragment): void {
|
||||||
|
if ($this->columnExists($connection, $table, $column)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$connection->executeStatement(
|
||||||
|
sprintf("ALTER TABLE `%s` ADD COLUMN `%s` %s", $table, $column, $sqlFragment)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds index to the table if it's missing.
|
||||||
|
*
|
||||||
|
* @param \Doctrine\DBAL\Connection $connection
|
||||||
|
* @param string $table
|
||||||
|
* @param string $indexName
|
||||||
|
* @param string $sqlFragment
|
||||||
|
*/
|
||||||
|
private function ensureIndexBySql(Connection $connection, string $table, string $indexName, string $sqlFragment): void {
|
||||||
|
if ($this->indexExists($connection, $table, $indexName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$connection->executeStatement(
|
||||||
|
sprintf("ALTER TABLE `%s` ADD %s", $table, $sqlFragment)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds foreign key constraint to the table if it's missing.
|
||||||
|
*
|
||||||
|
* @param \Doctrine\DBAL\Connection $connection
|
||||||
|
* @param string $table
|
||||||
|
* @param string $constraintName
|
||||||
|
* @param string $columns
|
||||||
|
* @param string $refTable
|
||||||
|
* @param string $refColumns
|
||||||
|
* @param string|null $onDelete
|
||||||
|
* @param string|null $onUpdate
|
||||||
|
*/
|
||||||
|
private function ensureForeignKey(
|
||||||
|
Connection $connection,
|
||||||
|
string $table,
|
||||||
|
string $constraintName,
|
||||||
|
array $columns,
|
||||||
|
string $refTable,
|
||||||
|
array $refColumns,
|
||||||
|
?string $onDelete,
|
||||||
|
?string $onUpdate
|
||||||
|
): void {
|
||||||
|
if ($this->foreignKeyExists($connection, $table, $constraintName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$columnsList = '`' . implode('`,`', $columns) . '`';
|
||||||
|
$refColumnsList = '`' . implode('`,`', $refColumns) . '`';
|
||||||
|
$connection->executeStatement(
|
||||||
|
sprintf(
|
||||||
|
"ALTER TABLE `%s`
|
||||||
|
ADD CONSTRAINT `%s` FOREIGN KEY (%s)
|
||||||
|
REFERENCES `%s` (%s)%s%s",
|
||||||
|
$table,
|
||||||
|
$constraintName,
|
||||||
|
$columnsList,
|
||||||
|
$refTable,
|
||||||
|
$refColumnsList,
|
||||||
|
$onDelete ? " ON DELETE {$onDelete}" : "",
|
||||||
|
$onUpdate ? " ON UPDATE {$onUpdate}" : ""
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if foreign key constraint exists.
|
||||||
|
*
|
||||||
|
* @param \Doctrine\DBAL\Connection $connection
|
||||||
|
* @param string $table
|
||||||
|
* @param string $constraintName
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function foreignKeyExists(Connection $connection, string $table, $constraintName): bool {
|
||||||
|
$result = $connection->fetchOne(
|
||||||
|
"SELECT 1 FROM information_schema.referential_constraints
|
||||||
|
WHERE constraint_schema = DATABASE()
|
||||||
|
AND table_name = ?
|
||||||
|
AND constraint_name = ?
|
||||||
|
LIMIT 1",
|
||||||
|
[$table,$constraintName]
|
||||||
|
);
|
||||||
|
return $result !== false && $result !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update destructive changes
|
||||||
|
*
|
||||||
|
* @param \Doctrine\DBAL\Connection $connection
|
||||||
|
*/
|
||||||
|
public function updateDestructive(Connection $connection): void
|
||||||
|
{
|
||||||
|
// implement update destructive
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
-6
@@ -70,13 +70,19 @@ Component.register('vrpayment-order-action-refund-by-amount', {
|
|||||||
});
|
});
|
||||||
}).catch((errorResponse) => {
|
}).catch((errorResponse) => {
|
||||||
try {
|
try {
|
||||||
var errorTitle;
|
var errorTitle = errorResponse?.response?.data?.errors?.[0]?.title ?? this.$tc('vrpayment-order.refundAction.refundCreateError.errorTitle')
|
||||||
var errorMessage;
|
var errorMessage;
|
||||||
if (errorResponse.response.data == 'refundExceedsAmount') {
|
switch(errorResponse.response.data) {
|
||||||
errorTitle = this.$tc('vrpayment-order.refundAction.refundExceedsTotalError.title');
|
case 'refundAmountZero':
|
||||||
errorMessage = this.$tc('vrpayment-order.refundAction.refundExceedsTotalError.messageRefundAmountExceedsAvailableBalance');
|
errorMessage = this.$tc('vrpayment-order.refundAction.refundCreateError.messageRefundAmountIsZero');
|
||||||
} else {
|
break;
|
||||||
errorTitle = errorResponse.response.data.errors[0].title;
|
case 'refundExceedsAmount':
|
||||||
|
errorMessage = this.$tc('vrpayment-order.refundAction.refundCreateError.messageRefundAmountExceedsAvailableBalance');
|
||||||
|
break;
|
||||||
|
case 'methodDoesNotSupportRefund':
|
||||||
|
errorMessage = this.$tc('vrpayment-order.refundAction.refundCreateError.messagePaymentMethodDoesNotSupportRefund');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
errorMessage = errorResponse.response.data.errors[0].detail;
|
errorMessage = errorResponse.response.data.errors[0].detail;
|
||||||
}
|
}
|
||||||
this.createNotificationError({
|
this.createNotificationError({
|
||||||
|
|||||||
+11
-2
@@ -71,9 +71,18 @@ Component.register('vrpayment-order-action-refund-partial', {
|
|||||||
});
|
});
|
||||||
}).catch((errorResponse) => {
|
}).catch((errorResponse) => {
|
||||||
try {
|
try {
|
||||||
|
var errorTitle = errorResponse?.response?.data?.errors?.[0]?.title ?? this.$tc('vrpayment-order.refundAction.refundCreateError.errorTitle')
|
||||||
|
var errorMessage;
|
||||||
|
switch(errorResponse.response.data) {
|
||||||
|
case 'methodDoesNotSupportRefund':
|
||||||
|
errorMessage = this.$tc('vrpayment-order.refundAction.refundCreateError.messagePaymentMethodDoesNotSupportRefund');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
errorMessage = errorResponse.response.data.errors[0].detail;
|
||||||
|
}
|
||||||
this.createNotificationError({
|
this.createNotificationError({
|
||||||
title: errorResponse.response.data.errors[0].title,
|
title: errorTitle,
|
||||||
message: errorResponse.response.data.errors[0].detail,
|
message: errorMessage,
|
||||||
autoClose: false
|
autoClose: false
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
+11
-2
@@ -70,9 +70,18 @@ Component.register('vrpayment-order-action-refund-selected', {
|
|||||||
});
|
});
|
||||||
}).catch((errorResponse) => {
|
}).catch((errorResponse) => {
|
||||||
try {
|
try {
|
||||||
|
var errorTitle = errorResponse?.response?.data?.errors?.[0]?.title ?? this.$tc('vrpayment-order.refundAction.refundCreateError.errorTitle')
|
||||||
|
var errorMessage;
|
||||||
|
switch(errorResponse.response.data) {
|
||||||
|
case 'methodDoesNotSupportRefund':
|
||||||
|
errorMessage = this.$tc('vrpayment-order.refundAction.refundCreateError.messagePaymentMethodDoesNotSupportRefund');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
errorMessage = errorResponse.response.data.errors[0].detail;
|
||||||
|
}
|
||||||
this.createNotificationError({
|
this.createNotificationError({
|
||||||
title: errorResponse.response.data.errors[0].title,
|
title: errorTitle,
|
||||||
message: errorResponse.response.data.errors[0].detail,
|
message: errorMessage,
|
||||||
autoClose: false
|
autoClose: false
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
+1
@@ -9,6 +9,7 @@
|
|||||||
:max="this.$parent.$parent.itemRefundableQuantity"
|
:max="this.$parent.$parent.itemRefundableQuantity"
|
||||||
:min="0"
|
:min="0"
|
||||||
v-model="refundQuantity"
|
v-model="refundQuantity"
|
||||||
|
number-type="int"
|
||||||
:label="$tc('vrpayment-order.refund.refundQuantity.label')">
|
:label="$tc('vrpayment-order.refund.refundQuantity.label')">
|
||||||
</mt-number-field>
|
</mt-number-field>
|
||||||
|
|
||||||
|
|||||||
+17
-2
@@ -68,9 +68,24 @@ Component.register('vrpayment-order-action-refund', {
|
|||||||
});
|
});
|
||||||
}).catch((errorResponse) => {
|
}).catch((errorResponse) => {
|
||||||
try {
|
try {
|
||||||
|
var errorTitle = errorResponse?.response?.data?.errors?.[0]?.title ?? this.$tc('vrpayment-order.refundAction.refundCreateError.errorTitle')
|
||||||
|
var errorMessage;
|
||||||
|
switch(errorResponse.response.data) {
|
||||||
|
case 'refundQuantityZero':
|
||||||
|
errorMessage = this.$tc('vrpayment-order.refundAction.refundCreateError.messageRefundQuantityIsZero');
|
||||||
|
break;
|
||||||
|
case 'refundExceedsQuantity':
|
||||||
|
errorMessage = this.$tc('vrpayment-order.refundAction.refundCreateError.messageRefundQuantityExceedsAvailableBalance');
|
||||||
|
break;
|
||||||
|
case 'methodDoesNotSupportRefund':
|
||||||
|
errorMessage = this.$tc('vrpayment-order.refundAction.refundCreateError.messagePaymentMethodDoesNotSupportRefund');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
errorMessage = errorResponse.response.data.errors[0].detail;
|
||||||
|
}
|
||||||
this.createNotificationError({
|
this.createNotificationError({
|
||||||
title: errorResponse.response.data.errors[0].title,
|
title: errorTitle,
|
||||||
message: errorResponse.response.data.errors[0].detail,
|
message: errorMessage,
|
||||||
autoClose: false
|
autoClose: false
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
+1
-1
@@ -98,7 +98,7 @@
|
|||||||
<template #actions="{ item }">
|
<template #actions="{ item }">
|
||||||
<sw-context-menu-item
|
<sw-context-menu-item
|
||||||
:disabled="transaction.state != 'FULFILL' || item.refundableQuantity != item.quantity || item.refundableAmount == 0 || item.itemRefundedAmount > 0 || item.itemRefundedQuantity > 0"
|
:disabled="transaction.state != 'FULFILL' || item.refundableQuantity != item.quantity || item.refundableAmount == 0 || item.itemRefundedAmount > 0 || item.itemRefundedQuantity > 0"
|
||||||
@click="lineItemRefund(item.uniqueId)">
|
@click="lineItemRefund(item.uniqueId, item.quantity)">
|
||||||
{{ $tc('vrpayment-order.buttons.label.refund-whole-line-item') }}
|
{{ $tc('vrpayment-order.buttons.label.refund-whole-line-item') }}
|
||||||
</sw-context-menu-item>
|
</sw-context-menu-item>
|
||||||
|
|
||||||
|
|||||||
+32
-10
@@ -332,12 +332,12 @@ Component.register('vrpayment-order-detail', {
|
|||||||
this.modalType = '';
|
this.modalType = '';
|
||||||
},
|
},
|
||||||
|
|
||||||
lineItemRefund(lineItemId) {
|
lineItemRefund(lineItemId, itemQuantity) {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
this.VRPaymentRefundService.createRefund(
|
this.VRPaymentRefundService.createRefund(
|
||||||
this.transactionData.transactions[0].metaData.salesChannelId,
|
this.transactionData.transactions[0].metaData.salesChannelId,
|
||||||
this.transactionData.transactions[0].id,
|
this.transactionData.transactions[0].id,
|
||||||
0,
|
itemQuantity,
|
||||||
lineItemId
|
lineItemId
|
||||||
).then(() => {
|
).then(() => {
|
||||||
this.createNotificationSuccess({
|
this.createNotificationSuccess({
|
||||||
@@ -351,9 +351,18 @@ Component.register('vrpayment-order-detail', {
|
|||||||
});
|
});
|
||||||
}).catch((errorResponse) => {
|
}).catch((errorResponse) => {
|
||||||
try {
|
try {
|
||||||
|
var errorTitle = errorResponse?.response?.data?.errors?.[0]?.title ?? this.$tc('vrpayment-order.refundAction.refundCreateError.errorTitle')
|
||||||
|
var errorMessage;
|
||||||
|
switch(errorResponse.response.data) {
|
||||||
|
case 'methodDoesNotSupportRefund':
|
||||||
|
errorMessage = this.$tc('vrpayment-order.refundAction.refundCreateError.messagePaymentMethodDoesNotSupportRefund');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
errorMessage = errorResponse.response.data.errors[0].detail;
|
||||||
|
}
|
||||||
this.createNotificationError({
|
this.createNotificationError({
|
||||||
title: errorResponse.response.data.errors[0].title,
|
title: errorTitle,
|
||||||
message: errorResponse.response.data.errors[0].detail,
|
message: errorMessage,
|
||||||
autoClose: false
|
autoClose: false
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -385,7 +394,7 @@ Component.register('vrpayment-order-detail', {
|
|||||||
// Force the DOM to update before proceeding with the asynchronous operations
|
// Force the DOM to update before proceeding with the asynchronous operations
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
const refundPromises = this.selectedItems.map((item) => {
|
const refundPromises = this.selectedItems.map((item) => {
|
||||||
return this.lineItemRefundBulk(item.uniqueId); // Simulated refund action with delay
|
return this.lineItemRefundBulk(item.uniqueId, item.quantity); // Simulated refund action with delay
|
||||||
});
|
});
|
||||||
|
|
||||||
// Wait for all refund promises to complete
|
// Wait for all refund promises to complete
|
||||||
@@ -399,6 +408,10 @@ Component.register('vrpayment-order-detail', {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
if (error?.response?.data === 'methodDoesNotSupportRefund') {
|
||||||
|
this.isLoading = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Handle any errors during the refund process
|
// Handle any errors during the refund process
|
||||||
this.createNotificationError({
|
this.createNotificationError({
|
||||||
title: 'Error',
|
title: 'Error',
|
||||||
@@ -410,12 +423,12 @@ Component.register('vrpayment-order-detail', {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
lineItemRefundBulk(lineItemId) {
|
lineItemRefundBulk(lineItemId, itemQuantity) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.VRPaymentRefundService.createRefund(
|
this.VRPaymentRefundService.createRefund(
|
||||||
this.transactionData.transactions[0].metaData.salesChannelId,
|
this.transactionData.transactions[0].metaData.salesChannelId,
|
||||||
this.transactionData.transactions[0].id,
|
this.transactionData.transactions[0].id,
|
||||||
0,
|
itemQuantity,
|
||||||
lineItemId
|
lineItemId
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@@ -427,9 +440,18 @@ Component.register('vrpayment-order-detail', {
|
|||||||
})
|
})
|
||||||
.catch((errorResponse) => {
|
.catch((errorResponse) => {
|
||||||
try {
|
try {
|
||||||
|
var errorTitle = errorResponse?.response?.data?.errors?.[0]?.title ?? this.$tc('vrpayment-order.refundAction.refundCreateError.errorTitle')
|
||||||
|
var errorMessage;
|
||||||
|
switch(errorResponse.response.data) {
|
||||||
|
case 'methodDoesNotSupportRefund':
|
||||||
|
errorMessage = this.$tc('vrpayment-order.refundAction.refundCreateError.messagePaymentMethodDoesNotSupportRefund');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
errorMessage = errorResponse.response.data.errors[0].detail;
|
||||||
|
}
|
||||||
this.createNotificationError({
|
this.createNotificationError({
|
||||||
title: errorResponse.response.data.errors[0].title,
|
title: errorTitle,
|
||||||
message: errorResponse.response.data.errors[0].detail,
|
message: errorMessage,
|
||||||
autoClose: false
|
autoClose: false
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -439,7 +461,7 @@ Component.register('vrpayment-order-detail', {
|
|||||||
autoClose: false
|
autoClose: false
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
reject();
|
reject(errorResponse);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -78,9 +78,13 @@
|
|||||||
"successTitle": "Erfolg",
|
"successTitle": "Erfolg",
|
||||||
"maxAvailableItemsToRefund": "Maximal Verfügbare Artikel zum Erstatten",
|
"maxAvailableItemsToRefund": "Maximal Verfügbare Artikel zum Erstatten",
|
||||||
"maxAvailableAmountToRefund": "Maximal verfügbarer Erstattungsbetrag",
|
"maxAvailableAmountToRefund": "Maximal verfügbarer Erstattungsbetrag",
|
||||||
"refundExceedsTotalError": {
|
"refundCreateError": {
|
||||||
"title": "Fehler beim Erstellen der Rückerstattung.",
|
"errorTitle": "Fehler beim Erstellen der Rückerstattung.",
|
||||||
"messageRefundAmountExceedsAvailableBalance": "Der Rückerstattungsbetrag übersteigt das verfügbare Guthaben."
|
"messageRefundAmountExceedsAvailableBalance": "Der Rückerstattungsbetrag übersteigt das verfügbare Guthaben.",
|
||||||
|
"messageRefundAmountIsZero": "Der Rückerstattungsbetrag muss größer als 0 sein.",
|
||||||
|
"messageRefundQuantityExceedsAvailableBalance": "Rückerstattung nach Menge überschreitet die maximal verfügbare Anzahl an Artikeln zur Rückerstattung.",
|
||||||
|
"messageRefundQuantityIsZero": "Rückerstattung nach Menge muss größer als 0 sein.",
|
||||||
|
"messagePaymentMethodDoesNotSupportRefund": "Die Zahlungsmethode unterstützt keine Online-Rückerstattungen."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"transactionHistory": {
|
"transactionHistory": {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
"void": "Cancel authorization",
|
"void": "Cancel authorization",
|
||||||
"refund-whole-line-item": "Refund whole line item",
|
"refund-whole-line-item": "Refund whole line item",
|
||||||
"refund-line-item-by-quantity": "Refund by quantity",
|
"refund-line-item-by-quantity": "Refund by quantity",
|
||||||
"refund-line-item-selected": "Rembourser sélectionnés",
|
|
||||||
"refund-line-item-selected": "Refund selected",
|
"refund-line-item-selected": "Refund selected",
|
||||||
"refund-line-item-parial": "Partial refund"
|
"refund-line-item-parial": "Partial refund"
|
||||||
}
|
}
|
||||||
@@ -79,9 +78,13 @@
|
|||||||
"successTitle": "Success",
|
"successTitle": "Success",
|
||||||
"maxAvailableItemsToRefund": "Maximum available items to refund",
|
"maxAvailableItemsToRefund": "Maximum available items to refund",
|
||||||
"maxAvailableAmountToRefund": "Maximum available amount to refund",
|
"maxAvailableAmountToRefund": "Maximum available amount to refund",
|
||||||
"refundExceedsTotalError": {
|
"refundCreateError": {
|
||||||
"title": "Error while creating the refund.",
|
"errorTitle": "Error while creating the refund.",
|
||||||
"messageRefundAmountExceedsAvailableBalance": "Refund amount exceeds available balance."
|
"messageRefundAmountExceedsAvailableBalance": "Refund amount exceeds available balance.",
|
||||||
|
"messageRefundAmountIsZero": "Refund amount must be greater than 0.",
|
||||||
|
"messageRefundQuantityExceedsAvailableBalance": "Refund by quantity exceeds maximum available items to refund.",
|
||||||
|
"messageRefundQuantityIsZero": "Refund by quantity must be greater than 0.",
|
||||||
|
"messagePaymentMethodDoesNotSupportRefund": "Payment method does not support online refunds."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"transactionHistory": {
|
"transactionHistory": {
|
||||||
|
|||||||
@@ -78,9 +78,13 @@
|
|||||||
"successTitle": "Succès",
|
"successTitle": "Succès",
|
||||||
"maxAvailableItemsToRefund": "Nombre maximum d'articles disponibles pour le remboursement",
|
"maxAvailableItemsToRefund": "Nombre maximum d'articles disponibles pour le remboursement",
|
||||||
"maxAvailableAmountToRefund": "Montant maximal disponible pour le remboursement",
|
"maxAvailableAmountToRefund": "Montant maximal disponible pour le remboursement",
|
||||||
"refundExceedsTotalError": {
|
"refundCreateError": {
|
||||||
"title": "Erreur lors de la création du remboursement.",
|
"errorTitle": "Erreur lors de la création du remboursement.",
|
||||||
"messageRefundAmountExceedsAvailableBalance": "Le montant du remboursement dépasse le solde disponible."
|
"messageRefundAmountExceedsAvailableBalance": "Le montant du remboursement dépasse le solde disponible.",
|
||||||
|
"messageRefundAmountIsZero": "Le montant du remboursement doit être supérieur à 0.",
|
||||||
|
"messageRefundQuantityExceedsAvailableBalance": "Le remboursement par quantité dépasse le nombre maximal d’articles remboursables.",
|
||||||
|
"messageRefundQuantityIsZero": "Le remboursement par quantité doit être supérieur à 0.",
|
||||||
|
"messagePaymentMethodDoesNotSupportRefund": "Le mode de paiement ne prend pas en charge les remboursements en ligne."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"transactionHistory": {
|
"transactionHistory": {
|
||||||
|
|||||||
@@ -78,9 +78,13 @@
|
|||||||
"successTitle": "Successo",
|
"successTitle": "Successo",
|
||||||
"maxAvailableItemsToRefund": "Numero massimo di articoli disponibili da rimborsare",
|
"maxAvailableItemsToRefund": "Numero massimo di articoli disponibili da rimborsare",
|
||||||
"maxAvailableAmountToRefund": "Importo massimo disponibile per il rimborso",
|
"maxAvailableAmountToRefund": "Importo massimo disponibile per il rimborso",
|
||||||
"refundExceedsTotalError": {
|
"refundCreateError": {
|
||||||
"title": "Errore durante la creazione del rimborso.",
|
"errorTitle": "Errore durante la creazione del rimborso.",
|
||||||
"messageRefundAmountExceedsAvailableBalance": "LL'importo del rimborso supera il saldo disponibile."
|
"messageRefundAmountExceedsAvailableBalance": "LL'importo del rimborso supera il saldo disponibile.",
|
||||||
|
"messageRefundAmountIsZero": "L'importo del rimborso deve essere superiore a 0.",
|
||||||
|
"messageRefundQuantityExceedsAvailableBalance": "Il rimborso per quantità supera il numero massimo di articoli rimborsabili.",
|
||||||
|
"messageRefundQuantityIsZero": "Il rimborso per quantità deve essere maggiore di 0.",
|
||||||
|
"messagePaymentMethodDoesNotSupportRefund": "Il metodo di pagamento non supporta i rimborsi online."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"transactionHistory": {
|
"transactionHistory": {
|
||||||
|
|||||||
+8
-8
@@ -7,16 +7,16 @@
|
|||||||
:inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_STOREFRONT_WEBHOOKS_UPDATE_ENABLED]"
|
:inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_STOREFRONT_WEBHOOKS_UPDATE_ENABLED]"
|
||||||
:customInheritationCheckFunction="checkBoolFieldInheritance">
|
:customInheritationCheckFunction="checkBoolFieldInheritance">
|
||||||
<template #content="props">
|
<template #content="props">
|
||||||
<mt-switch
|
<sw-switch-field
|
||||||
:name="CONFIG_STOREFRONT_WEBHOOKS_UPDATE_ENABLED"
|
:name="CONFIG_STOREFRONT_WEBHOOKS_UPDATE_ENABLED"
|
||||||
bordered
|
bordered
|
||||||
:mapInheritance="props"
|
:mapInheritance="props"
|
||||||
:label="$tc('vrpayment-settings.settingForm.advancedOptions.webhooksUpdateEnabled.label')"
|
:label="$tc('vrpayment-settings.settingForm.advancedOptions.webhooksUpdateEnabled.label')"
|
||||||
:helpText="$tc('vrpayment-settings.settingForm.advancedOptions.webhooksUpdateEnabled.tooltipText')"
|
:helpText="$tc('vrpayment-settings.settingForm.advancedOptions.webhooksUpdateEnabled.tooltipText')"
|
||||||
:disabled="props.isInherited"
|
:disabled="props.isInherited"
|
||||||
:checked="props.currentValue"
|
:value="props.currentValue"
|
||||||
@update:checked="props.updateCurrentValue">
|
@update:value="props.updateCurrentValue">
|
||||||
</mt-switch>
|
</sw-switch-field>
|
||||||
</template>
|
</template>
|
||||||
</sw-inherit-wrapper>
|
</sw-inherit-wrapper>
|
||||||
|
|
||||||
@@ -25,16 +25,16 @@
|
|||||||
:inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_STOREFRONT_PAYMENTS_UPDATE_ENABLED]"
|
:inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_STOREFRONT_PAYMENTS_UPDATE_ENABLED]"
|
||||||
:customInheritationCheckFunction="checkBoolFieldInheritance">
|
:customInheritationCheckFunction="checkBoolFieldInheritance">
|
||||||
<template #content="props">
|
<template #content="props">
|
||||||
<mt-switch
|
<sw-switch-field
|
||||||
:name="CONFIG_STOREFRONT_PAYMENTS_UPDATE_ENABLED"
|
:name="CONFIG_STOREFRONT_PAYMENTS_UPDATE_ENABLED"
|
||||||
bordered
|
bordered
|
||||||
:mapInheritance="props"
|
:mapInheritance="props"
|
||||||
:label="$tc('vrpayment-settings.settingForm.advancedOptions.paymentsUpdateEnabled.label')"
|
:label="$tc('vrpayment-settings.settingForm.advancedOptions.paymentsUpdateEnabled.label')"
|
||||||
:helpText="$tc('vrpayment-settings.settingForm.advancedOptions.paymentsUpdateEnabled.tooltipText')"
|
:helpText="$tc('vrpayment-settings.settingForm.advancedOptions.paymentsUpdateEnabled.tooltipText')"
|
||||||
:disabled="props.isInherited"
|
:disabled="props.isInherited"
|
||||||
:checked="props.currentValue"
|
:value="props.currentValue"
|
||||||
@update:checked="props.updateCurrentValue">
|
@update:value="props.updateCurrentValue">
|
||||||
</mt-switch>
|
</sw-switch-field>
|
||||||
</template>
|
</template>
|
||||||
</sw-inherit-wrapper>
|
</sw-inherit-wrapper>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@
|
|||||||
<sw-inherit-wrapper
|
<sw-inherit-wrapper
|
||||||
v-model:value="actualConfigData[CONFIG_SPACE_ID]"
|
v-model:value="actualConfigData[CONFIG_SPACE_ID]"
|
||||||
:inheritedValue="getInheritedValue(CONFIG_SPACE_ID)"
|
:inheritedValue="getInheritedValue(CONFIG_SPACE_ID)"
|
||||||
:customInheritationCheckFunction="checkNumberFieldInheritance">
|
@update:value="onSwitchInput">
|
||||||
<template #content="props">
|
<template #content="props">
|
||||||
<mt-number-field
|
<mt-number-field
|
||||||
:name="CONFIG_SPACE_ID"
|
:name="CONFIG_SPACE_ID"
|
||||||
|
|||||||
+8
-8
@@ -55,16 +55,16 @@
|
|||||||
:inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_LINE_ITEM_CONSISTENCY_ENABLED]"
|
:inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_LINE_ITEM_CONSISTENCY_ENABLED]"
|
||||||
:customInheritationCheckFunction="checkBoolFieldInheritance">
|
:customInheritationCheckFunction="checkBoolFieldInheritance">
|
||||||
<template #content="props">
|
<template #content="props">
|
||||||
<mt-switch
|
<sw-switch-field
|
||||||
:name="CONFIG_LINE_ITEM_CONSISTENCY_ENABLED"
|
:name="CONFIG_LINE_ITEM_CONSISTENCY_ENABLED"
|
||||||
bordered
|
bordered
|
||||||
:mapInheritance="props"
|
:mapInheritance="props"
|
||||||
:label="$tc('vrpayment-settings.settingForm.options.lineItemConsistencyEnabled.label')"
|
:label="$tc('vrpayment-settings.settingForm.options.lineItemConsistencyEnabled.label')"
|
||||||
:helpText="$tc('vrpayment-settings.settingForm.options.lineItemConsistencyEnabled.tooltipText')"
|
:helpText="$tc('vrpayment-settings.settingForm.options.lineItemConsistencyEnabled.tooltipText')"
|
||||||
:disabled="props.isInherited"
|
:disabled="props.isInherited"
|
||||||
:checked="props.currentValue"
|
:value="props.currentValue"
|
||||||
@update:checked="props.updateCurrentValue">
|
@update:value="props.updateCurrentValue">
|
||||||
</mt-switch>
|
</sw-switch-field>
|
||||||
</template>
|
</template>
|
||||||
</sw-inherit-wrapper>
|
</sw-inherit-wrapper>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -75,16 +75,16 @@
|
|||||||
:inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_EMAIL_ENABLED]"
|
:inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_EMAIL_ENABLED]"
|
||||||
:customInheritationCheckFunction="checkBoolFieldInheritance">
|
:customInheritationCheckFunction="checkBoolFieldInheritance">
|
||||||
<template #content="props">
|
<template #content="props">
|
||||||
<mt-switch
|
<sw-switch-field
|
||||||
:name="CONFIG_EMAIL_ENABLED"
|
:name="CONFIG_EMAIL_ENABLED"
|
||||||
bordered
|
bordered
|
||||||
:mapInheritance="props"
|
:mapInheritance="props"
|
||||||
:label="$tc('vrpayment-settings.settingForm.options.emailEnabled.label')"
|
:label="$tc('vrpayment-settings.settingForm.options.emailEnabled.label')"
|
||||||
:helpText="$tc('vrpayment-settings.settingForm.options.emailEnabled.tooltipText')"
|
:helpText="$tc('vrpayment-settings.settingForm.options.emailEnabled.tooltipText')"
|
||||||
:disabled="props.isInherited"
|
:disabled="props.isInherited"
|
||||||
:checked="props.currentValue"
|
:value="props.currentValue"
|
||||||
@update:checked="props.updateCurrentValue">
|
@update:value="props.updateCurrentValue">
|
||||||
</mt-switch>
|
</sw-switch-field>
|
||||||
</template>
|
</template>
|
||||||
</sw-inherit-wrapper>
|
</sw-inherit-wrapper>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
+4
-4
@@ -7,16 +7,16 @@
|
|||||||
:inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_STOREFRONT_INVOICE_DOWNLOAD_ENABLED]"
|
:inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_STOREFRONT_INVOICE_DOWNLOAD_ENABLED]"
|
||||||
:customInheritationCheckFunction="checkBoolFieldInheritance">
|
:customInheritationCheckFunction="checkBoolFieldInheritance">
|
||||||
<template #content="props">
|
<template #content="props">
|
||||||
<mt-switch
|
<sw-switch-field
|
||||||
:name="CONFIG_STOREFRONT_INVOICE_DOWNLOAD_ENABLED"
|
:name="CONFIG_STOREFRONT_INVOICE_DOWNLOAD_ENABLED"
|
||||||
bordered
|
bordered
|
||||||
:mapInheritance="props"
|
:mapInheritance="props"
|
||||||
:label="$tc('vrpayment-settings.settingForm.storefrontOptions.invoiceDownloadEnabled.label')"
|
:label="$tc('vrpayment-settings.settingForm.storefrontOptions.invoiceDownloadEnabled.label')"
|
||||||
:helpText="$tc('vrpayment-settings.settingForm.storefrontOptions.invoiceDownloadEnabled.tooltipText')"
|
:helpText="$tc('vrpayment-settings.settingForm.storefrontOptions.invoiceDownloadEnabled.tooltipText')"
|
||||||
:disabled="props.isInherited"
|
:disabled="props.isInherited"
|
||||||
:checked="props.currentValue"
|
:value="props.currentValue"
|
||||||
@update:checked="props.updateCurrentValue">
|
@update:value="props.updateCurrentValue">
|
||||||
</mt-switch>
|
</sw-switch-field>
|
||||||
</template>
|
</template>
|
||||||
</sw-inherit-wrapper>
|
</sw-inherit-wrapper>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+5
-1
@@ -10,6 +10,9 @@ export const CONFIG_USER_ID = CONFIG_DOMAIN + '.' + 'userId';
|
|||||||
export const CONFIG_STOREFRONT_WEBHOOKS_UPDATE_ENABLED = CONFIG_DOMAIN + '.' + 'storefrontWebhooksUpdateEnabled';
|
export const CONFIG_STOREFRONT_WEBHOOKS_UPDATE_ENABLED = CONFIG_DOMAIN + '.' + 'storefrontWebhooksUpdateEnabled';
|
||||||
export const CONFIG_STOREFRONT_PAYMENTS_UPDATE_ENABLED = CONFIG_DOMAIN + '.' + 'storefrontPaymentsUpdateEnabled';
|
export const CONFIG_STOREFRONT_PAYMENTS_UPDATE_ENABLED = CONFIG_DOMAIN + '.' + 'storefrontPaymentsUpdateEnabled';
|
||||||
|
|
||||||
|
// References vendor/shopware/core/Defaults.php::SALES_CHANNEL_TYPE_STOREFRONT
|
||||||
|
export const STOREFRONT_SALES_CHANNEL_TYPE_ID = '8a243080f92e4c719546314b577cf82b';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
CONFIG_DOMAIN,
|
CONFIG_DOMAIN,
|
||||||
CONFIG_APPLICATION_KEY,
|
CONFIG_APPLICATION_KEY,
|
||||||
@@ -21,5 +24,6 @@ export default {
|
|||||||
CONFIG_STOREFRONT_INVOICE_DOWNLOAD_ENABLED,
|
CONFIG_STOREFRONT_INVOICE_DOWNLOAD_ENABLED,
|
||||||
CONFIG_USER_ID,
|
CONFIG_USER_ID,
|
||||||
CONFIG_STOREFRONT_WEBHOOKS_UPDATE_ENABLED,
|
CONFIG_STOREFRONT_WEBHOOKS_UPDATE_ENABLED,
|
||||||
CONFIG_STOREFRONT_PAYMENTS_UPDATE_ENABLED
|
CONFIG_STOREFRONT_PAYMENTS_UPDATE_ENABLED,
|
||||||
|
STOREFRONT_SALES_CHANNEL_TYPE_ID
|
||||||
};
|
};
|
||||||
+5
-8
@@ -35,6 +35,7 @@
|
|||||||
|
|
||||||
{% block vrpayment_settings_content_card_channel_config %}
|
{% block vrpayment_settings_content_card_channel_config %}
|
||||||
<sw-sales-channel-config v-model:value="config"
|
<sw-sales-channel-config v-model:value="config"
|
||||||
|
v-model:selectedSalesChannelId="selectedSalesChannelId"
|
||||||
ref="configComponent"
|
ref="configComponent"
|
||||||
:domain="CONFIG_DOMAIN">
|
:domain="CONFIG_DOMAIN">
|
||||||
|
|
||||||
@@ -45,14 +46,10 @@
|
|||||||
<mt-card title="Sales Channel Switch">
|
<mt-card title="Sales Channel Switch">
|
||||||
|
|
||||||
{% block vrpayment_settings_content_card_channel_config_sales_channel_card_title %}
|
{% block vrpayment_settings_content_card_channel_config_sales_channel_card_title %}
|
||||||
<sw-single-select
|
<sw-sales-channel-switch
|
||||||
:value="selectedSalesChannelId"
|
ref="channelSwitch"
|
||||||
:options="salesChannel.map(sc => ({ id: sc.id, name: sc.translated.name }))"
|
@change-sales-channel-id="onSalesChannelSwitchChange($event, onInput)">
|
||||||
labelProperty="name"
|
</sw-sales-channel-switch>
|
||||||
valueProperty="id"
|
|
||||||
:isLoading="isLoading"
|
|
||||||
@update:value="onInput"
|
|
||||||
/>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block vrpayment_settings_content_card_channel_config_sales_channel_card_footer %}
|
{% block vrpayment_settings_content_card_channel_config_sales_channel_card_footer %}
|
||||||
<template #footer>
|
<template #footer>
|
||||||
|
|||||||
+100
-9
@@ -3,7 +3,7 @@
|
|||||||
import template from './index.html.twig';
|
import template from './index.html.twig';
|
||||||
import constants from './configuration-constants';
|
import constants from './configuration-constants';
|
||||||
|
|
||||||
const {Component, Mixin} = Shopware;
|
const { Component, Mixin } = Shopware;
|
||||||
|
|
||||||
Component.register('vrpayment-settings', {
|
Component.register('vrpayment-settings', {
|
||||||
|
|
||||||
@@ -11,7 +11,8 @@ Component.register('vrpayment-settings', {
|
|||||||
|
|
||||||
inject: [
|
inject: [
|
||||||
'acl',
|
'acl',
|
||||||
'VRPaymentConfigurationService'
|
'VRPaymentConfigurationService',
|
||||||
|
'repositoryFactory'
|
||||||
],
|
],
|
||||||
|
|
||||||
mixins: [
|
mixins: [
|
||||||
@@ -40,6 +41,7 @@ Component.register('vrpayment-settings', {
|
|||||||
|
|
||||||
isSetDefaultPaymentSuccessful: false,
|
isSetDefaultPaymentSuccessful: false,
|
||||||
isSettingDefaultPaymentMethods: false,
|
isSettingDefaultPaymentMethods: false,
|
||||||
|
selectedSalesChannelId: null,
|
||||||
|
|
||||||
configIntegrationDefaultValue: 'payment_page',
|
configIntegrationDefaultValue: 'payment_page',
|
||||||
configEmailEnabledDefaultValue: true,
|
configEmailEnabledDefaultValue: true,
|
||||||
@@ -69,7 +71,7 @@ Component.register('vrpayment-settings', {
|
|||||||
config: {
|
config: {
|
||||||
handler(configData) {
|
handler(configData) {
|
||||||
const defaultConfig = (this.$refs.configComponent.allConfigs || {}).null || {};
|
const defaultConfig = (this.$refs.configComponent.allConfigs || {}).null || {};
|
||||||
const salesChannelId = this.$refs.configComponent.selectedSalesChannelId;
|
const salesChannelId = this.selectedSalesChannelId;
|
||||||
if (salesChannelId === null) {
|
if (salesChannelId === null) {
|
||||||
|
|
||||||
this.applicationKeyFilled = !!this.config[this.CONFIG_APPLICATION_KEY];
|
this.applicationKeyFilled = !!this.config[this.CONFIG_APPLICATION_KEY];
|
||||||
@@ -136,6 +138,16 @@ Component.register('vrpayment-settings', {
|
|||||||
this.$emit('update:value', configData);
|
this.$emit('update:value', configData);
|
||||||
},
|
},
|
||||||
deep: true
|
deep: true
|
||||||
|
},
|
||||||
|
|
||||||
|
selectedSalesChannelId: {
|
||||||
|
handler(newValue) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.$refs.channelSwitch) {
|
||||||
|
this.$refs.channelSwitch.salesChannelId = newValue || '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -161,21 +173,93 @@ Component.register('vrpayment-settings', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getInheritValue(key) {
|
getInheritValue(key) {
|
||||||
if (this.selectedSalesChannelId == null ) {
|
if (this.selectedSalesChannelId == null) {
|
||||||
return this.actualConfigData[key];
|
return this.actualConfigData[key];
|
||||||
} else {
|
} else {
|
||||||
return this.allConfigs['null'][key];
|
return this.allConfigs['null'][key];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onSave() {
|
async onSave() {
|
||||||
if (!(this.spaceIdFilled && this.userIdFilled && this.applicationKeyFilled)) {
|
if (!(this.spaceIdFilled && this.userIdFilled && this.applicationKeyFilled)) {
|
||||||
this.setErrorStates();
|
this.setErrorStates();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.isLoading = true;
|
||||||
|
const validationError = await this.validateHeadlessIntegration();
|
||||||
|
|
||||||
|
if (validationError === 'HEADLESS') {
|
||||||
|
this.createNotificationError({
|
||||||
|
title: this.$tc('vrpayment-settings.settingForm.titleError'),
|
||||||
|
message: this.$tc('vrpayment-settings.settingForm.messageHeadlessIntegrationError')
|
||||||
|
});
|
||||||
|
this.isLoading = false;
|
||||||
|
return;
|
||||||
|
} else if (validationError === 'GLOBAL') {
|
||||||
|
this.createNotificationError({
|
||||||
|
title: this.$tc('vrpayment-settings.settingForm.titleError'),
|
||||||
|
message: this.$tc('vrpayment-settings.settingForm.messageGlobalIframeError')
|
||||||
|
});
|
||||||
|
this.isLoading = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.save();
|
this.save();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async validateHeadlessIntegration() {
|
||||||
|
const salesChannelId = this.selectedSalesChannelId;
|
||||||
|
const currentIntegration = this.config[this.CONFIG_INTEGRATION];
|
||||||
|
|
||||||
|
// If integration is 'payment_page', it is always valid.
|
||||||
|
if (currentIntegration === 'payment_page') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const salesChannelRepo = this.repositoryFactory.create('sales_channel');
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (salesChannelId) {
|
||||||
|
// Specific Sales Channel Check
|
||||||
|
const salesChannel = await salesChannelRepo.get(salesChannelId, Shopware.Context.api);
|
||||||
|
|
||||||
|
const currentTypeId = salesChannel.typeId.replace(/-/g, '');
|
||||||
|
|
||||||
|
// REST-2: Inverted Logic
|
||||||
|
// We only allow 'iframe' integration if the Sales Channel is of type 'Storefront'.
|
||||||
|
// Any other type (Headless, Product Export, Custom, etc.) does not support Iframe injection.
|
||||||
|
const isStorefront = currentTypeId === constants.STOREFRONT_SALES_CHANNEL_TYPE_ID;
|
||||||
|
if (!isStorefront) {
|
||||||
|
return 'HEADLESS';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Global Scope ("All Sales Channels") Check
|
||||||
|
// We must check if there is ANY Sales Channel that is NOT Storefront.
|
||||||
|
// If so, we cannot allow "Iframe" globally, as it would break those channels.
|
||||||
|
const criteria = new Shopware.Data.Criteria();
|
||||||
|
criteria.addFilter(
|
||||||
|
Shopware.Data.Criteria.not(
|
||||||
|
'AND',
|
||||||
|
[Shopware.Data.Criteria.equals('typeId', constants.STOREFRONT_SALES_CHANNEL_TYPE_ID)]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
criteria.setLimit(1); // We only need to know if at least one exists
|
||||||
|
|
||||||
|
const result = await salesChannelRepo.search(criteria, Shopware.Context.api);
|
||||||
|
|
||||||
|
if (result.total > 0) {
|
||||||
|
return 'GLOBAL';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
|
|
||||||
@@ -197,7 +281,7 @@ Component.register('vrpayment-settings', {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.VRPaymentConfigurationService.registerWebHooks(this.$refs.configComponent.selectedSalesChannelId)
|
this.VRPaymentConfigurationService.registerWebHooks(this.selectedSalesChannelId)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.createNotificationSuccess({
|
this.createNotificationSuccess({
|
||||||
title: this.$tc('vrpayment-settings.settingForm.titleSuccess'),
|
title: this.$tc('vrpayment-settings.settingForm.titleSuccess'),
|
||||||
@@ -218,7 +302,7 @@ Component.register('vrpayment-settings', {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.VRPaymentConfigurationService.synchronizePaymentMethodConfiguration(this.$refs.configComponent.selectedSalesChannelId)
|
this.VRPaymentConfigurationService.synchronizePaymentMethodConfiguration(this.selectedSalesChannelId)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.createNotificationSuccess({
|
this.createNotificationSuccess({
|
||||||
title: this.$tc('vrpayment-settings.settingForm.titleSuccess'),
|
title: this.$tc('vrpayment-settings.settingForm.titleSuccess'),
|
||||||
@@ -235,7 +319,7 @@ Component.register('vrpayment-settings', {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
installOrderDeliveryStates(){
|
installOrderDeliveryStates() {
|
||||||
this.VRPaymentConfigurationService.installOrderDeliveryStates()
|
this.VRPaymentConfigurationService.installOrderDeliveryStates()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.createNotificationSuccess({
|
this.createNotificationSuccess({
|
||||||
@@ -255,7 +339,7 @@ Component.register('vrpayment-settings', {
|
|||||||
onSetPaymentMethodDefault() {
|
onSetPaymentMethodDefault() {
|
||||||
this.isSettingDefaultPaymentMethods = true;
|
this.isSettingDefaultPaymentMethods = true;
|
||||||
this.VRPaymentConfigurationService.setVRPaymentAsSalesChannelPaymentDefault(
|
this.VRPaymentConfigurationService.setVRPaymentAsSalesChannelPaymentDefault(
|
||||||
this.$refs.configComponent.selectedSalesChannelId
|
this.selectedSalesChannelId
|
||||||
).then(() => {
|
).then(() => {
|
||||||
this.isSettingDefaultPaymentMethods = false;
|
this.isSettingDefaultPaymentMethods = false;
|
||||||
this.isSetDefaultPaymentSuccessful = true;
|
this.isSetDefaultPaymentSuccessful = true;
|
||||||
@@ -312,6 +396,13 @@ Component.register('vrpayment-settings', {
|
|||||||
});
|
});
|
||||||
this.isTesting = false;
|
this.isTesting = false;
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onSalesChannelSwitchChange(id, onInput) {
|
||||||
|
this.selectedSalesChannelId = id;
|
||||||
|
if (typeof onInput === 'function') {
|
||||||
|
onInput(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -56,6 +56,8 @@
|
|||||||
"messagePaymentMethodConfigurationUpdated": "VRPayment PaymentMethodConfiguration has been registered.",
|
"messagePaymentMethodConfigurationUpdated": "VRPayment PaymentMethodConfiguration has been registered.",
|
||||||
"messageWebHookError": "VRPayment WebHook could not be saved. Please check your credentials.",
|
"messageWebHookError": "VRPayment WebHook could not be saved. Please check your credentials.",
|
||||||
"messageWebHookUpdated": "VRPayment WebHook has been updated.",
|
"messageWebHookUpdated": "VRPayment WebHook has been updated.",
|
||||||
|
"messageHeadlessIntegrationError": "Iframe integration is only supported for Storefront Sales Channels.",
|
||||||
|
"messageGlobalIframeError": "Iframe integration cannot be set globally because you have non-Storefront Sales Channels.",
|
||||||
"options": {
|
"options": {
|
||||||
"cardTitle": "Options",
|
"cardTitle": "Options",
|
||||||
"emailEnabled": {
|
"emailEnabled": {
|
||||||
|
|||||||
+8
File diff suppressed because one or more lines are too long
+154
-9
@@ -3,25 +3,170 @@
|
|||||||
// noinspection NpmUsedModulesInstalled
|
// noinspection NpmUsedModulesInstalled
|
||||||
import Plugin from 'src/plugin-system/plugin.class';
|
import Plugin from 'src/plugin-system/plugin.class';
|
||||||
import HttpClient from 'src/service/http-client.service';
|
import HttpClient from 'src/service/http-client.service';
|
||||||
|
import DomAccess from 'src/helper/dom-access.helper';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VRPaymentCheckoutPlugin
|
||||||
|
*
|
||||||
|
* This plugin handles the initialization and lifecycle of the WhitelabelMachineName payment iframe
|
||||||
|
* within the Shopware 6 checkout confirm page.
|
||||||
|
*/
|
||||||
class VRPaymentCheckoutPlugin extends Plugin {
|
class VRPaymentCheckoutPlugin extends Plugin {
|
||||||
|
|
||||||
static options = {
|
static options = {
|
||||||
payment_method_tabs: 'ul.vrpayment-payment-panel li',
|
payment_panel_id: 'vrpayment-payment-panel',
|
||||||
payment_method_iframe_prefix: 'iframe_payment_method_',
|
payment_method_iframe_id: 'vrpayment-payment-iframe',
|
||||||
payment_method_iframe_class: '.vrpayment-payment-iframe',
|
|
||||||
payment_method_handler_name: 'vrpayment_payment_handler',
|
|
||||||
payment_method_handler_prefix: 'vrpayment_handler_',
|
|
||||||
payment_method_handler_status: 'input[name="vrpayment_payment_handler_validation_status"]',
|
payment_method_handler_status: 'input[name="vrpayment_payment_handler_validation_status"]',
|
||||||
payment_form: 'confirmOrderForm',
|
payment_form_id: 'confirmOrderForm',
|
||||||
|
loader_id: 'vrpaymentLoader',
|
||||||
|
checkout_url_id: 'checkoutUrl',
|
||||||
|
cart_recreate_url_id: 'cartRecreateUrl',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the plugin, variables, and events.
|
||||||
|
*/
|
||||||
init() {
|
init() {
|
||||||
// @TODO Move JS to Plugin
|
try {
|
||||||
this._client = new HttpClient(window.accessKey);
|
this._initVariables();
|
||||||
|
this._registerEvents();
|
||||||
|
this._getIframe();
|
||||||
|
} catch (e) {
|
||||||
|
// Silently fail if elements are not found; this allows the plugin to be loaded on pages where it might be conditionally absent.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds and stores references to relevant DOM elements.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_initVariables() {
|
||||||
|
this.checkoutUrl = DomAccess.getElement(document, `#${this.options.checkout_url_id}`).value;
|
||||||
|
this.cartRecreateUrl = DomAccess.getElement(document, `#${this.options.cart_recreate_url_id}`).value;
|
||||||
|
this.paymentForm = DomAccess.getElement(document, `#${this.options.payment_form_id}`);
|
||||||
|
this.paymentPanel = this.el;
|
||||||
|
this.iframeContainer = DomAccess.getElement(this.paymentPanel, `#${this.options.payment_method_iframe_id}`);
|
||||||
|
this.handler = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers event listeners for the payment form and browser history.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_registerEvents() {
|
||||||
|
this.paymentForm.addEventListener('submit', this._submitPayment.bind(this), false);
|
||||||
|
|
||||||
|
// Handle back button/popstate to ensure cart consistency
|
||||||
|
window.addEventListener('popstate', this._onPopstate.bind(this), false);
|
||||||
|
window.history.pushState({}, document.title, this.cartRecreateUrl);
|
||||||
|
window.history.pushState({}, document.title, this.checkoutUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles browser back button activity.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_onPopstate() {
|
||||||
|
if (window.history.state == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.location.href = this.cartRecreateUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intercepts the form submission to validate the iframe content first.
|
||||||
|
* @param {Event} event
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_submitPayment(event) {
|
||||||
|
this._activateLoader(true);
|
||||||
|
if (this.handler) {
|
||||||
|
this.handler.validate();
|
||||||
|
event.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the WhitelabelMachineName Iframe handler and creates the iframe.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_getIframe() {
|
||||||
|
const paymentMethodConfigurationId = this.paymentPanel.dataset.id;
|
||||||
|
|
||||||
|
if (!this.handler) {
|
||||||
|
// IframeCheckoutHandler is expected to be global from the SDK script
|
||||||
|
if (typeof window.IframeCheckoutHandler === 'function') {
|
||||||
|
this.handler = window.IframeCheckoutHandler(paymentMethodConfigurationId);
|
||||||
|
this.handler.setValidationCallback(this._validationCallBack.bind(this));
|
||||||
|
this.handler.setInitializeCallback(this._hideLoader.bind(this));
|
||||||
|
this.handler.setHeightChangeCallback((height) => {
|
||||||
|
if (height < 1) {
|
||||||
|
this.handler.submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.handler.create(this.iframeContainer);
|
||||||
|
setTimeout(this._hideLoader.bind(this), 10000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for iframe validation results.
|
||||||
|
* @param {Object} validationResult
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_validationCallBack(validationResult) {
|
||||||
|
const statusInputs = document.querySelectorAll(this.options.payment_method_handler_status);
|
||||||
|
if (validationResult.success) {
|
||||||
|
statusInputs.forEach(input => {
|
||||||
|
input.value = 'true';
|
||||||
|
});
|
||||||
|
this.handler.submit();
|
||||||
|
} else {
|
||||||
|
statusInputs.forEach(input => {
|
||||||
|
input.value = 'false';
|
||||||
|
});
|
||||||
|
this._activateLoader(false);
|
||||||
|
if (validationResult.errors) {
|
||||||
|
this._showErrors(validationResult.errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables or disables buttons on the page to indicate loading.
|
||||||
|
* @param {boolean} activate
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_activateLoader(activate) {
|
||||||
|
const buttons = document.querySelectorAll('button');
|
||||||
|
buttons.forEach(button => {
|
||||||
|
button.disabled = activate;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides the loader overlay once the iframe is ready.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_hideLoader() {
|
||||||
|
const loader = document.getElementById(this.options.loader_id);
|
||||||
|
if (loader && loader.parentNode) {
|
||||||
|
loader.parentNode.removeChild(loader);
|
||||||
|
}
|
||||||
|
this._activateLoader(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays validation errors to the user.
|
||||||
|
* @param {Array} errors
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_showErrors(errors) {
|
||||||
|
// Fallback to alert if no native Shopware mechanism is easily accessible here
|
||||||
|
alert(errors.join('\n'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default VRPaymentCheckoutPlugin;
|
export default VRPaymentCheckoutPlugin;
|
||||||
@@ -13,7 +13,9 @@
|
|||||||
<import resource="./services/core/storefront/checkout.xml"/>
|
<import resource="./services/core/storefront/checkout.xml"/>
|
||||||
|
|
||||||
<import resource="./services/core/checkout.xml"/>
|
<import resource="./services/core/checkout.xml"/>
|
||||||
|
<import resource="./services/core/checkout_services.xml"/>
|
||||||
<import resource="./services/core/settings.xml"/>
|
<import resource="./services/core/settings.xml"/>
|
||||||
|
<import resource="./services/core/store_api.xml"/>
|
||||||
<import resource="./services/core/util.xml"/>
|
<import resource="./services/core/util.xml"/>
|
||||||
</imports>
|
</imports>
|
||||||
<services>
|
<services>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
<service id="VRPaymentPayment\Core\Api\Refund\Controller\RefundController" public="true">
|
<service id="VRPaymentPayment\Core\Api\Refund\Controller\RefundController" public="true">
|
||||||
<argument type="service" id="VRPaymentPayment\Core\Api\Refund\Service\RefundService"/>
|
<argument type="service" id="VRPaymentPayment\Core\Api\Refund\Service\RefundService"/>
|
||||||
<argument type="service" id="VRPaymentPayment\Core\Settings\Service\SettingsService"/>
|
<argument type="service" id="VRPaymentPayment\Core\Settings\Service\SettingsService"/>
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Api\Transaction\Service\TransactionService"/>
|
||||||
<call method="setLogger">
|
<call method="setLogger">
|
||||||
<argument type="service" id="monolog.logger.vrpayment_payment"/>
|
<argument type="service" id="monolog.logger.vrpayment_payment"/>
|
||||||
</call>
|
</call>
|
||||||
|
|||||||
@@ -58,6 +58,8 @@
|
|||||||
<argument type="service" id="service_container"/>
|
<argument type="service" id="service_container"/>
|
||||||
<argument type="service" id="VRPaymentPayment\Core\Util\LocaleCodeProvider"/>
|
<argument type="service" id="VRPaymentPayment\Core\Util\LocaleCodeProvider"/>
|
||||||
<argument type="service" id="VRPaymentPayment\Core\Settings\Service\SettingsService"/>
|
<argument type="service" id="VRPaymentPayment\Core\Settings\Service\SettingsService"/>
|
||||||
|
<!-- Cache for headless transaction persistence -->
|
||||||
|
<argument type="service" id="cache.system"/>
|
||||||
<call method="setLogger">
|
<call method="setLogger">
|
||||||
<argument type="service" id="monolog.logger.vrpayment_payment"/>
|
<argument type="service" id="monolog.logger.vrpayment_payment"/>
|
||||||
</call>
|
</call>
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" ?>
|
||||||
|
|
||||||
|
<container xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://symfony.com/schema/dic/services"
|
||||||
|
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||||
|
|
||||||
|
<services>
|
||||||
|
<service id="VRPaymentPayment\Core\Checkout\Service\TransactionManagementService">
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Api\Transaction\Service\TransactionService"/>
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Settings\Service\SettingsService"/>
|
||||||
|
<!-- Cache for headless transaction persistence -->
|
||||||
|
<argument type="service" id="cache.system"/>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<service id="VRPaymentPayment\Core\Checkout\Service\PaymentMethodFilterService">
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Settings\Service\SettingsService"/>
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Api\Transaction\Service\TransactionService"/>
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Service\PaymentMethodConfigurationService"/>
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Util\PaymentMethodUtil"/>
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Checkout\Service\TransactionManagementService"/>
|
||||||
|
<argument type="service" id="Shopware\Core\Checkout\Cart\SalesChannel\CartService"/>
|
||||||
|
<call method="setLogger">
|
||||||
|
<argument type="service" id="monolog.logger.vrpayment_payment"/>
|
||||||
|
</call>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<service id="VRPaymentPayment\Core\Checkout\Service\PaymentIntegrationService">
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Api\Transaction\Service\TransactionService"/>
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Settings\Service\SettingsService"/>
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Checkout\Service\TransactionManagementService"/>
|
||||||
|
<argument type="service" id="router"/>
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Util\LocaleCodeProvider"/>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<service id="VRPaymentPayment\Core\Checkout\Service\CartRecoveryService">
|
||||||
|
<argument type="service" id="Shopware\Core\Checkout\Cart\SalesChannel\CartService"/>
|
||||||
|
<argument type="service" id="Shopware\Core\Checkout\Cart\LineItemFactoryRegistry"/>
|
||||||
|
<argument type="service" id="order.repository"/>
|
||||||
|
<argument type="service" id="Swag\CustomizedProducts\Core\Checkout\Cart\Route\AddCustomizedProductsToCartRoute" on-invalid="null"/>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<service id="VRPaymentPayment\Core\Checkout\Service\InvoiceService">
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Settings\Service\SettingsService"/>
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Api\Transaction\Service\TransactionService"/>
|
||||||
|
</service>
|
||||||
|
</services>
|
||||||
|
</container>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" ?>
|
||||||
|
|
||||||
|
<container xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://symfony.com/schema/dic/services"
|
||||||
|
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||||
|
|
||||||
|
<services>
|
||||||
|
<service id="VRPaymentPayment\Core\Checkout\PaymentMethod\SalesChannel\PaymentMethodRouteDecorator"
|
||||||
|
decorates="Shopware\Core\Checkout\Payment\SalesChannel\PaymentMethodRoute"
|
||||||
|
decoration-inner-name="VRPaymentPayment\Core\Checkout\PaymentMethod\SalesChannel\PaymentMethodRouteDecorator.inner">
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Checkout\PaymentMethod\SalesChannel\PaymentMethodRouteDecorator.inner"/>
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Checkout\Service\PaymentMethodFilterService"/>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<service id="VRPaymentPayment\Core\Checkout\StoreApi\Route\WhitelabelMachineNameTransactionInfoRoute">
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Checkout\Service\PaymentIntegrationService"/>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<service id="VRPaymentPayment\Core\Checkout\StoreApi\Route\CartRecoveryRoute">
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Checkout\Service\CartRecoveryService"/>
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Api\Transaction\Service\TransactionService"/>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<service id="VRPaymentPayment\Core\Checkout\StoreApi\Route\InvoiceRoute">
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Checkout\Service\InvoiceService"/>
|
||||||
|
</service>
|
||||||
|
</services>
|
||||||
|
</container>
|
||||||
@@ -7,9 +7,9 @@
|
|||||||
<services>
|
<services>
|
||||||
<!-- Controllers -->
|
<!-- Controllers -->
|
||||||
<service id="VRPaymentPayment\Core\Storefront\Account\Controller\AccountOrderController" public="true">
|
<service id="VRPaymentPayment\Core\Storefront\Account\Controller\AccountOrderController" public="true">
|
||||||
<argument type="service" id="VRPaymentPayment\Core\Settings\Service\SettingsService"/>
|
|
||||||
<argument type="service" id="VRPaymentPayment\Core\Api\Transaction\Service\TransactionService"/>
|
<argument type="service" id="VRPaymentPayment\Core\Api\Transaction\Service\TransactionService"/>
|
||||||
<argument type="service" id="Symfony\Component\HttpFoundation\RequestStack"/>
|
<argument type="service" id="Symfony\Component\HttpFoundation\RequestStack"/>
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Checkout\Service\InvoiceService"/>
|
||||||
<call method="setLogger">
|
<call method="setLogger">
|
||||||
<argument type="service" id="monolog.logger.vrpayment_payment"/>
|
<argument type="service" id="monolog.logger.vrpayment_payment"/>
|
||||||
</call>
|
</call>
|
||||||
|
|||||||
@@ -7,31 +7,25 @@
|
|||||||
<services>
|
<services>
|
||||||
<!-- Controllers -->
|
<!-- Controllers -->
|
||||||
<service id="VRPaymentPayment\Core\Storefront\Checkout\Controller\CheckoutController" public="true">
|
<service id="VRPaymentPayment\Core\Storefront\Checkout\Controller\CheckoutController" public="true">
|
||||||
<argument type="service" id="Shopware\Core\Checkout\Cart\LineItemFactoryRegistry"/>
|
|
||||||
<argument type="service" id="Shopware\Core\Checkout\Cart\SalesChannel\CartService"/>
|
|
||||||
<argument type="service" id="VRPaymentPayment\Core\Settings\Service\SettingsService"/>
|
<argument type="service" id="VRPaymentPayment\Core\Settings\Service\SettingsService"/>
|
||||||
<argument type="service" id="VRPaymentPayment\Core\Api\Transaction\Service\TransactionService"/>
|
|
||||||
<argument type="service" id="Shopware\Storefront\Page\GenericPageLoader"/>
|
<argument type="service" id="Shopware\Storefront\Page\GenericPageLoader"/>
|
||||||
<argument type="service" id="Shopware\Core\Checkout\Order\SalesChannel\OrderRoute"/>
|
<argument type="service" id="Shopware\Core\Checkout\Order\SalesChannel\OrderRoute"/>
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Checkout\Service\CartRecoveryService"/>
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Checkout\Service\PaymentIntegrationService"/>
|
||||||
|
<argument type="service" id="VRPaymentPayment\Core\Api\Transaction\Service\TransactionService"/>
|
||||||
<call method="setLogger">
|
<call method="setLogger">
|
||||||
<argument type="service" id="monolog.logger.vrpayment_payment"/>
|
<argument type="service" id="monolog.logger.vrpayment_payment"/>
|
||||||
</call>
|
</call>
|
||||||
<call method="setContainer">
|
<call method="setContainer">
|
||||||
<argument type="service" id="service_container"/>
|
<argument type="service" id="service_container"/>
|
||||||
</call>
|
</call>
|
||||||
<!-- Removed in 6.7 -->
|
|
||||||
<!-- <call method="setTwig">
|
|
||||||
<argument type="service" id="twig"/>
|
|
||||||
</call> -->
|
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<!-- Subscribers -->
|
<!-- Subscribers -->
|
||||||
<service id="VRPaymentPayment\Core\Storefront\Checkout\Subscriber\CheckoutSubscriber">
|
<service id="VRPaymentPayment\Core\Storefront\Checkout\Subscriber\CheckoutSubscriber">
|
||||||
<argument id="VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Service\PaymentMethodConfigurationService" type="service"/>
|
|
||||||
<argument id="VRPaymentPayment\Core\Api\Transaction\Service\TransactionService" type="service"/>
|
|
||||||
<argument id="VRPaymentPayment\Core\Settings\Service\SettingsService" type="service"/>
|
<argument id="VRPaymentPayment\Core\Settings\Service\SettingsService" type="service"/>
|
||||||
<argument id="VRPaymentPayment\Core\Util\PaymentMethodUtil" type="service"/>
|
<argument id="VRPaymentPayment\Core\Checkout\Service\PaymentMethodFilterService" type="service"/>
|
||||||
<argument id="payment_method.repository" type="service"/>
|
<argument id="VRPaymentPayment\Core\Checkout\Service\PaymentIntegrationService" type="service"/>
|
||||||
<call method="setLogger">
|
<call method="setLogger">
|
||||||
<argument type="service" id="monolog.logger.vrpayment_payment"/>
|
<argument type="service" id="monolog.logger.vrpayment_payment"/>
|
||||||
</call>
|
</call>
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"base": "/bundles/vrpaymentpayment/administration/",
|
||||||
|
"entryPoints": {
|
||||||
|
"v-r-payment-payment": {
|
||||||
|
"css": [
|
||||||
|
"/bundles/vrpaymentpayment/administration/assets/v-r-payment-payment-D4AH6HY2.css"
|
||||||
|
],
|
||||||
|
"dynamic": [],
|
||||||
|
"js": [
|
||||||
|
"/bundles/vrpaymentpayment/administration/assets/v-r-payment-payment-CPfpiGQp.js"
|
||||||
|
],
|
||||||
|
"legacy": false,
|
||||||
|
"preload": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"legacy": false,
|
||||||
|
"metadatas": {},
|
||||||
|
"version": [
|
||||||
|
"7.1.0",
|
||||||
|
7,
|
||||||
|
1,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"viteServer": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"main.js": {
|
||||||
|
"file": "assets/v-r-payment-payment-CPfpiGQp.js",
|
||||||
|
"name": "v-r-payment-payment",
|
||||||
|
"src": "main.js",
|
||||||
|
"isEntry": true,
|
||||||
|
"css": [
|
||||||
|
"assets/v-r-payment-payment-D4AH6HY2.css"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
|||||||
|
.sw-order-detail .sw-tabs{margin-top:40px}.sw-order-detail .sw-order-detail-base .mt-card-view__content{overflow-x:visible;overflow-y:visible}.vrpayment-order-detail__data{display:grid}.vrpayment-order-detail__heading{padding-top:15px}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{% sw_extends '@Storefront/storefront/component/payment/payment-method.html.twig' %}
|
||||||
|
|
||||||
|
{% block component_payment_method_description %}
|
||||||
|
{{ parent() }}
|
||||||
|
|
||||||
|
{# Check if the payment method handler belongs to WhitelabelMachineName #}
|
||||||
|
{% if payment.handlerIdentifier == 'VRPaymentPayment\\Core\\Checkout\\PaymentHandler\\VRPaymentPaymentHandler' %}
|
||||||
|
{# Retrieve the runtime-attached configuration from the extension #}
|
||||||
|
{% set vrpConfig = payment.extensions.vrpayment_config %}
|
||||||
|
|
||||||
|
{% if vrpConfig %}
|
||||||
|
{% set paymentMethodConfigurationId = vrpConfig.paymentMethodConfigurationId %}
|
||||||
|
|
||||||
|
{# Include the iframe container template #}
|
||||||
|
{% sw_include '@VRPaymentPayment/storefront/component/payment/vrpayment_iframe.html.twig' with {
|
||||||
|
'paymentMethodConfigurationId': paymentMethodConfigurationId
|
||||||
|
} %}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<div id="vrpayment-payment-panel"
|
||||||
|
class="vrpayment-payment-panel"
|
||||||
|
data-vrpayment-checkout-plugin="true"
|
||||||
|
data-id="{{ paymentMethodConfigurationId }}">
|
||||||
|
<div id="vrpaymentLoader"><div></div></div>
|
||||||
|
<input value="false" type="hidden" name="vrpayment_payment_handler_validation_status"
|
||||||
|
form="confirmOrderForm">
|
||||||
|
<div id="vrpayment-payment-iframe"
|
||||||
|
class="vrpayment-payment-iframe"></div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{% sw_extends '@Storefront/storefront/page/checkout/confirm/index.html.twig' %}
|
||||||
|
|
||||||
|
{% block base_body_script %}
|
||||||
|
{{ parent() }}
|
||||||
|
|
||||||
|
{# Provide WhitelabelMachineName integration data if available in page extensions #}
|
||||||
|
{% if page.extensions.vRPaymentData %}
|
||||||
|
{% set vrpData = page.extensions.vRPaymentData %}
|
||||||
|
|
||||||
|
{# Hidden inputs required by the JS components #}
|
||||||
|
<input type="hidden" id="cartRecreateUrl" value="{{ vrpData.cartRecreateUrl }}" />
|
||||||
|
<input type="hidden" id="checkoutUrl" value="{{ vrpData.checkoutUrl }}" />
|
||||||
|
|
||||||
|
{# Load WhitelabelMachineName SDK scripts #}
|
||||||
|
{% if vrpData.deviceJavascriptUrl %}
|
||||||
|
<script src="{{ vrpData.deviceJavascriptUrl }}" async="async"></script>
|
||||||
|
{% endif %}
|
||||||
|
{% if vrpData.javascriptUrl %}
|
||||||
|
<script src="{{ vrpData.javascriptUrl }}"></script>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{# We don't need app.js if we use the modern plugin, but we might keep it for now if other pages depend on it.
|
||||||
|
However, for the confirm page, the modern plugin registered in main.js will take over
|
||||||
|
whenever it finds [data-vrpayment-checkout-plugin]. #}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user