mirror of
https://github.com/vr-payment/shopware-6.git
synced 2026-06-04 10:53:18 +00:00
Release 7.1.0
This commit is contained in:
@@ -1,2 +0,0 @@
|
||||
src/Resources/app/storefront/dist/storefront/js/*
|
||||
src/Resources/public/administration/js/*
|
||||
-361
@@ -1,361 +0,0 @@
|
||||
# 7.0.1
|
||||
## Feature
|
||||
- Add plugin version metric
|
||||
|
||||
## Bugfix
|
||||
- Fixed error message when refund amount exceeds total
|
||||
- Fixed bug where only 25 sales channels showed in the dropdown
|
||||
- Removed erroneous logs
|
||||
|
||||
# 7.0.0
|
||||
- Compatibility with Shopware 6.7.0
|
||||
|
||||
# 6.1.14
|
||||
- Disable Recreate Cart for Headless Storefront Order
|
||||
- Added the correct Exception Type to the finalize method
|
||||
|
||||
# 6.1.13
|
||||
- Updated English documentation
|
||||
- Added French, German and Italian documentation
|
||||
|
||||
# 6.1.12
|
||||
- Compatibility with 6.6.10.x
|
||||
- Prevent duplicate transactions being created when the first times out
|
||||
- Fix for error when changing space credentials
|
||||
- Payment status now shows refunded/partially refunded
|
||||
- Order delivery status now shows 'open' instead of 'hold'
|
||||
|
||||
# 6.1.11
|
||||
- Implement payment page integration.
|
||||
- Fixed bug with duplicate payment methods appearing
|
||||
|
||||
# 6.1.10
|
||||
- Multiple/bulk refund for line item.
|
||||
- Partial line item refund.
|
||||
- Fixed refund by line item option.
|
||||
- Composer dependencies are managed by Shopware system
|
||||
|
||||
# 6.1.9
|
||||
- Prevents calling a non existing method in webhook invocation.
|
||||
- Prevents error if delivery data is empty
|
||||
- Refactored composer.json, completing its require section.
|
||||
|
||||
# 6.1.8
|
||||
- Implemented key signing
|
||||
- Tax rate adjustment when products have different tax rates
|
||||
|
||||
# 6.1.7
|
||||
- Fixed messaging which showed that shipping and billing address were always the same
|
||||
|
||||
# 6.1.6
|
||||
- Bumped sdk version
|
||||
|
||||
# 6.1.5
|
||||
- Support for Shopware 6.6.3.1 and Vue 3
|
||||
|
||||
# 6.1.4
|
||||
- Improved plugin's settings form
|
||||
- Support for Shopware 6.6.2.0
|
||||
|
||||
# 6.1.3
|
||||
- Solvency check support for Powerpay and MF Group Invoice payment methods
|
||||
- Improved handling of abandoned transactions
|
||||
|
||||
# 6.1.2
|
||||
- Fixed redirect to confirmation page after reload
|
||||
|
||||
# 6.1.1
|
||||
- Fixed deprecated OrderNotFoundException
|
||||
|
||||
# 6.1.0
|
||||
- Fixed checkout issues after deactivating/activating plugin
|
||||
- Fixed plugin uninstall action
|
||||
- Fixed invoice payment method email function when order is shipped
|
||||
|
||||
# 6.0.0
|
||||
- Support for Shopware 6.6
|
||||
|
||||
# 5.0.7
|
||||
- Fix for refunds when a discount code is used
|
||||
|
||||
# 5.0.6
|
||||
- Version bump for marketplace release
|
||||
|
||||
# 5.0.5
|
||||
- Fixed an issue where all payment methods disappeared upon activation of the plugin.
|
||||
- Fixed an issue where the shopping cart doubled in quantity if the customer placed an item in the shopping cart, went to payment via TWINT, cancelled the transaction in TWINT using "Cancel payment", and was then redirected back to the store.
|
||||
|
||||
# 5.0.4
|
||||
- Adjust documentation and release command
|
||||
|
||||
# 5.0.3
|
||||
- Support of PHP 8.2
|
||||
- Cast to string the option of product attribute.
|
||||
- If delivery is null do not try to hold it.
|
||||
|
||||
# 5.0.2
|
||||
- Fix bug which happens when pressing back to shop or home clears cart.
|
||||
|
||||
# 5.0.1
|
||||
- Adjust documentation
|
||||
|
||||
# 5.0.0
|
||||
- Update composer file to only support 6.5
|
||||
|
||||
# 4.0.56
|
||||
- Adjustment of the documentation
|
||||
|
||||
# 4.0.54
|
||||
- Support of Shopware 6.5
|
||||
- Support of latest PHP SDK 3.2.0
|
||||
|
||||
# 4.0.53
|
||||
- Creation of a new column in the transaction table called erp_merchant_id
|
||||
|
||||
# 4.0.52
|
||||
- Support of Shopware 6.4.20.1
|
||||
|
||||
# 4.0.51
|
||||
- Wrong link format in error message.
|
||||
|
||||
# 4.0.45
|
||||
- Add additional information of the Credit Card (Validity Date, Pseudo Credit Card number and PayID) for transaction using this Payment Method
|
||||
- Compatibility SW v6.4.17.1
|
||||
-
|
||||
# 4.0.42
|
||||
- Rollback to remove functionality of sending version to payment portal
|
||||
|
||||
# 4.0.41
|
||||
- Sends to the payment portal a more specific version of shopware being used.
|
||||
|
||||
# 4.0.36
|
||||
- Compatibility SW v6.4.13.0
|
||||
|
||||
# 4.0.29
|
||||
- Fix to hide birthdate field if it's already provided
|
||||
- Tested against SW v6.4.9.0
|
||||
|
||||
# 4.0.28
|
||||
- Added italian translations
|
||||
- Tested against SW v6.4.9.0
|
||||
|
||||
# 4.0.26
|
||||
- Added documentation around flow builder
|
||||
|
||||
# 4.0.25
|
||||
- Fixed transaction invoice instant payment handling.
|
||||
- Tested with v6.4.7.0
|
||||
|
||||
# 4.0.24
|
||||
- Added refunds by amount
|
||||
|
||||
# 4.0.23
|
||||
- Fixed cart recreate function for custom products
|
||||
|
||||
# 4.0.22
|
||||
- Added support for French
|
||||
|
||||
# 4.0.21
|
||||
- Custom products options displayed as separate line items
|
||||
|
||||
# 4.0.20
|
||||
- Fixed company name for shipping address
|
||||
|
||||
# 4.0.17
|
||||
- Fixed settings to import webhooks and payment methods
|
||||
|
||||
# 4.0.16
|
||||
- Added settings to control update of webhooks and payment methods
|
||||
|
||||
# 4.0.15
|
||||
- Adjust VR Payment/SW6 documentation - how to do refunds
|
||||
|
||||
# 4.0.14
|
||||
- Support for Shopware 6.4.6
|
||||
|
||||
# 4.0.13
|
||||
- Loader Chrome IOS fix
|
||||
|
||||
# 4.0.12
|
||||
- Security fix
|
||||
|
||||
# 4.0.11
|
||||
- Reverted auto-submit on empty iframe as it is not working properly at all cases
|
||||
|
||||
# 4.0.10
|
||||
- Fixed "Allow payment change after checkout" option behavior
|
||||
|
||||
# 4.0.9
|
||||
- Allow to mark payment status as paid from status reminded
|
||||
|
||||
# 4.0.8
|
||||
- Checkout form auto submission implemented when iFrame returns no input fields
|
||||
|
||||
# 4.0.7
|
||||
- Fix Transaction Rollback error on unsupported languages
|
||||
|
||||
# 4.0.6
|
||||
- Fix for delivery state change error
|
||||
|
||||
# 4.0.5
|
||||
- Fixed plugin uninstall action
|
||||
|
||||
# 4.0.4
|
||||
- Line item based refunds
|
||||
|
||||
# 4.0.3
|
||||
- Update SDK
|
||||
|
||||
# 4.0.2
|
||||
- Fixed shipping line item name
|
||||
|
||||
# 4.0.1
|
||||
- Fixed tax calculation for custom products
|
||||
|
||||
# 4.0.0
|
||||
- Support for Shopware 6.4
|
||||
|
||||
# 3.1.0
|
||||
- Support for Custom Products plugin
|
||||
|
||||
# 3.0.0
|
||||
- Fix transaction versioning
|
||||
- Update SDK
|
||||
|
||||
# 2.1.1
|
||||
- Round amounts
|
||||
- Redirect if the cart can not be recreated
|
||||
|
||||
# 2.1.0
|
||||
- Fix email issues
|
||||
|
||||
# 2.0.0
|
||||
- Fix cart recreation on promotions
|
||||
- Remove availability rules
|
||||
- Handle orders less than or equal to zero
|
||||
|
||||
# 1.4.3
|
||||
- Silence missing order webhook errors
|
||||
- Fix iframe breakout
|
||||
|
||||
# 1.4.2
|
||||
- Fix payment method bug on first time install
|
||||
|
||||
# 1.4.1
|
||||
- Fetch active payment methods only
|
||||
|
||||
# 1.4.0
|
||||
- Fix payment method availability rule
|
||||
- Fix email sending
|
||||
- Cancel failed orders
|
||||
|
||||
# 1.3.0
|
||||
- Update payment method syncing
|
||||
|
||||
# 1.2.0
|
||||
- Add payment method availability rule
|
||||
- Hardcoded system languages
|
||||
|
||||
# 1.1.27
|
||||
- Retry orders on unavailable payment method
|
||||
|
||||
# 1.1.26
|
||||
- Fix locales and translations
|
||||
|
||||
# 1.1.25
|
||||
- Fix Email sending
|
||||
|
||||
# 1.1.24
|
||||
- Fix webhook response
|
||||
- Fix translation
|
||||
- Prepare for Shopware 6.4
|
||||
|
||||
# 1.1.23
|
||||
- Submit payment form when iframe has no fields
|
||||
|
||||
# 1.1.22
|
||||
- Order invoice download setting
|
||||
|
||||
# 1.1.21
|
||||
- Remove hardcoded Shopware API version
|
||||
|
||||
# 1.1.20
|
||||
- Update webhook URLs on plugin update
|
||||
- Add translations
|
||||
- Fix email bug
|
||||
|
||||
# 1.1.19
|
||||
- Allow customers to download order invoices
|
||||
|
||||
# 1.1.18
|
||||
- Test against Shopware 6.3
|
||||
- Fix error on invalid space id
|
||||
- Remove hardcoded Shopware API version
|
||||
|
||||
# 1.1.17
|
||||
- Use DAL on webhook locks
|
||||
|
||||
# 1.1.16
|
||||
- Only provide translations for available languages
|
||||
- Return CustomerCanceledAsyncPaymentException on cancelled transactions
|
||||
- Update SDK to 2.1.1
|
||||
|
||||
# 1.1.15
|
||||
- Send customer first name and last name from billing and shipping profiles
|
||||
- Respect Shop URL
|
||||
|
||||
# 1.1.14
|
||||
- Add cookies to the cookie manager
|
||||
- Resize icon to 40px * 40px
|
||||
- Fix line item attributes
|
||||
|
||||
# 1.1.13
|
||||
- Include vendor folder in Shopware store releases
|
||||
|
||||
# 1.1.12
|
||||
- Update doc path
|
||||
|
||||
# 1.1.11
|
||||
- Add documentation
|
||||
|
||||
# 1.1.10
|
||||
- Stop responding with server errors when orders are not found
|
||||
|
||||
# 1.1.9
|
||||
- Put try catch on webhook install
|
||||
|
||||
# 1.1.8
|
||||
- Remove unhelpful tickets info in release comments
|
||||
|
||||
# 1.1.7
|
||||
- Implement promotions
|
||||
- Code refactoring
|
||||
|
||||
# 1.1.6
|
||||
- Disable sales channel selection on showcases
|
||||
- Add product attributes to transaction payload
|
||||
|
||||
# 1.1.5
|
||||
- Fix settings bug
|
||||
|
||||
# 1.1.4
|
||||
- Disable changing credentials on the showcases
|
||||
|
||||
# 1.1.3
|
||||
- Make line item consistency default
|
||||
- Confirm transaction right away
|
||||
- Update settings descriptions
|
||||
|
||||
# 1.1.2
|
||||
- Prepare internal server side install for showcases and demos
|
||||
|
||||
# 1.1.1
|
||||
- Stop default emails being sent
|
||||
- Prettify payment page
|
||||
|
||||
# 1.1.0
|
||||
- Handle empty/default Settings values
|
||||
- Save refunds to db, and reload order tab on changes
|
||||
|
||||
# 1.0.0
|
||||
- First version of the VRPayment integrations for Shopware 6
|
||||
@@ -1,356 +0,0 @@
|
||||
# 7.0.1
|
||||
- Plugin-Versionsmetrik hinzugefügt
|
||||
- Fehlermeldung behoben, wenn der Rückerstattungsbetrag den Gesamtbetrag überschreitet
|
||||
- Fehler behoben, bei dem nur 25 Vertriebskanäle in der Dropdown-Liste angezeigt wurden
|
||||
- Fehlerhafte Protokolle entfernt
|
||||
|
||||
# 7.0.0
|
||||
- Kompatibilität mit Shopware 6.7.0
|
||||
|
||||
# 6.1.14
|
||||
– Warenkorb neu erstellen für Headless Storefront Order deaktivieren
|
||||
– Der korrekte Ausnahmetyp wurde zur Finalisierungsmethode hinzugefügt
|
||||
|
||||
# 6.1.13
|
||||
– Englische Dokumentation aktualisiert
|
||||
– Französische, deutsche und italienische Dokumentation hinzugefügt
|
||||
|
||||
# 6.1.12
|
||||
- Kompatibilität mit 6.6.10.x
|
||||
- Verhindern Sie, dass beim ersten Timeout doppelte Transaktionen erstellt werden
|
||||
- Fehler beim Ändern der Space-Anmeldeinformationen behoben
|
||||
- Der Zahlungsstatus zeigt jetzt „erstattet/teilweise erstattet“ an
|
||||
- Der Lieferstatus der Bestellung wird jetzt „Offen“ statt „Halten“ angezeigt.
|
||||
|
||||
# 6.1.11
|
||||
- Integration der Zahlungsseite implementieren.
|
||||
- Fehler mit doppelten angezeigten Zahlungsmethoden behoben
|
||||
|
||||
# 6.1.10
|
||||
- Mehrfache/gesammelte Rückerstattung für Einzelposten.
|
||||
- Teilweise Rückerstattung von Einzelposten.
|
||||
- Fehler bei der Rückerstattung durch die Option Einzelposten behoben.
|
||||
- Composer-Abhängigkeiten werden vom Shopware-System verwaltet
|
||||
|
||||
# 6.1.9
|
||||
- Verhindert das Aufrufen einer nicht existierenden Methode bei der Webhook-Ausführung.
|
||||
- Verhindert einen Fehler, wenn Lieferdaten leer sind.
|
||||
- Composer.json überarbeitet und den Abschnitt "require" vervollständigt.
|
||||
|
||||
# 6.1.8
|
||||
- Schlüsselsignatur implementiert
|
||||
- Steuersatzanpassung, wenn Produkte unterschiedliche Steuersätze haben
|
||||
|
||||
# 6.1.7
|
||||
- Meldung behoben, die anzeigte, dass Versand- und Rechnungsadresse immer identisch waren
|
||||
|
||||
# 6.1.6
|
||||
- Versionserhöhung für das SDK
|
||||
|
||||
# 6.1.5
|
||||
- Unterstützung von Shopware 6.6.3.1 und Vue 3
|
||||
|
||||
# 6.1.4
|
||||
- Das Einstellungsformular des Plugins wurde verbessert
|
||||
- Unterstützung von Shopware 6.6.2.0
|
||||
|
||||
# 6.1.3
|
||||
- Unterstützung der Bonitätsprüfung für die Zahlungsmethoden Powerpay und MF Group Invoice
|
||||
- Verbesserte Handhabung abgebrochener Transaktionen
|
||||
|
||||
# 6.1.2
|
||||
- Die Weiterleitung zur Bestätigungsseite nach dem Neuladen wurde behoben
|
||||
|
||||
# 6.1.1
|
||||
- Fixed deprecated OrderNotFoundException
|
||||
|
||||
# 6.1.0
|
||||
- Checkout-Probleme nach dem Deaktivieren/Aktivieren des Plugins behoben
|
||||
- Plugin-Deinstallationsaktion behoben
|
||||
- Die E-Mail-Funktion für die Rechnungszahlungsmethode beim Versand der Bestellung wurde korrigiert
|
||||
|
||||
# 6.0.0
|
||||
- Unterstützung von Shopware 6.6
|
||||
|
||||
# 5.0.7
|
||||
- Fix für Rückerstattungen, wenn ein Rabattcode verwendet wird
|
||||
|
||||
# 5.0.6
|
||||
- Versionserhöhung für die Marktveröffentlichung
|
||||
|
||||
# 5.0.5
|
||||
– Es wurde ein Problem behoben, bei dem alle Zahlungsmethoden nach der Aktivierung des Plugins verschwanden.
|
||||
- Es wurde ein Problem behoben, bei dem sich die Warenkorbmenge verdoppelte, wenn der Kunde einen Artikel in den Warenkorb legte, über TWINT zur Zahlung ging, die Transaktion in TWINT mit „Zahlung stornieren“ abbrach und anschliessend zurück zum Shop weitergeleitet wurde.
|
||||
|
||||
# 5.0.4
|
||||
- Dokumentation und Freigabebefehl anpassen
|
||||
|
||||
# 5.0.3
|
||||
- Unterstützung des neuesten PHP 8.2
|
||||
– Umwandeln, um die Option des Produktattributs in einen String umzuwandeln.
|
||||
- Wenn die Lieferung null ist, versuchen Sie nicht, sie zurückzuhalten.
|
||||
|
||||
# 5.0.2
|
||||
- Behebung eines Fehlers, der auftritt, wenn Sie auf „Zurück zum Shop“ oder „Zuhause“ drücken, um den Warenkorb zu löschen.
|
||||
|
||||
# 5.0.1
|
||||
- Anpassung der Dokumentation
|
||||
|
||||
# 5.0.0
|
||||
- Aktualisieren Sie die Composer-Datei so, dass sie nur 6.5 unterstützt
|
||||
|
||||
# 4.0.56
|
||||
- Anpassung der Dokumentation
|
||||
|
||||
# 4.0.54
|
||||
- Unterstützung von Shopware 6.5
|
||||
- Unterstützung des neuesten PHP SDK 3.2.0
|
||||
|
||||
# 4.0.53
|
||||
- Erstellung einer neuen Spalte in der Transaktionstabelle mit dem Namen erp_merchant_id
|
||||
|
||||
# 4.0.52
|
||||
- Unterstützung von Shopware 6.4.20.1
|
||||
|
||||
# 4.0.51
|
||||
- Falsches Linkformat in der Fehlermeldung.
|
||||
|
||||
# 4.0.50
|
||||
- Steuerinformationen wurden von der Versands- zu der Rechnungsstellung verschoben.
|
||||
- Teilweise war die Synchronisierung der Portal Daten zu SW6 unvollständig.
|
||||
- Lösen eines Fehlers: Der bezahlte Betrag im Portal wurde nicht an SW6 gemeldet.
|
||||
- Lösen eines Fehlers: Nach Wechsel der Zahlungsmethode im Checkout, wurde der Zahlungsstatus auf "bezahlt" gesetzt.
|
||||
- Lösen eines Fehlers: Beim Auflisten der Zahlungsmethoden durch den Kunden im Checkout Prozess.
|
||||
|
||||
# 4.0.45
|
||||
- Fügen Sie zusätzliche Informationen der Kreditkarte (Gültigkeitsdatum, Pseudo-Kreditkartennummer und PayID) für Transaktionen mit dieser Zahlungsmethode hinzu
|
||||
- Getestet mit SW v6.4.17.1
|
||||
-
|
||||
# 4.0.29
|
||||
- Korrektur zum Ausblenden des Geburtsdatumsfelds, wenn es bereits vorhanden ist
|
||||
- Getestet mit SW v6.4.9.0
|
||||
|
||||
# 4.0.28
|
||||
- Italienische Übersetzungen hinzugefügt
|
||||
- Getestet mit SW v6.4.9.0
|
||||
|
||||
# 4.0.26
|
||||
- Dokumentation zum Flow Builder hinzugefügt
|
||||
|
||||
# 4.0.25
|
||||
- Die Handhabung der sofortigen Zahlung von Transaktionsrechnungen wurde korrigiert.
|
||||
- Getestet mit v6.4.7.0
|
||||
|
||||
# 4.0.24
|
||||
- Rückerstattungen nach Betrag hinzugefügt
|
||||
|
||||
# 4.0.23
|
||||
- Korrigierte Warenkorb-Neuerstellungsfunktion für benutzerdefinierte Produkte
|
||||
|
||||
# 4.0.22
|
||||
- Unterstützung für Französisch hinzugefügt
|
||||
|
||||
# 4.0.21
|
||||
- Benutzerdefinierte Produktoptionen werden als separate Einzelposten angezeigt
|
||||
|
||||
# 4.0.20
|
||||
- Fester Firmenname für Lieferadresse
|
||||
|
||||
# 4.0.17
|
||||
- Einstellungen zum Importieren von Webhooks und Zahlungsmethoden korrigiert
|
||||
|
||||
# 4.0.16
|
||||
- Einstellungen zur Steuerung der Aktualisierung von Webhooks und Zahlungsmethoden hinzugefügt
|
||||
|
||||
# 4.0.15
|
||||
- VR Payment/SW6-Dokumentation anpassen – wie man Rückerstattungen durchführt
|
||||
|
||||
# 4.0.14
|
||||
- Unterstützung für Shopware 6.4.6
|
||||
|
||||
# 4.0.13
|
||||
- Loader Chrome IOS beheben
|
||||
|
||||
# 4.0.12
|
||||
- Implementierte Sicherheitskorrektur
|
||||
|
||||
# 4.0.11
|
||||
- Automatisches Senden bei leerem iframe zurückgesetzt, da es nicht in allen Fällen richtig funktioniert
|
||||
|
||||
# 4.0.10
|
||||
- Das Verhalten der Option "Zahlungsänderung nach der Kasse zulassen" behoben
|
||||
|
||||
# 4.0.9
|
||||
- Erlaube, den Zahlungsstatus als bezahlt ab Status erinnert zu markieren
|
||||
|
||||
# 4.0.8
|
||||
- Automatische Übermittlung des Checkout-Formulars implementiert, wenn iFrame keine Eingabefelder zurückgibt
|
||||
|
||||
# 4.0.7
|
||||
- Behebung des Transaktions-Rollback-Fehlers in nicht unterstützten Sprachen
|
||||
|
||||
# 4.0.6
|
||||
- Fehler beim Ändern des Lieferstatus behoben
|
||||
|
||||
# 4.0.5
|
||||
- Deinstallation Aktion des Plugins behoben
|
||||
|
||||
# 4.0.4
|
||||
- Erstattungen von Werbebuchungen
|
||||
|
||||
# 4.0.3
|
||||
- Aktualisieren Sie das SDK
|
||||
|
||||
# 4.0.2
|
||||
- Der Name der Versand-Einzelposten wurde korrigiert
|
||||
|
||||
# 4.0.1
|
||||
- Feste Steuerberechnung für kundenspezifische Produkte
|
||||
|
||||
# 4.0.0
|
||||
- Unterstützung für Shopware 6.4
|
||||
|
||||
# 3.1.0
|
||||
- Unterstützung für Custom Products Plugin
|
||||
|
||||
# 3.0.0
|
||||
- Korrigieren Sie die Transaktionsversionierung
|
||||
- Aktualisieren Sie das SDK
|
||||
|
||||
# 2.1.1
|
||||
- Runde Beträge
|
||||
- Weiterleiten, wenn der Wagen nicht neu erstellt werden kann
|
||||
|
||||
# 2.1.0
|
||||
- E-Mail-Probleme behoben
|
||||
|
||||
# 2.0.0
|
||||
- Warenkorb-Wiederherstellung bei Werbeaktionen korrigiert
|
||||
- Verfügbarkeitsregeln entfernt
|
||||
- Verbessertes Behandeln von Aufträge kleiner oder gleich Null
|
||||
|
||||
# 1.4.3
|
||||
- Fehlende Webhook-Fehler ausschließen
|
||||
- Iframe-Ausbruch behoben
|
||||
|
||||
# 1.4.2
|
||||
- Behebung des Fehlers bei der Zahlungsmethode bei der Erstinstallation
|
||||
|
||||
# 1.4.1
|
||||
- Rufen Sie nur aktive Zahlungsmethoden ab
|
||||
|
||||
# 1.4.0
|
||||
- Festlegen der Verfügbarkeitsregel für Zahlungsmethoden
|
||||
- E-Mail-Versand korrigiert
|
||||
- Fehlgeschlagene Bestellungen stornieren
|
||||
|
||||
# 1.3.0
|
||||
- Aktualisieren Sie die Synchronisierung der Zahlungsmethode
|
||||
|
||||
# 1.2.0
|
||||
- Verfügbarkeitsregel für Zahlungsmethoden hinzufügen
|
||||
- Hardcodierte Systemsprachen
|
||||
|
||||
# 1.1.27
|
||||
- Wiederholen Sie Bestellungen bei nicht verfügbarer Zahlungsmethode
|
||||
|
||||
# 1.1.26
|
||||
- Korrigieren Sie Gebietsschemas und Übersetzungen
|
||||
|
||||
# 1.1.25
|
||||
- E-Mail-Versand korrigiert
|
||||
|
||||
# 1.1.24
|
||||
- Webhook-Antwort korrigiert
|
||||
- Übersetzung korrigieren
|
||||
- Bereiten Sie sich auf Shopware vor 6.4
|
||||
|
||||
# 1.1.23
|
||||
- Senden Sie das Zahlungsformular, wenn iframe keine Felder enthält
|
||||
|
||||
# 1.1.22
|
||||
- Einstellung zum Herunterladen der Bestellrechnung
|
||||
|
||||
# 1.1.21
|
||||
- Entfernen Sie die fest codierte Shopware-API-Version
|
||||
|
||||
# 1.1.20
|
||||
- Aktualisieren Sie die Webhook-URLs beim Plugin-Update
|
||||
- Übersetzungen hinzufügen
|
||||
- E-Mail-Fehler behoben
|
||||
|
||||
# 1.1.19
|
||||
- Kunden können Bestellrechnungen herunterladen
|
||||
|
||||
# 1.1.18
|
||||
- Test gegen Shopware 6.3
|
||||
- Fehler bei ungültiger Speicherplatz-ID behoben
|
||||
- Entfernen Sie die fest codierte Shopware-API-Version
|
||||
|
||||
# 1.1.17
|
||||
- Verwenden Sie DAL für Webhook-Sperren
|
||||
|
||||
# 1.1.16
|
||||
- Stellen Sie nur Übersetzungen für verfügbare Sprachen bereit
|
||||
- CustomerCanceledAsyncPaymentException für stornierte Transaktionen zurückgeben
|
||||
- Aktualisieren Sie das SDK auf 2.1.1
|
||||
|
||||
# 1.1.15
|
||||
- Senden Sie den Vor- und Nachnamen des Kunden aus den Rechnungs- und Versandprofilen
|
||||
- Respektieren Sie die Shop-URL
|
||||
|
||||
# 1.1.14
|
||||
- Fügen Sie dem Cookie-Manager Cookies hinzu
|
||||
- Ändern Sie die Größe des Symbols auf 40px * 40px
|
||||
- Korrektur von Werbebuchungsattributen
|
||||
|
||||
# 1.1.13
|
||||
- Fügen Sie den Lieferantenordner in Shopware Store-Versionen ein
|
||||
|
||||
# 1.1.12
|
||||
- Dokumentpfad aktualisieren
|
||||
|
||||
# 1.1.11
|
||||
- Dokumentation hinzufügen
|
||||
|
||||
# 1.1.10
|
||||
- Reagieren Sie nicht mehr mit Serverfehlern, wenn keine Bestellungen gefunden werden
|
||||
|
||||
# 1.1.9
|
||||
- Setzen Sie try catch auf die Webhook-Installation
|
||||
|
||||
# 1.1.8
|
||||
- Entfernen Sie nicht hilfreiche Ticketinformationen in den Release-Kommentaren
|
||||
|
||||
# 1.1.7
|
||||
- Werbeaktionen durchführen
|
||||
- Code Refactoring
|
||||
|
||||
# 1.1.6
|
||||
- Deaktivieren Sie die Auswahl der Vertriebskanäle für Vitrinen
|
||||
- Fügen Sie der Transaktionsnutzlast Produktattribute hinzu
|
||||
|
||||
# 1.1.5
|
||||
- Einstellungsfehler behoben
|
||||
|
||||
# 1.1.4
|
||||
- Deaktivieren Sie das Ändern der Anmeldeinformationen für die Vitrinen
|
||||
|
||||
# 1.1.3
|
||||
- Legen Sie die Konsistenz der Werbebuchung als Standard fest
|
||||
- Bestätigen Sie die Transaktion sofort
|
||||
- Aktualisieren Sie die Einstellungsbeschreibungen
|
||||
|
||||
# 1.1.2
|
||||
- Bereiten Sie die interne serverseitige Installation für Vitrinen und Demos vor
|
||||
|
||||
# 1.1.1
|
||||
- Stoppen Sie das Senden von Standard-E-Mails
|
||||
- Verschönern Sie die Zahlungsseite
|
||||
|
||||
# 1.1.0
|
||||
- Behandeln Sie leere / Standardeinstellungswerte
|
||||
- Speichern Sie Rückerstattungen in db und laden Sie die Registerkarte Bestellung bei Änderungen neu
|
||||
|
||||
# 1.0.0
|
||||
- Erste Version der VRPayment-Integrationen für Shopware 6
|
||||
-201
@@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2025 VR Payment GmbH
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,106 +0,0 @@
|
||||
|
||||
|
||||
VR Payment Integration for Shopware 6
|
||||
=============================
|
||||
|
||||
The VR Payment plugin wraps around the VR Payment API. This library facilitates your interaction with various services such as transactions.
|
||||
Please note that this plugin is for versions 6.5, 6.6 or 6.7. For the 6.4 plugin please visit [our Shopware 6.4 plugin](https://github.com/vr-payment/shopware-6-4).
|
||||
|
||||
## Requirements
|
||||
|
||||
- Shopware 6.7.x, 6.6.x or 6.5.x. See table below.
|
||||
- PHP minimum version supported by the each shop version.
|
||||
|
||||
## Documentation
|
||||
|
||||
- For English documentation click [here](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.0.1/docs/en/documentation.html)
|
||||
- Für die deutsche Dokumentation klicken Sie [hier](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.0.1/docs/de/documentation.html)
|
||||
- Pour la documentation Française, cliquez [ici](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.0.1/docs/fr/documentation.html)
|
||||
- Per la documentazione in tedesco, clicca [qui](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.0.1/docs/it/documentation.html)
|
||||
|
||||
## Installation
|
||||
|
||||
### **Via Composer (Recommended)**
|
||||
1. Navigate to your Shopware root directory.
|
||||
2. Run:
|
||||
|
||||
```bash
|
||||
Copy
|
||||
composer require vrpayment/shopware-6
|
||||
php bin/console plugin:refresh
|
||||
php bin/console plugin:install --activate --clearCache VRPaymentPayment
|
||||
```
|
||||
|
||||
### Manual Installation
|
||||
|
||||
1. Download the latest [Release](../../releases)
|
||||
2. Extract the ZIP to custom/plugins/VRPaymentPayment.
|
||||
|
||||
```bash
|
||||
Copy
|
||||
bin/console plugin:refresh
|
||||
bin/console plugin:install --activate --clearCache VRPaymentPayment
|
||||
```
|
||||
|
||||
## Configuration
|
||||
### API Credentials
|
||||
|
||||
1. Navigate to Shopware Admin > Settings > VRPayment Payment.
|
||||
2. Enter your Space ID, User ID, and API Key (obtained from the [VR Payment Portal](https://gateway.vr-payment.de/)).
|
||||
|
||||
### Payment Methods
|
||||
|
||||
Configure supported methods (e.g., credit cards, Apple Pay) via the [VR Payment Portal](https://gateway.vr-payment.de/).
|
||||
|
||||
### Key Features
|
||||
**iFrame Integration**: Embed payment forms directly into your checkout.
|
||||
|
||||
**Refunds & Captures**: Trigger full/partial refunds and captures from Shopware or the [VR Payment Portal](https://gateway.vr-payment.de/).
|
||||
|
||||
**Multi-Store Support**: Manage configurations across multiple stores.
|
||||
|
||||
**Automatic Updates**: Payment methods sync dynamically via the VRPayment API.
|
||||
|
||||
## Compatibiliity
|
||||
|
||||
___________________________________________________________________________________
|
||||
| Shopware 6 version | Plugin major version | Supported until |
|
||||
|-------------------------------|------------------------|------------------------|
|
||||
| Shopware 6.7.x | 7.x | Further notice |
|
||||
| Shopware 6.6.x | 6.x | December 2025 |
|
||||
| Shopware 6.5.x | 5.x | October 2024 |
|
||||
-----------------------------------------------------------------------------------
|
||||
|
||||
### Troubleshooting
|
||||
**Logs**: Check payment logs with:
|
||||
|
||||
```bash
|
||||
Copy
|
||||
tail -f var/log/vrpayment_payment*.log
|
||||
```
|
||||
### Common Issues:
|
||||
|
||||
Ensure composer update vrpayment/shopware-6 is run after updates.
|
||||
|
||||
Verify API credentials match your VRPayment account.
|
||||
|
||||
## FAQs
|
||||
**Q: Does this plugin support one-click payments?**
|
||||
A: Yes, via tokenization in the VRPayment Portal.
|
||||
|
||||
**Q: How do I handle PCI compliance?**
|
||||
A: The plugin uses iFrame integration, reducing PCI requirements to SAQ-A.
|
||||
|
||||
### Changelog
|
||||
For version-specific updates, see the [GitHub Releases](https://github.com/vr-payment/shopware-6/releases).
|
||||
|
||||
### Contributing
|
||||
Report issues via GitHub Issues.
|
||||
|
||||
Follow the Shopware Plugin Base Guide for development.
|
||||
|
||||
This template combines technical clarity with user-friendly guidance. For advanced customization (e.g., overriding templates or payment handlers), refer to the Shopware Documentation.
|
||||
|
||||
## License
|
||||
|
||||
Please see the [license file](https://github.com/vr-payment/shopware-6/blob/master/LICENSE.txt) for more information.
|
||||
@@ -1,63 +0,0 @@
|
||||
{
|
||||
"authors": [
|
||||
{
|
||||
"homepage": "https://www.vr-payment.de/",
|
||||
"name": "VR Payment"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"VRPaymentPayment\\": "src/"
|
||||
}
|
||||
},
|
||||
"description": "VRPayment integration for Shopware 6",
|
||||
"extra": {
|
||||
"copyright": "(c) by VR Payment",
|
||||
"description": {
|
||||
"de-DE": "VRPayment integration für Shopware 6",
|
||||
"en-GB": "VRPayment integration for Shopware 6",
|
||||
"fr-FR": "Intégration de VRPayment pour Shopware 6",
|
||||
"it-IT": "Integrazione VRPayment per Shopware"
|
||||
},
|
||||
"label": {
|
||||
"de-DE": "VRPayment Produkte für Shopware 6",
|
||||
"en-GB": "VRPayment Products for Shopware 6",
|
||||
"fr-FR": "VRPayment Produits for Shopware 6",
|
||||
"it-IT": "VRPayment Prodotti per Shopware 6"
|
||||
},
|
||||
"manufacturerLink": {
|
||||
"de-DE": "https://www.vr-payment.de/",
|
||||
"en-GB": "https://www.vr-payment.de/",
|
||||
"fr-FR": "https://www.vr-payment.de/",
|
||||
"it-IT": "https://www.vr-payment.de/"
|
||||
},
|
||||
"supportLink": {
|
||||
"de-DE": "https://www.vr-payment.de/hotline",
|
||||
"en-GB": "https://www.vr-payment.de/hotline",
|
||||
"fr-FR": "https://www.vr-payment.de/hotline",
|
||||
"it-IT": "https://www.vr-payment.de/hotline"
|
||||
},
|
||||
"shopware-plugin-class": "VRPaymentPayment\\VRPaymentPayment"
|
||||
},
|
||||
"homepage": "https://www.vr-payment.de//",
|
||||
"keywords": [
|
||||
"VR Payment",
|
||||
"payment",
|
||||
"php",
|
||||
"shopware"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"name": "vrpayment/shopware-6",
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"php": ">=8.2",
|
||||
"shopware/core": "~6.7.0",
|
||||
"shopware/administration": "~6.7.0",
|
||||
"shopware/storefront":"~6.7.0",
|
||||
"vrpayment/sdk": "^4.0.0"
|
||||
},
|
||||
"type": "shopware-platform-plugin",
|
||||
"version": "7.0.1"
|
||||
}
|
||||
@@ -1,692 +0,0 @@
|
||||
* {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
*:before, *:after {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 100%;
|
||||
font-family: sans-serif;
|
||||
line-height: 1.15;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-ms-overflow-style: scrollbar;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
@-ms-viewport {
|
||||
width: device-width;
|
||||
}
|
||||
|
||||
article, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
font-size: 1rem;
|
||||
font-weight: 300;
|
||||
line-height: 1.5;
|
||||
color: #212529;
|
||||
text-align: left;
|
||||
background-color: #fff;
|
||||
padding-right: 0 !important;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
html,body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
[tabindex="-1"]:focus {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
hr {
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 200;
|
||||
margin-bottom: 1.875rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.625rem;
|
||||
font-weight: 300;
|
||||
margin-bottom: 1.3rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 300;
|
||||
margin-top: 1.3rem;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 400;
|
||||
margin-top: 1.875rem;
|
||||
margin-bottom: 1.3rem;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
margin-top: 1.875rem;
|
||||
margin-bottom: 1.3rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title], abbr[data-original-title] {
|
||||
text-decoration: underline;
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol, ul, dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol, ul ul, ol ul, ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: .5rem;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
b, strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
sub, sup {
|
||||
position: relative;
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
-webkit-text-decoration-skip: objects;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #0056b3;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]) {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]):focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
pre, code, kbd, samp {
|
||||
font-family: monospace, monospace;
|
||||
font-size: 90%;
|
||||
padding: 2px 4px 2px 4px;
|
||||
color: #c7254e;
|
||||
background-color: #f9f2f4;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: scrollbar;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img {
|
||||
vertical-align: middle;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
color: #a7a7a7;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
table col[class*="col-"] {
|
||||
position: static;
|
||||
float: none;
|
||||
display: table-column;
|
||||
}
|
||||
|
||||
table td[class*="col-"],table th[class*="col-"] {
|
||||
position: static;
|
||||
float: none;
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
ol.glossary {
|
||||
counter-reset: glossary-counter;
|
||||
list-style: none;
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
ol.glossary li {
|
||||
counter-increment: glossary-counter;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
ol.glossary li::before {
|
||||
content: counter(glossary-counter);
|
||||
position: absolute;
|
||||
background-color: #73EAA9;
|
||||
color: #fff;
|
||||
border-radius: 100px;
|
||||
width: 24px;
|
||||
left: -40px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.layout-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.layout-title {
|
||||
padding: 1.875rem 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.layout-title h1 {
|
||||
font-size: 3rem;
|
||||
font-weight: 200;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.layout-title h2 {
|
||||
font-size: 2rem;
|
||||
font-weight: 200;
|
||||
text-align: center;
|
||||
color: #999;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.layout-navigation .nav {
|
||||
padding: 1.875rem 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.layout-navigation .nav > li {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.layout-navigation .nav > li > a {
|
||||
border: 1px solid #007bff;
|
||||
border-radius: 100px;
|
||||
padding: 6px 12px;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
.layout-navigation .nav > li > a:hover, .layout-navigation .nav > li > a:active, .layout-navigation .nav > li > a:focus {
|
||||
border: 1px solid #0056b3;
|
||||
color: #0056b3;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.layout-content {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.layout-content:before, .layout-content:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
|
||||
.layout-content:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.layout-content .col-right {
|
||||
width: 25%;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.layout-content .col-right-wrapper {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding: 2.5rem 2rem 0;
|
||||
}
|
||||
|
||||
.layout-content .col-body {
|
||||
width: 75%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.layout-content .col-body:before, .layout-content .col-body:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
|
||||
.layout-content .col-body:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.layout-content .col-body-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding: 2.5rem 2rem 0;
|
||||
}
|
||||
|
||||
.nav {
|
||||
padding-left: 0;
|
||||
margin-bottom: 0;
|
||||
list-style: none;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
.table-of-contents {
|
||||
padding: 1.25rem 0;
|
||||
}
|
||||
|
||||
.table-of-contents .nav > li > a {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.table-of-contents .nav > li > a .item-number {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.table-of-contents .nav > li > a .item-title {
|
||||
color: #212529;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
flex-grow: 1;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.table-of-contents .nav > li > a .item-title:hover {
|
||||
color: #0056b3;
|
||||
}
|
||||
|
||||
.table-of-contents .nav > li.extended > a .item-title, .table-of-contents .nav > li.active > a .item-title, .table-of-contents .nav > li.extended > a .item-title:hover, .table-of-contents .nav > li.active > a .item-title:hover {
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li > .nav {
|
||||
display: none;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li > .nav > li > a {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li > .nav > li > a .item-title {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li > .nav > li > .nav > li > a {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li > .nav > li > .nav > li > a .item-title {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li.active > .nav {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.chapter {
|
||||
margin: 0 0 6rem;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.chapter > .chapter-title h1, .chapter > .chapter-title h2, .chapter > .chapter-title h3, .chapter > .chapter-title h4, .chapter > .chapter-title h5, .chapter > .chapter-title h6, .section > .section-title h1, .section > .section-title h2, .section > .section-title h3, .section > .section-title h4, .section > .section-title h5, .section > .section-title h6 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.chapter > .chapter-title h1 {
|
||||
border-bottom: 2px solid #eeeeee;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 0.2em;
|
||||
}
|
||||
|
||||
.chapter-title .title-number, .section-title .title-number {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.paragraph {
|
||||
line-height: 1.75em;
|
||||
}
|
||||
|
||||
.paragraph + .paragraph {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.dlist {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.dlist dl dt {
|
||||
float: left;
|
||||
width: 160px;
|
||||
clear: left;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dlist dl dd {
|
||||
margin-left: 180px;
|
||||
}
|
||||
|
||||
.ulist {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.imageblock {
|
||||
margin: 30px auto;
|
||||
}
|
||||
|
||||
.imageblock .content img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.imageblock .title {
|
||||
padding: 10px 0 0;
|
||||
}
|
||||
|
||||
.exampleblock, .quoteblock, .literalblock {
|
||||
background: #f5f4f4;
|
||||
padding: 20px;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.exampleblock .title, .quoteblock .title, .literalblock .title {
|
||||
text-transform: uppercase;
|
||||
font-size: 0.75em;
|
||||
font-weight: 400;
|
||||
color: #979797;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.quoteblock blockquote {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.quoteblock blockquote p:last-child, .quoteblock blockquote ul:last-child, .quoteblock blockquote ol:last-child {
|
||||
margin-bottom: 9px;
|
||||
}
|
||||
|
||||
.literalblock pre {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.listingblock {
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.listingblock pre {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.listingblock pre code {
|
||||
display: block;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.admonitionblock {
|
||||
line-height: 1.8em;
|
||||
padding: 20px;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.admonitionblock .icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.admonitionblock.important {
|
||||
background: #fce1e1;
|
||||
border-left: 5px solid #ff6060;
|
||||
}
|
||||
|
||||
.admonitionblock.note, .admonitionblock.tip {
|
||||
background: #e0f2fc;
|
||||
border-left: 5px solid #88d5ff;
|
||||
}
|
||||
|
||||
.admonitionblock.caution, .admonitionblock.warning {
|
||||
background: #fdf3d8;
|
||||
border-left: 5px solid #f1c654;
|
||||
}
|
||||
|
||||
table.tableblock {
|
||||
background-color: #fff;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin-bottom: 18px;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
table.tableblock > thead > tr > th, table.tableblock > tbody > tr > th, table.tableblock > tfoot > tr > th, table.tableblock > thead > tr > td, table.tableblock > tbody > tr > td, table.tableblock > tfoot > tr > td {
|
||||
padding: 8px;
|
||||
line-height: 1.42857143;
|
||||
vertical-align: top;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
table.tableblock > thead > tr > th {
|
||||
vertical-align: bottom;
|
||||
border-bottom: 2px solid #eee;
|
||||
}
|
||||
|
||||
table.tableblock > caption + thead > tr:first-child > th, table.tableblock > colgroup + thead > tr:first-child > th, table.tableblock > thead:first-child > tr:first-child > th, table.tableblock > caption + thead > tr:first-child > td, table.tableblock > colgroup + thead > tr:first-child > td, table.tableblock > thead:first-child > tr:first-child > td {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
table.tableblock > tbody + tbody {
|
||||
border-top: 2px solid #eee;
|
||||
}
|
||||
|
||||
table.tableblock .table {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
table.tableblock > tbody > tr:nth-of-type(odd) {
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
|
||||
table.tableblock > thead > tr > th p:last-child, table.tableblock > tbody > tr > th p:last-child, table.tableblock > tfoot > tr > th p:last-child, table.tableblock > thead > tr > td p:last-child, table.tableblock > tbody > tr > td p:last-child, table.tableblock > tfoot > tr > td p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.loaded .table-of-contents .nav .nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.layout-wrapper .layout-title, .layout-wrapper .layout-navigation, .layout-wrapper .layout-content {
|
||||
max-width: 1200px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
html {
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.layout-content .col-right {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.layout-content .col-body {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
color: #000;
|
||||
font-family: Georgia, "Times New Roman", Times, serif;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.layout-title h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.layout-content .col-right {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.layout-content .col-body {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chapter {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
(function($){
|
||||
|
||||
hljs.initHighlightingOnLoad();
|
||||
|
||||
$(document).ready(function(){
|
||||
$('.col-right-wrapper').stick_in_parent({
|
||||
parent: '.layout-content'
|
||||
});
|
||||
$('body').scrollspy({
|
||||
target: '.table-of-contents'
|
||||
});
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
File diff suppressed because one or more lines are too long
Vendored
-2
File diff suppressed because one or more lines are too long
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
|
||||
Monokai Sublime style. Derived from Monokai by noformnocontent http://nn.mit-license.org/
|
||||
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
background: #23241f;
|
||||
}
|
||||
|
||||
.hljs,
|
||||
.hljs-tag,
|
||||
.hljs-subst {
|
||||
color: #f8f8f2;
|
||||
}
|
||||
|
||||
.hljs-strong,
|
||||
.hljs-emphasis {
|
||||
color: #a8a8a2;
|
||||
}
|
||||
|
||||
.hljs-bullet,
|
||||
.hljs-quote,
|
||||
.hljs-number,
|
||||
.hljs-regexp,
|
||||
.hljs-literal,
|
||||
.hljs-link {
|
||||
color: #ae81ff;
|
||||
}
|
||||
|
||||
.hljs-code,
|
||||
.hljs-title,
|
||||
.hljs-section,
|
||||
.hljs-selector-class {
|
||||
color: #a6e22e;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-name,
|
||||
.hljs-attr {
|
||||
color: #f92672;
|
||||
}
|
||||
|
||||
.hljs-symbol,
|
||||
.hljs-attribute {
|
||||
color: #66d9ef;
|
||||
}
|
||||
|
||||
.hljs-params,
|
||||
.hljs-class .hljs-title {
|
||||
color: #f8f8f2;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-type,
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo,
|
||||
.hljs-addition,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable {
|
||||
color: #e6db74;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-deletion,
|
||||
.hljs-meta {
|
||||
color: #75715e;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
/* ========================================================================
|
||||
* Bootstrap: scrollspy.js v3.3.7
|
||||
* http://getbootstrap.com/javascript/#scrollspy
|
||||
* ========================================================================
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
!function(r){"use strict";function o(t,s){this.$body=r(document.body),this.$scrollElement=r(t).is(document.body)?r(window):r(t),this.options=r.extend({},o.DEFAULTS,s),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",r.proxy(this.process,this)),this.refresh(),this.process()}function s(i){return this.each(function(){var t=r(this),s=t.data("bs.scrollspy"),e="object"==typeof i&&i;s||t.data("bs.scrollspy",s=new o(this,e)),"string"==typeof i&&s[i]()})}o.VERSION="3.3.7",o.DEFAULTS={offset:10},o.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},o.prototype.refresh=function(){var t=this,i="offset",o=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),r.isWindow(this.$scrollElement[0])||(i="position",o=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var t=r(this),s=t.data("target")||t.attr("href"),e=/^#./.test(s)&&r(s);return e&&e.length&&e.is(":visible")&&[[e[i]().top+o,s]]||null}).sort(function(t,s){return t[0]-s[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},o.prototype.process=function(){var t,s=this.$scrollElement.scrollTop()+this.options.offset,e=this.getScrollHeight(),i=this.options.offset+e-this.$scrollElement.height(),o=this.offsets,r=this.targets,l=this.activeTarget;if(this.scrollHeight!=e&&this.refresh(),i<=s)return l!=(t=r[r.length-1])&&this.activate(t);if(l&&s<o[0])return this.activeTarget=null,this.clear();for(t=o.length;t--;)l!=r[t]&&s>=o[t]&&(void 0===o[t+1]||s<o[t+1])&&this.activate(r[t])},o.prototype.activate=function(t){this.activeTarget=t,this.clear();var s=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',e=r(s).parents("li").addClass("active");e.parent(".dropdown-menu").length&&(e=e.closest("li.dropdown").addClass("active")),e.trigger("activate.bs.scrollspy")},o.prototype.clear=function(){r(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var t=r.fn.scrollspy;r.fn.scrollspy=s,r.fn.scrollspy.Constructor=o,r.fn.scrollspy.noConflict=function(){return r.fn.scrollspy=t,this},r(window).on("load.bs.scrollspy.data-api",function(){r('[data-spy="scroll"]').each(function(){var t=r(this);s.call(t,t.data())})})}(jQuery);
|
||||
@@ -1,5 +0,0 @@
|
||||
/**
|
||||
@license Sticky-kit v1.1.2 | WTFPL | Leaf Corcoran 2015 | http://leafo.net
|
||||
*/
|
||||
|
||||
(function(){var M,Q;M=this.jQuery||window.jQuery,Q=M(window),M.fn.stick_in_parent=function(t){var x,o,C,i,e,P,s,V,F,z,r,I,A,j;for(null==t&&(t={}),j=t.sticky_class,P=t.inner_scrolling,A=t.recalc_every,I=t.parent,F=t.offset_top,z=t.offset_bottom,V=t.spacer,C=t.bottoming,null==F&&(F=0),null==z&&(z=0),null==I&&(I=void 0),null==P&&(P=!0),null==j&&(j="is_stuck"),x=M(document),null==C&&(C=!0),r=function(t){var o,i;return window.getComputedStyle?(t[0],o=window.getComputedStyle(t[0]),i=parseFloat(o.getPropertyValue("width"))+parseFloat(o.getPropertyValue("margin-left"))+parseFloat(o.getPropertyValue("margin-right")),"border-box"!==o.getPropertyValue("box-sizing")&&(i+=parseFloat(o.getPropertyValue("border-left-width"))+parseFloat(o.getPropertyValue("border-right-width"))+parseFloat(o.getPropertyValue("padding-left"))+parseFloat(o.getPropertyValue("padding-right"))),i):t.outerWidth(!0)},i=function(n,l,a,c,p,d,u,f){var h,t,g,m,k,y,b,v,o,_,w,e;if(!n.data("sticky_kit")){if(n.data("sticky_kit",!0),k=x.height(),b=n.parent(),null!=I&&(b=b.closest(I)),!b.length)throw"failed to find stick parent";return h=g=!1,(w=null!=V?V&&n.closest(V):M('<div class="sticky-kit-manual-spacer" />'))&&w.css("position",n.css("position")),(v=function(){var t,o,i;if(!f)return k=x.height(),t=parseInt(b.css("border-top-width"),10),o=parseInt(b.css("padding-top"),10),l=parseInt(b.css("padding-bottom"),10),a=b.offset().top+t+o,c=b.height(),g&&(h=g=!1,null==V&&(n.insertAfter(w),w.detach()),n.css({position:"",top:"",width:"",bottom:""}).removeClass(j),i=!0),p=n.offset().top-(parseInt(n.css("margin-top"),10)||0)-F,d=n.outerHeight(!0),u=n.css("float"),w&&w.css({width:r(n),height:d,display:n.css("display"),"vertical-align":n.css("vertical-align"),float:u}),i?e():void 0})(),m=void 0,y=F,_=A,e=function(){var t,o,i,e,s,r;if(d!==c&&!f)return i=!1,null!=_&&(_-=1)<=0&&(_=A,v(),i=!0),i||x.height()===k||(v(),i=!0),e=Q.scrollTop(),null!=m&&(o=e-m),m=e,g?(C&&(s=c+a<e+d+y+z,h&&!s&&(h=!1,n.css({position:"fixed",bottom:"",top:y}).removeClass("is_bottomed").trigger("sticky_kit:unbottom"))),e<p&&(g=!1,y=F,null==V&&("left"!==u&&"right"!==u||n.insertAfter(w),w.detach()),t={position:"",width:"",top:""},n.css(t).removeClass(j).trigger("sticky_kit:unstick")),P&&(r=Q.height())<d+F&&(h||(y-=o,y=Math.max(r-d,y),y=Math.min(F,y),g&&n.css({top:y+"px"})))):p<e&&(g=!0,(t={position:"fixed",top:y}).width="border-box"===n.css("box-sizing")?n.outerWidth()+"px":n.width()+"px",n.css(t).addClass(j),null==V&&(n.after(w),"left"!==u&&"right"!==u||w.append(n)),n.trigger("sticky_kit:stick")),g&&C&&(null==s&&(s=c+a<e+d+y+z),!h&&s)?(h=!0,"static"===b.css("position")&&b.css({position:"relative"}),n.css({position:"absolute",bottom:l+z,top:"auto"}).addClass("is_bottomed").trigger("sticky_kit:bottom")):void 0},o=function(){return v(),e()},t=function(){if(f=!0,Q.off("touchmove",e),Q.off("scroll",e),Q.off("resize",o),M(document.body).off("sticky_kit:recalc",o),n.off("sticky_kit:detach",t),n.removeData("sticky_kit"),n.css({position:"",bottom:"",top:"",width:""}),b.position("position",""),g)return null==V&&("left"!==u&&"right"!==u||n.insertAfter(w),w.remove()),n.removeClass(j)},Q.on("touchmove",e),Q.on("scroll",e),Q.on("resize",o),M(document.body).on("sticky_kit:recalc",o),n.on("sticky_kit:detach",t),setTimeout(e,0)}},e=0,s=this.length;e<s;e++)o=this[e],i(M(o));return this}}).call(this);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,692 +0,0 @@
|
||||
* {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
*:before, *:after {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 100%;
|
||||
font-family: sans-serif;
|
||||
line-height: 1.15;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-ms-overflow-style: scrollbar;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
@-ms-viewport {
|
||||
width: device-width;
|
||||
}
|
||||
|
||||
article, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
font-size: 1rem;
|
||||
font-weight: 300;
|
||||
line-height: 1.5;
|
||||
color: #212529;
|
||||
text-align: left;
|
||||
background-color: #fff;
|
||||
padding-right: 0 !important;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
html,body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
[tabindex="-1"]:focus {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
hr {
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 200;
|
||||
margin-bottom: 1.875rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.625rem;
|
||||
font-weight: 300;
|
||||
margin-bottom: 1.3rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 300;
|
||||
margin-top: 1.3rem;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 400;
|
||||
margin-top: 1.875rem;
|
||||
margin-bottom: 1.3rem;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
margin-top: 1.875rem;
|
||||
margin-bottom: 1.3rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title], abbr[data-original-title] {
|
||||
text-decoration: underline;
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol, ul, dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol, ul ul, ol ul, ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: .5rem;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
b, strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
sub, sup {
|
||||
position: relative;
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
-webkit-text-decoration-skip: objects;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #0056b3;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]) {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]):focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
pre, code, kbd, samp {
|
||||
font-family: monospace, monospace;
|
||||
font-size: 90%;
|
||||
padding: 2px 4px 2px 4px;
|
||||
color: #c7254e;
|
||||
background-color: #f9f2f4;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: scrollbar;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img {
|
||||
vertical-align: middle;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
color: #a7a7a7;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
table col[class*="col-"] {
|
||||
position: static;
|
||||
float: none;
|
||||
display: table-column;
|
||||
}
|
||||
|
||||
table td[class*="col-"],table th[class*="col-"] {
|
||||
position: static;
|
||||
float: none;
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
ol.glossary {
|
||||
counter-reset: glossary-counter;
|
||||
list-style: none;
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
ol.glossary li {
|
||||
counter-increment: glossary-counter;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
ol.glossary li::before {
|
||||
content: counter(glossary-counter);
|
||||
position: absolute;
|
||||
background-color: #73EAA9;
|
||||
color: #fff;
|
||||
border-radius: 100px;
|
||||
width: 24px;
|
||||
left: -40px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.layout-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.layout-title {
|
||||
padding: 1.875rem 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.layout-title h1 {
|
||||
font-size: 3rem;
|
||||
font-weight: 200;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.layout-title h2 {
|
||||
font-size: 2rem;
|
||||
font-weight: 200;
|
||||
text-align: center;
|
||||
color: #999;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.layout-navigation .nav {
|
||||
padding: 1.875rem 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.layout-navigation .nav > li {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.layout-navigation .nav > li > a {
|
||||
border: 1px solid #007bff;
|
||||
border-radius: 100px;
|
||||
padding: 6px 12px;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
.layout-navigation .nav > li > a:hover, .layout-navigation .nav > li > a:active, .layout-navigation .nav > li > a:focus {
|
||||
border: 1px solid #0056b3;
|
||||
color: #0056b3;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.layout-content {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.layout-content:before, .layout-content:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
|
||||
.layout-content:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.layout-content .col-right {
|
||||
width: 25%;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.layout-content .col-right-wrapper {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding: 2.5rem 2rem 0;
|
||||
}
|
||||
|
||||
.layout-content .col-body {
|
||||
width: 75%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.layout-content .col-body:before, .layout-content .col-body:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
|
||||
.layout-content .col-body:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.layout-content .col-body-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding: 2.5rem 2rem 0;
|
||||
}
|
||||
|
||||
.nav {
|
||||
padding-left: 0;
|
||||
margin-bottom: 0;
|
||||
list-style: none;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
.table-of-contents {
|
||||
padding: 1.25rem 0;
|
||||
}
|
||||
|
||||
.table-of-contents .nav > li > a {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.table-of-contents .nav > li > a .item-number {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.table-of-contents .nav > li > a .item-title {
|
||||
color: #212529;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
flex-grow: 1;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.table-of-contents .nav > li > a .item-title:hover {
|
||||
color: #0056b3;
|
||||
}
|
||||
|
||||
.table-of-contents .nav > li.extended > a .item-title, .table-of-contents .nav > li.active > a .item-title, .table-of-contents .nav > li.extended > a .item-title:hover, .table-of-contents .nav > li.active > a .item-title:hover {
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li > .nav {
|
||||
display: none;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li > .nav > li > a {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li > .nav > li > a .item-title {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li > .nav > li > .nav > li > a {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li > .nav > li > .nav > li > a .item-title {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li.active > .nav {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.chapter {
|
||||
margin: 0 0 6rem;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.chapter > .chapter-title h1, .chapter > .chapter-title h2, .chapter > .chapter-title h3, .chapter > .chapter-title h4, .chapter > .chapter-title h5, .chapter > .chapter-title h6, .section > .section-title h1, .section > .section-title h2, .section > .section-title h3, .section > .section-title h4, .section > .section-title h5, .section > .section-title h6 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.chapter > .chapter-title h1 {
|
||||
border-bottom: 2px solid #eeeeee;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 0.2em;
|
||||
}
|
||||
|
||||
.chapter-title .title-number, .section-title .title-number {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.paragraph {
|
||||
line-height: 1.75em;
|
||||
}
|
||||
|
||||
.paragraph + .paragraph {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.dlist {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.dlist dl dt {
|
||||
float: left;
|
||||
width: 160px;
|
||||
clear: left;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dlist dl dd {
|
||||
margin-left: 180px;
|
||||
}
|
||||
|
||||
.ulist {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.imageblock {
|
||||
margin: 30px auto;
|
||||
}
|
||||
|
||||
.imageblock .content img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.imageblock .title {
|
||||
padding: 10px 0 0;
|
||||
}
|
||||
|
||||
.exampleblock, .quoteblock, .literalblock {
|
||||
background: #f5f4f4;
|
||||
padding: 20px;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.exampleblock .title, .quoteblock .title, .literalblock .title {
|
||||
text-transform: uppercase;
|
||||
font-size: 0.75em;
|
||||
font-weight: 400;
|
||||
color: #979797;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.quoteblock blockquote {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.quoteblock blockquote p:last-child, .quoteblock blockquote ul:last-child, .quoteblock blockquote ol:last-child {
|
||||
margin-bottom: 9px;
|
||||
}
|
||||
|
||||
.literalblock pre {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.listingblock {
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.listingblock pre {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.listingblock pre code {
|
||||
display: block;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.admonitionblock {
|
||||
line-height: 1.8em;
|
||||
padding: 20px;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.admonitionblock .icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.admonitionblock.important {
|
||||
background: #fce1e1;
|
||||
border-left: 5px solid #ff6060;
|
||||
}
|
||||
|
||||
.admonitionblock.note, .admonitionblock.tip {
|
||||
background: #e0f2fc;
|
||||
border-left: 5px solid #88d5ff;
|
||||
}
|
||||
|
||||
.admonitionblock.caution, .admonitionblock.warning {
|
||||
background: #fdf3d8;
|
||||
border-left: 5px solid #f1c654;
|
||||
}
|
||||
|
||||
table.tableblock {
|
||||
background-color: #fff;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin-bottom: 18px;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
table.tableblock > thead > tr > th, table.tableblock > tbody > tr > th, table.tableblock > tfoot > tr > th, table.tableblock > thead > tr > td, table.tableblock > tbody > tr > td, table.tableblock > tfoot > tr > td {
|
||||
padding: 8px;
|
||||
line-height: 1.42857143;
|
||||
vertical-align: top;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
table.tableblock > thead > tr > th {
|
||||
vertical-align: bottom;
|
||||
border-bottom: 2px solid #eee;
|
||||
}
|
||||
|
||||
table.tableblock > caption + thead > tr:first-child > th, table.tableblock > colgroup + thead > tr:first-child > th, table.tableblock > thead:first-child > tr:first-child > th, table.tableblock > caption + thead > tr:first-child > td, table.tableblock > colgroup + thead > tr:first-child > td, table.tableblock > thead:first-child > tr:first-child > td {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
table.tableblock > tbody + tbody {
|
||||
border-top: 2px solid #eee;
|
||||
}
|
||||
|
||||
table.tableblock .table {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
table.tableblock > tbody > tr:nth-of-type(odd) {
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
|
||||
table.tableblock > thead > tr > th p:last-child, table.tableblock > tbody > tr > th p:last-child, table.tableblock > tfoot > tr > th p:last-child, table.tableblock > thead > tr > td p:last-child, table.tableblock > tbody > tr > td p:last-child, table.tableblock > tfoot > tr > td p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.loaded .table-of-contents .nav .nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.layout-wrapper .layout-title, .layout-wrapper .layout-navigation, .layout-wrapper .layout-content {
|
||||
max-width: 1200px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
html {
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.layout-content .col-right {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.layout-content .col-body {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
color: #000;
|
||||
font-family: Georgia, "Times New Roman", Times, serif;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.layout-title h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.layout-content .col-right {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.layout-content .col-body {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chapter {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
(function($){
|
||||
|
||||
hljs.initHighlightingOnLoad();
|
||||
|
||||
$(document).ready(function(){
|
||||
$('.col-right-wrapper').stick_in_parent({
|
||||
parent: '.layout-content'
|
||||
});
|
||||
$('body').scrollspy({
|
||||
target: '.table-of-contents'
|
||||
});
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
File diff suppressed because one or more lines are too long
Vendored
-2
File diff suppressed because one or more lines are too long
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
|
||||
Monokai Sublime style. Derived from Monokai by noformnocontent http://nn.mit-license.org/
|
||||
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
background: #23241f;
|
||||
}
|
||||
|
||||
.hljs,
|
||||
.hljs-tag,
|
||||
.hljs-subst {
|
||||
color: #f8f8f2;
|
||||
}
|
||||
|
||||
.hljs-strong,
|
||||
.hljs-emphasis {
|
||||
color: #a8a8a2;
|
||||
}
|
||||
|
||||
.hljs-bullet,
|
||||
.hljs-quote,
|
||||
.hljs-number,
|
||||
.hljs-regexp,
|
||||
.hljs-literal,
|
||||
.hljs-link {
|
||||
color: #ae81ff;
|
||||
}
|
||||
|
||||
.hljs-code,
|
||||
.hljs-title,
|
||||
.hljs-section,
|
||||
.hljs-selector-class {
|
||||
color: #a6e22e;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-name,
|
||||
.hljs-attr {
|
||||
color: #f92672;
|
||||
}
|
||||
|
||||
.hljs-symbol,
|
||||
.hljs-attribute {
|
||||
color: #66d9ef;
|
||||
}
|
||||
|
||||
.hljs-params,
|
||||
.hljs-class .hljs-title {
|
||||
color: #f8f8f2;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-type,
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo,
|
||||
.hljs-addition,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable {
|
||||
color: #e6db74;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-deletion,
|
||||
.hljs-meta {
|
||||
color: #75715e;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
/* ========================================================================
|
||||
* Bootstrap: scrollspy.js v3.3.7
|
||||
* http://getbootstrap.com/javascript/#scrollspy
|
||||
* ========================================================================
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
!function(r){"use strict";function o(t,s){this.$body=r(document.body),this.$scrollElement=r(t).is(document.body)?r(window):r(t),this.options=r.extend({},o.DEFAULTS,s),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",r.proxy(this.process,this)),this.refresh(),this.process()}function s(i){return this.each(function(){var t=r(this),s=t.data("bs.scrollspy"),e="object"==typeof i&&i;s||t.data("bs.scrollspy",s=new o(this,e)),"string"==typeof i&&s[i]()})}o.VERSION="3.3.7",o.DEFAULTS={offset:10},o.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},o.prototype.refresh=function(){var t=this,i="offset",o=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),r.isWindow(this.$scrollElement[0])||(i="position",o=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var t=r(this),s=t.data("target")||t.attr("href"),e=/^#./.test(s)&&r(s);return e&&e.length&&e.is(":visible")&&[[e[i]().top+o,s]]||null}).sort(function(t,s){return t[0]-s[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},o.prototype.process=function(){var t,s=this.$scrollElement.scrollTop()+this.options.offset,e=this.getScrollHeight(),i=this.options.offset+e-this.$scrollElement.height(),o=this.offsets,r=this.targets,l=this.activeTarget;if(this.scrollHeight!=e&&this.refresh(),i<=s)return l!=(t=r[r.length-1])&&this.activate(t);if(l&&s<o[0])return this.activeTarget=null,this.clear();for(t=o.length;t--;)l!=r[t]&&s>=o[t]&&(void 0===o[t+1]||s<o[t+1])&&this.activate(r[t])},o.prototype.activate=function(t){this.activeTarget=t,this.clear();var s=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',e=r(s).parents("li").addClass("active");e.parent(".dropdown-menu").length&&(e=e.closest("li.dropdown").addClass("active")),e.trigger("activate.bs.scrollspy")},o.prototype.clear=function(){r(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var t=r.fn.scrollspy;r.fn.scrollspy=s,r.fn.scrollspy.Constructor=o,r.fn.scrollspy.noConflict=function(){return r.fn.scrollspy=t,this},r(window).on("load.bs.scrollspy.data-api",function(){r('[data-spy="scroll"]').each(function(){var t=r(this);s.call(t,t.data())})})}(jQuery);
|
||||
@@ -1,5 +0,0 @@
|
||||
/**
|
||||
@license Sticky-kit v1.1.2 | WTFPL | Leaf Corcoran 2015 | http://leafo.net
|
||||
*/
|
||||
|
||||
(function(){var M,Q;M=this.jQuery||window.jQuery,Q=M(window),M.fn.stick_in_parent=function(t){var x,o,C,i,e,P,s,V,F,z,r,I,A,j;for(null==t&&(t={}),j=t.sticky_class,P=t.inner_scrolling,A=t.recalc_every,I=t.parent,F=t.offset_top,z=t.offset_bottom,V=t.spacer,C=t.bottoming,null==F&&(F=0),null==z&&(z=0),null==I&&(I=void 0),null==P&&(P=!0),null==j&&(j="is_stuck"),x=M(document),null==C&&(C=!0),r=function(t){var o,i;return window.getComputedStyle?(t[0],o=window.getComputedStyle(t[0]),i=parseFloat(o.getPropertyValue("width"))+parseFloat(o.getPropertyValue("margin-left"))+parseFloat(o.getPropertyValue("margin-right")),"border-box"!==o.getPropertyValue("box-sizing")&&(i+=parseFloat(o.getPropertyValue("border-left-width"))+parseFloat(o.getPropertyValue("border-right-width"))+parseFloat(o.getPropertyValue("padding-left"))+parseFloat(o.getPropertyValue("padding-right"))),i):t.outerWidth(!0)},i=function(n,l,a,c,p,d,u,f){var h,t,g,m,k,y,b,v,o,_,w,e;if(!n.data("sticky_kit")){if(n.data("sticky_kit",!0),k=x.height(),b=n.parent(),null!=I&&(b=b.closest(I)),!b.length)throw"failed to find stick parent";return h=g=!1,(w=null!=V?V&&n.closest(V):M('<div class="sticky-kit-manual-spacer" />'))&&w.css("position",n.css("position")),(v=function(){var t,o,i;if(!f)return k=x.height(),t=parseInt(b.css("border-top-width"),10),o=parseInt(b.css("padding-top"),10),l=parseInt(b.css("padding-bottom"),10),a=b.offset().top+t+o,c=b.height(),g&&(h=g=!1,null==V&&(n.insertAfter(w),w.detach()),n.css({position:"",top:"",width:"",bottom:""}).removeClass(j),i=!0),p=n.offset().top-(parseInt(n.css("margin-top"),10)||0)-F,d=n.outerHeight(!0),u=n.css("float"),w&&w.css({width:r(n),height:d,display:n.css("display"),"vertical-align":n.css("vertical-align"),float:u}),i?e():void 0})(),m=void 0,y=F,_=A,e=function(){var t,o,i,e,s,r;if(d!==c&&!f)return i=!1,null!=_&&(_-=1)<=0&&(_=A,v(),i=!0),i||x.height()===k||(v(),i=!0),e=Q.scrollTop(),null!=m&&(o=e-m),m=e,g?(C&&(s=c+a<e+d+y+z,h&&!s&&(h=!1,n.css({position:"fixed",bottom:"",top:y}).removeClass("is_bottomed").trigger("sticky_kit:unbottom"))),e<p&&(g=!1,y=F,null==V&&("left"!==u&&"right"!==u||n.insertAfter(w),w.detach()),t={position:"",width:"",top:""},n.css(t).removeClass(j).trigger("sticky_kit:unstick")),P&&(r=Q.height())<d+F&&(h||(y-=o,y=Math.max(r-d,y),y=Math.min(F,y),g&&n.css({top:y+"px"})))):p<e&&(g=!0,(t={position:"fixed",top:y}).width="border-box"===n.css("box-sizing")?n.outerWidth()+"px":n.width()+"px",n.css(t).addClass(j),null==V&&(n.after(w),"left"!==u&&"right"!==u||w.append(n)),n.trigger("sticky_kit:stick")),g&&C&&(null==s&&(s=c+a<e+d+y+z),!h&&s)?(h=!0,"static"===b.css("position")&&b.css({position:"relative"}),n.css({position:"absolute",bottom:l+z,top:"auto"}).addClass("is_bottomed").trigger("sticky_kit:bottom")):void 0},o=function(){return v(),e()},t=function(){if(f=!0,Q.off("touchmove",e),Q.off("scroll",e),Q.off("resize",o),M(document.body).off("sticky_kit:recalc",o),n.off("sticky_kit:detach",t),n.removeData("sticky_kit"),n.css({position:"",bottom:"",top:"",width:""}),b.position("position",""),g)return null==V&&("left"!==u&&"right"!==u||n.insertAfter(w),w.remove()),n.removeClass(j)},Q.on("touchmove",e),Q.on("scroll",e),Q.on("resize",o),M(document.body).on("sticky_kit:recalc",o),n.on("sticky_kit:detach",t),setTimeout(e,0)}},e=0,s=this.length;e<s;e++)o=this[e],i(M(o));return this}}).call(this);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,692 +0,0 @@
|
||||
* {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
*:before, *:after {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 100%;
|
||||
font-family: sans-serif;
|
||||
line-height: 1.15;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-ms-overflow-style: scrollbar;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
@-ms-viewport {
|
||||
width: device-width;
|
||||
}
|
||||
|
||||
article, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
font-size: 1rem;
|
||||
font-weight: 300;
|
||||
line-height: 1.5;
|
||||
color: #212529;
|
||||
text-align: left;
|
||||
background-color: #fff;
|
||||
padding-right: 0 !important;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
html,body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
[tabindex="-1"]:focus {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
hr {
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 200;
|
||||
margin-bottom: 1.875rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.625rem;
|
||||
font-weight: 300;
|
||||
margin-bottom: 1.3rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 300;
|
||||
margin-top: 1.3rem;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 400;
|
||||
margin-top: 1.875rem;
|
||||
margin-bottom: 1.3rem;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
margin-top: 1.875rem;
|
||||
margin-bottom: 1.3rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title], abbr[data-original-title] {
|
||||
text-decoration: underline;
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol, ul, dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol, ul ul, ol ul, ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: .5rem;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
b, strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
sub, sup {
|
||||
position: relative;
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
-webkit-text-decoration-skip: objects;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #0056b3;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]) {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]):focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
pre, code, kbd, samp {
|
||||
font-family: monospace, monospace;
|
||||
font-size: 90%;
|
||||
padding: 2px 4px 2px 4px;
|
||||
color: #c7254e;
|
||||
background-color: #f9f2f4;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: scrollbar;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img {
|
||||
vertical-align: middle;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
color: #a7a7a7;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
table col[class*="col-"] {
|
||||
position: static;
|
||||
float: none;
|
||||
display: table-column;
|
||||
}
|
||||
|
||||
table td[class*="col-"],table th[class*="col-"] {
|
||||
position: static;
|
||||
float: none;
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
ol.glossary {
|
||||
counter-reset: glossary-counter;
|
||||
list-style: none;
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
ol.glossary li {
|
||||
counter-increment: glossary-counter;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
ol.glossary li::before {
|
||||
content: counter(glossary-counter);
|
||||
position: absolute;
|
||||
background-color: #73EAA9;
|
||||
color: #fff;
|
||||
border-radius: 100px;
|
||||
width: 24px;
|
||||
left: -40px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.layout-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.layout-title {
|
||||
padding: 1.875rem 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.layout-title h1 {
|
||||
font-size: 3rem;
|
||||
font-weight: 200;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.layout-title h2 {
|
||||
font-size: 2rem;
|
||||
font-weight: 200;
|
||||
text-align: center;
|
||||
color: #999;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.layout-navigation .nav {
|
||||
padding: 1.875rem 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.layout-navigation .nav > li {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.layout-navigation .nav > li > a {
|
||||
border: 1px solid #007bff;
|
||||
border-radius: 100px;
|
||||
padding: 6px 12px;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
.layout-navigation .nav > li > a:hover, .layout-navigation .nav > li > a:active, .layout-navigation .nav > li > a:focus {
|
||||
border: 1px solid #0056b3;
|
||||
color: #0056b3;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.layout-content {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.layout-content:before, .layout-content:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
|
||||
.layout-content:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.layout-content .col-right {
|
||||
width: 25%;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.layout-content .col-right-wrapper {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding: 2.5rem 2rem 0;
|
||||
}
|
||||
|
||||
.layout-content .col-body {
|
||||
width: 75%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.layout-content .col-body:before, .layout-content .col-body:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
|
||||
.layout-content .col-body:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.layout-content .col-body-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding: 2.5rem 2rem 0;
|
||||
}
|
||||
|
||||
.nav {
|
||||
padding-left: 0;
|
||||
margin-bottom: 0;
|
||||
list-style: none;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
.table-of-contents {
|
||||
padding: 1.25rem 0;
|
||||
}
|
||||
|
||||
.table-of-contents .nav > li > a {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.table-of-contents .nav > li > a .item-number {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.table-of-contents .nav > li > a .item-title {
|
||||
color: #212529;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
flex-grow: 1;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.table-of-contents .nav > li > a .item-title:hover {
|
||||
color: #0056b3;
|
||||
}
|
||||
|
||||
.table-of-contents .nav > li.extended > a .item-title, .table-of-contents .nav > li.active > a .item-title, .table-of-contents .nav > li.extended > a .item-title:hover, .table-of-contents .nav > li.active > a .item-title:hover {
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li > .nav {
|
||||
display: none;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li > .nav > li > a {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li > .nav > li > a .item-title {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li > .nav > li > .nav > li > a {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li > .nav > li > .nav > li > a .item-title {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li.active > .nav {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.chapter {
|
||||
margin: 0 0 6rem;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.chapter > .chapter-title h1, .chapter > .chapter-title h2, .chapter > .chapter-title h3, .chapter > .chapter-title h4, .chapter > .chapter-title h5, .chapter > .chapter-title h6, .section > .section-title h1, .section > .section-title h2, .section > .section-title h3, .section > .section-title h4, .section > .section-title h5, .section > .section-title h6 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.chapter > .chapter-title h1 {
|
||||
border-bottom: 2px solid #eeeeee;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 0.2em;
|
||||
}
|
||||
|
||||
.chapter-title .title-number, .section-title .title-number {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.paragraph {
|
||||
line-height: 1.75em;
|
||||
}
|
||||
|
||||
.paragraph + .paragraph {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.dlist {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.dlist dl dt {
|
||||
float: left;
|
||||
width: 160px;
|
||||
clear: left;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dlist dl dd {
|
||||
margin-left: 180px;
|
||||
}
|
||||
|
||||
.ulist {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.imageblock {
|
||||
margin: 30px auto;
|
||||
}
|
||||
|
||||
.imageblock .content img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.imageblock .title {
|
||||
padding: 10px 0 0;
|
||||
}
|
||||
|
||||
.exampleblock, .quoteblock, .literalblock {
|
||||
background: #f5f4f4;
|
||||
padding: 20px;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.exampleblock .title, .quoteblock .title, .literalblock .title {
|
||||
text-transform: uppercase;
|
||||
font-size: 0.75em;
|
||||
font-weight: 400;
|
||||
color: #979797;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.quoteblock blockquote {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.quoteblock blockquote p:last-child, .quoteblock blockquote ul:last-child, .quoteblock blockquote ol:last-child {
|
||||
margin-bottom: 9px;
|
||||
}
|
||||
|
||||
.literalblock pre {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.listingblock {
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.listingblock pre {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.listingblock pre code {
|
||||
display: block;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.admonitionblock {
|
||||
line-height: 1.8em;
|
||||
padding: 20px;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.admonitionblock .icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.admonitionblock.important {
|
||||
background: #fce1e1;
|
||||
border-left: 5px solid #ff6060;
|
||||
}
|
||||
|
||||
.admonitionblock.note, .admonitionblock.tip {
|
||||
background: #e0f2fc;
|
||||
border-left: 5px solid #88d5ff;
|
||||
}
|
||||
|
||||
.admonitionblock.caution, .admonitionblock.warning {
|
||||
background: #fdf3d8;
|
||||
border-left: 5px solid #f1c654;
|
||||
}
|
||||
|
||||
table.tableblock {
|
||||
background-color: #fff;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin-bottom: 18px;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
table.tableblock > thead > tr > th, table.tableblock > tbody > tr > th, table.tableblock > tfoot > tr > th, table.tableblock > thead > tr > td, table.tableblock > tbody > tr > td, table.tableblock > tfoot > tr > td {
|
||||
padding: 8px;
|
||||
line-height: 1.42857143;
|
||||
vertical-align: top;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
table.tableblock > thead > tr > th {
|
||||
vertical-align: bottom;
|
||||
border-bottom: 2px solid #eee;
|
||||
}
|
||||
|
||||
table.tableblock > caption + thead > tr:first-child > th, table.tableblock > colgroup + thead > tr:first-child > th, table.tableblock > thead:first-child > tr:first-child > th, table.tableblock > caption + thead > tr:first-child > td, table.tableblock > colgroup + thead > tr:first-child > td, table.tableblock > thead:first-child > tr:first-child > td {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
table.tableblock > tbody + tbody {
|
||||
border-top: 2px solid #eee;
|
||||
}
|
||||
|
||||
table.tableblock .table {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
table.tableblock > tbody > tr:nth-of-type(odd) {
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
|
||||
table.tableblock > thead > tr > th p:last-child, table.tableblock > tbody > tr > th p:last-child, table.tableblock > tfoot > tr > th p:last-child, table.tableblock > thead > tr > td p:last-child, table.tableblock > tbody > tr > td p:last-child, table.tableblock > tfoot > tr > td p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.loaded .table-of-contents .nav .nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.layout-wrapper .layout-title, .layout-wrapper .layout-navigation, .layout-wrapper .layout-content {
|
||||
max-width: 1200px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
html {
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.layout-content .col-right {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.layout-content .col-body {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
color: #000;
|
||||
font-family: Georgia, "Times New Roman", Times, serif;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.layout-title h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.layout-content .col-right {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.layout-content .col-body {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chapter {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
(function($){
|
||||
|
||||
hljs.initHighlightingOnLoad();
|
||||
|
||||
$(document).ready(function(){
|
||||
$('.col-right-wrapper').stick_in_parent({
|
||||
parent: '.layout-content'
|
||||
});
|
||||
$('body').scrollspy({
|
||||
target: '.table-of-contents'
|
||||
});
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
File diff suppressed because one or more lines are too long
Vendored
-2
File diff suppressed because one or more lines are too long
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
|
||||
Monokai Sublime style. Derived from Monokai by noformnocontent http://nn.mit-license.org/
|
||||
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
background: #23241f;
|
||||
}
|
||||
|
||||
.hljs,
|
||||
.hljs-tag,
|
||||
.hljs-subst {
|
||||
color: #f8f8f2;
|
||||
}
|
||||
|
||||
.hljs-strong,
|
||||
.hljs-emphasis {
|
||||
color: #a8a8a2;
|
||||
}
|
||||
|
||||
.hljs-bullet,
|
||||
.hljs-quote,
|
||||
.hljs-number,
|
||||
.hljs-regexp,
|
||||
.hljs-literal,
|
||||
.hljs-link {
|
||||
color: #ae81ff;
|
||||
}
|
||||
|
||||
.hljs-code,
|
||||
.hljs-title,
|
||||
.hljs-section,
|
||||
.hljs-selector-class {
|
||||
color: #a6e22e;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-name,
|
||||
.hljs-attr {
|
||||
color: #f92672;
|
||||
}
|
||||
|
||||
.hljs-symbol,
|
||||
.hljs-attribute {
|
||||
color: #66d9ef;
|
||||
}
|
||||
|
||||
.hljs-params,
|
||||
.hljs-class .hljs-title {
|
||||
color: #f8f8f2;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-type,
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo,
|
||||
.hljs-addition,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable {
|
||||
color: #e6db74;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-deletion,
|
||||
.hljs-meta {
|
||||
color: #75715e;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
/* ========================================================================
|
||||
* Bootstrap: scrollspy.js v3.3.7
|
||||
* http://getbootstrap.com/javascript/#scrollspy
|
||||
* ========================================================================
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
!function(r){"use strict";function o(t,s){this.$body=r(document.body),this.$scrollElement=r(t).is(document.body)?r(window):r(t),this.options=r.extend({},o.DEFAULTS,s),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",r.proxy(this.process,this)),this.refresh(),this.process()}function s(i){return this.each(function(){var t=r(this),s=t.data("bs.scrollspy"),e="object"==typeof i&&i;s||t.data("bs.scrollspy",s=new o(this,e)),"string"==typeof i&&s[i]()})}o.VERSION="3.3.7",o.DEFAULTS={offset:10},o.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},o.prototype.refresh=function(){var t=this,i="offset",o=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),r.isWindow(this.$scrollElement[0])||(i="position",o=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var t=r(this),s=t.data("target")||t.attr("href"),e=/^#./.test(s)&&r(s);return e&&e.length&&e.is(":visible")&&[[e[i]().top+o,s]]||null}).sort(function(t,s){return t[0]-s[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},o.prototype.process=function(){var t,s=this.$scrollElement.scrollTop()+this.options.offset,e=this.getScrollHeight(),i=this.options.offset+e-this.$scrollElement.height(),o=this.offsets,r=this.targets,l=this.activeTarget;if(this.scrollHeight!=e&&this.refresh(),i<=s)return l!=(t=r[r.length-1])&&this.activate(t);if(l&&s<o[0])return this.activeTarget=null,this.clear();for(t=o.length;t--;)l!=r[t]&&s>=o[t]&&(void 0===o[t+1]||s<o[t+1])&&this.activate(r[t])},o.prototype.activate=function(t){this.activeTarget=t,this.clear();var s=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',e=r(s).parents("li").addClass("active");e.parent(".dropdown-menu").length&&(e=e.closest("li.dropdown").addClass("active")),e.trigger("activate.bs.scrollspy")},o.prototype.clear=function(){r(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var t=r.fn.scrollspy;r.fn.scrollspy=s,r.fn.scrollspy.Constructor=o,r.fn.scrollspy.noConflict=function(){return r.fn.scrollspy=t,this},r(window).on("load.bs.scrollspy.data-api",function(){r('[data-spy="scroll"]').each(function(){var t=r(this);s.call(t,t.data())})})}(jQuery);
|
||||
@@ -1,5 +0,0 @@
|
||||
/**
|
||||
@license Sticky-kit v1.1.2 | WTFPL | Leaf Corcoran 2015 | http://leafo.net
|
||||
*/
|
||||
|
||||
(function(){var M,Q;M=this.jQuery||window.jQuery,Q=M(window),M.fn.stick_in_parent=function(t){var x,o,C,i,e,P,s,V,F,z,r,I,A,j;for(null==t&&(t={}),j=t.sticky_class,P=t.inner_scrolling,A=t.recalc_every,I=t.parent,F=t.offset_top,z=t.offset_bottom,V=t.spacer,C=t.bottoming,null==F&&(F=0),null==z&&(z=0),null==I&&(I=void 0),null==P&&(P=!0),null==j&&(j="is_stuck"),x=M(document),null==C&&(C=!0),r=function(t){var o,i;return window.getComputedStyle?(t[0],o=window.getComputedStyle(t[0]),i=parseFloat(o.getPropertyValue("width"))+parseFloat(o.getPropertyValue("margin-left"))+parseFloat(o.getPropertyValue("margin-right")),"border-box"!==o.getPropertyValue("box-sizing")&&(i+=parseFloat(o.getPropertyValue("border-left-width"))+parseFloat(o.getPropertyValue("border-right-width"))+parseFloat(o.getPropertyValue("padding-left"))+parseFloat(o.getPropertyValue("padding-right"))),i):t.outerWidth(!0)},i=function(n,l,a,c,p,d,u,f){var h,t,g,m,k,y,b,v,o,_,w,e;if(!n.data("sticky_kit")){if(n.data("sticky_kit",!0),k=x.height(),b=n.parent(),null!=I&&(b=b.closest(I)),!b.length)throw"failed to find stick parent";return h=g=!1,(w=null!=V?V&&n.closest(V):M('<div class="sticky-kit-manual-spacer" />'))&&w.css("position",n.css("position")),(v=function(){var t,o,i;if(!f)return k=x.height(),t=parseInt(b.css("border-top-width"),10),o=parseInt(b.css("padding-top"),10),l=parseInt(b.css("padding-bottom"),10),a=b.offset().top+t+o,c=b.height(),g&&(h=g=!1,null==V&&(n.insertAfter(w),w.detach()),n.css({position:"",top:"",width:"",bottom:""}).removeClass(j),i=!0),p=n.offset().top-(parseInt(n.css("margin-top"),10)||0)-F,d=n.outerHeight(!0),u=n.css("float"),w&&w.css({width:r(n),height:d,display:n.css("display"),"vertical-align":n.css("vertical-align"),float:u}),i?e():void 0})(),m=void 0,y=F,_=A,e=function(){var t,o,i,e,s,r;if(d!==c&&!f)return i=!1,null!=_&&(_-=1)<=0&&(_=A,v(),i=!0),i||x.height()===k||(v(),i=!0),e=Q.scrollTop(),null!=m&&(o=e-m),m=e,g?(C&&(s=c+a<e+d+y+z,h&&!s&&(h=!1,n.css({position:"fixed",bottom:"",top:y}).removeClass("is_bottomed").trigger("sticky_kit:unbottom"))),e<p&&(g=!1,y=F,null==V&&("left"!==u&&"right"!==u||n.insertAfter(w),w.detach()),t={position:"",width:"",top:""},n.css(t).removeClass(j).trigger("sticky_kit:unstick")),P&&(r=Q.height())<d+F&&(h||(y-=o,y=Math.max(r-d,y),y=Math.min(F,y),g&&n.css({top:y+"px"})))):p<e&&(g=!0,(t={position:"fixed",top:y}).width="border-box"===n.css("box-sizing")?n.outerWidth()+"px":n.width()+"px",n.css(t).addClass(j),null==V&&(n.after(w),"left"!==u&&"right"!==u||w.append(n)),n.trigger("sticky_kit:stick")),g&&C&&(null==s&&(s=c+a<e+d+y+z),!h&&s)?(h=!0,"static"===b.css("position")&&b.css({position:"relative"}),n.css({position:"absolute",bottom:l+z,top:"auto"}).addClass("is_bottomed").trigger("sticky_kit:bottom")):void 0},o=function(){return v(),e()},t=function(){if(f=!0,Q.off("touchmove",e),Q.off("scroll",e),Q.off("resize",o),M(document.body).off("sticky_kit:recalc",o),n.off("sticky_kit:detach",t),n.removeData("sticky_kit"),n.css({position:"",bottom:"",top:"",width:""}),b.position("position",""),g)return null==V&&("left"!==u&&"right"!==u||n.insertAfter(w),w.remove()),n.removeClass(j)},Q.on("touchmove",e),Q.on("scroll",e),Q.on("resize",o),M(document.body).on("sticky_kit:recalc",o),n.on("sticky_kit:detach",t),setTimeout(e,0)}},e=0,s=this.length;e<s;e++)o=this[e],i(M(o));return this}}).call(this);
|
||||
@@ -1,999 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<meta name="keywords" value="VR Payment, Shopware, Shopware Plugin, Payment, Payment Integration, Documentation"><meta name="description" value="The documentation for the Shopware 6 plugin that enables processing payments with VR Payment.">
|
||||
<link rel="canonical" href="https://plugin-documentation.wallee.com/wallee-payment/shopware-6/master/VRPaymentPayment/docs/fr/documentation.html" />
|
||||
<title>Wallee Payment Plugin pour Shopware 6</title>
|
||||
<link href="assets/monokai-sublime.css" rel="stylesheet" />
|
||||
<link href="assets/base.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body class="documentation">
|
||||
<div class="layout-wrapper">
|
||||
<div class="layout-title">
|
||||
<h1>Wallee Payment Plugin pour Shopware 6</h1>
|
||||
<h2>Documentation</h2> </div>
|
||||
<div class="layout-navigation">
|
||||
<ul class="nav">
|
||||
<li>
|
||||
<a href="https://gateway.vr-payment.de/user/login">
|
||||
Sign Up
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/vr-payment/shopware-6/releases/tag/7.0.1/">
|
||||
Source
|
||||
</a>
|
||||
</li>
|
||||
</ul> </div>
|
||||
<div class="layout-content">
|
||||
<div class="col-body">
|
||||
<div class="col-body-wrapper">
|
||||
<div class="body-container">
|
||||
<div class="chapter" id="_sommaire">
|
||||
<div class="chapter-title">
|
||||
<h1>
|
||||
<span class="title-number">1</span>Sommaire </h1>
|
||||
</div>
|
||||
<div class="chapter-body">
|
||||
<div class="paragraph">
|
||||
<p>Le plugin de paiement Wallee intègre un traitement moderne des paiements dans Shopware 6, offrant des fonctionnalités telles que les paiements basés sur iFrame, les remboursements, les captures et la conformité PCI. Il permet une intégration transparente avec le portail Wallee pour la gestion des transactions et des méthodes de paiement.</p>
|
||||
</div><div class="paragraph">
|
||||
<p>Important : Veuillez noter que seules les mises à jour majeures (par exemple 6.x.0.0) et mineures (par exemple 6.0.x.0) seront testées pour la compatibilité dans les 2 semaines suivant la publication.</p>
|
||||
</div> </div>
|
||||
</div> <div class="chapter" id="_pré_requis">
|
||||
<div class="chapter-title">
|
||||
<h1>
|
||||
<span class="title-number">2</span>Pré-Requis </h1>
|
||||
</div>
|
||||
<div class="chapter-body">
|
||||
<div class="paragraph">
|
||||
<p><strong>Shopware Version</strong>: 6.5.x or 6.6.x (voir <a href="#compatibility">tableau de compatibilité</a>).</p>
|
||||
</div><div class="paragraph">
|
||||
<p><strong>PHP</strong>: Version minimale requise pour votre installation Shopware (e.g., 7.4+).</p>
|
||||
</div><div class="paragraph">
|
||||
<p><strong>Compte Wallee</strong>: Obtenir Space ID, User ID, et clé API du <strong>Portail Wallee</strong> (voir le <a href="#portal-startup-guide">Guide de démarrage du Portail</a>).</p>
|
||||
</div> </div>
|
||||
</div> <div class="chapter" id="compatibility">
|
||||
<div class="chapter-title">
|
||||
<h1>
|
||||
<span class="title-number">3</span>Compatibilité </h1>
|
||||
</div>
|
||||
<div class="chapter-body">
|
||||
<table class="tableblock frame-all grid-all spread">
|
||||
<colgroup>
|
||||
<col style="width: 25%;"/>
|
||||
<col style="width: 25%;"/>
|
||||
<col style="width: 25%;"/>
|
||||
<col style="width: 25%;"/>
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong>Shopware Version</strong></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong>Plugin Version</strong></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong>PHP Version</strong></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong>Support Until</strong></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">6.6.x</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">6.x.x</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">PHP 8.2 and 8.3</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">En cours</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">6.5.x - Deprecated</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">5.x.x</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">PHP 8.2 and 8.3</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">October 2024</p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table> </div>
|
||||
</div> <div class="chapter" id="_installation">
|
||||
<div class="chapter-title">
|
||||
<h1>
|
||||
<span class="title-number">4</span>Installation </h1>
|
||||
</div>
|
||||
<div class="chapter-body">
|
||||
<div class="section" id="_via_marketplace">
|
||||
<div class="section-title">
|
||||
<h2>
|
||||
<span class="title-number">4.1</span>Via Marketplace </h2>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="olist arabic">
|
||||
<ol class="arabic">
|
||||
<li>
|
||||
<p>Connectez-vous au backend de votre boutique Shopware.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Naviguez vers Paramètres → Système → Plugins.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Cliquez sur la flèche du menu et sélectionnez le lien d’installation du plugin à installer</p>
|
||||
<div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/plugin-installation.png" alt="plugin installation"/>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p>Activez le plugin wallee Payment à partir du gestionnaire de plugins</p>
|
||||
</li>
|
||||
</ol>
|
||||
</div> </div>
|
||||
</div> <div class="section" id="_via_composer_recommendé">
|
||||
<div class="section-title">
|
||||
<h2>
|
||||
<span class="title-number">4.2</span>Via Composer (Recommendé) </h2>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="olist arabic">
|
||||
<ol class="arabic">
|
||||
<li>
|
||||
<p>Naviguez jusqu’au répertoire racine de votre Shopware.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Exécutez:</p>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre class="highlight"><code class="language-bash" data-lang="bash">composer require vrpayment/shopware-6
|
||||
php bin/console plugin:refresh
|
||||
php bin/console plugin:install --activate --clearCache VRPaymentPayment</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</div> </div>
|
||||
</div> <div class="section" id="_via_composer_recommended">
|
||||
<div class="section-title">
|
||||
<h2>
|
||||
<span class="title-number">4.3</span>Via Composer (Recommended) </h2>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="paragraph">
|
||||
<p>Téléchargez la <a href="https://github.com/vr-payment/shopware-6/releases" target="_blank">dernière version</a>.
|
||||
. Extrayez le ZIP dans <code>custom/plugins/</code>
|
||||
. Exécutez</p>
|
||||
</div><div class="paragraph">
|
||||
<p>+</p>
|
||||
</div><div class="listingblock">
|
||||
<div class="content">
|
||||
<pre class="highlight"><code class="language-bash" data-lang="bash">php bin/console plugin:refresh
|
||||
php bin/console plugin:install --activate --clearCache VRPaymentPayment</code></pre>
|
||||
</div>
|
||||
</div> </div>
|
||||
</div> </div>
|
||||
</div> <div class="chapter" id="portal-startup-guide">
|
||||
<div class="chapter-title">
|
||||
<h1>
|
||||
<span class="title-number">5</span>Guide de démarrage pour le Portail </h1>
|
||||
</div>
|
||||
<div class="chapter-body">
|
||||
<div class="paragraph">
|
||||
<p>Allez sur <a href="https://gateway.vr-payment.de/user/login/user/login">VR Payment</a> et créez un Compte si vous n’en avez pas déjà un</p>
|
||||
</div><div class="admonitionblock tip">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="icon">
|
||||
<div class="title">Tip</div>
|
||||
</td>
|
||||
<td class="content">
|
||||
Veuillez sélectionner le plan d’abonnement approprié - il doit prendre en charge les transactions de e-commerce.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div> <div class="section" id="_créez_la_clé_api">
|
||||
<div class="section-title">
|
||||
<h2>
|
||||
<span class="title-number">5.1</span>Créez la clé API: </h2>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="olist arabic">
|
||||
<ol class="arabic">
|
||||
<li>
|
||||
<p>Une fois que votre compte est activé, naviguez vers Compte → Utilisateurs de l’application</p>
|
||||
<div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/application-users.png" alt="application users"/>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p>Cliquez sur Créer un utilisateur d’application</p>
|
||||
<div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/user.png" alt="user"/>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p>Spécifiez un nom pour cette <strong>Clé API</strong> - par exemple Test Shop - et cliquez sur <strong>Créer un utilisateur user</strong></p>
|
||||
<div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/api-key.png" alt="api key"/>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p>Votre utilisateur d’application est ainsi créé. Veuillez copier l’User ID et la Clé d’authentification, car vous en aurez besoin pour reliez votre magasin au portail.</p>
|
||||
<div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/token.png" alt="token"/>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p>Enfin, vous devrez accorder certaines autorisations à cet utilisateur d’application afin qu’il puisse communiquer avec votre espace. Pour ce faire, sous Rôle, naviguez jusqu’à Gérer.</p>
|
||||
<div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/roles.png" alt="roles"/>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p>Cliquez sur le sign + à côté de Space Roles</p>
|
||||
<div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/assign-role.png" alt="assign role"/>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p>Veuillez ajouter le rôle <strong>Space Admin</strong> et cliquez sur and click on <strong>Assign Role</strong>.</p>
|
||||
<div class="admonitionblock tip">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="icon">
|
||||
<div class="title">Tip</div>
|
||||
</td>
|
||||
<td class="content">
|
||||
Veuillez noter que le chargement des rôles peut durer quelques secondes.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/loading-roles.png" alt="loading roles"/>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p>Enfin, cliquez sur Enregistrer les Roles; vous devrez entrer votre mot de passe pour confirmer.</p>
|
||||
<div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/save-role.png" alt="save role"/>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</div> </div>
|
||||
</div> <div class="section" id="_configurer_les_modes_de_paiement">
|
||||
<div class="section-title">
|
||||
<h2>
|
||||
<span class="title-number">5.2</span>Configurer les modes de paiement </h2>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="paragraph">
|
||||
<p>Vous pouvez maintenant configurer les méthodes de paiement que vous souhaitez utiliser ; à des fins de test, nous utiliserons le Bogus Processor. Veuillez noter que si vous souhaitez effectuer une transaction de production, vous pouvez utiliser notre offre Payfac ou vous connecter directement à un PSP spécifique (Worldline ; ACI ; …​).</p>
|
||||
</div><div class="olist arabic">
|
||||
<ol class="arabic">
|
||||
<li>
|
||||
<p>Naviguez vers Space → Paramètres et cliquez sur Processors</p>
|
||||
<div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/payment-settings.png" alt="payment settings"/>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p>Cliquez sur Configurez le processeur</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Sélectionnez Bogus Processor et cliquez sur Continue</p>
|
||||
<div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/bogus-processor.png" alt="bogus processor"/>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p>Définissez le <strong>nom</strong> que vous souhaitez donner à votre processeur - e.g. Test Processor - et cliquez sur <strong>Créer</strong></p>
|
||||
<div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/name-processor.png" alt="name processor"/>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p>Sélectionnez tous les connecteurs qui s’appliquent et cliquez sur Enregistrer</p>
|
||||
<div class="admonitionblock tip">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="icon">
|
||||
<div class="title">Tip</div>
|
||||
</td>
|
||||
<td class="content">
|
||||
Veuillez noter que les connecteurs semblent faire double emploi, mais c’est parce que l’un concerne le paiement par terminal physique et l’autre le paiement par ecommerce.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/connectors.png" alt="connectors"/>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</div><div class="paragraph">
|
||||
<p>Les méthodes de paiement sont désormais disponibles dans le portail.</p>
|
||||
</div> </div>
|
||||
</div> </div>
|
||||
</div> <div class="chapter" id="_guide_de_démarrage_pour_shopware">
|
||||
<div class="chapter-title">
|
||||
<h1>
|
||||
<span class="title-number">6</span>Guide de démarrage pour Shopware </h1>
|
||||
</div>
|
||||
<div class="chapter-body">
|
||||
<div class="olist arabic">
|
||||
<ol class="arabic">
|
||||
<li>
|
||||
<p><strong>Identifiants API</strong></p>
|
||||
<div class="olist loweralpha">
|
||||
<ol class="loweralpha" type="a">
|
||||
<li>
|
||||
<p>Naviguez vers <strong>Shopware Admin → Paramètres → Extensions → Wallee Payment</strong> et cliquez sur Sauvegarder.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Entrez votre <code>Space ID</code>, <code>User ID</code>, et <code>clé API</code></p>
|
||||
<div class="olist lowerroman">
|
||||
<ol class="lowerroman" type="i">
|
||||
<li>
|
||||
<p>Pour le Space id; vous pouvez naviguer vers le Space et il sera fourni - par exemple, l’identifiant du Space id = 76231</p>
|
||||
<div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/plugin-configuration.png" alt="plugin configuration"/>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Méthodes de Paiment</strong></p>
|
||||
<div class="paragraph">
|
||||
<p>Les méthodes de paiement disponibles à la caisse sont gérées par le Portail Wallee. Si vous souhaitez désactiver une méthode de paiement, vous devez la désactiver à partir du portail.</p>
|
||||
</div>
|
||||
<div class="admonitionblock note">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="icon">
|
||||
<div class="title">Note</div>
|
||||
</td>
|
||||
<td class="content">
|
||||
Veuillez noter qu’en raison de la synchronisation entre le portail et la boutique, vous pouvez effectuer la même opération à partir de la boutique, sous <strong>Paramètres → Méthodes de paiement.</strong>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="olist loweralpha">
|
||||
<ol class="loweralpha" type="a">
|
||||
<li>
|
||||
<p>a.Allez dans <strong>Space → Paramètres → Paiements → Méthodes de Paiement</strong></p>
|
||||
<div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/payment-methods.png" alt="payment methods"/>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p>Sélectionnez le mode de paiement que vous souhaitez désactiver. Cliquez sur le <strong>curseur</strong> pour le désactiver → il devrait devenir <strong>inactif</strong>.</p>
|
||||
<div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/cc-enable.png" alt="cc enable"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/cc-disable.png" alt="cc disable"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="admonitionblock tip">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="icon">
|
||||
<div class="title">Tip</div>
|
||||
</td>
|
||||
<td class="content">
|
||||
Si vous souhaitez désactiver uniquement un Connecteur d’un mode de paiement (par exemple, Mastercard pour une carte de crédit), veuillez consulter la section Connecteur.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Options</strong></p>
|
||||
<div class="olist loweralpha">
|
||||
<ol class="loweralpha" type="a">
|
||||
<li>
|
||||
<p><strong>Space View Id</strong>: Ce champ vous permet d’appliquer un style personnalisé au formulaire et à la page de paiement. Le style est défini dans les paramètres de votre espace dans le portail.</p>
|
||||
<div class="admonitionblock note">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="icon">
|
||||
<div class="title">Note</div>
|
||||
</td>
|
||||
<td class="content">
|
||||
Veuillez noter que si vous n’utilisez pas Space View Id, cette option doit rester vide.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Intégration</strong>: Le paramètre Options d’intégration détermine comment le formulaire de paiement est affiché pendant la procédure de paiement. Les options suivantes sont disponibles :</p>
|
||||
<div class="olist lowerroman">
|
||||
<ol class="lowerroman" type="i">
|
||||
<li>
|
||||
<p><strong>IFrame</strong>: Incorpore le formulaire de paiement directement dans la page de paiement de Shopware pour une expérience transparente.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Lightbox</strong>: Ouvre une fenêtre contextuelle sécurisée pour que les clients puissent effectuer leur paiement sans quitter la page de paiement.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Payment Page</strong>: Redirige les clients vers une page de paiement dédiée, hébergée par le fournisseur de paiement.</p>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Consistence des articles</strong>: Shopware calcule les taxes au niveau de l’article, ce qui peut entraîner des différences mineures (généralement de quelques centimes) entre le total des taxes de la commande et le prix affiché. Cet écart est dû à des différences d’arrondi lors du calcul de chaque article. Si le paramètre « Renforcer la cohérence » est activé, le portail rejettera automatiquement les commandes présentant de tels écarts. Pour éviter les problèmes liés au traitement des paiements, nous vous recommandons de désactiver ce paramètre, à moins qu’une validation stricte du total des taxes ne soit nécessaire.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Envoyer un email de confirmation de commande</strong>: Activez cette option pour envoyer des emails de confirmation de commande directement depuis Shopware au lieu du portail.</p>
|
||||
<div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/order-confirmation-email.png" alt="order confirmation email"/>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</div> </div>
|
||||
</div> <div class="chapter" id="_différents_etats_pour_une_transaction">
|
||||
<div class="chapter-title">
|
||||
<h1>
|
||||
<span class="title-number">7</span>Différents Etats pour une Transaction </h1>
|
||||
</div>
|
||||
<div class="chapter-body">
|
||||
<div class="paragraph">
|
||||
<p>Le processus de paiement de wallee est complètement standardisé pour chaque méthode de paiement que vous pouvez traiter. Cela vous permet d’ajouter simplement une méthode de paiement ou un processeur sans modifier la configuration de votre Shopware. Une vue d’ensemble des états et des processus de paiement de wallee peut être trouvée dans la <a href="https://gateway.vr-payment.de/en-us/doc/payment/transaction-process" target="_blank">documentation sur les paiments.</a>.</p>
|
||||
</div><div class="paragraph">
|
||||
<p>Dans la section suivante, nous vous donnons un aperçu de la façon dont les états de wallee sont mappés dans le graphique des états de Shopware pour les commandes et les états de paiement.</p>
|
||||
</div> <div class="section" id="_cartographie_des_différents_états_d_une_commande_de_shopware">
|
||||
<div class="section-title">
|
||||
<h2>
|
||||
<span class="title-number">7.1</span>Cartographie des différents états d’une commande de Shopware </h2>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="paragraph">
|
||||
<p>Actuellement, nous ne modifions pas l’état de la commande. Nous ne modifions que l’état du paiement et l’état de la livraison.</p>
|
||||
</div> <div class="section" id="_remarque_générales_concernant_les_status_des_commandes">
|
||||
<div class="section-title">
|
||||
<h3>
|
||||
<span class="title-number">7.1.1</span>Remarque générales concernant les status des commandes </h3>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="paragraph">
|
||||
<p>Nous vous recommandons de ne modifier l’état de la commande que lorsque l’état du paiement est définitif (Payée, Annulée ou Echouée) .</p>
|
||||
</div> </div>
|
||||
</div> </div>
|
||||
</div> <div class="section" id="_cartographie_des_différents_états_du_paiement_de_shopware">
|
||||
<div class="section-title">
|
||||
<h2>
|
||||
<span class="title-number">7.2</span>Cartographie des différents états du paiement de Shopware </h2>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="paragraph">
|
||||
<p>Vous trouverez ci-dessous un diagramme qui montre l’association des différents états de Shopware pour l’état de paiement pour wallee, ainsi que des informations supplémentaires sur les transitions entre les états.</p>
|
||||
</div><div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/shopware_6_stage_graph_order.svg" alt="shopware 6 stage graph order"/>
|
||||
</div>
|
||||
</div><div class="olist glossary">
|
||||
<ol class="glossary">
|
||||
<li>
|
||||
<p>Si la transaction est <code>Autorisée</code> dans wallee, le statut du paiement de la commande dans le Shopware est marqué comme étant <code>En Cours</code>.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Si la transaction échoue avant ou pendant le processus d’autorisation, le statut du paiement de la commande du Shopware est marqué comme <code>Échouée</code>.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Si la transaction échoue après l’autorisation, le statut du paiement de la commande du Shopware est marqué comme <code>Annulée</code>.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Si la facture de la transaction dans wallee est marquée comme <code>Payée</code> ou <code>Non Applicable</code>, le statut du paiement de la commande dans le Shopware est marqué comme <code>Payée</code>.</p>
|
||||
</li>
|
||||
</ol>
|
||||
</div> <div class="section" id="_remarques_générales_concernant_les_différents_status_pour_les_paiements">
|
||||
<div class="section-title">
|
||||
<h3>
|
||||
<span class="title-number">7.2.1</span>Remarques générales concernant les différents status pour les paiements </h3>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="paragraph">
|
||||
<p>Nous vous recommandons de ne pas modifier manuellement l’état du paiement. Si vous le faites, il peut être modifié à nouveau par le plugin.</p>
|
||||
</div> </div>
|
||||
</div> </div>
|
||||
</div> <div class="section" id="_carthographie_des_différents_états_de_livraison_chez_shopware">
|
||||
<div class="section-title">
|
||||
<h2>
|
||||
<span class="title-number">7.3</span>Carthographie des différents états de livraison chez Shopware </h2>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="paragraph">
|
||||
<p>Vous trouverez ci-dessous un diagramme qui montre les différents états pour la livraison chez Shopware avec des informations supplémentaires pour les transitions d’états.</p>
|
||||
</div><div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/shopware_6_stage_graph_delivery.svg" alt="shopware 6 stage graph delivery"/>
|
||||
</div>
|
||||
</div><div class="olist glossary">
|
||||
<ol class="glossary">
|
||||
<li>
|
||||
<p>Si la transaction est <code>confirmée</code> dans wallee, le statut de livraison de la commande dans le Shopware est indiqué comme étant <code>En Attente</code>.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Si la transaction dans wallee est marquée comme <code>Délivrée</code>, le statut de livraison de la commande Shopware est marqué comme <code>Ouvert</code>.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Si la transaction est en statut <code>Déclinée</code>, <code>Échouée</code> ou <code>Annulée</code>, le statut de livraison de la commande du Shopware est marqué comme <code>Annulée</code>.</p>
|
||||
</li>
|
||||
</ol>
|
||||
</div> </div>
|
||||
</div> </div>
|
||||
</div> <div class="chapter" id="_gestion_des_transactions">
|
||||
<div class="chapter-title">
|
||||
<h1>
|
||||
<span class="title-number">8</span>Gestion des Transactions </h1>
|
||||
</div>
|
||||
<div class="chapter-body">
|
||||
<div class="paragraph">
|
||||
<p>Vous pouvez capturer, annuler et rembourser des transactions directement depuis le backend de Shopware. Veuillez noter que si vous remboursez, annulez ou capturez des transactions dans wallee, les événements seront synchronisés dans Shopware. Cependant, il y a quelques limitations (voir ci-dessous).</p>
|
||||
</div> <div class="section" id="_complete_capture_an_order">
|
||||
<div class="section-title">
|
||||
<h2>
|
||||
<span class="title-number">8.1</span>Complete (capture) an order </h2>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="paragraph">
|
||||
<p>Vous avez la possibilité pour vos transactions de n’autoriser le paiement qu’une fois la commande passée. Dans la configuration du connecteur, vous avez la possibilité, si la méthode de paiement le permet, de définir si le paiement doit être effectué immédiatement ou différé.</p>
|
||||
</div><div class="paragraph">
|
||||
<p>Pour capturer une transaction, ouvrez la commande et cliquez sur le bouton Terminer.</p>
|
||||
</div><div class="admonitionblock note">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="icon">
|
||||
<div class="title">Note</div>
|
||||
</td>
|
||||
<td class="content">
|
||||
Lorsque le paiement est en attente dans wallee, la commande reste en attente.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div><div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/capture-transaction.png" alt="capture transaction"/>
|
||||
</div>
|
||||
</div><div class="paragraph">
|
||||
<p><strong>Finalisation du paiement de manière différée</strong></p>
|
||||
</div><div class="paragraph">
|
||||
<p>Les détaillants souhaitent souvent autoriser les transactions et lancer le processus d’exécution une fois que tous les articles peuvent être expédiés. Cela est également possible avec wallee.</p>
|
||||
</div><div class="paragraph">
|
||||
<p>Cependant, certains processus doivent être suivis. Si vous avez configuré la finalisation du paiement pour qu’il soit différé, vous devez capturer la transaction avant d’initier l’expédition, car il peut toujours arriver qu’une finalisation échoue. Si vous voulez être sûr de ne pas expédier d’articles pour lesquels vous n’avez pas été payé, vous devez reporter l’expédition jusqu’à ce que l’état <code>Confirmé</code> soit atteint. Au départ, la transaction sera dans l’état <code>Autorisé</code> dans wallee et <code>En cours</code> dans Shopware. Si vous souhaitez lancer le processus d’exécution, assurez-vous de lancer le processus d’achèvement comme décrit ci-dessus. Une fois le processus terminé avec succès, la commande passera à l’état <code>Confirmée</code> dans wallee et à l’état Payée dans Shopware. Vous pouvez maintenant lancer le processus de livraison .</p>
|
||||
</div> </div>
|
||||
</div> <div class="section" id="_annuler_une_transaction">
|
||||
<div class="section-title">
|
||||
<h2>
|
||||
<span class="title-number">8.2</span>Annuler une transaction </h2>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="paragraph">
|
||||
<p>Pour annuler une transaction, ouvrez la commande et cliquez sur le bouton <code>Annuler l’autorisation</code>.</p>
|
||||
</div><div class="admonitionblock note">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="icon">
|
||||
<div class="title">Note</div>
|
||||
</td>
|
||||
<td class="content">
|
||||
Vous ne pouvez annuler que les transactions qui ne sont pas encore complétée..
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div><div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/void-transaction.png" alt="void transaction"/>
|
||||
</div>
|
||||
</div> </div>
|
||||
</div> <div class="section" id="_remboursement_d_une_transaction">
|
||||
<div class="section-title">
|
||||
<h2>
|
||||
<span class="title-number">8.3</span>Remboursement d’une transaction </h2>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="paragraph">
|
||||
<p>Vous avez la possibilité de rembourser des transactions déjà effectuées. Pour ce faire, ouvrez l’ordre saisi. En cliquant sur les 3 points (…​) sur un article, vous pouvez rembourser l’article partiellement (s’il a une quantité supérieure à 1), ou vous pouvez rembourser l’article en entier. Si la méthode de paiement ne prend pas en charge les remboursements, vous n’aurez pas la possibilité d’effectuer des remboursements en ligne.</p>
|
||||
</div><div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/refund-transaction.png" alt="refund transaction"/>
|
||||
</div>
|
||||
</div><div class="paragraph">
|
||||
<p>Vous pouvez effectuer autant de remboursements individuels que vous le souhaitez jusqu’à ce que vous ayez atteint le montant total de la commande initiale. Le statut de la commande passe alors automatiquement à <code>completée</code>.</p>
|
||||
</div><div class="admonitionblock note">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="icon">
|
||||
<div class="title">Note</div>
|
||||
</td>
|
||||
<td class="content">
|
||||
Il peut s’écouler un certain temps avant que vous ne voyiez le remboursement dans Shopware. Les remboursements ne seront visibles que lorsqu’ils auront été traités avec succès.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div> </div>
|
||||
</div> <div class="section" id="_commandes_en_attente">
|
||||
<div class="section-title">
|
||||
<h2>
|
||||
<span class="title-number">8.4</span>Commandes en attente </h2>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="paragraph">
|
||||
<p>La livraison ne doit pas être effectuée tant que l’état de la livraison est en attente. Cela se produit lorsque la transaction dans wallee n’a pas atteint l’état Confirmé.</p>
|
||||
</div><div class="paragraph">
|
||||
<p>Il y a essentiellement deux raisons pour lesquelles cela peut se produire :</p>
|
||||
</div><div class="ulist">
|
||||
<ul>
|
||||
<li>
|
||||
<p>La transaction n’est pas terminée. Dans ce cas, vous devez compléter la transaction comme indiqué ci-dessus..</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Nous ne sommes pas en mesure de déterminer si vous devez honorer la commande. La décision de livraison est prise automatiquement. Si cela ne se produit pas dans le délai défini, wallee génère une tâche manuelle que vous devez observer et suivre les instructions.</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div><div class="paragraph">
|
||||
<p>Vous trouverez plus d’informations sur les tâches manuelles dans notre <a href="https://gateway.vr-payment.de/en-us/doc/manual-tasks" target="_blank">Documentation sur les Tâches Manuelles.</a>.</p>
|
||||
</div> </div>
|
||||
</div> <div class="section" id="_limites_de_la_synchronisation_entre_wallee_et_shopware">
|
||||
<div class="section-title">
|
||||
<h2>
|
||||
<span class="title-number">8.5</span>Limites de la synchronisation entre wallee et Shopware </h2>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="paragraph">
|
||||
<p>Veuillez noter que les captures, annulations et remboursements effectués dans wallee sont synchronisés. Cependant, il y a quelques limitations. Dans wallee, vous pouvez modifier le <strong>prix unitaire</strong> et <strong>la quantité</strong> en une seule fois. Cela n’est pas possible dans le backend du Shopware. Nous vous recommandons donc d’effectuer les remboursements toujours dans le backend de Shopware et non dans wallee. Si un remboursement ne peut pas être synchronisé, il sera envoyé au processeur, mais il se peut que vous ne le voyiez pas dans votre backend Shopware.</p>
|
||||
</div><div class="paragraph">
|
||||
<p>Vous pouvez trouver plus d’informations sur les remboursements dans wallee dans notre <a href="https://gateway.vr-payment.de/en-us/doc/payment/refund" target="_blank">Documentation sur les Remboursements.</a>.</p>
|
||||
</div> </div>
|
||||
</div> <div class="section" id="_tokenisation">
|
||||
<div class="section-title">
|
||||
<h2>
|
||||
<span class="title-number">8.6</span>Tokenisation </h2>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="paragraph">
|
||||
<p>Si la méthode de paiement prend en charge la tokenisation, vous pouvez stocker les détails de paiement de votre client pour des achats futurs. Afin d’utiliser cette fonctionnalité, assurez-vous que le Mode de <strong>paiement en un clic</strong> dans la configuration de votre méthode de paiement est réglé sur <strong>autoriser</strong> ou <strong>forcer</strong> le stockage.</p>
|
||||
</div><div class="admonitionblock note">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="icon">
|
||||
<div class="title">Note</div>
|
||||
</td>
|
||||
<td class="content">
|
||||
La tokenisation n’est pas disponible pour les paiements par les invités.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div> </div>
|
||||
</div> <div class="section" id="_caractéristiques_pricinpales">
|
||||
<div class="section-title">
|
||||
<h2>
|
||||
<span class="title-number">8.7</span>Caractéristiques Pricinpales </h2>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="ulist">
|
||||
<ul>
|
||||
<li>
|
||||
<p><strong>Intégration iFrame</strong>: Intégrez des formulaires de paiement directement dans votre checkout.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Remboursements & Captures</strong>: Déclenchez des remboursements complets/partiels et des captures à partir de Shopware ou du portail Wallee.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Support Multi-Magasins</strong>: Gérez les configurations sur plusieurs magasins.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Mises à jour automatiques</strong>: Les méthodes de paiement se synchronisent dynamiquement via l’API Wallee.</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div> </div>
|
||||
</div> <div class="section" id="_troubleshooting">
|
||||
<div class="section-title">
|
||||
<h2>
|
||||
<span class="title-number">8.8</span>Troubleshooting </h2>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="ulist">
|
||||
<ul>
|
||||
<li>
|
||||
<p>Logs: Vérifiez les logs des payments avec:</p>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre class="highlight"><code class="language-bash" data-lang="bash">tail -f var/log/whitelabelname_payment*.log</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<p>Problèmes courants:</p>
|
||||
<div class="ulist">
|
||||
<ul>
|
||||
<li>
|
||||
<p>Assurez-vous que la commande <code>composer update whitelabelname/shopware-6</code> est exécutée après les mises à jour.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Vérifier que les identifiants de l’API correspondent à votre compte Wallee.</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div> </div>
|
||||
</div> <div class="section" id="_faqs">
|
||||
<div class="section-title">
|
||||
<h2>
|
||||
<span class="title-number">8.9</span>FAQs </h2>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<div class="paragraph">
|
||||
<p><strong>Q: Comment s’assurer que la connexion entre le portail et la boutique fonctionne?</strong>
|
||||
A: Vous devez vérifier que les webhooks ont été correctement créés. Pour ce faire, naviguez vers votre espace dans le portail, allez dans Paramètres → Général → Webhook Listeners.</p>
|
||||
</div><div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/webhooks.png" alt="webhooks"/>
|
||||
</div>
|
||||
</div><div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="resource/webhook-listeners.png" alt="webhook listeners"/>
|
||||
</div>
|
||||
</div><div class="paragraph">
|
||||
<p><strong>Q: Ce plugin prend-il en charge les paiements en un clic ?</strong>
|
||||
A: Oui, via la tokenisation dans le portail Wallee.</p>
|
||||
</div><div class="paragraph">
|
||||
<p><strong>Q: Comment gérer la conformité PCI ?</strong>
|
||||
A: Le plugin utilise l’intégration iFrame, réduisant les exigences PCI à SAQ-A.</p>
|
||||
</div><div class="paragraph">
|
||||
<p><strong>Q: Le plugin prend-il en charge Apple Pay ?</strong>
|
||||
A: Oui, le plugin prend en charge les portefeuilles comme Apple Pay.</p>
|
||||
</div> </div>
|
||||
</div> </div>
|
||||
</div> <div class="chapter" id="_changelog">
|
||||
<div class="chapter-title">
|
||||
<h1>
|
||||
<span class="title-number">9</span>Changelog </h1>
|
||||
</div>
|
||||
<div class="chapter-body">
|
||||
<div class="paragraph">
|
||||
<p>Pour les mises à jour spécifiques à une version, voir les <a href="https://github.com/vr-payment/shopware-6/releases" target="_blank">GitHub Releases</a>.</p>
|
||||
</div> </div>
|
||||
</div> <div class="chapter" id="_contribuer">
|
||||
<div class="chapter-title">
|
||||
<h1>
|
||||
<span class="title-number">10</span>Contribuer </h1>
|
||||
</div>
|
||||
<div class="chapter-body">
|
||||
<div class="paragraph">
|
||||
<p>Signaler les problèmes via <a href="https://github.com/vr-payment/shopware-6/issues" target="_blank">GitHub Issues</a>.</p>
|
||||
</div><div class="paragraph">
|
||||
<p>Suivez le <a href="https://developer.shopware.com/docs/guides/plugins/plugins/plugin-base-guide.html" target="_blank">Shopware Plugin Base Guide</a> pour le développement.</p>
|
||||
</div> </div>
|
||||
</div> <div class="chapter" id="_support">
|
||||
<div class="chapter-title">
|
||||
<h1>
|
||||
<span class="title-number">11</span>Support </h1>
|
||||
</div>
|
||||
<div class="chapter-body">
|
||||
<div class="paragraph">
|
||||
<p>Si vous avez besoin d’aide, n’hésitez pas à contacter notre <a href="https://www.vr-payment.de/hotline">support</a>.</p>
|
||||
</div> </div>
|
||||
</div> </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-right">
|
||||
<div class="col-right-wrapper">
|
||||
<div class="table-of-contents">
|
||||
<ul class="nav">
|
||||
<li class="nav-level-1">
|
||||
<a href="#_sommaire">
|
||||
<span class="item-number">1</span>
|
||||
<span class="item-title">Sommaire</span>
|
||||
</a>
|
||||
</li> <li class="nav-level-1">
|
||||
<a href="#_pré_requis">
|
||||
<span class="item-number">2</span>
|
||||
<span class="item-title">Pré-Requis</span>
|
||||
</a>
|
||||
</li> <li class="nav-level-1">
|
||||
<a href="#compatibility">
|
||||
<span class="item-number">3</span>
|
||||
<span class="item-title">Compatibilité</span>
|
||||
</a>
|
||||
</li> <li class="nav-level-1">
|
||||
<a href="#_installation">
|
||||
<span class="item-number">4</span>
|
||||
<span class="item-title">Installation</span>
|
||||
</a>
|
||||
<ul class="nav">
|
||||
<li class="nav-level-2">
|
||||
<a href="#_via_marketplace">
|
||||
<span class="item-number">4.1</span>
|
||||
<span class="item-title">Via Marketplace</span>
|
||||
</a>
|
||||
</li> <li class="nav-level-2">
|
||||
<a href="#_via_composer_recommendé">
|
||||
<span class="item-number">4.2</span>
|
||||
<span class="item-title">Via Composer (Recommendé)</span>
|
||||
</a>
|
||||
</li> <li class="nav-level-2">
|
||||
<a href="#_via_composer_recommended">
|
||||
<span class="item-number">4.3</span>
|
||||
<span class="item-title">Via Composer (Recommended)</span>
|
||||
</a>
|
||||
</li> </ul>
|
||||
</li> <li class="nav-level-1">
|
||||
<a href="#portal-startup-guide">
|
||||
<span class="item-number">5</span>
|
||||
<span class="item-title">Guide de démarrage pour le Portail</span>
|
||||
</a>
|
||||
<ul class="nav">
|
||||
<li class="nav-level-2">
|
||||
<a href="#_créez_la_clé_api">
|
||||
<span class="item-number">5.1</span>
|
||||
<span class="item-title">Créez la clé API:</span>
|
||||
</a>
|
||||
</li> <li class="nav-level-2">
|
||||
<a href="#_configurer_les_modes_de_paiement">
|
||||
<span class="item-number">5.2</span>
|
||||
<span class="item-title">Configurer les modes de paiement</span>
|
||||
</a>
|
||||
</li> </ul>
|
||||
</li> <li class="nav-level-1">
|
||||
<a href="#_guide_de_démarrage_pour_shopware">
|
||||
<span class="item-number">6</span>
|
||||
<span class="item-title">Guide de démarrage pour Shopware</span>
|
||||
</a>
|
||||
</li> <li class="nav-level-1">
|
||||
<a href="#_différents_etats_pour_une_transaction">
|
||||
<span class="item-number">7</span>
|
||||
<span class="item-title">Différents Etats pour une Transaction</span>
|
||||
</a>
|
||||
<ul class="nav">
|
||||
<li class="nav-level-2">
|
||||
<a href="#_cartographie_des_différents_états_d_une_commande_de_shopware">
|
||||
<span class="item-number">7.1</span>
|
||||
<span class="item-title">Cartographie des différents états d’une commande de Shopware</span>
|
||||
</a>
|
||||
<ul class="nav">
|
||||
<li class="nav-level-3">
|
||||
<a href="#_remarque_générales_concernant_les_status_des_commandes">
|
||||
<span class="item-number">7.1.1</span>
|
||||
<span class="item-title">Remarque générales concernant les status des commandes</span>
|
||||
</a>
|
||||
</li> </ul>
|
||||
</li> <li class="nav-level-2">
|
||||
<a href="#_cartographie_des_différents_états_du_paiement_de_shopware">
|
||||
<span class="item-number">7.2</span>
|
||||
<span class="item-title">Cartographie des différents états du paiement de Shopware</span>
|
||||
</a>
|
||||
<ul class="nav">
|
||||
<li class="nav-level-3">
|
||||
<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-title">Remarques générales concernant les différents status pour les paiements</span>
|
||||
</a>
|
||||
</li> </ul>
|
||||
</li> <li class="nav-level-2">
|
||||
<a href="#_carthographie_des_différents_états_de_livraison_chez_shopware">
|
||||
<span class="item-number">7.3</span>
|
||||
<span class="item-title">Carthographie des différents états de livraison chez Shopware</span>
|
||||
</a>
|
||||
</li> </ul>
|
||||
</li> <li class="nav-level-1">
|
||||
<a href="#_gestion_des_transactions">
|
||||
<span class="item-number">8</span>
|
||||
<span class="item-title">Gestion des Transactions</span>
|
||||
</a>
|
||||
<ul class="nav">
|
||||
<li class="nav-level-2">
|
||||
<a href="#_complete_capture_an_order">
|
||||
<span class="item-number">8.1</span>
|
||||
<span class="item-title">Complete (capture) an order</span>
|
||||
</a>
|
||||
</li> <li class="nav-level-2">
|
||||
<a href="#_annuler_une_transaction">
|
||||
<span class="item-number">8.2</span>
|
||||
<span class="item-title">Annuler une transaction</span>
|
||||
</a>
|
||||
</li> <li class="nav-level-2">
|
||||
<a href="#_remboursement_d_une_transaction">
|
||||
<span class="item-number">8.3</span>
|
||||
<span class="item-title">Remboursement d&#8217;une transaction</span>
|
||||
</a>
|
||||
</li> <li class="nav-level-2">
|
||||
<a href="#_commandes_en_attente">
|
||||
<span class="item-number">8.4</span>
|
||||
<span class="item-title">Commandes en attente</span>
|
||||
</a>
|
||||
</li> <li class="nav-level-2">
|
||||
<a href="#_limites_de_la_synchronisation_entre_wallee_et_shopware">
|
||||
<span class="item-number">8.5</span>
|
||||
<span class="item-title">Limites de la synchronisation entre wallee et Shopware</span>
|
||||
</a>
|
||||
</li> <li class="nav-level-2">
|
||||
<a href="#_tokenisation">
|
||||
<span class="item-number">8.6</span>
|
||||
<span class="item-title">Tokenisation</span>
|
||||
</a>
|
||||
</li> <li class="nav-level-2">
|
||||
<a href="#_caractéristiques_pricinpales">
|
||||
<span class="item-number">8.7</span>
|
||||
<span class="item-title">Caractéristiques Pricinpales</span>
|
||||
</a>
|
||||
</li> <li class="nav-level-2">
|
||||
<a href="#_troubleshooting">
|
||||
<span class="item-number">8.8</span>
|
||||
<span class="item-title">Troubleshooting</span>
|
||||
</a>
|
||||
</li> <li class="nav-level-2">
|
||||
<a href="#_faqs">
|
||||
<span class="item-number">8.9</span>
|
||||
<span class="item-title">FAQs</span>
|
||||
</a>
|
||||
</li> </ul>
|
||||
</li> <li class="nav-level-1">
|
||||
<a href="#_changelog">
|
||||
<span class="item-number">9</span>
|
||||
<span class="item-title">Changelog</span>
|
||||
</a>
|
||||
</li> <li class="nav-level-1">
|
||||
<a href="#_contribuer">
|
||||
<span class="item-number">10</span>
|
||||
<span class="item-title">Contribuer</span>
|
||||
</a>
|
||||
</li> <li class="nav-level-1">
|
||||
<a href="#_support">
|
||||
<span class="item-number">11</span>
|
||||
<span class="item-title">Support</span>
|
||||
</a>
|
||||
</li> </ul>
|
||||
</div> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="assets/jquery.js"></script>
|
||||
<script type="text/javascript" src="assets/scrollspy.js"></script>
|
||||
<script type="text/javascript" src="assets/sticky-kit.js"></script>
|
||||
<script type="text/javascript" src="assets/highlight.js"></script>
|
||||
<script type="text/javascript" src="assets/base.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,692 +0,0 @@
|
||||
* {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
*:before, *:after {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 100%;
|
||||
font-family: sans-serif;
|
||||
line-height: 1.15;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-ms-overflow-style: scrollbar;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
@-ms-viewport {
|
||||
width: device-width;
|
||||
}
|
||||
|
||||
article, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
font-size: 1rem;
|
||||
font-weight: 300;
|
||||
line-height: 1.5;
|
||||
color: #212529;
|
||||
text-align: left;
|
||||
background-color: #fff;
|
||||
padding-right: 0 !important;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
html,body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
[tabindex="-1"]:focus {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
hr {
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 200;
|
||||
margin-bottom: 1.875rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.625rem;
|
||||
font-weight: 300;
|
||||
margin-bottom: 1.3rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 300;
|
||||
margin-top: 1.3rem;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 400;
|
||||
margin-top: 1.875rem;
|
||||
margin-bottom: 1.3rem;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
margin-top: 1.875rem;
|
||||
margin-bottom: 1.3rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title], abbr[data-original-title] {
|
||||
text-decoration: underline;
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol, ul, dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol, ul ul, ol ul, ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: .5rem;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
b, strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
sub, sup {
|
||||
position: relative;
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
-webkit-text-decoration-skip: objects;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #0056b3;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]) {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]):focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
pre, code, kbd, samp {
|
||||
font-family: monospace, monospace;
|
||||
font-size: 90%;
|
||||
padding: 2px 4px 2px 4px;
|
||||
color: #c7254e;
|
||||
background-color: #f9f2f4;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: scrollbar;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img {
|
||||
vertical-align: middle;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
color: #a7a7a7;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
table col[class*="col-"] {
|
||||
position: static;
|
||||
float: none;
|
||||
display: table-column;
|
||||
}
|
||||
|
||||
table td[class*="col-"],table th[class*="col-"] {
|
||||
position: static;
|
||||
float: none;
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
ol.glossary {
|
||||
counter-reset: glossary-counter;
|
||||
list-style: none;
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
ol.glossary li {
|
||||
counter-increment: glossary-counter;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
ol.glossary li::before {
|
||||
content: counter(glossary-counter);
|
||||
position: absolute;
|
||||
background-color: #73EAA9;
|
||||
color: #fff;
|
||||
border-radius: 100px;
|
||||
width: 24px;
|
||||
left: -40px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.layout-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.layout-title {
|
||||
padding: 1.875rem 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.layout-title h1 {
|
||||
font-size: 3rem;
|
||||
font-weight: 200;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.layout-title h2 {
|
||||
font-size: 2rem;
|
||||
font-weight: 200;
|
||||
text-align: center;
|
||||
color: #999;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.layout-navigation .nav {
|
||||
padding: 1.875rem 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.layout-navigation .nav > li {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.layout-navigation .nav > li > a {
|
||||
border: 1px solid #007bff;
|
||||
border-radius: 100px;
|
||||
padding: 6px 12px;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
.layout-navigation .nav > li > a:hover, .layout-navigation .nav > li > a:active, .layout-navigation .nav > li > a:focus {
|
||||
border: 1px solid #0056b3;
|
||||
color: #0056b3;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.layout-content {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.layout-content:before, .layout-content:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
|
||||
.layout-content:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.layout-content .col-right {
|
||||
width: 25%;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.layout-content .col-right-wrapper {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding: 2.5rem 2rem 0;
|
||||
}
|
||||
|
||||
.layout-content .col-body {
|
||||
width: 75%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.layout-content .col-body:before, .layout-content .col-body:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
|
||||
.layout-content .col-body:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.layout-content .col-body-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding: 2.5rem 2rem 0;
|
||||
}
|
||||
|
||||
.nav {
|
||||
padding-left: 0;
|
||||
margin-bottom: 0;
|
||||
list-style: none;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
.table-of-contents {
|
||||
padding: 1.25rem 0;
|
||||
}
|
||||
|
||||
.table-of-contents .nav > li > a {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.table-of-contents .nav > li > a .item-number {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.table-of-contents .nav > li > a .item-title {
|
||||
color: #212529;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
flex-grow: 1;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.table-of-contents .nav > li > a .item-title:hover {
|
||||
color: #0056b3;
|
||||
}
|
||||
|
||||
.table-of-contents .nav > li.extended > a .item-title, .table-of-contents .nav > li.active > a .item-title, .table-of-contents .nav > li.extended > a .item-title:hover, .table-of-contents .nav > li.active > a .item-title:hover {
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li > .nav {
|
||||
display: none;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li > .nav > li > a {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li > .nav > li > a .item-title {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li > .nav > li > .nav > li > a {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li > .nav > li > .nav > li > a .item-title {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.table-of-contents > .nav > li.active > .nav {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.chapter {
|
||||
margin: 0 0 6rem;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.chapter > .chapter-title h1, .chapter > .chapter-title h2, .chapter > .chapter-title h3, .chapter > .chapter-title h4, .chapter > .chapter-title h5, .chapter > .chapter-title h6, .section > .section-title h1, .section > .section-title h2, .section > .section-title h3, .section > .section-title h4, .section > .section-title h5, .section > .section-title h6 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.chapter > .chapter-title h1 {
|
||||
border-bottom: 2px solid #eeeeee;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 0.2em;
|
||||
}
|
||||
|
||||
.chapter-title .title-number, .section-title .title-number {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.paragraph {
|
||||
line-height: 1.75em;
|
||||
}
|
||||
|
||||
.paragraph + .paragraph {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.dlist {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.dlist dl dt {
|
||||
float: left;
|
||||
width: 160px;
|
||||
clear: left;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dlist dl dd {
|
||||
margin-left: 180px;
|
||||
}
|
||||
|
||||
.ulist {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.imageblock {
|
||||
margin: 30px auto;
|
||||
}
|
||||
|
||||
.imageblock .content img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.imageblock .title {
|
||||
padding: 10px 0 0;
|
||||
}
|
||||
|
||||
.exampleblock, .quoteblock, .literalblock {
|
||||
background: #f5f4f4;
|
||||
padding: 20px;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.exampleblock .title, .quoteblock .title, .literalblock .title {
|
||||
text-transform: uppercase;
|
||||
font-size: 0.75em;
|
||||
font-weight: 400;
|
||||
color: #979797;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.quoteblock blockquote {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.quoteblock blockquote p:last-child, .quoteblock blockquote ul:last-child, .quoteblock blockquote ol:last-child {
|
||||
margin-bottom: 9px;
|
||||
}
|
||||
|
||||
.literalblock pre {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.listingblock {
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.listingblock pre {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.listingblock pre code {
|
||||
display: block;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.admonitionblock {
|
||||
line-height: 1.8em;
|
||||
padding: 20px;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.admonitionblock .icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.admonitionblock.important {
|
||||
background: #fce1e1;
|
||||
border-left: 5px solid #ff6060;
|
||||
}
|
||||
|
||||
.admonitionblock.note, .admonitionblock.tip {
|
||||
background: #e0f2fc;
|
||||
border-left: 5px solid #88d5ff;
|
||||
}
|
||||
|
||||
.admonitionblock.caution, .admonitionblock.warning {
|
||||
background: #fdf3d8;
|
||||
border-left: 5px solid #f1c654;
|
||||
}
|
||||
|
||||
table.tableblock {
|
||||
background-color: #fff;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin-bottom: 18px;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
table.tableblock > thead > tr > th, table.tableblock > tbody > tr > th, table.tableblock > tfoot > tr > th, table.tableblock > thead > tr > td, table.tableblock > tbody > tr > td, table.tableblock > tfoot > tr > td {
|
||||
padding: 8px;
|
||||
line-height: 1.42857143;
|
||||
vertical-align: top;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
table.tableblock > thead > tr > th {
|
||||
vertical-align: bottom;
|
||||
border-bottom: 2px solid #eee;
|
||||
}
|
||||
|
||||
table.tableblock > caption + thead > tr:first-child > th, table.tableblock > colgroup + thead > tr:first-child > th, table.tableblock > thead:first-child > tr:first-child > th, table.tableblock > caption + thead > tr:first-child > td, table.tableblock > colgroup + thead > tr:first-child > td, table.tableblock > thead:first-child > tr:first-child > td {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
table.tableblock > tbody + tbody {
|
||||
border-top: 2px solid #eee;
|
||||
}
|
||||
|
||||
table.tableblock .table {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
table.tableblock > tbody > tr:nth-of-type(odd) {
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
|
||||
table.tableblock > thead > tr > th p:last-child, table.tableblock > tbody > tr > th p:last-child, table.tableblock > tfoot > tr > th p:last-child, table.tableblock > thead > tr > td p:last-child, table.tableblock > tbody > tr > td p:last-child, table.tableblock > tfoot > tr > td p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.loaded .table-of-contents .nav .nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.layout-wrapper .layout-title, .layout-wrapper .layout-navigation, .layout-wrapper .layout-content {
|
||||
max-width: 1200px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
html {
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.layout-content .col-right {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.layout-content .col-body {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
color: #000;
|
||||
font-family: Georgia, "Times New Roman", Times, serif;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.layout-title h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.layout-content .col-right {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.layout-content .col-body {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chapter {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
(function($){
|
||||
|
||||
hljs.initHighlightingOnLoad();
|
||||
|
||||
$(document).ready(function(){
|
||||
$('.col-right-wrapper').stick_in_parent({
|
||||
parent: '.layout-content'
|
||||
});
|
||||
$('body').scrollspy({
|
||||
target: '.table-of-contents'
|
||||
});
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
File diff suppressed because one or more lines are too long
Vendored
-2
File diff suppressed because one or more lines are too long
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
|
||||
Monokai Sublime style. Derived from Monokai by noformnocontent http://nn.mit-license.org/
|
||||
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
background: #23241f;
|
||||
}
|
||||
|
||||
.hljs,
|
||||
.hljs-tag,
|
||||
.hljs-subst {
|
||||
color: #f8f8f2;
|
||||
}
|
||||
|
||||
.hljs-strong,
|
||||
.hljs-emphasis {
|
||||
color: #a8a8a2;
|
||||
}
|
||||
|
||||
.hljs-bullet,
|
||||
.hljs-quote,
|
||||
.hljs-number,
|
||||
.hljs-regexp,
|
||||
.hljs-literal,
|
||||
.hljs-link {
|
||||
color: #ae81ff;
|
||||
}
|
||||
|
||||
.hljs-code,
|
||||
.hljs-title,
|
||||
.hljs-section,
|
||||
.hljs-selector-class {
|
||||
color: #a6e22e;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-name,
|
||||
.hljs-attr {
|
||||
color: #f92672;
|
||||
}
|
||||
|
||||
.hljs-symbol,
|
||||
.hljs-attribute {
|
||||
color: #66d9ef;
|
||||
}
|
||||
|
||||
.hljs-params,
|
||||
.hljs-class .hljs-title {
|
||||
color: #f8f8f2;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-type,
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo,
|
||||
.hljs-addition,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable {
|
||||
color: #e6db74;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-deletion,
|
||||
.hljs-meta {
|
||||
color: #75715e;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
/* ========================================================================
|
||||
* Bootstrap: scrollspy.js v3.3.7
|
||||
* http://getbootstrap.com/javascript/#scrollspy
|
||||
* ========================================================================
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
!function(r){"use strict";function o(t,s){this.$body=r(document.body),this.$scrollElement=r(t).is(document.body)?r(window):r(t),this.options=r.extend({},o.DEFAULTS,s),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",r.proxy(this.process,this)),this.refresh(),this.process()}function s(i){return this.each(function(){var t=r(this),s=t.data("bs.scrollspy"),e="object"==typeof i&&i;s||t.data("bs.scrollspy",s=new o(this,e)),"string"==typeof i&&s[i]()})}o.VERSION="3.3.7",o.DEFAULTS={offset:10},o.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},o.prototype.refresh=function(){var t=this,i="offset",o=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),r.isWindow(this.$scrollElement[0])||(i="position",o=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var t=r(this),s=t.data("target")||t.attr("href"),e=/^#./.test(s)&&r(s);return e&&e.length&&e.is(":visible")&&[[e[i]().top+o,s]]||null}).sort(function(t,s){return t[0]-s[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},o.prototype.process=function(){var t,s=this.$scrollElement.scrollTop()+this.options.offset,e=this.getScrollHeight(),i=this.options.offset+e-this.$scrollElement.height(),o=this.offsets,r=this.targets,l=this.activeTarget;if(this.scrollHeight!=e&&this.refresh(),i<=s)return l!=(t=r[r.length-1])&&this.activate(t);if(l&&s<o[0])return this.activeTarget=null,this.clear();for(t=o.length;t--;)l!=r[t]&&s>=o[t]&&(void 0===o[t+1]||s<o[t+1])&&this.activate(r[t])},o.prototype.activate=function(t){this.activeTarget=t,this.clear();var s=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',e=r(s).parents("li").addClass("active");e.parent(".dropdown-menu").length&&(e=e.closest("li.dropdown").addClass("active")),e.trigger("activate.bs.scrollspy")},o.prototype.clear=function(){r(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var t=r.fn.scrollspy;r.fn.scrollspy=s,r.fn.scrollspy.Constructor=o,r.fn.scrollspy.noConflict=function(){return r.fn.scrollspy=t,this},r(window).on("load.bs.scrollspy.data-api",function(){r('[data-spy="scroll"]').each(function(){var t=r(this);s.call(t,t.data())})})}(jQuery);
|
||||
@@ -1,5 +0,0 @@
|
||||
/**
|
||||
@license Sticky-kit v1.1.2 | WTFPL | Leaf Corcoran 2015 | http://leafo.net
|
||||
*/
|
||||
|
||||
(function(){var M,Q;M=this.jQuery||window.jQuery,Q=M(window),M.fn.stick_in_parent=function(t){var x,o,C,i,e,P,s,V,F,z,r,I,A,j;for(null==t&&(t={}),j=t.sticky_class,P=t.inner_scrolling,A=t.recalc_every,I=t.parent,F=t.offset_top,z=t.offset_bottom,V=t.spacer,C=t.bottoming,null==F&&(F=0),null==z&&(z=0),null==I&&(I=void 0),null==P&&(P=!0),null==j&&(j="is_stuck"),x=M(document),null==C&&(C=!0),r=function(t){var o,i;return window.getComputedStyle?(t[0],o=window.getComputedStyle(t[0]),i=parseFloat(o.getPropertyValue("width"))+parseFloat(o.getPropertyValue("margin-left"))+parseFloat(o.getPropertyValue("margin-right")),"border-box"!==o.getPropertyValue("box-sizing")&&(i+=parseFloat(o.getPropertyValue("border-left-width"))+parseFloat(o.getPropertyValue("border-right-width"))+parseFloat(o.getPropertyValue("padding-left"))+parseFloat(o.getPropertyValue("padding-right"))),i):t.outerWidth(!0)},i=function(n,l,a,c,p,d,u,f){var h,t,g,m,k,y,b,v,o,_,w,e;if(!n.data("sticky_kit")){if(n.data("sticky_kit",!0),k=x.height(),b=n.parent(),null!=I&&(b=b.closest(I)),!b.length)throw"failed to find stick parent";return h=g=!1,(w=null!=V?V&&n.closest(V):M('<div class="sticky-kit-manual-spacer" />'))&&w.css("position",n.css("position")),(v=function(){var t,o,i;if(!f)return k=x.height(),t=parseInt(b.css("border-top-width"),10),o=parseInt(b.css("padding-top"),10),l=parseInt(b.css("padding-bottom"),10),a=b.offset().top+t+o,c=b.height(),g&&(h=g=!1,null==V&&(n.insertAfter(w),w.detach()),n.css({position:"",top:"",width:"",bottom:""}).removeClass(j),i=!0),p=n.offset().top-(parseInt(n.css("margin-top"),10)||0)-F,d=n.outerHeight(!0),u=n.css("float"),w&&w.css({width:r(n),height:d,display:n.css("display"),"vertical-align":n.css("vertical-align"),float:u}),i?e():void 0})(),m=void 0,y=F,_=A,e=function(){var t,o,i,e,s,r;if(d!==c&&!f)return i=!1,null!=_&&(_-=1)<=0&&(_=A,v(),i=!0),i||x.height()===k||(v(),i=!0),e=Q.scrollTop(),null!=m&&(o=e-m),m=e,g?(C&&(s=c+a<e+d+y+z,h&&!s&&(h=!1,n.css({position:"fixed",bottom:"",top:y}).removeClass("is_bottomed").trigger("sticky_kit:unbottom"))),e<p&&(g=!1,y=F,null==V&&("left"!==u&&"right"!==u||n.insertAfter(w),w.detach()),t={position:"",width:"",top:""},n.css(t).removeClass(j).trigger("sticky_kit:unstick")),P&&(r=Q.height())<d+F&&(h||(y-=o,y=Math.max(r-d,y),y=Math.min(F,y),g&&n.css({top:y+"px"})))):p<e&&(g=!0,(t={position:"fixed",top:y}).width="border-box"===n.css("box-sizing")?n.outerWidth()+"px":n.width()+"px",n.css(t).addClass(j),null==V&&(n.after(w),"left"!==u&&"right"!==u||w.append(n)),n.trigger("sticky_kit:stick")),g&&C&&(null==s&&(s=c+a<e+d+y+z),!h&&s)?(h=!0,"static"===b.css("position")&&b.css({position:"relative"}),n.css({position:"absolute",bottom:l+z,top:"auto"}).addClass("is_bottomed").trigger("sticky_kit:bottom")):void 0},o=function(){return v(),e()},t=function(){if(f=!0,Q.off("touchmove",e),Q.off("scroll",e),Q.off("resize",o),M(document.body).off("sticky_kit:recalc",o),n.off("sticky_kit:detach",t),n.removeData("sticky_kit"),n.css({position:"",bottom:"",top:"",width:""}),b.position("position",""),g)return null==V&&("left"!==u&&"right"!==u||n.insertAfter(w),w.remove()),n.removeClass(j)},Q.on("touchmove",e),Q.on("scroll",e),Q.on("resize",o),M(document.body).on("sticky_kit:recalc",o),n.on("sticky_kit:detach",t),setTimeout(e,0)}},e=0,s=this.length;e<s;e++)o=this[e],i(M(o));return this}}).call(this);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,238 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\Configuration\Controller;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shopware\Core\{
|
||||
Framework\Context,
|
||||
Framework\Log\Package};
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\{
|
||||
HttpFoundation\JsonResponse,
|
||||
HttpFoundation\Request,
|
||||
HttpFoundation\Response,
|
||||
Routing\Attribute\Route};
|
||||
use VRPaymentPayment\Core\{
|
||||
Api\OrderDeliveryState\Service\OrderDeliveryStateService,
|
||||
Api\PaymentMethodConfiguration\Service\PaymentMethodConfigurationService,
|
||||
Api\WebHooks\Service\WebHooksService,
|
||||
Api\Space\Service\SpaceService,
|
||||
Settings\Service\SettingsService,
|
||||
Util\PaymentMethodUtil};
|
||||
|
||||
/**
|
||||
* Class ConfigurationController
|
||||
*
|
||||
* This class handles web calls that are made via the VRPaymentPayment settings page.
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\Config\Controller
|
||||
*/
|
||||
#[Package('system-settings')]
|
||||
#[Route(defaults: ['_routeScope' => ['api']])]
|
||||
class ConfigurationController extends AbstractController {
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Api\WebHooks\Service\WebHooksService
|
||||
*/
|
||||
protected $webHooksService;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Api\Space\Service\SpaceService
|
||||
*/
|
||||
protected $spaceService;
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Settings\Service\SettingsService
|
||||
*/
|
||||
protected $settingsService;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Util\PaymentMethodUtil
|
||||
*/
|
||||
private $paymentMethodUtil;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Service\PaymentMethodConfigurationService
|
||||
*/
|
||||
private $paymentMethodConfigurationService;
|
||||
|
||||
/**
|
||||
* @param PaymentMethodUtil $paymentMethodUtil
|
||||
* @param PaymentMethodConfigurationService $paymentMethodConfigurationService
|
||||
* @param WebHooksService $webHooksService
|
||||
* @param SpaceService $spaceService
|
||||
* @param SettingsService $settingsService
|
||||
*/
|
||||
public function __construct(
|
||||
PaymentMethodUtil $paymentMethodUtil,
|
||||
PaymentMethodConfigurationService $paymentMethodConfigurationService,
|
||||
WebHooksService $webHooksService,
|
||||
SpaceService $spaceService,
|
||||
SettingsService $settingsService
|
||||
)
|
||||
{
|
||||
$this->webHooksService = $webHooksService;
|
||||
$this->spaceService = $spaceService;
|
||||
$this->paymentMethodUtil = $paymentMethodUtil;
|
||||
$this->paymentMethodConfigurationService = $paymentMethodConfigurationService;
|
||||
$this->settingsService = $settingsService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
* @internal
|
||||
* @required
|
||||
*
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger): void
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set VRPaymentPayment as the default payment for a give sales channel
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse
|
||||
*
|
||||
*/
|
||||
#[Route("/api/_action/vrpayment/configuration/set-vrpayment-as-sales-channel-payment-default",
|
||||
name: "api.action.vrpayment.configuration.set-vrpayment-as-sales-channel-payment-default",
|
||||
methods: ['POST'])]
|
||||
public function setVRPaymentAsSalesChannelPaymentDefault(Request $request, Context $context): JsonResponse
|
||||
{
|
||||
$salesChannelId = $request->request->get('salesChannelId');
|
||||
$salesChannelId = ($salesChannelId == 'null') ? null : $salesChannelId;
|
||||
|
||||
$this->paymentMethodUtil->setVRPaymentAsDefaultPaymentMethod($context, $salesChannelId);
|
||||
return new JsonResponse([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register web hooks
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*
|
||||
*/
|
||||
#[Route("/api/_action/vrpayment/configuration/register-web-hooks",
|
||||
name: "api.action.vrpayment.configuration.register-web-hooks",
|
||||
methods: ['POST'])]
|
||||
public function registerWebHooks(Request $request): JsonResponse
|
||||
{
|
||||
$settings = $this->settingsService->getSettings();
|
||||
if ($settings->isWebhooksUpdateEnabled() === false) {
|
||||
$this->logger->info('Webhooks update disabled by settings');
|
||||
return new JsonResponse([]);
|
||||
}
|
||||
|
||||
$salesChannelId = $request->request->get('salesChannelId');
|
||||
$salesChannelId = ($salesChannelId == 'null') ? null : $salesChannelId;
|
||||
|
||||
$result = $this->webHooksService->setSalesChannelId($salesChannelId)->install();
|
||||
|
||||
return new JsonResponse(['result' => $result]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test API connection
|
||||
* If the API data is incorrect, an entry must appear in the event log file in the Shopware folder /var/log/
|
||||
* @see https://developer.shopware.com/docs/resources/guidelines/testing/store/quality-guidelines-plugins/#every-app-accessing-external-api-services
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*
|
||||
*/
|
||||
#[Route("/api/_action/vrpayment/configuration/check-api-connection",
|
||||
name: "api.action.vrpayment.configuration.check-api-connection",
|
||||
methods: ['POST'])]
|
||||
public function checkApiConnection(Request $request): JsonResponse
|
||||
{
|
||||
$spaceId = (int)$request->request->getInt('spaceId');
|
||||
$userId = (int)$request->request->getInt('userId');
|
||||
$applicationId = $request->request->get('applicationId');
|
||||
|
||||
$result = $this->spaceService
|
||||
->setSpaceId($spaceId)
|
||||
->setUserId($userId)
|
||||
->setApplicationId($applicationId)
|
||||
->checkSpace();
|
||||
|
||||
if (null === $result) {
|
||||
$this->logger->error('API test connection was failed. Wrong credentials');
|
||||
return new JsonResponse([['result' => 400]]);
|
||||
}
|
||||
|
||||
$this->logger->info('API test connection was successfully tested.');
|
||||
return new JsonResponse(['result' => 200]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronize payment method configurations
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse
|
||||
*
|
||||
*/
|
||||
#[Route("/api/_action/vrpayment/configuration/synchronize-payment-method-configuration",
|
||||
name: "api.action.vrpayment.configuration.synchronize-payment-method-configuration",
|
||||
methods: ['POST'])]
|
||||
public function synchronizePaymentMethodConfiguration(Request $request, Context $context): JsonResponse
|
||||
{
|
||||
$settings = $this->settingsService->getSettings();
|
||||
if ($settings->isPaymentsUpdateEnabled() === false) {
|
||||
$this->logger->info('Payment methods update disabled by settings');
|
||||
return new JsonResponse([]);
|
||||
}
|
||||
|
||||
$salesChannelId = $request->request->get('salesChannelId');
|
||||
$salesChannelId = ($salesChannelId == 'null') ? null : $salesChannelId;
|
||||
$status = Response::HTTP_OK;
|
||||
try {
|
||||
$result = $this->paymentMethodConfigurationService->setSalesChannelId($salesChannelId)->synchronize($context);
|
||||
} catch (\Exception $exception) {
|
||||
$status = Response::HTTP_NOT_ACCEPTABLE;
|
||||
$result = [
|
||||
'errorTitle' => $exception->getMessage(),
|
||||
'errorMessage' => $exception->getTraceAsString()
|
||||
];
|
||||
$this->logger->emergency($exception->getTraceAsString());
|
||||
}
|
||||
|
||||
return new JsonResponse(['result' => $result], $status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Install OrderDeliveryStates
|
||||
*
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse
|
||||
*
|
||||
*/
|
||||
#[Route("/api/_action/vrpayment/configuration/install-order-delivery-states",
|
||||
name: "api.action.vrpayment.configuration.install-order-delivery-states",
|
||||
methods: ['POST'])]
|
||||
public function installOrderDeliveryStates(Context $context): JsonResponse
|
||||
{
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Api\OrderDeliveryState\Service\OrderDeliveryStateService $orderDeliveryStateService
|
||||
*/
|
||||
$orderDeliveryStateService = $this->container->get(OrderDeliveryStateService::class);
|
||||
$orderDeliveryStateService->install($context);
|
||||
|
||||
return new JsonResponse([]);
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\OrderDeliveryState\Command;
|
||||
|
||||
use Shopware\Core\Framework\Context;
|
||||
use Symfony\Component\{
|
||||
Console\Command\Command,
|
||||
Console\Attribute\AsCommand,
|
||||
Console\Input\InputInterface,
|
||||
Console\Output\OutputInterface};
|
||||
use VRPaymentPayment\Core\Api\OrderDeliveryState\Service\OrderDeliveryStateService;
|
||||
|
||||
/**
|
||||
* Class OrderDeliveryStateCommand
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\OrderDeliveryState\Command
|
||||
*/
|
||||
#[AsCommand(name: 'vrpayment:order-delivery-states:install')]
|
||||
class OrderDeliveryStateCommand extends Command {
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Api\OrderDeliveryState\Service\OrderDeliveryStateService
|
||||
*/
|
||||
protected $orderDeliveryStateService;
|
||||
|
||||
/**
|
||||
* OrderDeliveryStateCommand constructor.
|
||||
*
|
||||
* @param \VRPaymentPayment\Core\Api\OrderDeliveryState\Service\OrderDeliveryStateService $orderDeliveryStateService
|
||||
*/
|
||||
public function __construct(OrderDeliveryStateService $orderDeliveryStateService)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->orderDeliveryStateService = $orderDeliveryStateService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$output->writeln('Install VRPaymentPayment extra delivery states...');
|
||||
$this->orderDeliveryStateService->install(Context::createDefaultContext());
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the current command.
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDescription('Installs VRPaymentPayment extra delivery states.')
|
||||
->setHelp('This command installs VRPaymentPayment extra delivery states.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\OrderDeliveryState\Handler;
|
||||
|
||||
use Shopware\Core\{
|
||||
Checkout\Order\Aggregate\OrderDelivery\OrderDeliveryDefinition,
|
||||
Framework\Context,
|
||||
System\StateMachine\Aggregation\StateMachineTransition\StateMachineTransitionActions,
|
||||
System\StateMachine\StateMachineRegistry,
|
||||
System\StateMachine\Transition};
|
||||
|
||||
/**
|
||||
* Class OrderDeliveryStateHandler
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\OrderDeliveryState\Handler
|
||||
*/
|
||||
class OrderDeliveryStateHandler {
|
||||
|
||||
public const STATE_HOLD = 'hold';
|
||||
public const ACTION_HOLD = 'hold';
|
||||
public const ACTION_UNHOLD = 'unhold';
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\System\StateMachine\StateMachineRegistry
|
||||
*/
|
||||
private $stateMachineRegistry;
|
||||
|
||||
/**
|
||||
* OrderDeliveryStateHandler constructor.
|
||||
*
|
||||
* @param \Shopware\Core\System\StateMachine\StateMachineRegistry $stateMachineRegistry
|
||||
*/
|
||||
public function __construct(StateMachineRegistry $stateMachineRegistry)
|
||||
{
|
||||
$this->stateMachineRegistry = $stateMachineRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $entityId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*/
|
||||
public function hold(string $entityId, Context $context): void
|
||||
{
|
||||
$this->stateMachineRegistry->transition(
|
||||
new Transition(
|
||||
OrderDeliveryDefinition::ENTITY_NAME,
|
||||
$entityId,
|
||||
self::ACTION_HOLD,
|
||||
'stateId'
|
||||
),
|
||||
$context
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $entityId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*/
|
||||
public function unhold(string $entityId, Context $context): void
|
||||
{
|
||||
$this->stateMachineRegistry->transition(
|
||||
new Transition(
|
||||
OrderDeliveryDefinition::ENTITY_NAME,
|
||||
$entityId,
|
||||
self::ACTION_UNHOLD,
|
||||
'stateId'
|
||||
),
|
||||
$context
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $entityId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*/
|
||||
public function cancel(string $entityId, Context $context): void
|
||||
{
|
||||
$this->stateMachineRegistry->transition(
|
||||
new Transition(
|
||||
OrderDeliveryDefinition::ENTITY_NAME,
|
||||
$entityId,
|
||||
StateMachineTransitionActions::ACTION_CANCEL,
|
||||
'stateId'
|
||||
),
|
||||
$context
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\OrderDeliveryState\Service;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Shopware\Core\{
|
||||
Checkout\Order\Aggregate\OrderDelivery\OrderDeliveryStates,
|
||||
Framework\Context,
|
||||
Framework\DataAbstractionLayer\Search\Criteria,
|
||||
Framework\DataAbstractionLayer\Search\Filter\EqualsFilter,
|
||||
Framework\Uuid\Uuid};
|
||||
use VRPaymentPayment\Core\{
|
||||
Api\OrderDeliveryState\Handler\OrderDeliveryStateHandler,
|
||||
Util\LocaleCodeProvider};
|
||||
|
||||
/**
|
||||
* Class OrderDeliveryStateService
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\OrderDeliveryState\Service
|
||||
*/
|
||||
class OrderDeliveryStateService {
|
||||
|
||||
/**
|
||||
* @var \Psr\Container\ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Util\LocaleCodeProvider
|
||||
*/
|
||||
protected $localeCodeProvider;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\System\StateMachine\StateMachineDefinition
|
||||
*/
|
||||
protected $stateMachineRepository;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\System\StateMachine\Aggregation\StateMachineTransition\StateMachineTransitionDefinition
|
||||
*/
|
||||
protected $stateMachineTransitionRepository;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\System\StateMachine\Aggregation\StateMachineState\StateMachineStateDefinition
|
||||
*/
|
||||
protected $stateMachineStateRepository;
|
||||
|
||||
/**
|
||||
* OrderDeliveryStateHandler constructor.
|
||||
*
|
||||
* @param \Psr\Container\ContainerInterface $container
|
||||
*/
|
||||
public function __construct(ContainerInterface $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->localeCodeProvider = $this->container->get(LocaleCodeProvider::class);
|
||||
$this->stateMachineRepository = $this->container->get('state_machine.repository');
|
||||
$this->stateMachineStateRepository = $this->container->get('state_machine_state.repository');
|
||||
$this->stateMachineTransitionRepository = $this->container->get('state_machine_transition.repository');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*/
|
||||
public function install(Context $context): void
|
||||
{
|
||||
$stateMachineId = $this->getStateMachineEntity($context);
|
||||
$holdStateId = $this->getHoldStateId($stateMachineId, $context);
|
||||
$openStateId = $this->getOpenStateId($stateMachineId, $context);
|
||||
|
||||
$this->upsertHoldTransition($stateMachineId, $openStateId, $holdStateId, $context);
|
||||
$this->upsertUnholdTransition($stateMachineId, $holdStateId, $openStateId, $context);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return \Shopware\Core\System\StateMachine\StateMachineEntity
|
||||
*/
|
||||
protected function getStateMachineEntity(Context $context): string
|
||||
{
|
||||
$stateMachineCriteria = (new Criteria())
|
||||
->addFilter(new EqualsFilter('technicalName', OrderDeliveryStates::STATE_MACHINE));
|
||||
return $this->stateMachineRepository->search($stateMachineCriteria, $context)->first()->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $stateMachineId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getHoldStateId(string $stateMachineId, Context $context): string
|
||||
{
|
||||
$holdStateMachineStateCriteria = (new Criteria())
|
||||
->addFilter(
|
||||
new EqualsFilter('technicalName', OrderDeliveryStateHandler::STATE_HOLD),
|
||||
new EqualsFilter('stateMachineId', $stateMachineId)
|
||||
);
|
||||
|
||||
$holdStateMachineStateEntity = $this->stateMachineStateRepository->search($holdStateMachineStateCriteria, $context)->first();
|
||||
|
||||
$holdStateId = is_null($holdStateMachineStateEntity) ? Uuid::randomHex() : $holdStateMachineStateEntity->getId();
|
||||
|
||||
if (is_null($holdStateMachineStateEntity)) {
|
||||
$translations = $this->localeCodeProvider->getAvailableTranslations('vrpayment.deliveryState.hold', 'Hold', $context);
|
||||
$data = [
|
||||
'id' => $holdStateId,
|
||||
'technicalName' => OrderDeliveryStateHandler::STATE_HOLD,
|
||||
'stateMachineId' => $stateMachineId,
|
||||
'translations' => $translations,
|
||||
];
|
||||
$this->stateMachineStateRepository->upsert([$data], $context);
|
||||
}
|
||||
|
||||
return $holdStateId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $stateMachineId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getOpenStateId(string $stateMachineId, Context $context): string
|
||||
{
|
||||
$stateMachineStateCriteria = (new Criteria())
|
||||
->addFilter(
|
||||
new EqualsFilter('technicalName', OrderDeliveryStates::STATE_OPEN),
|
||||
new EqualsFilter('stateMachineId', $stateMachineId)
|
||||
);
|
||||
|
||||
return $this->stateMachineStateRepository->search($stateMachineStateCriteria, $context)->first()->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $stateMachineId
|
||||
* @param string $openStateId
|
||||
* @param string $holdStateId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*/
|
||||
protected function upsertHoldTransition(string $stateMachineId, string $openStateId, string $holdStateId, Context $context): void
|
||||
{
|
||||
$translations = $this->localeCodeProvider->getAvailableTranslations('vrpayment.deliveryState.hold','Hold', $context);
|
||||
|
||||
$this->upsertTransition(OrderDeliveryStateHandler::ACTION_HOLD, $stateMachineId, $openStateId, $holdStateId, $translations, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $actionName
|
||||
* @param string $stateMachineId
|
||||
* @param string $fromStateId
|
||||
* @param string $toStateId
|
||||
* @param array $translations
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*/
|
||||
protected function upsertTransition(string $actionName, string $stateMachineId, string $fromStateId, string $toStateId, array $translations, Context $context): void
|
||||
{
|
||||
$criteria = (new Criteria())
|
||||
->addFilter(
|
||||
new EqualsFilter('actionName', $actionName),
|
||||
new EqualsFilter('stateMachineId', $stateMachineId),
|
||||
new EqualsFilter('fromStateId', $fromStateId),
|
||||
new EqualsFilter('toStateId', $toStateId)
|
||||
);
|
||||
|
||||
$stateMachineTransitionEntity = $this->stateMachineTransitionRepository->search($criteria, $context)->first();
|
||||
$transitionId = is_null($stateMachineTransitionEntity) ? Uuid::randomHex() : $stateMachineTransitionEntity->getId();
|
||||
|
||||
if (is_null($stateMachineTransitionEntity)) {
|
||||
$data = [
|
||||
'id' => $transitionId,
|
||||
'actionName' => $actionName,
|
||||
'stateMachineId' => $stateMachineId,
|
||||
'fromStateId' => $fromStateId,
|
||||
'toStateId' => $toStateId,
|
||||
'translations' => $translations,
|
||||
];
|
||||
$this->stateMachineTransitionRepository->upsert([$data], $context);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $stateMachineId
|
||||
* @param string $openStateId
|
||||
* @param string $holdStateId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*/
|
||||
protected function upsertUnholdTransition(string $stateMachineId, string $holdStateId, string $openStateId, Context $context): void
|
||||
{
|
||||
$translations = $this->localeCodeProvider->getAvailableTranslations('vrpayment.deliveryState.unhold','Unhold',$context);
|
||||
|
||||
$this->upsertTransition(OrderDeliveryStateHandler::ACTION_UNHOLD, $stateMachineId, $holdStateId, $openStateId, $translations, $context);
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Command;
|
||||
|
||||
use Shopware\Core\Framework\Context;
|
||||
use Symfony\Component\{
|
||||
Console\Command\Command,
|
||||
Console\Attribute\AsCommand,
|
||||
Console\Input\InputInterface,
|
||||
Console\Output\OutputInterface};
|
||||
use VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Service\PaymentMethodConfigurationService;
|
||||
|
||||
/**
|
||||
* Class PaymentMethodConfigurationCommand
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Command
|
||||
*/
|
||||
#[AsCommand(name: 'vrpayment:payment-method:configuration')]
|
||||
class PaymentMethodConfigurationCommand extends Command {
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Service\PaymentMethodConfigurationService
|
||||
*/
|
||||
protected $paymentMethodConfigurationService;
|
||||
|
||||
/**
|
||||
* PaymentMethodConfigurationCommand constructor.
|
||||
*
|
||||
* @param \VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Service\PaymentMethodConfigurationService $paymentMethodConfigurationService
|
||||
*/
|
||||
public function __construct(PaymentMethodConfigurationService $paymentMethodConfigurationService)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->paymentMethodConfigurationService = $paymentMethodConfigurationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return int
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$output->writeln('Fetch VRPaymentPayment space available payment methods...');
|
||||
$this->paymentMethodConfigurationService->synchronize(Context::createDefaultContext());
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the current command.
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDescription('Fetches VRPaymentPayment space available payment methods.')
|
||||
->setHelp('This command fetches VRPaymentPayment space available payment methods.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Command;
|
||||
|
||||
use Shopware\Core\Framework\Context;
|
||||
use Symfony\Component\{
|
||||
Console\Command\Command,
|
||||
Console\Attribute\AsCommand,
|
||||
Console\Input\InputInterface,
|
||||
Console\Output\OutputInterface};
|
||||
use VRPaymentPayment\Core\Util\PaymentMethodUtil;
|
||||
|
||||
/**
|
||||
* Class PaymentMethodDefaultCommand
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Command
|
||||
*/
|
||||
#[AsCommand(name: 'vrpayment:payment-method:default')]
|
||||
class PaymentMethodDefaultCommand extends Command {
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Util\PaymentMethodUtil
|
||||
*/
|
||||
protected $paymentMethodUtil;
|
||||
|
||||
/**
|
||||
* PaymentMethodDefaultCommand constructor.
|
||||
*
|
||||
* @param \VRPaymentPayment\Core\Util\PaymentMethodUtil $paymentMethodUtil
|
||||
*/
|
||||
public function __construct(PaymentMethodUtil $paymentMethodUtil)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->paymentMethodUtil = $paymentMethodUtil;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$output->writeln('Set VRPaymentPayment as default payment method...');
|
||||
$context = Context::createDefaultContext();
|
||||
$this->paymentMethodUtil->setVRPaymentAsDefaultPaymentMethod($context);
|
||||
$this->paymentMethodUtil->disableSystemPaymentMethods($context);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the current command.
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDescription('Sets VRPaymentPayment as default payment method.')
|
||||
->setHelp('This command updates VRPaymentPayment as default payment method for all SalesChannels.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Entity;
|
||||
|
||||
use Shopware\Core\{
|
||||
Checkout\Payment\PaymentMethodEntity,
|
||||
Framework\DataAbstractionLayer\Entity,
|
||||
Framework\DataAbstractionLayer\EntityIdTrait};
|
||||
|
||||
/**
|
||||
* Class PaymentMethodConfigurationEntity
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Entity
|
||||
*/
|
||||
class PaymentMethodConfigurationEntity extends Entity {
|
||||
|
||||
use EntityIdTrait;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Checkout\Payment\PaymentMethodEntity|null
|
||||
*/
|
||||
protected $paymentMethod;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $paymentMethodConfigurationId;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $paymentMethodId;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $sortOrder;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $spaceId;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getData(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData(array $data): void
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Shopware\Core\Checkout\Payment\PaymentMethodEntity|null
|
||||
*/
|
||||
public function getPaymentMethod(): ?PaymentMethodEntity
|
||||
{
|
||||
return $this->paymentMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Checkout\Payment\PaymentMethodEntity $paymentMethod
|
||||
*/
|
||||
public function setPaymentMethod(PaymentMethodEntity $paymentMethod): void
|
||||
{
|
||||
$this->paymentMethod = $paymentMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getPaymentMethodConfigurationId(): int
|
||||
{
|
||||
return $this->paymentMethodConfigurationId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $paymentMethodConfigurationId
|
||||
*/
|
||||
public function setPaymentMethodConfigurationId(int $paymentMethodConfigurationId): void
|
||||
{
|
||||
$this->paymentMethodConfigurationId = $paymentMethodConfigurationId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPaymentMethodId(): string
|
||||
{
|
||||
return $this->paymentMethodId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $paymentMethodId
|
||||
*/
|
||||
public function setPaymentMethodId(string $paymentMethodId): void
|
||||
{
|
||||
$this->paymentMethodId = $paymentMethodId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSortOrder(): string
|
||||
{
|
||||
return $this->sortOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sortOrder
|
||||
*/
|
||||
public function setSortOrder(string $sortOrder): void
|
||||
{
|
||||
$this->sortOrder = $sortOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSpaceId(): int
|
||||
{
|
||||
return $this->spaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $spaceId
|
||||
*/
|
||||
public function setSpaceId(int $spaceId): void
|
||||
{
|
||||
$this->spaceId = $spaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getState(): string
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $state
|
||||
*/
|
||||
public function setState(string $state): void
|
||||
{
|
||||
$this->state = $state;
|
||||
}
|
||||
}
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Entity;
|
||||
|
||||
use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection;
|
||||
|
||||
/**
|
||||
* Class PaymentMethodConfigurationEntityCollection
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Entity
|
||||
*
|
||||
* @method void add(PaymentMethodConfigurationEntity $entity)
|
||||
* @method void set(string $key, PaymentMethodConfigurationEntity $entity)
|
||||
* @method PaymentMethodConfigurationEntity[] getIterator()
|
||||
* @method PaymentMethodConfigurationEntity[] getElements()
|
||||
* @method PaymentMethodConfigurationEntity|null get(string $key)
|
||||
* @method PaymentMethodConfigurationEntity|null first()
|
||||
* @method PaymentMethodConfigurationEntity|null last()
|
||||
*/
|
||||
class PaymentMethodConfigurationEntityCollection extends EntityCollection {
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getExpectedClass(): string
|
||||
{
|
||||
return PaymentMethodConfigurationEntity::class;
|
||||
}
|
||||
}
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Entity;
|
||||
|
||||
use Shopware\Core\{
|
||||
Checkout\Payment\PaymentMethodDefinition,
|
||||
Framework\DataAbstractionLayer\EntityDefinition,
|
||||
Framework\DataAbstractionLayer\Field\CreatedAtField,
|
||||
Framework\DataAbstractionLayer\Field\FkField,
|
||||
Framework\DataAbstractionLayer\Field\Flag\PrimaryKey,
|
||||
Framework\DataAbstractionLayer\Field\Flag\Required,
|
||||
Framework\DataAbstractionLayer\Field\IdField,
|
||||
Framework\DataAbstractionLayer\Field\IntField,
|
||||
Framework\DataAbstractionLayer\Field\JsonField,
|
||||
Framework\DataAbstractionLayer\Field\OneToOneAssociationField,
|
||||
Framework\DataAbstractionLayer\Field\StringField,
|
||||
Framework\DataAbstractionLayer\Field\UpdatedAtField,
|
||||
Framework\DataAbstractionLayer\FieldCollection};
|
||||
|
||||
/**
|
||||
* Class PaymentMethodConfigurationEntityDefinition
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Entity
|
||||
*/
|
||||
class PaymentMethodConfigurationEntityDefinition extends EntityDefinition {
|
||||
|
||||
public const ENTITY_NAME = 'vrpayment_payment_method_configuration';
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getEntityName(): string
|
||||
{
|
||||
return self::ENTITY_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Shopware\Core\Framework\DataAbstractionLayer\FieldCollection
|
||||
*/
|
||||
protected function defineFields(): FieldCollection
|
||||
{
|
||||
return new FieldCollection([
|
||||
(new IdField('id', 'id'))->addFlags(new PrimaryKey(), new Required()),
|
||||
(new JsonField('data', 'data'))->addFlags(new Required()),
|
||||
(new IntField('payment_method_configuration_id', 'paymentMethodConfigurationId'))->addFlags(new Required()),
|
||||
(new FkField('payment_method_id', 'paymentMethodId', PaymentMethodDefinition::class))->addFlags(new Required()),
|
||||
(new IntField('sort_order', 'sortOrder'))->addFlags(new Required()),
|
||||
(new IntField('space_id', 'spaceId'))->addFlags(new Required()),
|
||||
(new StringField('state', 'state'))->addFlags(new Required()),
|
||||
new OneToOneAssociationField('paymentMethod', 'payment_method_id', 'id', PaymentMethodDefinition::class, true),
|
||||
new CreatedAtField(),
|
||||
new UpdatedAtField(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCollectionClass(): string
|
||||
{
|
||||
return PaymentMethodConfigurationEntityCollection::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getEntityClass(): string
|
||||
{
|
||||
return PaymentMethodConfigurationEntity::class;
|
||||
}
|
||||
|
||||
}
|
||||
-678
@@ -1,678 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Service;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shopware\Core\{
|
||||
Content\ImportExport\DataAbstractionLayer\Serializer\Entity\MediaSerializer,
|
||||
Content\ImportExport\DataAbstractionLayer\Serializer\SerializerRegistry,
|
||||
Content\ImportExport\Struct\Config,
|
||||
Content\Media\MediaDefinition,
|
||||
Framework\Context,
|
||||
Framework\DataAbstractionLayer\EntityRepository,
|
||||
Framework\DataAbstractionLayer\Search\Criteria,
|
||||
Framework\DataAbstractionLayer\Search\EntitySearchResult,
|
||||
Framework\DataAbstractionLayer\Search\Filter\EqualsFilter,
|
||||
Framework\Plugin\Util\PluginIdProvider,
|
||||
Framework\Uuid\Uuid};
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use VRPayment\Sdk\{
|
||||
ApiClient,
|
||||
Model\CreationEntityState,
|
||||
Model\CriteriaOperator,
|
||||
Model\EntityQuery,
|
||||
Model\EntityQueryFilter,
|
||||
Model\EntityQueryFilterType,
|
||||
Model\PaymentMethodConfiguration,
|
||||
Model\RestLanguage};
|
||||
use VRPaymentPayment\Core\{
|
||||
Api\PaymentMethodConfiguration\Entity\PaymentMethodConfigurationEntity,
|
||||
Api\PaymentMethodConfiguration\Entity\PaymentMethodConfigurationEntityDefinition,
|
||||
Checkout\PaymentHandler\VRPaymentPaymentHandler,
|
||||
Settings\Service\SettingsService,
|
||||
Util\LocaleCodeProvider};
|
||||
use VRPaymentPayment\VRPaymentPayment;
|
||||
|
||||
/**
|
||||
* Class PaymentMethodConfigurationService
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Service
|
||||
*/
|
||||
class PaymentMethodConfigurationService {
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Settings\Service\SettingsService
|
||||
*/
|
||||
protected $settingsService;
|
||||
|
||||
/**
|
||||
* @var \VRPayment\Sdk\ApiClient
|
||||
*/
|
||||
protected $apiClient;
|
||||
|
||||
/**
|
||||
* Space Id
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $spaceId;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\DependencyInjection\ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Content\ImportExport\DataAbstractionLayer\Serializer\SerializerRegistry
|
||||
*/
|
||||
protected $serializerRegistry;
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @var ?string $salesChannelId
|
||||
*/
|
||||
private $salesChannelId;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Content\ImportExport\DataAbstractionLayer\Serializer\Entity\MediaSerializer
|
||||
*/
|
||||
private $mediaSerializer;
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
private $languages;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Util\LocaleCodeProvider
|
||||
*/
|
||||
private $localeCodeProvider;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface
|
||||
*/
|
||||
private $ruleRepository;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface
|
||||
*/
|
||||
private $paymentMethodRepository;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface
|
||||
*/
|
||||
private $salesChannelPaymentRepository;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface
|
||||
*/
|
||||
private $mediaRepository;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface
|
||||
*/
|
||||
private $mediaFolderRepository;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface
|
||||
*/
|
||||
private $mediaDefaultFolderRepository;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface
|
||||
*/
|
||||
private $vRPaymentPaymentMethodConfigurationRepository;
|
||||
|
||||
/**
|
||||
* PaymentMethodConfigurationService constructor.
|
||||
*
|
||||
* @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
|
||||
* @param \Shopware\Core\Content\ImportExport\DataAbstractionLayer\Serializer\Entity\MediaSerializer $mediaSerializer
|
||||
* @param \Shopware\Core\Content\ImportExport\DataAbstractionLayer\Serializer\SerializerRegistry $serializerRegistry
|
||||
* @param \Shopware\Core\Framework\DataAbstractionLayer\EntityRepository $salesChannelPaymentRepository
|
||||
* @param \Shopware\Core\Framework\DataAbstractionLayer\EntityRepository $paymentMethodRepository
|
||||
* @param \Shopware\Core\Framework\DataAbstractionLayer\EntityRepository $mediaRepository
|
||||
* @param \Shopware\Core\Framework\DataAbstractionLayer\EntityRepository $mediaFolderRepository
|
||||
* @param \Shopware\Core\Framework\DataAbstractionLayer\EntityRepository $mediaDefaultFolderRepository
|
||||
* @param \Shopware\Core\Framework\DataAbstractionLayer\EntityRepository $ruleRepository
|
||||
* @param \Shopware\Core\Framework\DataAbstractionLayer\EntityRepository $vRPaymentPaymentMethodConfigurationRepository
|
||||
*/
|
||||
public function __construct(
|
||||
SettingsService $settingsService,
|
||||
ContainerInterface $container,
|
||||
MediaSerializer $mediaSerializer,
|
||||
SerializerRegistry $serializerRegistry,
|
||||
EntityRepository $salesChannelPaymentRepository,
|
||||
EntityRepository $paymentMethodRepository,
|
||||
EntityRepository $mediaRepository,
|
||||
EntityRepository $mediaFolderRepository,
|
||||
EntityRepository $mediaDefaultFolderRepository,
|
||||
EntityRepository $ruleRepository,
|
||||
EntityRepository $vRPaymentPaymentMethodConfigurationRepository,
|
||||
) {
|
||||
$this->settingsService = $settingsService;
|
||||
$this->container = $container;
|
||||
$this->mediaSerializer = $mediaSerializer;
|
||||
$this->serializerRegistry = $serializerRegistry;
|
||||
$this->salesChannelPaymentRepository = $salesChannelPaymentRepository;
|
||||
$this->paymentMethodRepository = $paymentMethodRepository;
|
||||
$this->mediaRepository = $mediaRepository;
|
||||
$this->mediaFolderRepository = $mediaFolderRepository;
|
||||
$this->mediaDefaultFolderRepository = $mediaDefaultFolderRepository;
|
||||
$this->ruleRepository = $ruleRepository;
|
||||
$this->vRPaymentPaymentMethodConfigurationRepository = $vRPaymentPaymentMethodConfigurationRepository;
|
||||
$this->localeCodeProvider = $this->container->get(LocaleCodeProvider::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
*
|
||||
* @internal
|
||||
* @required
|
||||
*
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger): void
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \VRPayment\Sdk\ApiClient
|
||||
*/
|
||||
public function getApiClient(): ApiClient
|
||||
{
|
||||
return $this->apiClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \VRPayment\Sdk\ApiClient $apiClient
|
||||
*
|
||||
* @return \VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Service\PaymentMethodConfigurationService
|
||||
*/
|
||||
public function setApiClient(ApiClient $apiClient): PaymentMethodConfigurationService
|
||||
{
|
||||
$this->apiClient = $apiClient;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return array
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*/
|
||||
public function synchronize(Context $context): array
|
||||
{
|
||||
// Configuration
|
||||
$settings = $this->settingsService->getSettings($this->getSalesChannelId());
|
||||
$this->setSpaceId($settings->getSpaceId())->setApiClient($settings->getApiClient());
|
||||
|
||||
$this->disablePaymentMethodConfigurations($context);
|
||||
$this->enablePaymentMethodConfigurations($context);
|
||||
$this->disableOrphanedPaymentMethods();
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sales channel id
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getSalesChannelId(): ?string
|
||||
{
|
||||
return $this->salesChannelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set sales channel id
|
||||
*
|
||||
* @param string|null $salesChannelId
|
||||
*
|
||||
* @return \VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Service\PaymentMethodConfigurationService
|
||||
*/
|
||||
public function setSalesChannelId(?string $salesChannelId = null): PaymentMethodConfigurationService
|
||||
{
|
||||
$this->salesChannelId = $salesChannelId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*/
|
||||
private function disablePaymentMethodConfigurations(Context $context): void
|
||||
{
|
||||
$data = [];
|
||||
$paymentMethodData = [];
|
||||
$salesChannelPaymentMethodData = [];
|
||||
|
||||
$criteria = (new Criteria())->addFilter(new EqualsFilter('state', 'ACTIVE'));
|
||||
|
||||
/**
|
||||
* @var $vRPaymentPMConfigurationRepository
|
||||
*/
|
||||
$vRPaymentPMConfigurationRepository = $this->container->get(PaymentMethodConfigurationEntityDefinition::ENTITY_NAME . '.repository');
|
||||
|
||||
$paymentMethodConfigurationEntities = $vRPaymentPMConfigurationRepository
|
||||
->search($criteria, $context)
|
||||
->getEntities();
|
||||
|
||||
if (!empty($paymentMethodConfigurationEntities)) {
|
||||
|
||||
/**
|
||||
* @var $paymentMethodConfigurationEntity \VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Entity\PaymentMethodConfigurationEntity
|
||||
*/
|
||||
foreach ($paymentMethodConfigurationEntities as $paymentMethodConfigurationEntity) {
|
||||
$data[] = [
|
||||
'id' => $paymentMethodConfigurationEntity->getId(),
|
||||
'state' => CreationEntityState::INACTIVE,
|
||||
];
|
||||
|
||||
$paymentMethodData[] = [
|
||||
'id' => $paymentMethodConfigurationEntity->getId(),
|
||||
'active' => false,
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
$this->vRPaymentPaymentMethodConfigurationRepository->update($data, $context);
|
||||
$this->paymentMethodRepository->update($paymentMethodData, $context);
|
||||
} catch (\Exception $exception) {
|
||||
$this->logger->critical($exception->getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Full proof method to disable any orphaned payment methods
|
||||
*
|
||||
*/
|
||||
protected function disableOrphanedPaymentMethods(): void
|
||||
{
|
||||
try {
|
||||
$query = "UPDATE payment_method
|
||||
SET active=0
|
||||
WHERE handler_identifier=:handler_identifier AND id NOT IN (
|
||||
SELECT payment_method_id FROM vrpayment_payment_method_configuration
|
||||
)";
|
||||
|
||||
$params = [
|
||||
'handler_identifier' => VRPaymentPaymentHandler::class,
|
||||
];
|
||||
|
||||
$connection = $this->container->get(Connection::class);
|
||||
$connection->executeQuery($query, $params);
|
||||
} catch (\Exception $exception) {
|
||||
$this->logger->critical($exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $paymentMethodId
|
||||
* @param bool $active
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*/
|
||||
protected function setPaymentMethodIsActive(string $paymentMethodId, bool $active, Context $context): void
|
||||
{
|
||||
$paymentMethod = [
|
||||
'id' => $paymentMethodId,
|
||||
'active' => $active,
|
||||
];
|
||||
$this->paymentMethodRepository->update([$paymentMethod], $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable payment methods from VRPayment API
|
||||
*
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*/
|
||||
private function enablePaymentMethodConfigurations(Context $context): void
|
||||
{
|
||||
$paymentMethodConfigurations = $this->getPaymentMethodConfigurations();
|
||||
$this->logger->debug('Updating payment methods', $paymentMethodConfigurations);
|
||||
|
||||
/**
|
||||
* @var $paymentMethodConfiguration \VRPayment\Sdk\Model\PaymentMethodConfiguration
|
||||
*/
|
||||
foreach ($paymentMethodConfigurations as $paymentMethodConfiguration) {
|
||||
|
||||
$paymentMethodConfigurationEntity = $this->getPaymentMethodConfigurationEntity(
|
||||
$paymentMethodConfiguration->getSpaceId(),
|
||||
$paymentMethodConfiguration->getId(),
|
||||
$context
|
||||
);
|
||||
|
||||
$id = is_null($paymentMethodConfigurationEntity) ? Uuid::randomHex() : $paymentMethodConfigurationEntity->getId();
|
||||
|
||||
$data = [
|
||||
'id' => $id,
|
||||
'paymentMethodConfigurationId' => $paymentMethodConfiguration->getId(),
|
||||
'paymentMethodId' => $id,
|
||||
'data' => json_decode(strval($paymentMethodConfiguration), true),
|
||||
'sortOrder' => $paymentMethodConfiguration->getSortOrder(),
|
||||
'spaceId' => $paymentMethodConfiguration->getSpaceId(),
|
||||
'state' => CreationEntityState::ACTIVE,
|
||||
];
|
||||
|
||||
$this->upsertPaymentMethod($id, $paymentMethodConfiguration, $context);
|
||||
|
||||
try {
|
||||
$this->container->get(PaymentMethodConfigurationEntityDefinition::ENTITY_NAME . '.repository')->upsert([$data], $context);
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error($e->getMessage(), [$e->getTraceAsString()]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch active merchant payment methods from VRPayment API
|
||||
*
|
||||
* @return \VRPayment\Sdk\Model\PaymentMethodConfiguration[]
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*/
|
||||
private function getPaymentMethodConfigurations(): array
|
||||
{
|
||||
$entityQueryFilter = (new EntityQueryFilter())
|
||||
->setOperator(CriteriaOperator::EQUALS)
|
||||
->setFieldName('state')
|
||||
->setType(EntityQueryFilterType::LEAF)
|
||||
->setValue(CreationEntityState::ACTIVE);
|
||||
|
||||
$entityQuery = (new EntityQuery())->setFilter($entityQueryFilter);
|
||||
|
||||
$paymentMethodConfigurations = $this->apiClient->getPaymentMethodConfigurationService()->search(
|
||||
$this->getSpaceId(),
|
||||
$entityQuery
|
||||
);
|
||||
|
||||
usort($paymentMethodConfigurations, function (PaymentMethodConfiguration $item1, PaymentMethodConfiguration $item2) {
|
||||
return $item1->getSortOrder() <=> $item2->getSortOrder();
|
||||
});
|
||||
|
||||
return $paymentMethodConfigurations;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSpaceId(): int
|
||||
{
|
||||
return $this->spaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $spaceId
|
||||
*
|
||||
* @return \VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Service\PaymentMethodConfigurationService
|
||||
*/
|
||||
public function setSpaceId(int $spaceId): PaymentMethodConfigurationService
|
||||
{
|
||||
$this->spaceId = $spaceId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $spaceId
|
||||
* @param int $paymentMethodConfigurationId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return \VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Entity\PaymentMethodConfigurationEntity|null
|
||||
*/
|
||||
protected function getPaymentMethodConfigurationEntity(
|
||||
int $spaceId,
|
||||
int $paymentMethodConfigurationId,
|
||||
Context $context
|
||||
): ?PaymentMethodConfigurationEntity
|
||||
{
|
||||
$criteria = (new Criteria())->addFilter(
|
||||
new EqualsFilter('spaceId', $spaceId),
|
||||
new EqualsFilter('paymentMethodConfigurationId', $paymentMethodConfigurationId)
|
||||
);
|
||||
|
||||
return $this->vRPaymentPaymentMethodConfigurationRepository
|
||||
->search($criteria, $context)
|
||||
->getEntities()
|
||||
->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $spaceId
|
||||
* @param Context $context
|
||||
* @return array
|
||||
*/
|
||||
public function getAllPaymentMethodConfigurations(int $spaceId, Context $context): array
|
||||
{
|
||||
$criteria = (new Criteria())->addFilter(new EqualsFilter('spaceId', $spaceId));
|
||||
|
||||
$configurations = $this->vRPaymentPaymentMethodConfigurationRepository
|
||||
->search($criteria, $context)
|
||||
->getEntities();
|
||||
|
||||
return $configurations->getElements();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update or insert Payment Method
|
||||
*
|
||||
* @param string $id
|
||||
* @param \VRPayment\Sdk\Model\PaymentMethodConfiguration $paymentMethodConfiguration
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*/
|
||||
protected function upsertPaymentMethod(
|
||||
string $id,
|
||||
PaymentMethodConfiguration $paymentMethodConfiguration,
|
||||
Context $context
|
||||
): void
|
||||
{
|
||||
/** @var PluginIdProvider $pluginIdProvider */
|
||||
$pluginIdProvider = $this->container->get(PluginIdProvider::class);
|
||||
$pluginId = $pluginIdProvider->getPluginIdByBaseClass(
|
||||
VRPaymentPayment::class,
|
||||
$context
|
||||
);
|
||||
|
||||
$data = [
|
||||
'id' => $id,
|
||||
'handlerIdentifier' => VRPaymentPaymentHandler::class,
|
||||
'pluginId' => $pluginId,
|
||||
'position' => $paymentMethodConfiguration->getSortOrder() - 100,
|
||||
'afterOrderEnabled' => true,
|
||||
'active' => true,
|
||||
'translations' => $this->getPaymentMethodConfigurationTranslation($paymentMethodConfiguration, $context),
|
||||
'technicalName' => $paymentMethodConfiguration->getName(),
|
||||
];
|
||||
|
||||
$data['mediaId'] = $this->upsertMedia($id, $paymentMethodConfiguration, $context);
|
||||
|
||||
$data = array_filter($data);
|
||||
|
||||
try {
|
||||
$this->paymentMethodRepository->upsert([$data], $context);
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error($e->getMessage(), [$e->getTraceAsString()]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \VRPayment\Sdk\Model\PaymentMethodConfiguration $paymentMethodConfiguration
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return array
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*/
|
||||
protected function getPaymentMethodConfigurationTranslation(PaymentMethodConfiguration $paymentMethodConfiguration, Context $context): array
|
||||
{
|
||||
$translations = [];
|
||||
$locales = $this->localeCodeProvider->getAvailableLocales($context);
|
||||
foreach ($locales as $locale) {
|
||||
$translations[$locale] = [
|
||||
'name' => $this->translate($paymentMethodConfiguration->getResolvedTitle(), $locale) ?? $paymentMethodConfiguration->getName(),
|
||||
'description' => $this->translate($paymentMethodConfiguration->getResolvedDescription(), $locale) ?? '',
|
||||
];
|
||||
}
|
||||
return $translations;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $translatedString
|
||||
* @param string $locale
|
||||
*
|
||||
* @return string|null
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*/
|
||||
protected function translate(array $translatedString, string $locale): ?string
|
||||
{
|
||||
$translation = null;
|
||||
|
||||
if (isset($translatedString[$locale])) {
|
||||
$translation = $translatedString[$locale];
|
||||
}
|
||||
|
||||
if (is_null($translation)) {
|
||||
|
||||
$primaryLanguage = $this->findPrimaryLanguage($locale);
|
||||
if (!is_null($primaryLanguage) && isset($translatedString[$primaryLanguage->getIetfCode()])) {
|
||||
$translation = $translatedString[$primaryLanguage->getIetfCode()];
|
||||
}
|
||||
|
||||
if (is_null($translation) && isset($translatedString['en-US'])) {
|
||||
$translation = $translatedString['en-US'];
|
||||
}
|
||||
}
|
||||
|
||||
return $translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the primary language in the given group.
|
||||
*
|
||||
* @param $code
|
||||
*
|
||||
* @return \VRPayment\Sdk\Model\RestLanguage|null
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*/
|
||||
protected function findPrimaryLanguage(string $code): ?RestLanguage
|
||||
{
|
||||
$code = substr($code, 0, 2);
|
||||
foreach ($this->getLanguages() as $language) {
|
||||
if (($language->getIso2Code() == $code) && $language->getPrimaryOfGroup()) {
|
||||
return $language;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return array
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*/
|
||||
protected function getLanguages(): array
|
||||
{
|
||||
if (is_null($this->languages)) {
|
||||
$this->languages = $this->apiClient->getLanguageService()->all();
|
||||
}
|
||||
return $this->languages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload Payment Method icons
|
||||
*
|
||||
* @param string $id
|
||||
* @param \VRPayment\Sdk\Model\PaymentMethodConfiguration $paymentMethodConfiguration
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function upsertMedia(string $id, PaymentMethodConfiguration $paymentMethodConfiguration, Context $context): ?string
|
||||
{
|
||||
try {
|
||||
$existingRecord = $this->getMediaDefaultFolderForPaymentMethod($paymentMethodConfiguration, $context);
|
||||
|
||||
if ($existingRecord->count() > 0) {
|
||||
$id = $existingRecord->first()->getId();
|
||||
}
|
||||
|
||||
$this->mediaDefaultFolderRepository->upsert([
|
||||
[
|
||||
'id' => $id,
|
||||
'associationFields' => [],
|
||||
'entity' => 'payment_method_' . $paymentMethodConfiguration->getId(),
|
||||
],
|
||||
], $context);
|
||||
|
||||
$this->mediaFolderRepository->upsert([
|
||||
[
|
||||
'id' => $id,
|
||||
'defaultFolderId' => $id,
|
||||
'name' => $paymentMethodConfiguration->getName(),
|
||||
'useParentConfiguration' => false,
|
||||
'configuration' => [],
|
||||
],
|
||||
], $context);
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Content\Media\MediaDefinition
|
||||
*/
|
||||
$mediaDefinition = $this->container->get(MediaDefinition::class);
|
||||
$this->mediaSerializer->setRegistry($this->serializerRegistry);
|
||||
$data = [
|
||||
'id' => $id,
|
||||
'title' => $paymentMethodConfiguration->getName(),
|
||||
'url' => $paymentMethodConfiguration->getResolvedImageUrl(),
|
||||
'mediaFolderId' => $id,
|
||||
];
|
||||
$data = $this->mediaSerializer->deserialize(new Config([], [], []), $mediaDefinition, $data);
|
||||
$this->mediaRepository->upsert([$data], $context);
|
||||
return $id;
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->critical($e->getMessage(), [$e->getTraceAsString()]);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves media default folder for a given payment method configuration.
|
||||
*
|
||||
* @param PaymentMethodConfiguration $paymentMethodConfiguration The payment method configuration to check.
|
||||
* @param Context $context The current context.
|
||||
*
|
||||
* @return EntitySearchResult The search result for the media default folder.
|
||||
*/
|
||||
private function getMediaDefaultFolderForPaymentMethod(PaymentMethodConfiguration $paymentMethodConfiguration, Context $context): ?EntitySearchResult
|
||||
{
|
||||
$criteria = new Criteria();
|
||||
$criteria->addFilter(new EqualsFilter('entity', 'payment_method_' . $paymentMethodConfiguration->getId()));
|
||||
return $this->mediaDefaultFolderRepository->search($criteria, $context);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\Refund\Controller;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shopware\Core\Framework\Context;
|
||||
use Shopware\Core\Framework\Log\Package;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\{
|
||||
HttpFoundation\Request,
|
||||
HttpFoundation\Response,
|
||||
Routing\Attribute\Route,
|
||||
};
|
||||
use VRPaymentPayment\Core\{
|
||||
Api\Refund\Service\RefundService,
|
||||
Settings\Service\SettingsService
|
||||
};
|
||||
|
||||
/**
|
||||
* Class RefundController
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\Refund\Controller
|
||||
*
|
||||
*/
|
||||
#[Package('sales-channel')]
|
||||
#[Route(defaults: ['_routeScope' => ['api']])]
|
||||
class RefundController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Api\Refund\Service\RefundService
|
||||
*/
|
||||
protected $refundService;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Settings\Service\SettingsService
|
||||
*/
|
||||
protected $settingsService;
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* RefundController constructor.
|
||||
*
|
||||
* @param \VRPaymentPayment\Core\Api\Refund\Service\RefundService $refundService
|
||||
* @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService
|
||||
*/
|
||||
public function __construct(RefundService $refundService, SettingsService $settingsService)
|
||||
{
|
||||
$this->settingsService = $settingsService;
|
||||
$this->refundService = $refundService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
* @internal
|
||||
* @required
|
||||
*
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger): void
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*/
|
||||
#[Route("/api/_action/vrpayment/refund/create-refund/",
|
||||
name: "api.action.vrpayment.refund.create-refund",
|
||||
methods: ['POST'])]
|
||||
public function createRefund(Request $request, Context $context): Response
|
||||
{
|
||||
$salesChannelId = $request->request->get('salesChannelId');
|
||||
$transactionId = $request->request->get('transactionId');
|
||||
$quantity = (int)$request->request->get('quantity');
|
||||
$lineItemId = $request->request->get('lineItemId');
|
||||
|
||||
$settings = $this->settingsService->getSettings($salesChannelId);
|
||||
$apiClient = $settings->getApiClient();
|
||||
|
||||
$transaction = $apiClient->getTransactionService()->read($settings->getSpaceId(), $transactionId);
|
||||
$refund = $this->refundService->create($transaction, $context, $lineItemId, $quantity);
|
||||
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(null, Response::HTTP_NO_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*/
|
||||
#[Route("/api/_action/vrpayment/refund/create-refund-by-amount/",
|
||||
name: "api.action.vrpayment.refund.create.refund.by.amount",
|
||||
methods: ['POST'])]
|
||||
public function createRefundByAmount(Request $request, Context $context): Response
|
||||
{
|
||||
$salesChannelId = $request->request->get('salesChannelId');
|
||||
$transactionId = $request->request->get('transactionId');
|
||||
$refundableAmount = $request->request->get('refundableAmount');
|
||||
|
||||
$settings = $this->settingsService->getSettings($salesChannelId);
|
||||
$apiClient = $settings->getApiClient();
|
||||
|
||||
$transaction = $apiClient->getTransactionService()->read($settings->getSpaceId(), $transactionId);
|
||||
$refund = $this->refundService->createRefundByAmount($transaction, $refundableAmount, $context);
|
||||
|
||||
if ($refund === null) {
|
||||
return new Response('refundExceedsAmount', Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
return new Response(null, Response::HTTP_NO_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*/
|
||||
#[Route("/api/_action/vrpayment/refund/create-partial-refund/",
|
||||
name: "api.action.vrpayment.refund.create.partial.refund",
|
||||
methods: ['POST'])]
|
||||
public function createPartialRefund(Request $request, Context $context): Response
|
||||
{
|
||||
$salesChannelId = $request->request->get('salesChannelId');
|
||||
$transactionId = $request->request->get('transactionId');
|
||||
$refundableAmount = $request->request->get('refundableAmount');
|
||||
$lineItemId = $request->request->get('lineItemId');
|
||||
|
||||
$settings = $this->settingsService->getSettings($salesChannelId);
|
||||
$apiClient = $settings->getApiClient();
|
||||
|
||||
$transaction = $apiClient->getTransactionService()->read($settings->getSpaceId(), $transactionId);
|
||||
$this->refundService->createPartialRefund($transaction, $context, $lineItemId, $refundableAmount);
|
||||
|
||||
return new Response(null, Response::HTTP_NO_CONTENT);
|
||||
}
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\Refund\Entity;
|
||||
|
||||
use Shopware\Core\{
|
||||
Framework\DataAbstractionLayer\Entity,
|
||||
Framework\DataAbstractionLayer\EntityIdTrait};
|
||||
use VRPaymentPayment\Core\Api\Transaction\Entity\TransactionEntityDefinition;
|
||||
|
||||
/**
|
||||
* Class RefundEntity
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\Refund\Entity
|
||||
*/
|
||||
class RefundEntity extends Entity {
|
||||
|
||||
use EntityIdTrait;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $refundId;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $spaceId;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* @var ?\VRPaymentPayment\Core\Api\Transaction\Entity\TransactionEntityDefinition
|
||||
*/
|
||||
protected $transaction;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $transactionId;
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getData(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData(array $data): void
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getRefundId(): int
|
||||
{
|
||||
return $this->refundId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $refundId
|
||||
*/
|
||||
public function setRefundId(int $refundId): void
|
||||
{
|
||||
$this->refundId = $refundId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSpaceId(): int
|
||||
{
|
||||
return $this->spaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $spaceId
|
||||
*/
|
||||
public function setSpaceId(int $spaceId): void
|
||||
{
|
||||
$this->spaceId = $spaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getState(): string
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $state
|
||||
*/
|
||||
public function setState(string $state): void
|
||||
{
|
||||
$this->state = $state;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return \VRPaymentPayment\Core\Api\Transaction\Entity\TransactionEntityDefinition|null
|
||||
*/
|
||||
public function getTransaction(): ?TransactionEntityDefinition
|
||||
{
|
||||
return $this->transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \VRPaymentPayment\Core\Api\Transaction\Entity\TransactionEntityDefinition $transaction
|
||||
*/
|
||||
public function setTransaction(TransactionEntityDefinition $transaction): void
|
||||
{
|
||||
$this->transaction = $transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getTransactionId(): int
|
||||
{
|
||||
return $this->transactionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $transactionId
|
||||
*/
|
||||
public function setTransactionId(int $transactionId): void
|
||||
{
|
||||
$this->transactionId = $transactionId;
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\Refund\Entity;
|
||||
|
||||
use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection;
|
||||
|
||||
/**
|
||||
* Class RefundEntityCollection
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\Refund\Entity
|
||||
*
|
||||
* @method void add(RefundEntity $entity)
|
||||
* @method void set(string $key, RefundEntity $entity)
|
||||
* @method RefundEntity[] getIterator()
|
||||
* @method RefundEntity[] getElements()
|
||||
* @method RefundEntity|null get(string $key)
|
||||
* @method RefundEntity|null first()
|
||||
* @method RefundEntity|null last()
|
||||
*/
|
||||
class RefundEntityCollection extends EntityCollection {
|
||||
|
||||
/**
|
||||
* @param int $transactionId
|
||||
* @return \VRPaymentPayment\Core\Api\Refund\Entity\RefundEntityCollection
|
||||
*/
|
||||
public function filterByTransactionId(int $transactionId): RefundEntityCollection
|
||||
{
|
||||
return $this->filter(function (RefundEntity $refund) use ($transactionId) {
|
||||
return $refund->getTransactionId() === $transactionId;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get by refund id
|
||||
*
|
||||
* @param int $refundId
|
||||
* @return \VRPaymentPayment\Core\Api\Refund\Entity\RefundEntity|null
|
||||
*/
|
||||
public function getByRefundId(int $refundId): ?RefundEntity
|
||||
{
|
||||
foreach ($this->getIterator() as $element) {
|
||||
if ($element->getRefundId() === $refundId) {
|
||||
return $element;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getExpectedClass(): string
|
||||
{
|
||||
return RefundEntity::class;
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\Refund\Entity;
|
||||
|
||||
use Shopware\Core\{
|
||||
Framework\DataAbstractionLayer\EntityDefinition,
|
||||
Framework\DataAbstractionLayer\Field\CreatedAtField,
|
||||
Framework\DataAbstractionLayer\Field\Flag\PrimaryKey,
|
||||
Framework\DataAbstractionLayer\Field\Flag\Required,
|
||||
Framework\DataAbstractionLayer\Field\IdField,
|
||||
Framework\DataAbstractionLayer\Field\IntField,
|
||||
Framework\DataAbstractionLayer\Field\JsonField,
|
||||
Framework\DataAbstractionLayer\Field\ManyToOneAssociationField,
|
||||
Framework\DataAbstractionLayer\Field\StringField,
|
||||
Framework\DataAbstractionLayer\Field\UpdatedAtField,
|
||||
Framework\DataAbstractionLayer\FieldCollection};
|
||||
use VRPaymentPayment\Core\Api\Transaction\Entity\TransactionEntityDefinition;
|
||||
|
||||
/**
|
||||
* Class RefundEntityDefinition
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\Refund\Entity
|
||||
*/
|
||||
class RefundEntityDefinition extends EntityDefinition {
|
||||
|
||||
public const ENTITY_NAME = 'vrpayment_refund';
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getEntityName(): string
|
||||
{
|
||||
return self::ENTITY_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Shopware\Core\Framework\DataAbstractionLayer\FieldCollection
|
||||
*/
|
||||
protected function defineFields(): FieldCollection
|
||||
{
|
||||
return new FieldCollection([
|
||||
(new IdField('id', 'id'))->addFlags(new PrimaryKey(), new Required()),
|
||||
(new JsonField('data', 'data'))->addFlags(new Required()),
|
||||
(new IntField('refund_id', 'refundId'))->addFlags(new Required()),
|
||||
(new IntField('space_id', 'spaceId'))->addFlags(new Required()),
|
||||
(new StringField('state', 'state'))->addFlags(new Required()),
|
||||
(new IntField('transaction_id', 'transactionId'))->addFlags(new Required()),
|
||||
new ManyToOneAssociationField('transaction', 'transaction_id', TransactionEntityDefinition::class, 'transaction_id'),
|
||||
new CreatedAtField(),
|
||||
new UpdatedAtField(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCollectionClass(): string
|
||||
{
|
||||
return RefundEntityCollection::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getEntityClass(): string
|
||||
{
|
||||
return RefundEntity::class;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,244 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\Refund\Service;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shopware\Core\{
|
||||
Framework\Context,
|
||||
Framework\DataAbstractionLayer\Search\Criteria,
|
||||
Framework\DataAbstractionLayer\Search\Filter\EqualsFilter,
|
||||
Framework\Uuid\Uuid
|
||||
};
|
||||
use VRPayment\Sdk\{
|
||||
Model\Refund,
|
||||
Model\Transaction
|
||||
};
|
||||
use VRPaymentPayment\Core\{
|
||||
Api\Refund\Entity\RefundEntity,
|
||||
Api\Transaction\Entity\TransactionEntity,
|
||||
Api\Transaction\Entity\TransactionEntityDefinition,
|
||||
Settings\Service\SettingsService,
|
||||
Util\Payload\RefundPayload
|
||||
};
|
||||
|
||||
/**
|
||||
* Class RefundService
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\Refund\Service
|
||||
*/
|
||||
class RefundService
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \Psr\Container\ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Settings\Service\SettingsService
|
||||
*/
|
||||
private $settingsService;
|
||||
|
||||
/**
|
||||
* RefundService constructor.
|
||||
*
|
||||
* @param \Psr\Container\ContainerInterface $container
|
||||
* @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService
|
||||
*/
|
||||
public function __construct(ContainerInterface $container, SettingsService $settingsService)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->settingsService = $settingsService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
*
|
||||
* @internal
|
||||
* @required
|
||||
*
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger): void
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* The pay function will be called after the customer completed the order.
|
||||
* Allows to process the order and store additional information.
|
||||
*
|
||||
* A redirect to the url will be performed
|
||||
*
|
||||
* @param \VRPayment\Sdk\Model\Transaction $transaction
|
||||
* @param string|null $lineItemId
|
||||
* @param int $quantity
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return \VRPayment\Sdk\Model\Refund|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function create(Transaction $transaction, Context $context, ?string $lineItemId, int $quantity): ?Refund
|
||||
{
|
||||
try {
|
||||
$transactionEntity = $this->getTransactionEntityByTransactionId($transaction->getId(), $context);
|
||||
$settings = $this->settingsService->getSettings($transactionEntity->getSalesChannel()->getId());
|
||||
$apiClient = $settings->getApiClient();
|
||||
$refundPayloadClass = new RefundPayload();
|
||||
$refundPayloadClass->setLogger($this->logger);
|
||||
|
||||
$refundPayload = $refundPayloadClass->get($transaction, $lineItemId, $quantity);
|
||||
|
||||
if (!is_null($refundPayload)) {
|
||||
$refund = $apiClient->getRefundService()->refund($settings->getSpaceId(), $refundPayload);
|
||||
$this->upsert($refund, $context);
|
||||
return $refund;
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
$this->logger->critical($exception->getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The pay function will be called after the customer completed the order.
|
||||
* Allows to process the order and store additional information.
|
||||
*
|
||||
* A redirect to the url will be performed
|
||||
*
|
||||
* @param \VRPayment\Sdk\Model\Transaction $transaction
|
||||
* @param float $refundableAmount
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return \VRPayment\Sdk\Model\Refund|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function createRefundByAmount(Transaction $transaction, float $refundableAmount, Context $context): ?Refund
|
||||
{
|
||||
try {
|
||||
$transactionEntity = $this->getTransactionEntityByTransactionId($transaction->getId(), $context);
|
||||
$settings = $this->settingsService->getSettings($transactionEntity->getSalesChannel()->getId());
|
||||
$apiClient = $settings->getApiClient();
|
||||
$refundPayloadClass = new RefundPayload();
|
||||
$refundPayloadClass->setLogger($this->logger);
|
||||
|
||||
$refundPayload = $refundPayloadClass->getByAmount($transaction, $refundableAmount);
|
||||
|
||||
if (!is_null($refundPayload)) {
|
||||
$refund = $apiClient->getRefundService()->refund($settings->getSpaceId(), $refundPayload);
|
||||
$this->upsert($refund, $context);
|
||||
return $refund;
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
$this->logger->critical($exception->getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The pay function will be called after the customer completed the order.
|
||||
* Allows to process the order and store additional information.
|
||||
*
|
||||
* A redirect to the url will be performed
|
||||
*
|
||||
* @param \VRPayment\Sdk\Model\Transaction $transaction
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @param string $lineItemId
|
||||
* @param float $amount
|
||||
*
|
||||
* @return \VRPayment\Sdk\Model\Refund|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function createPartialRefund(Transaction $transaction, Context $context, string $lineItemId, float $amount): ?Refund
|
||||
{
|
||||
try {
|
||||
$transactionEntity = $this->getTransactionEntityByTransactionId($transaction->getId(), $context);
|
||||
$settings = $this->settingsService->getSettings($transactionEntity->getSalesChannel()->getId());
|
||||
$apiClient = $settings->getApiClient();
|
||||
$refundPayloadClass = new RefundPayload();
|
||||
$refundPayloadClass->setLogger($this->logger);
|
||||
|
||||
$refundPayload = $refundPayloadClass->getForPartial($transaction, $lineItemId, $amount);
|
||||
|
||||
if (!is_null($refundPayload)) {
|
||||
$refund = $apiClient->getRefundService()->refund($settings->getSpaceId(), $refundPayload);
|
||||
$this->upsert($refund, $context);
|
||||
return $refund;
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
$this->logger->critical($exception->getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transaction entity by VRPayment transaction id
|
||||
*
|
||||
* @param int $transactionId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return \VRPaymentPayment\Core\Api\Transaction\Entity\TransactionEntity
|
||||
*/
|
||||
public function getTransactionEntityByTransactionId(int $transactionId, Context $context): TransactionEntity
|
||||
{
|
||||
return $this->container->get(TransactionEntityDefinition::ENTITY_NAME . '.repository')
|
||||
->search(
|
||||
(new Criteria())->addFilter(new EqualsFilter('transactionId', $transactionId)),
|
||||
$context
|
||||
)
|
||||
->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist VRPayment transaction
|
||||
*
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @param \VRPayment\Sdk\Model\Refund $refund
|
||||
*/
|
||||
public function upsert(Refund $refund, Context $context): void
|
||||
{
|
||||
$refundEntity = $this->getByRefundId($refund->getId(), $context);
|
||||
$id = is_null($refundEntity) ? Uuid::randomHex() : $refundEntity->getId();
|
||||
try {
|
||||
|
||||
$data = [
|
||||
'id' => $id,
|
||||
'data' => json_decode(strval($refund), true),
|
||||
'refundId' => $refund->getId(),
|
||||
'spaceId' => $refund->getLinkedSpaceId(),
|
||||
'state' => $refund->getState(),
|
||||
'transactionId' => $refund->getTransaction()->getId(),
|
||||
];
|
||||
|
||||
$data = array_filter($data);
|
||||
$this->container->get('vrpayment_refund.repository')->upsert([$data], $context);
|
||||
|
||||
} catch (\Exception $exception) {
|
||||
$this->logger->critical(__CLASS__ . ' : ' . __FUNCTION__ . ' : ' . $exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get refund entity by VRPayment refund id
|
||||
*
|
||||
* @param int $refundId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return \VRPaymentPayment\Core\Api\Refund\Entity\RefundEntity|null
|
||||
*/
|
||||
public function getByRefundId(int $refundId, Context $context): ?RefundEntity
|
||||
{
|
||||
return $this->container->get('vrpayment_refund.repository')
|
||||
->search(
|
||||
(new Criteria())->addFilter(new EqualsFilter('refundId', $refundId)),
|
||||
$context
|
||||
)
|
||||
->first();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\Space\Service;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shopware\Core\PlatformRequest;
|
||||
use VRPayment\Sdk\{
|
||||
ApiClient,
|
||||
Model\Space
|
||||
};
|
||||
|
||||
/**
|
||||
* Class SpaceService
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\Space\Service
|
||||
*/
|
||||
class SpaceService {
|
||||
|
||||
/**
|
||||
* @var \VRPayment\Sdk\ApiClient
|
||||
*/
|
||||
protected $apiClient;
|
||||
|
||||
/**
|
||||
* Space Id
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $spaceId;
|
||||
|
||||
/**
|
||||
* Application Id
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $applicationId;
|
||||
|
||||
/**
|
||||
* User Id
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $userId;
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
|
||||
/**
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
*
|
||||
* @internal
|
||||
* @required
|
||||
*
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger): void
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \VRPayment\Sdk\ApiClient
|
||||
*/
|
||||
public function getApiClient(): ApiClient
|
||||
{
|
||||
return $this->apiClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \VRPayment\Sdk\ApiClient $apiClient
|
||||
*
|
||||
* @return \VRPaymentPayment\Core\Api\Space\Service\SpaceService
|
||||
*/
|
||||
public function setApiClient(ApiClient $apiClient): SpaceService
|
||||
{
|
||||
$this->apiClient = $apiClient;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSpaceId(): int
|
||||
{
|
||||
return $this->spaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $spaceId
|
||||
*
|
||||
* @return \VRPaymentPayment\Core\Api\Space\Service\SpaceService
|
||||
*/
|
||||
public function setSpaceId(int $spaceId): SpaceService
|
||||
{
|
||||
$this->spaceId = $spaceId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user id
|
||||
* @return int
|
||||
*/
|
||||
public function getUserId(): int
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $userId
|
||||
*
|
||||
* @return \VRPaymentPayment\Core\Api\Space\Service\SpaceService
|
||||
*/
|
||||
public function setUserId(int $userId): SpaceService
|
||||
{
|
||||
$this->userId = $userId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user key credential
|
||||
* @return string
|
||||
*/
|
||||
public function getApplicationId(): string
|
||||
{
|
||||
return $this->applicationId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $applicationId
|
||||
*
|
||||
* @return \VRPaymentPayment\Core\Api\Space\Service\SpaceService
|
||||
*/
|
||||
public function setApplicationId(string $applicationId): SpaceService
|
||||
{
|
||||
$this->applicationId = $applicationId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check Space
|
||||
* Reads the entity with the given space id and user credentials and returns it.
|
||||
* If the user credentials are not valid, an exception is thrown.
|
||||
* The purpose of this method is simply to validate that a user has access
|
||||
* with their credentials to a space on the portal.
|
||||
* @see On the portal /doc/api/web-service#space-service--read
|
||||
*
|
||||
* @return Space|null
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*/
|
||||
public function checkSpace(): ?Space
|
||||
{
|
||||
// Configuration
|
||||
$this->setApiClient(new ApiClient($this->getUserId(), $this->getApplicationId()));
|
||||
|
||||
return $this->read();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read Space
|
||||
*
|
||||
* @return Space|null
|
||||
*/
|
||||
protected function read(): ?Space
|
||||
{
|
||||
$returnValue = null;
|
||||
try {
|
||||
$returnValue = $this->apiClient->getSpaceService()->read($this->getSpaceId());
|
||||
} catch (\Exception $exception) {
|
||||
$this->logger->critical($exception->getTraceAsString());
|
||||
}
|
||||
|
||||
return $returnValue;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\Transaction\Controller;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shopware\Core\Framework\Log\Package;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\{
|
||||
HttpFoundation\JsonResponse,
|
||||
HttpFoundation\Request,
|
||||
HttpFoundation\Response,
|
||||
Routing\Attribute\Route};
|
||||
use VRPayment\Sdk\{
|
||||
Model\TransactionState};
|
||||
use VRPaymentPayment\Core\Settings\Service\SettingsService;
|
||||
|
||||
|
||||
/**
|
||||
* Class TransactionCompletionController
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\Transaction\Controller
|
||||
*
|
||||
*/
|
||||
#[Package('sales-channel')]
|
||||
#[Route(defaults: ['_routeScope' => ['api']])]
|
||||
class TransactionCompletionController extends AbstractController {
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Settings\Service\SettingsService
|
||||
*/
|
||||
protected $settingsService;
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* TransactionCompletionController constructor.
|
||||
*
|
||||
* @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService
|
||||
*/
|
||||
public function __construct(SettingsService $settingsService)
|
||||
{
|
||||
$this->settingsService = $settingsService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
* @internal
|
||||
* @required
|
||||
*
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger): void
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*
|
||||
*/
|
||||
#[Route("/api/_action/vrpayment/transaction-completion/create-transaction-completion/",
|
||||
name: "api.action.vrpayment.transaction-completion.create-transaction-completion",
|
||||
methods: ['POST'])]
|
||||
public function createTransactionCompletion(Request $request): JsonResponse
|
||||
{
|
||||
$salesChannelId = $request->request->get('salesChannelId');
|
||||
$transactionId = $request->request->get('transactionId');
|
||||
|
||||
$settings = $this->settingsService->getSettings($salesChannelId);
|
||||
$apiClient = $settings->getApiClient();
|
||||
|
||||
|
||||
$transaction = $apiClient->getTransactionService()->read($settings->getSpaceId(), $transactionId);
|
||||
if ($transaction->getState() == TransactionState::AUTHORIZED) {
|
||||
$transactionCompletion = $apiClient->getTransactionCompletionService()->completeOnline($settings->getSpaceId(), $transaction->getId());
|
||||
return new JsonResponse(strval($transactionCompletion), Response::HTTP_OK, [], true);
|
||||
}
|
||||
|
||||
return new JsonResponse(
|
||||
[
|
||||
'message' => strtr('Transaction is in state {state}, it can not be completed at this time', ['{state}' => $transaction->getState()]),
|
||||
],
|
||||
Response::HTTP_NOT_ACCEPTABLE
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\Transaction\Controller;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shopware\Core\{
|
||||
Framework\Context,
|
||||
Framework\Log\Package};
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\{
|
||||
HttpFoundation\HeaderUtils,
|
||||
HttpFoundation\JsonResponse,
|
||||
HttpFoundation\Request,
|
||||
HttpFoundation\Response,
|
||||
Routing\Attribute\Route};
|
||||
use VRPaymentPayment\{
|
||||
Core\Api\Transaction\Service\TransactionService,
|
||||
Core\Settings\Service\SettingsService};
|
||||
|
||||
/**
|
||||
* Class TransactionController
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\Transaction\Controller
|
||||
*
|
||||
*/
|
||||
#[Package('sales-channel')]
|
||||
#[Route(defaults: ['_routeScope' => ['api']])]
|
||||
class TransactionController extends AbstractController {
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Settings\Service\SettingsService
|
||||
*/
|
||||
protected $settingsService;
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService
|
||||
*/
|
||||
protected $transactionService;
|
||||
|
||||
/**
|
||||
* TransactionController constructor.
|
||||
*
|
||||
* @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService
|
||||
* @param \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService $transactionService
|
||||
*/
|
||||
public function __construct(SettingsService $settingsService, TransactionService $transactionService)
|
||||
{
|
||||
$this->settingsService = $settingsService;
|
||||
$this->transactionService = $transactionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
*
|
||||
* @internal
|
||||
* @required
|
||||
*
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger): void
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse
|
||||
*/
|
||||
#[Route("/api/_action/vrpayment/transaction/get-transaction-data/",
|
||||
name: "api.action.vrpayment.transaction.get-transaction-data",
|
||||
methods: ['POST'])]
|
||||
public function getTransactionData(Request $request, Context $context): JsonResponse
|
||||
{
|
||||
$transactionId = $request->request->get('transactionId');
|
||||
|
||||
$transaction = $this->transactionService->getByTransactionId(intval($transactionId), $context);
|
||||
$refundCollection = $this->transactionService->getRefundEntityCollectionByTransactionId(intval($transactionId), $context);
|
||||
|
||||
$refunds = [];
|
||||
foreach ($refundCollection as $refundEntity) {
|
||||
$refunds[] = $refundEntity ? $refundEntity->getData() : [];
|
||||
}
|
||||
|
||||
return new JsonResponse([
|
||||
'refunds' => $refunds,
|
||||
'transactions' => [$transaction ? $transaction->getData() : []],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $salesChannelId
|
||||
* @param int $transactionId
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*
|
||||
*/
|
||||
#[Route("/api/_action/vrpayment/transaction/get-invoice-document/{salesChannelId}/{transactionId}",
|
||||
name: "api.action.vrpayment.transaction.get-invoice-document",
|
||||
methods: ['GET'],
|
||||
defaults: ["csrf_protected" => false, "auth_required" => false])]
|
||||
public function getInvoiceDocument(string $salesChannelId, int $transactionId): Response
|
||||
{
|
||||
$settings = $this->settingsService->getSettings($salesChannelId);
|
||||
$apiClient = $settings->getApiClient();
|
||||
|
||||
$invoiceDocument = $apiClient->getTransactionService()->getInvoiceDocument($settings->getSpaceId(), $transactionId);
|
||||
$forceDownload = true;
|
||||
$filename = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '_', $invoiceDocument->getTitle()) . '.pdf';
|
||||
$disposition = HeaderUtils::makeDisposition(
|
||||
$forceDownload ? HeaderUtils::DISPOSITION_ATTACHMENT : HeaderUtils::DISPOSITION_INLINE,
|
||||
$filename,
|
||||
$filename
|
||||
);
|
||||
$response = new Response(base64_decode($invoiceDocument->getData()));
|
||||
$response->headers->set('Content-Type', $invoiceDocument->getMimeType());
|
||||
$response->headers->set('Content-Disposition', $disposition);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $salesChannelId
|
||||
* @param int $transactionId
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*
|
||||
*/
|
||||
#[Route("/api/_action/vrpayment/transaction/get-packing-slip/{salesChannelId}/{transactionId}",
|
||||
name: "api.action.vrpayment.transaction.get-packing-slip",
|
||||
methods: ['GET'],
|
||||
defaults: ["csrf_protected" => false, "auth_required" => false])]
|
||||
public function getPackingSlip(string $salesChannelId, int $transactionId): Response
|
||||
{
|
||||
$settings = $this->settingsService->getSettings($salesChannelId);
|
||||
$apiClient = $settings->getApiClient();
|
||||
|
||||
$invoiceDocument = $apiClient->getTransactionService()->getPackingSlip($settings->getSpaceId(), $transactionId);
|
||||
$forceDownload = true;
|
||||
$filename = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '_', $invoiceDocument->getTitle()) . '.pdf';
|
||||
$disposition = HeaderUtils::makeDisposition(
|
||||
$forceDownload ? HeaderUtils::DISPOSITION_ATTACHMENT : HeaderUtils::DISPOSITION_INLINE,
|
||||
$filename,
|
||||
$filename
|
||||
// only printable ascii
|
||||
|
||||
);
|
||||
$response = new Response(base64_decode($invoiceDocument->getData()));
|
||||
$response->headers->set('Content-Type', $invoiceDocument->getMimeType());
|
||||
$response->headers->set('Content-Disposition', $disposition);
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\Transaction\Controller;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shopware\Core\Framework\Log\Package;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\{
|
||||
HttpFoundation\JsonResponse,
|
||||
HttpFoundation\Request,
|
||||
HttpFoundation\Response,
|
||||
Routing\Attribute\Route};
|
||||
use VRPayment\Sdk\{
|
||||
Model\TransactionState};
|
||||
use VRPaymentPayment\Core\Settings\Service\SettingsService;
|
||||
|
||||
/**
|
||||
* Class TransactionVoidController
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\Transaction\Controller
|
||||
*
|
||||
*/
|
||||
#[Package('sales-channel')]
|
||||
#[Route(defaults: ['_routeScope' => ['api']])]
|
||||
class TransactionVoidController extends AbstractController {
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Settings\Service\SettingsService
|
||||
*/
|
||||
protected $settingsService;
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* TransactionVoidController constructor.
|
||||
*
|
||||
* @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService
|
||||
*/
|
||||
public function __construct(SettingsService $settingsService)
|
||||
{
|
||||
$this->settingsService = $settingsService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
* @internal
|
||||
* @required
|
||||
*
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger): void
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*
|
||||
*/
|
||||
#[Route("/api/_action/vrpayment/transaction-void/create-transaction-void/",
|
||||
name: "api.action.vrpayment.transaction-void.create-transaction-void",
|
||||
methods: ['POST'])]
|
||||
public function createTransactionVoid(Request $request): JsonResponse
|
||||
{
|
||||
$salesChannelId = $request->request->get('salesChannelId');
|
||||
$transactionId = $request->request->get('transactionId');
|
||||
|
||||
$settings = $this->settingsService->getSettings($salesChannelId);
|
||||
$apiClient = $settings->getApiClient();
|
||||
|
||||
$transaction = $apiClient->getTransactionService()->read($settings->getSpaceId(), $transactionId);
|
||||
if ($transaction->getState() == TransactionState::AUTHORIZED) {
|
||||
$transactionVoid = $apiClient->getTransactionVoidService()->voidOnline($settings->getSpaceId(), $transaction->getId());
|
||||
return new JsonResponse(strval($transactionVoid), Response::HTTP_OK, [], true);
|
||||
}
|
||||
|
||||
return new JsonResponse(
|
||||
[
|
||||
'message' => strtr('Transaction is in state {state}, it can not be completed at this time.', ['{state}' => $transaction->getState()]),
|
||||
],
|
||||
Response::HTTP_NOT_ACCEPTABLE
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,321 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\Transaction\Entity;
|
||||
|
||||
use Shopware\Core\{
|
||||
Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity,
|
||||
Checkout\Order\OrderEntity,
|
||||
Checkout\Payment\PaymentMethodEntity,
|
||||
Framework\DataAbstractionLayer\Entity,
|
||||
Framework\DataAbstractionLayer\EntityIdTrait,
|
||||
System\SalesChannel\SalesChannelEntity};
|
||||
use VRPaymentPayment\Core\Api\Refund\Entity\RefundEntityCollection;
|
||||
|
||||
/**
|
||||
* Class TransactionEntity
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\Transaction\Entity
|
||||
*/
|
||||
class TransactionEntity extends Entity {
|
||||
|
||||
use EntityIdTrait;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $confirmationEmailSent;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $erpMerchantId;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Checkout\Payment\PaymentMethodEntity
|
||||
*/
|
||||
protected $paymentMethod;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $paymentMethodId;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Checkout\Order\OrderEntity
|
||||
*/
|
||||
protected $order;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $orderId;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity
|
||||
*/
|
||||
protected $orderTransaction;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $orderTransactionId;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Api\Refund\Entity\RefundEntityCollection
|
||||
*/
|
||||
protected $refunds;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $spaceId;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\System\SalesChannel\SalesChannelEntity
|
||||
*/
|
||||
protected $salesChannel;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $salesChannelId;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $transactionId;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isConfirmationEmailSent(): bool
|
||||
{
|
||||
return $this->confirmationEmailSent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $confirmationEmailSent
|
||||
*/
|
||||
public function setConfirmationEmailSent(bool $confirmationEmailSent): void
|
||||
{
|
||||
$this->confirmationEmailSent = $confirmationEmailSent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getData(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData(array $data): void
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPaymentMethodId(): string
|
||||
{
|
||||
return $this->paymentMethodId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $paymentMethodId
|
||||
*/
|
||||
public function setPaymentMethodId(string $paymentMethodId): void
|
||||
{
|
||||
$this->paymentMethodId = $paymentMethodId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getOrderId(): string
|
||||
{
|
||||
return $this->orderId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $orderId
|
||||
*/
|
||||
public function setOrderId(string $orderId): void
|
||||
{
|
||||
$this->orderId = $orderId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getOrderTransactionId(): string
|
||||
{
|
||||
return $this->orderTransactionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $orderTransactionId
|
||||
*/
|
||||
public function setOrderTransactionId(string $orderTransactionId): void
|
||||
{
|
||||
$this->orderTransactionId = $orderTransactionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSpaceId(): int
|
||||
{
|
||||
return $this->spaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $spaceId
|
||||
*/
|
||||
public function setSpaceId(int $spaceId): void
|
||||
{
|
||||
$this->spaceId = $spaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getState(): string
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $state
|
||||
*/
|
||||
public function setState(string $state): void
|
||||
{
|
||||
$this->state = $state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSalesChannelId(): string
|
||||
{
|
||||
return $this->salesChannelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $salesChannelId
|
||||
*/
|
||||
public function setSalesChannelId(string $salesChannelId): void
|
||||
{
|
||||
$this->salesChannelId = $salesChannelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getTransactionId(): int
|
||||
{
|
||||
return $this->transactionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $transactionId
|
||||
*/
|
||||
public function setTransactionId(int $transactionId): void
|
||||
{
|
||||
$this->transactionId = $transactionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Shopware\Core\Checkout\Payment\PaymentMethodEntity
|
||||
*/
|
||||
public function getPaymentMethod(): PaymentMethodEntity
|
||||
{
|
||||
return $this->paymentMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Checkout\Payment\PaymentMethodEntity $paymentMethod
|
||||
*/
|
||||
public function setPaymentMethod(PaymentMethodEntity $paymentMethod): void
|
||||
{
|
||||
$this->paymentMethod = $paymentMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Shopware\Core\Checkout\Order\OrderEntity
|
||||
*/
|
||||
public function getOrder(): OrderEntity
|
||||
{
|
||||
return $this->order;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Checkout\Order\OrderEntity $order
|
||||
*/
|
||||
public function setOrder(OrderEntity $order): void
|
||||
{
|
||||
$this->order = $order;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity
|
||||
*/
|
||||
public function getOrderTransaction(): OrderTransactionEntity
|
||||
{
|
||||
return $this->orderTransaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity $orderTransaction
|
||||
*/
|
||||
public function setOrderTransaction(OrderTransactionEntity $orderTransaction): void
|
||||
{
|
||||
$this->orderTransaction = $orderTransaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \VRPaymentPayment\Core\Api\Refund\Entity\RefundEntityCollection|null
|
||||
*/
|
||||
public function getRefunds(): ?RefundEntityCollection
|
||||
{
|
||||
return $this->refunds;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \VRPaymentPayment\Core\Api\Refund\Entity\RefundEntityCollection $refunds
|
||||
*/
|
||||
public function setRefunds(RefundEntityCollection $refunds): void
|
||||
{
|
||||
$this->refunds = $refunds;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Shopware\Core\System\SalesChannel\SalesChannelEntity
|
||||
*/
|
||||
public function getSalesChannel(): SalesChannelEntity
|
||||
{
|
||||
return $this->salesChannel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\System\SalesChannel\SalesChannelEntity $salesChannel
|
||||
*/
|
||||
public function setSalesChannel(SalesChannelEntity $salesChannel): void
|
||||
{
|
||||
$this->salesChannel = $salesChannel;
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\Transaction\Entity;
|
||||
|
||||
use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection;
|
||||
|
||||
/**
|
||||
* Class TransactionEntityCollection
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\Transaction\Entity
|
||||
*
|
||||
* @method void add(TransactionEntity $entity)
|
||||
* @method void set(string $key, TransactionEntity $entity)
|
||||
* @method TransactionEntity[] getIterator()
|
||||
* @method TransactionEntity[] getElements()
|
||||
* @method TransactionEntity|null get(string $key)
|
||||
* @method TransactionEntity|null first()
|
||||
* @method TransactionEntity|null last()
|
||||
*/
|
||||
class TransactionEntityCollection extends EntityCollection {
|
||||
|
||||
/**
|
||||
* Get by transaction id
|
||||
*
|
||||
* @param int $transactionId
|
||||
* @return \VRPaymentPayment\Core\Api\Transaction\Entity\TransactionEntity|null
|
||||
*/
|
||||
public function getByTransactionId(int $transactionId): ?TransactionEntity
|
||||
{
|
||||
foreach ($this->getIterator() as $element) {
|
||||
if ($element->getTransactionId() === $transactionId) {
|
||||
return $element;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getExpectedClass(): string
|
||||
{
|
||||
return TransactionEntity::class;
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\Transaction\Entity;
|
||||
|
||||
use Shopware\Core\{Checkout\Order\Aggregate\OrderTransaction\OrderTransactionDefinition,
|
||||
Checkout\Order\OrderDefinition,
|
||||
Checkout\Payment\PaymentMethodDefinition,
|
||||
Framework\DataAbstractionLayer\EntityDefinition,
|
||||
Framework\DataAbstractionLayer\Field\BoolField,
|
||||
Framework\DataAbstractionLayer\Field\CreatedAtField,
|
||||
Framework\DataAbstractionLayer\Field\FkField,
|
||||
Framework\DataAbstractionLayer\Field\Flag\ApiAware,
|
||||
Framework\DataAbstractionLayer\Field\Flag\CascadeDelete,
|
||||
Framework\DataAbstractionLayer\Field\Flag\PrimaryKey,
|
||||
Framework\DataAbstractionLayer\Field\Flag\Required,
|
||||
Framework\DataAbstractionLayer\Field\IdField,
|
||||
Framework\DataAbstractionLayer\Field\IntField,
|
||||
Framework\DataAbstractionLayer\Field\JsonField,
|
||||
Framework\DataAbstractionLayer\Field\OneToManyAssociationField,
|
||||
Framework\DataAbstractionLayer\Field\OneToOneAssociationField,
|
||||
Framework\DataAbstractionLayer\Field\ReferenceVersionField,
|
||||
Framework\DataAbstractionLayer\Field\StringField,
|
||||
Framework\DataAbstractionLayer\Field\UpdatedAtField,
|
||||
Framework\DataAbstractionLayer\FieldCollection,
|
||||
System\SalesChannel\SalesChannelDefinition};
|
||||
use VRPaymentPayment\Core\Api\Refund\Entity\RefundEntityDefinition;
|
||||
|
||||
/**
|
||||
* Class TransactionEntityDefinition
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\Transaction\Entity
|
||||
*/
|
||||
class TransactionEntityDefinition extends EntityDefinition {
|
||||
|
||||
public const ENTITY_NAME = 'vrpayment_transaction';
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getEntityName(): string
|
||||
{
|
||||
return self::ENTITY_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Shopware\Core\Framework\DataAbstractionLayer\FieldCollection
|
||||
*/
|
||||
protected function defineFields(): FieldCollection
|
||||
{
|
||||
return new FieldCollection([
|
||||
(new IdField('id', 'id'))->addFlags(new PrimaryKey(), new Required()),
|
||||
new BoolField('confirmation_email_sent', 'confirmationEmailSent'),
|
||||
new StringField('erp_merchant_id', 'erpMerchantId'),
|
||||
(new JsonField('data', 'data'))->addFlags(new Required()),
|
||||
(new FkField('payment_method_id', 'paymentMethodId', PaymentMethodDefinition::class))->addFlags(new Required()),
|
||||
(new FkField('order_id', 'orderId', OrderDefinition::class))->addFlags(new Required()),
|
||||
(new FkField('order_transaction_id', 'orderTransactionId', OrderTransactionDefinition::class))->addFlags(new Required()),
|
||||
(new IntField('space_id', 'spaceId'))->addFlags(new Required()),
|
||||
(new StringField('state', 'state'))->addFlags(new Required()),
|
||||
(new FkField('sales_channel_id', 'salesChannelId', SalesChannelDefinition::class))->addFlags(new Required()),
|
||||
(new IntField('transaction_id', 'transactionId'))->addFlags(new Required()),
|
||||
new OneToOneAssociationField('paymentMethod', 'payment_method_id', 'id', PaymentMethodDefinition::class, true),
|
||||
new OneToOneAssociationField('order', 'order_id', 'id', OrderDefinition::class, true),
|
||||
new OneToOneAssociationField('orderTransaction', 'order_transaction_id', 'id', OrderTransactionDefinition::class, true),
|
||||
(new OneToManyAssociationField('refunds', RefundEntityDefinition::class, 'transaction_id', 'transaction_id'))->addFlags(new CascadeDelete()),
|
||||
new OneToOneAssociationField('salesChannel', 'sales_channel_id', 'id', SalesChannelDefinition::class, true),
|
||||
(new ReferenceVersionField(OrderDefinition::class))->addFlags(new ApiAware(), new Required()),
|
||||
new CreatedAtField(),
|
||||
new UpdatedAtField(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCollectionClass(): string
|
||||
{
|
||||
return TransactionEntityCollection::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getEntityClass(): string
|
||||
{
|
||||
return TransactionEntity::class;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\Transaction\Service;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shopware\Core\{
|
||||
Checkout\Cart\Event\CheckoutOrderPlacedEvent,
|
||||
Checkout\Cart\CartException,
|
||||
Checkout\Order\OrderEntity,
|
||||
Content\MailTemplate\MailTemplateEntity,
|
||||
Content\Mail\Service\AbstractMailService,
|
||||
Framework\Context,
|
||||
Framework\DataAbstractionLayer\Search\Criteria,
|
||||
Framework\DataAbstractionLayer\Search\Filter\EqualsFilter,
|
||||
Framework\DataAbstractionLayer\Search\Filter\NotFilter,
|
||||
Framework\DataAbstractionLayer\Search\Filter\OrFilter,
|
||||
Framework\Event\EventAction\EventActionCollection};
|
||||
use VRPaymentPayment\Core\{
|
||||
Api\Transaction\Entity\TransactionEntity,
|
||||
Api\Transaction\Entity\TransactionEntityDefinition};
|
||||
|
||||
/**
|
||||
* Class OrderMailService
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\Transaction\Service
|
||||
*/
|
||||
class OrderMailService {
|
||||
|
||||
public const EMAIL_ORIGIN_IS_VRPAYMENT = "isVRPaymentPayment";
|
||||
/**
|
||||
* @var \Psr\Container\ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface
|
||||
*/
|
||||
protected $mailTemplateRepository;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Content\Mail\Service\AbstractMailService
|
||||
*/
|
||||
protected $mailService;
|
||||
|
||||
/**
|
||||
* MailService constructor.
|
||||
*
|
||||
* @param \Psr\Container\ContainerInterface $container
|
||||
* @param \Shopware\Core\Content\Mail\Service\AbstractMailService $mailService
|
||||
*/
|
||||
public function __construct(ContainerInterface $container, AbstractMailService $mailService)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->mailService = $mailService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
*
|
||||
* @internal
|
||||
* @required
|
||||
*
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger): void
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $orderId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*/
|
||||
public function send(string $orderId, Context $context): void
|
||||
{
|
||||
try {
|
||||
|
||||
$transactionEntity = $this->getTransactionEntityByOrderId($orderId, $context);
|
||||
if ($transactionEntity->isConfirmationEmailSent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$order = $this->getOrder($orderId, $context);
|
||||
if (is_null($order->getOrderCustomer())) {
|
||||
return;
|
||||
}
|
||||
|
||||
$languageIdChain[] = $order->getLanguageId();
|
||||
$contextLanguageIdChain = $context->getLanguageIdChain();
|
||||
foreach ($contextLanguageIdChain as $languageId) {
|
||||
$contextLanguageIdChain[] = $languageId;
|
||||
}
|
||||
array_unique($languageIdChain);
|
||||
|
||||
$context->assign(['languageIdChain' => $languageIdChain,]);
|
||||
|
||||
$templateData = [
|
||||
'order' => $order,
|
||||
self::EMAIL_ORIGIN_IS_VRPAYMENT => true,
|
||||
];
|
||||
|
||||
$data = $this->getData($order, $context);
|
||||
|
||||
foreach ($data as $datum){
|
||||
$this->mailService->send($datum, $context, $templateData);
|
||||
}
|
||||
$this->markTransactionEntityConfirmationEmailAsSent($orderId, $context);
|
||||
|
||||
|
||||
} catch (\Exception $exception) {
|
||||
$this->logger->critical($exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transaction entity by orderId
|
||||
*
|
||||
* @param string $orderId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return \VRPaymentPayment\Core\Api\Transaction\Entity\TransactionEntity
|
||||
*/
|
||||
protected function getTransactionEntityByOrderId(string $orderId, Context $context): TransactionEntity
|
||||
{
|
||||
return $this->container->get(TransactionEntityDefinition::ENTITY_NAME . '.repository')
|
||||
->search(new Criteria([$orderId]), $context)
|
||||
->get($orderId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $orderId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return \Shopware\Core\Checkout\Order\OrderEntity
|
||||
*/
|
||||
protected function getOrder(string $orderId, Context $context): OrderEntity
|
||||
{
|
||||
$orderCriteria = (new Criteria([$orderId]))->addAssociations([
|
||||
'addresses',
|
||||
'addresses.country',
|
||||
'currency',
|
||||
'deliveries',
|
||||
'deliveries.shippingCosts',
|
||||
'deliveries.shippingMethod',
|
||||
'deliveries.shippingOrderAddress',
|
||||
'deliveries.shippingOrderAddress.country',
|
||||
'documents',
|
||||
'language',
|
||||
'lineItems',
|
||||
'orderCustomer',
|
||||
'orderCustomer.customer',
|
||||
'orderCustomer.salutation',
|
||||
'salesChannel',
|
||||
'stateMachineState',
|
||||
'tags',
|
||||
'transactions',
|
||||
'transactions.paymentMethod',
|
||||
]);
|
||||
|
||||
/** @var OrderEntity|null $order */
|
||||
$order = $this->container->get('order.repository')->search($orderCriteria, $context)->first();
|
||||
if (is_null($order)) {
|
||||
throw CartException::orderNotFound($orderId);
|
||||
}
|
||||
return $order;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Checkout\Order\OrderEntity $order
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getData(OrderEntity $order, Context $context): array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
/** @var \Shopware\Core\Framework\Event\EventAction\EventActionCollection $eventActionEntities */
|
||||
$eventActionEntities = $this->getBusinessEvents($order, $context);
|
||||
$customerRecipient = [
|
||||
$order->getOrderCustomer()->getEmail() => $order->getOrderCustomer()->getFirstName() . ' ' . $order->getOrderCustomer()->getLastName(),
|
||||
];
|
||||
|
||||
foreach ($eventActionEntities as $eventActionEntity) {
|
||||
|
||||
$eventConfig = $eventActionEntity->getConfig();
|
||||
$mailTemplateId = $eventConfig['mail_template_id'];
|
||||
$recipients = !empty($eventConfig['recipients']) ? $eventConfig['recipients'] : $customerRecipient;
|
||||
$mailTemplate = $this->getMailTemplateById($context, $mailTemplateId);
|
||||
|
||||
$data[] = [
|
||||
'recipients' => $recipients,
|
||||
'senderName' => $mailTemplate->getTranslation('senderName'),
|
||||
'salesChannelId' => $order->getSalesChannelId(),
|
||||
'templateId' => $mailTemplateId,
|
||||
'customFields' => $mailTemplate->getCustomFields(),
|
||||
'contentHtml' => $mailTemplate->getTranslation('contentHtml'),
|
||||
'contentPlain' => $mailTemplate->getTranslation('contentPlain'),
|
||||
'subject' => $mailTemplate->getTranslation('subject'),
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function getBusinessEvents(OrderEntity $order, Context $context): EventActionCollection
|
||||
{
|
||||
$criteria = (new Criteria())
|
||||
->addAssociations([
|
||||
'rules',
|
||||
'salesChannels',
|
||||
])
|
||||
->addFilter(new EqualsFilter('eventName', CheckoutOrderPlacedEvent::EVENT_NAME))
|
||||
->addFilter(new EqualsFilter('active', true))
|
||||
->addFilter(new NotFilter(NotFilter::CONNECTION_AND, [new EqualsFilter('config.mail_template_id', null)]))
|
||||
->addFilter(new OrFilter([
|
||||
new EqualsFilter('salesChannels.id', $order->getSalesChannelId()),
|
||||
new EqualsFilter('salesChannels.id', null),
|
||||
]));
|
||||
|
||||
|
||||
/** @var EventActionCollection $events */
|
||||
$events = $this->container->get('event_action.repository')
|
||||
->search($criteria, $context)
|
||||
->getEntities();
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @param string $id
|
||||
*
|
||||
* @return \Shopware\Core\Content\MailTemplate\MailTemplateEntity
|
||||
*/
|
||||
protected function getMailTemplateById(Context $context, string $id): MailTemplateEntity
|
||||
{
|
||||
$criteria = (new Criteria([$id]))->addAssociations(['media', 'media.media', 'salesChannels', 'mailTemplateType']);
|
||||
|
||||
$mailTemplateEntity = $this->container->get('mail_template.repository')->search($criteria, $context)->first();
|
||||
|
||||
return $mailTemplateEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $orderId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*/
|
||||
protected function markTransactionEntityConfirmationEmailAsSent(string $orderId, Context $context)
|
||||
{
|
||||
$this->container->get(TransactionEntityDefinition::ENTITY_NAME . '.repository')->upsert([['id' => $orderId, 'confirmationEmailSent' => true]], $context);
|
||||
}
|
||||
}
|
||||
@@ -1,750 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\Transaction\Service;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shopware\Core\{
|
||||
Checkout\Cart\CartException,
|
||||
Checkout\Cart\LineItem\LineItem,
|
||||
Checkout\Order\OrderEntity,
|
||||
Checkout\Payment\Cart\PaymentTransactionStruct,
|
||||
Framework\Context,
|
||||
Framework\DataAbstractionLayer\Search\Criteria,
|
||||
Framework\DataAbstractionLayer\Search\Filter\EqualsFilter,
|
||||
System\SalesChannel\SalesChannelContext
|
||||
};
|
||||
use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use VRPayment\Sdk\{
|
||||
Model\AddressCreate,
|
||||
Model\ChargeAttempt,
|
||||
Model\CreationEntityState,
|
||||
Model\CriteriaOperator,
|
||||
Model\EntityQuery,
|
||||
Model\EntityQueryFilter,
|
||||
Model\EntityQueryFilterType,
|
||||
Model\Gender,
|
||||
Model\LineItemAttributeCreate,
|
||||
Model\LineItemCreate,
|
||||
Model\LineItemType,
|
||||
Model\Transaction,
|
||||
Model\TransactionCreate,
|
||||
Model\TransactionPending,
|
||||
Model\TransactionState,
|
||||
};
|
||||
use VRPaymentPayment\Core\{
|
||||
Api\OrderDeliveryState\Handler\OrderDeliveryStateHandler,
|
||||
Api\Refund\Entity\RefundEntityCollection,
|
||||
Api\Refund\Entity\RefundEntityDefinition,
|
||||
Api\Transaction\Entity\TransactionEntity,
|
||||
Api\Transaction\Entity\TransactionEntityDefinition,
|
||||
Settings\Options\Integration,
|
||||
Settings\Service\SettingsService,
|
||||
Util\LocaleCodeProvider,
|
||||
Util\Payload\CustomProducts\CustomProductsLineItemTypes,
|
||||
Util\Payload\TransactionPayload
|
||||
};
|
||||
|
||||
/**
|
||||
* Class TransactionService
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\Transaction\Service
|
||||
*/
|
||||
class TransactionService
|
||||
{
|
||||
/**
|
||||
* @var \Psr\Container\ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Util\LocaleCodeProvider
|
||||
*/
|
||||
private $localeCodeProvider;
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Settings\Service\SettingsService
|
||||
*/
|
||||
private $settingsService;
|
||||
|
||||
const CARD_HOLDER_KEY = '1456765000789';
|
||||
const PSEUDO_CODE_KEY = '1485172176673';
|
||||
const CARD_VALIDITY_KEY = '1456765711187';
|
||||
const PAY_ID_KEY = '1484042941549';
|
||||
const ADDITIONAL_TRANSACTION_DETAILS_ORDER_ID_KEY = '1464680013786';
|
||||
|
||||
/**
|
||||
* TransactionService constructor.
|
||||
*
|
||||
* @param \Psr\Container\ContainerInterface $container
|
||||
* @param \VRPaymentPayment\Core\Util\LocaleCodeProvider $localeCodeProvider
|
||||
* @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService
|
||||
*/
|
||||
public function __construct(
|
||||
ContainerInterface $container,
|
||||
LocaleCodeProvider $localeCodeProvider,
|
||||
SettingsService $settingsService
|
||||
)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->localeCodeProvider = $localeCodeProvider;
|
||||
$this->settingsService = $settingsService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
*
|
||||
* @internal
|
||||
* @required
|
||||
*
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger): void
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* The pay function will be called after the customer completed the order.
|
||||
* Allows to process the order and store additional information.
|
||||
*
|
||||
* A redirect to the url will be performed
|
||||
*
|
||||
* @param \Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct $transaction
|
||||
* @param \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext
|
||||
*
|
||||
* @return string
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*/
|
||||
public function create(
|
||||
PaymentTransactionStruct $transaction,
|
||||
SalesChannelContext $salesChannelContext
|
||||
): string
|
||||
{
|
||||
$criteria = new Criteria([$transaction->getOrderTransactionId()]);
|
||||
$criteria->addAssociation('order');
|
||||
$orderTransaction = $this->container->get('order_transaction.repository')->search($criteria, $salesChannelContext->getContext())->first();
|
||||
|
||||
$salesChannelId = $salesChannelContext->getSalesChannel()->getId();
|
||||
$settings = $this->settingsService->getSettings($salesChannelId);
|
||||
$apiClient = $settings->getApiClient();
|
||||
|
||||
$transactionId = $_SESSION['transactionId'] ?? null;
|
||||
if ($transactionId !== null) {
|
||||
$pendingTransaction = $this->read($_SESSION['transactionId'], $salesChannelId);
|
||||
}
|
||||
|
||||
if ($transactionId === null || $pendingTransaction === null || $pendingTransaction->getState() !== TransactionState::PENDING) {
|
||||
unset($_SESSION['transactionId']);
|
||||
$pendingTransactionId = $this->createPendingTransaction($salesChannelContext);
|
||||
$pendingTransaction = $this->read($pendingTransactionId, $salesChannelId);
|
||||
}
|
||||
|
||||
$transactionPayloadClass = (new TransactionPayload(
|
||||
$this->container,
|
||||
$this->localeCodeProvider,
|
||||
$salesChannelContext,
|
||||
$settings,
|
||||
$transaction
|
||||
));
|
||||
$transactionPayloadClass->setLogger($this->logger);
|
||||
$transactionPayload = $transactionPayloadClass->get($pendingTransaction->getVersion());
|
||||
|
||||
$createdTransaction = $apiClient->getTransactionService()
|
||||
->confirm($settings->getSpaceId(), $transactionPayload);
|
||||
|
||||
$this->addVRPaymentTransactionId(
|
||||
$transaction,
|
||||
$salesChannelContext->getContext(),
|
||||
$createdTransaction->getId(),
|
||||
$settings->getSpaceId()
|
||||
);
|
||||
|
||||
$redirectUrl = $this->container->get('router')->generate(
|
||||
'frontend.vrpayment.checkout.pay',
|
||||
['orderId' => $orderTransaction->getOrder()->getId(),],
|
||||
UrlGeneratorInterface::ABSOLUTE_URL
|
||||
);
|
||||
|
||||
if ($settings->getIntegration() == Integration::PAYMENT_PAGE) {
|
||||
$redirectUrl = $apiClient->getTransactionPaymentPageService()
|
||||
->paymentPageUrl($settings->getSpaceId(), $createdTransaction->getId());
|
||||
}
|
||||
|
||||
$this->upsert(
|
||||
$createdTransaction,
|
||||
$salesChannelContext->getContext(),
|
||||
$orderTransaction->getPaymentMethodId(),
|
||||
$orderTransaction->getOrder()->getSalesChannelId()
|
||||
);
|
||||
$_SESSION['transactionId'] = null;
|
||||
$_SESSION['arrayOfPossibleMethods'] = null;
|
||||
$_SESSION['addressCheck'] = null;
|
||||
$_SESSION['currencyCheck'] = null;
|
||||
|
||||
|
||||
$this->holdDelivery($orderTransaction->getOrder()->getId(), $salesChannelContext->getContext());
|
||||
|
||||
return $redirectUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct $transaction
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @param int $vrpaymentTransactionId
|
||||
* @param int $spaceId
|
||||
*/
|
||||
protected function addVRPaymentTransactionId(
|
||||
PaymentTransactionStruct $transaction,
|
||||
Context $context,
|
||||
int $vrpaymentTransactionId,
|
||||
int $spaceId
|
||||
): void
|
||||
{
|
||||
$data = [
|
||||
'id' => $transaction->getOrderTransactionId(),
|
||||
'customFields' => [
|
||||
TransactionPayload::ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_TRANSACTION_ID => $vrpaymentTransactionId,
|
||||
TransactionPayload::ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_SPACE_ID => $spaceId,
|
||||
],
|
||||
];
|
||||
$this->container->get('order_transaction.repository')->update([$data], $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist VRPayment transaction
|
||||
*
|
||||
* @param \VRPayment\Sdk\Model\Transaction $transaction
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @param string|null $paymentMethodId
|
||||
* @param string|null $salesChannelId
|
||||
*/
|
||||
public function upsert(
|
||||
Transaction $transaction,
|
||||
Context $context,
|
||||
string $paymentMethodId = null,
|
||||
string $salesChannelId = null
|
||||
): void
|
||||
{
|
||||
try {
|
||||
|
||||
$transactionId = $transaction->getId();
|
||||
$transactionMetaData = $transaction->getMetaData();
|
||||
|
||||
if (!$salesChannelId) {
|
||||
$salesChannelId = $transactionMetaData['salesChannelId'] ?? '';
|
||||
}
|
||||
|
||||
$orderId = $transactionMetaData[TransactionPayload::VRPAYMENT_METADATA_ORDER_ID];
|
||||
$orderTransactionId = $transactionMetaData[TransactionPayload::VRPAYMENT_METADATA_ORDER_TRANSACTION_ID];
|
||||
|
||||
$dataParamValue = json_decode(strval($transaction), true);
|
||||
$brandName = '';
|
||||
if (isset($dataParamValue['paymentConnectorConfiguration'])) {
|
||||
$brandName = $dataParamValue['paymentConnectorConfiguration']
|
||||
? $dataParamValue['paymentConnectorConfiguration']['name']
|
||||
: '';
|
||||
}
|
||||
$dataParamValue['brandName'] = $brandName;
|
||||
|
||||
$paymentMethodName = '';
|
||||
if (isset($dataParamValue['paymentConnectorConfiguration'])) {
|
||||
$paymentMethodName = $dataParamValue['paymentConnectorConfiguration']
|
||||
? $dataParamValue['paymentConnectorConfiguration']['paymentMethodConfiguration']['name']
|
||||
: '';
|
||||
}
|
||||
$dataParamValue['paymentMethodName'] = $paymentMethodName;
|
||||
|
||||
$chargeAttempt = $this->getChargeAttempt($salesChannelId, $transactionId);
|
||||
|
||||
$erpMerchantId = null;
|
||||
if ($chargeAttempt) {
|
||||
$creditCardHolder = $this->getChargeAttemptAdditionalData($chargeAttempt, self::CARD_HOLDER_KEY);
|
||||
$dataParamValue['creditCardHolder'] = $creditCardHolder ? $creditCardHolder[0] : '';
|
||||
|
||||
$pseudoCardNumber = $this->getChargeAttemptAdditionalData($chargeAttempt, self::PSEUDO_CODE_KEY);
|
||||
$dataParamValue['pseudoCardNumber'] = $pseudoCardNumber ? $pseudoCardNumber[0] : '';
|
||||
|
||||
$payId = $this->getChargeAttemptAdditionalData($chargeAttempt, self::PAY_ID_KEY);
|
||||
$dataParamValue['payId'] = $payId ? $payId[0] : '';
|
||||
|
||||
$dataParamValue['customerName'] = isset($transactionMetaData[TransactionPayload::VRPAYMENT_METADATA_CUSTOMER_NAME])
|
||||
? $transactionMetaData[TransactionPayload::VRPAYMENT_METADATA_CUSTOMER_NAME]
|
||||
: '';
|
||||
|
||||
$creditCardValidity = $this->getChargeAttemptAdditionalData($chargeAttempt, self::CARD_VALIDITY_KEY);
|
||||
|
||||
if (isset($creditCardValidity['cardExpireMonth']) && isset($creditCardValidity['cardExpireYear'])) {
|
||||
$creditCardExpireMonth = $creditCardValidity['cardExpireMonth'] ?? null;
|
||||
if (!empty($creditCardExpireMonth)) {
|
||||
$dataParamValue['cardExpireMonth'] = sprintf("%02d", $creditCardExpireMonth);
|
||||
}
|
||||
$creditCardExpireYear = $creditCardValidity['cardExpireYear'] ?? null;
|
||||
if (!empty($creditCardExpireYear)) {
|
||||
$dataParamValue['cardExpireYear'] = $creditCardExpireYear;
|
||||
}
|
||||
}
|
||||
|
||||
$erpMerchantId = $this->getChargeAttemptAdditionalData($chargeAttempt, self::ADDITIONAL_TRANSACTION_DETAILS_ORDER_ID_KEY);
|
||||
$erpMerchantId = $erpMerchantId ? $erpMerchantId[0] : null;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'id' => $orderId,
|
||||
'erpMerchantId' => $erpMerchantId,
|
||||
'data' => $dataParamValue,
|
||||
'paymentMethodId' => $paymentMethodId,
|
||||
'orderId' => $orderId,
|
||||
'orderTransactionId' => $orderTransactionId,
|
||||
'spaceId' => $transaction->getLinkedSpaceId(),
|
||||
'state' => $transaction->getState(),
|
||||
'salesChannelId' => $salesChannelId,
|
||||
'transactionId' => $transaction->getId(),
|
||||
];
|
||||
|
||||
$data = array_filter($data);
|
||||
$this->container->get(TransactionEntityDefinition::ENTITY_NAME . '.repository')->upsert([$data], $context);
|
||||
|
||||
} catch (\Exception $exception) {
|
||||
$this->logger->critical(__CLASS__ . ' : ' . __FUNCTION__ . ' : ' . $exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hold delivery
|
||||
*
|
||||
* @param string $orderId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*/
|
||||
private function holdDelivery(string $orderId, Context $context)
|
||||
{
|
||||
try {
|
||||
/**
|
||||
* @var OrderDeliveryStateHandler $orderDeliveryStateHandler
|
||||
*/
|
||||
$orderEntity = $this->getOrderEntity($orderId, $context);
|
||||
$orderDeliveryStateHandler = $this->container->get(OrderDeliveryStateHandler::class);
|
||||
if (null !== $orderEntity->getDeliveries()->last()) {
|
||||
$orderDeliveryStateHandler->hold($orderEntity->getDeliveries()->last()->getId(), $context);
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
$this->logger->critical($exception->getTraceAsString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get order
|
||||
*
|
||||
* @param String $orderId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return \Shopware\Core\Checkout\Order\OrderEntity
|
||||
*/
|
||||
protected function getOrderEntity(string $orderId, Context $context): OrderEntity
|
||||
{
|
||||
try {
|
||||
$criteria = (new Criteria([$orderId]))->addAssociations(['deliveries']);
|
||||
$order = $this->container->get('order.repository')->search(
|
||||
$criteria,
|
||||
$context
|
||||
)->first();
|
||||
if (is_null($order)) {
|
||||
throw CartException::orderNotFound($orderId);
|
||||
}
|
||||
return $order;
|
||||
} catch (\Exception $e) {
|
||||
throw CartException::orderNotFound($orderId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transaction entity by orderId
|
||||
*
|
||||
* @param string $orderId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return \VRPaymentPayment\Core\Api\Transaction\Entity\TransactionEntity
|
||||
*/
|
||||
public function getByOrderId(string $orderId, Context $context): TransactionEntity
|
||||
{
|
||||
return $this->container->get(TransactionEntityDefinition::ENTITY_NAME . '.repository')
|
||||
->search(new Criteria([$orderId]), $context)
|
||||
->get($orderId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read transaction from VRPayment API
|
||||
*
|
||||
* @param int $transactionId
|
||||
* @param string $salesChannelId
|
||||
*
|
||||
* @return \VRPayment\Sdk\Model\Transaction
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*/
|
||||
public function read(int $transactionId, string $salesChannelId): Transaction
|
||||
{
|
||||
$settings = $this->settingsService->getSettings($salesChannelId);
|
||||
return $settings->getApiClient()->getTransactionService()->read($settings->getSpaceId(), $transactionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transaction entity by VRPayment transaction id
|
||||
*
|
||||
* @param int $transactionId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return \VRPaymentPayment\Core\Api\Transaction\Entity\TransactionEntity|null
|
||||
*/
|
||||
public function getByTransactionId(int $transactionId, Context $context): ?TransactionEntity
|
||||
{
|
||||
return $this->container->get(TransactionEntityDefinition::ENTITY_NAME . '.repository')
|
||||
->search(
|
||||
(new Criteria())->addFilter(new EqualsFilter('transactionId', $transactionId))
|
||||
->addAssociations(['refunds']), $context
|
||||
)
|
||||
->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transaction entity by VRPayment order transaction id
|
||||
*
|
||||
* @param string $transactionId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return \VRPaymentPayment\Core\Api\Transaction\Entity\TransactionEntity|null
|
||||
*/
|
||||
public function getByOrderTransactionId(string $orderTransactionId, Context $context): ?TransactionEntity
|
||||
{
|
||||
return $this->container->get(TransactionEntityDefinition::ENTITY_NAME . '.repository')
|
||||
->search(
|
||||
(new Criteria())->addFilter(new EqualsFilter('orderTransactionId', $orderTransactionId))
|
||||
->addAssociations(['refunds']), $context
|
||||
)
|
||||
->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transaction entity by VRPayment transaction id
|
||||
*
|
||||
* @param int $transactionId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return \VRPaymentPayment\Core\Api\Refund\Entity\RefundEntityCollection
|
||||
*/
|
||||
public function getRefundEntityCollectionByTransactionId(int $transactionId, Context $context): ?RefundEntityCollection
|
||||
{
|
||||
return $this->container->get(RefundEntityDefinition::ENTITY_NAME . '.repository')
|
||||
->search(
|
||||
(new Criteria())->addFilter(new EqualsFilter('transactionId', $transactionId)), $context
|
||||
)
|
||||
->getEntities();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $orderId
|
||||
* @param float $invoicePaidAmount
|
||||
* @param Context $context
|
||||
* @return void
|
||||
*/
|
||||
public function updateOrderTotalPriceByInvoiceTotal(string $orderId, float $invoicePaidAmount, Context $context): void
|
||||
{
|
||||
$price = $this->getOrderEntity($orderId, $context)->getPrice();
|
||||
|
||||
if ($price->getTotalPrice() === $invoicePaidAmount) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'id' => $orderId,
|
||||
'price' => [
|
||||
'netPrice' => $price->getNetPrice(),
|
||||
'rawTotal' => $price->getRawTotal(),
|
||||
'taxRules' => $price->getTaxRules(),
|
||||
'taxStatus' => $price->getTaxStatus(),
|
||||
'totalPrice' => $invoicePaidAmount,
|
||||
'positionPrice' => $price->getPositionPrice(),
|
||||
'calculatedTaxes' => $price->getCalculatedTaxes()
|
||||
],
|
||||
];
|
||||
|
||||
$this->container->get('order.repository')->update([$data], $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SalesChannelContext $salesChannelContext
|
||||
* @param CheckoutConfirmPageLoadedEvent|null $event
|
||||
* @return int
|
||||
*/
|
||||
public function createPendingTransaction(SalesChannelContext $salesChannelContext, ?CheckoutConfirmPageLoadedEvent $event = null): int
|
||||
{
|
||||
$expiredTransaction = true;
|
||||
$transactionId = $_SESSION['transactionId'] ?? null;
|
||||
$settings = $this->settingsService->getValidSettings($salesChannelContext->getSalesChannel()->getId());
|
||||
|
||||
if ($transactionId) {
|
||||
$transactionService = $settings->getApiClient()->getTransactionService();
|
||||
$pendingTransaction = $transactionService->read($settings->getSpaceId(), $transactionId);
|
||||
$failedStates = [
|
||||
TransactionState::DECLINE,
|
||||
TransactionState::FAILED,
|
||||
TransactionState::VOIDED,
|
||||
];
|
||||
if (!in_array($pendingTransaction->getState(), $failedStates)) {
|
||||
$expiredTransaction = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$transactionId || $expiredTransaction) {
|
||||
$settings = $this->settingsService->getValidSettings($salesChannelContext->getSalesChannel()->getId());
|
||||
|
||||
$customer = $salesChannelContext->getCustomer();
|
||||
$customerBillingAddress = $customer->getActiveBillingAddress();
|
||||
|
||||
$billingAddress = new AddressCreate();
|
||||
|
||||
$customerAddressEntity = $customer->getActiveBillingAddress();
|
||||
|
||||
$familyName = "";
|
||||
if (!empty($customerAddressEntity->getLastName())) {
|
||||
$familyName = $customerAddressEntity->getLastName();
|
||||
} else {
|
||||
if (!empty($customer->getLastName())) {
|
||||
$familyName = $customer->getLastName();
|
||||
}
|
||||
}
|
||||
$billingAddress->setFamilyName($familyName);
|
||||
|
||||
$givenName = "";
|
||||
if (!empty($customerAddressEntity->getFirstName())) {
|
||||
$givenName = $customerAddressEntity->getFirstName();
|
||||
} else {
|
||||
if (!empty($customer->getFirstName())) {
|
||||
$givenName = $customer->getFirstName();
|
||||
}
|
||||
}
|
||||
$billingAddress->setGivenName($givenName);
|
||||
$billingAddress->setOrganizationName($customerBillingAddress->getCompany());
|
||||
$billingAddress->setPhoneNumber($customerAddressEntity->getPhoneNumber());
|
||||
$billingAddress->setCountry($customerBillingAddress->getCountry()->getIso());
|
||||
$postalState = $customerBillingAddress?->getCountryState()?->getName() ?? '';
|
||||
if (empty($postalState)) {
|
||||
$postalState = $customerBillingAddress?->getCountryState()?->getShortCode() ?? '';
|
||||
}
|
||||
$billingAddress->setPostalState($postalState);
|
||||
$billingAddress->setPostCode($customerBillingAddress->getZipcode());
|
||||
$billingAddress->setStreet($customerBillingAddress->getStreet());
|
||||
$billingAddress->setEmailAddress($customer->getEmail());
|
||||
|
||||
|
||||
if (!empty($customer->getBirthday())) {
|
||||
$birthday = new \DateTime();
|
||||
$birthday->setTimestamp($customer->getBirthday()->getTimestamp());
|
||||
$birthday = $birthday->format('Y-m-d');
|
||||
$billingAddress->setDateOfBirth($birthday);
|
||||
}
|
||||
|
||||
$salutation = "";
|
||||
if (!(
|
||||
empty($customerAddressEntity->getSalutation()) ||
|
||||
empty($customerAddressEntity->getSalutation()->getDisplayName())
|
||||
)) {
|
||||
$salutation = $customerAddressEntity->getSalutation()->getDisplayName();
|
||||
} else {
|
||||
if (!empty($customer->getSalutation())) {
|
||||
$salutation = $customer->getSalutation()->getDisplayName();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$billingAddress->setGender(strtolower($customerAddressEntity->getSalutation()->getSalutationKey()) === 'mr' ? Gender::MALE : Gender::FEMALE);
|
||||
$billingAddress->setSalutation($salutation);
|
||||
|
||||
$lineItems = [];
|
||||
if ($event) {
|
||||
$cartLineItems = $event->getPage()->getCart()->getLineItems()->getElements();
|
||||
foreach ($cartLineItems as $cartLineItem) {
|
||||
if ($cartLineItem->getType() === CustomProductsLineItemTypes::LINE_ITEM_TYPE_CUSTOMIZED_PRODUCTS) {
|
||||
continue;
|
||||
}
|
||||
$lineItems[] = $this->createTempLineItem($cartLineItem);
|
||||
}
|
||||
}
|
||||
|
||||
$customerId = "";
|
||||
if ($customer->getGuest() === false) {
|
||||
$customerId = $customer->getCustomerNumber();
|
||||
}
|
||||
|
||||
$protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https://' : 'http://';
|
||||
$homeUrl = $protocol . $_SERVER['HTTP_HOST'];
|
||||
$currency = $salesChannelContext->getCurrency()->getIsoCode();
|
||||
$transactionPayload = (new TransactionCreate())
|
||||
->setBillingAddress($billingAddress)
|
||||
->setLineItems($lineItems)
|
||||
->setCurrency($currency)
|
||||
->setSpaceViewId($settings->getSpaceViewId())
|
||||
->setAutoConfirmationEnabled(false)
|
||||
->setChargeRetryEnabled(false)
|
||||
->setCustomerEmailAddress($customer->getEmail())
|
||||
->setCustomerId($customerId)
|
||||
->setSuccessUrl($homeUrl . '?success')
|
||||
->setFailedUrl($homeUrl . '?fail');
|
||||
|
||||
$transactionService = $settings->getApiClient()->getTransactionService();
|
||||
$transaction = $transactionService->create($settings->getSpaceId(), $transactionPayload);
|
||||
$transactionId = $transaction->getId();
|
||||
$_SESSION['transactionId'] = $transactionId;
|
||||
}
|
||||
|
||||
return $transactionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SalesChannelContext $salesChannelContext
|
||||
* @param int $transactionId
|
||||
* @return void
|
||||
*/
|
||||
public function updateTempTransaction(SalesChannelContext $salesChannelContext, int $transactionId): void
|
||||
{
|
||||
$pendingTransaction = new TransactionPending();
|
||||
$pendingTransaction->setId($transactionId);
|
||||
|
||||
$settings = $this->settingsService->getValidSettings($salesChannelContext->getSalesChannel()->getId());
|
||||
$transaction = $settings->getApiClient()->getTransactionService()->read($settings->getSpaceId(), $transactionId);
|
||||
$pendingTransaction->setVersion($transaction->getVersion());
|
||||
|
||||
$customerBillingAddress = $salesChannelContext->getCustomer()->getActiveBillingAddress();
|
||||
|
||||
$billingAddress = new AddressCreate();
|
||||
$billingAddress->setStreet($customerBillingAddress->getStreet());
|
||||
$billingAddress->setCity($customerBillingAddress->getCity());
|
||||
$billingAddress->setCountry($customerBillingAddress->getCountry()->getIso());
|
||||
$billingAddress->setPostCode($customerBillingAddress->getZipcode());
|
||||
|
||||
$postalState = $customerBillingAddress?->getCountryState()?->getName() ?? '';
|
||||
if (empty($postalState)) {
|
||||
$postalState = $customerBillingAddress?->getCountryState()?->getShortCode() ?? '';
|
||||
}
|
||||
|
||||
$billingAddress->setPostalState($postalState);
|
||||
$billingAddress->setOrganizationName($customerBillingAddress->getCompany());
|
||||
|
||||
$currency = $salesChannelContext->getCurrency()->getIsoCode();
|
||||
$pendingTransaction->setCurrency($currency);
|
||||
$pendingTransaction->setBillingAddress($billingAddress);
|
||||
|
||||
$settings->getApiClient()->getTransactionService()
|
||||
->update($settings->getSpaceId(), $pendingTransaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ChargeAttempt|null $chargeAttempt
|
||||
* @param string $descriptorKey
|
||||
* @return array
|
||||
*/
|
||||
private function getChargeAttemptAdditionalData(?ChargeAttempt $chargeAttempt, string $descriptorKey): array
|
||||
{
|
||||
if (!$chargeAttempt) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$labels = $chargeAttempt->getLabels() ?? [];
|
||||
|
||||
if (empty($labels)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ($labels as $label) {
|
||||
$descriptor = $label->getDescriptor();
|
||||
if ((string)$descriptor->getId() !== $descriptorKey) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ($descriptorKey) {
|
||||
case self::CARD_HOLDER_KEY:
|
||||
return [$label->getContentAsString()];
|
||||
|
||||
case self::PSEUDO_CODE_KEY:
|
||||
return [$label->getContentAsString()];
|
||||
|
||||
case self::PAY_ID_KEY:
|
||||
return [$label->getContentAsString()];
|
||||
|
||||
case self::ADDITIONAL_TRANSACTION_DETAILS_ORDER_ID_KEY:
|
||||
return [$label->getContentAsString()];
|
||||
|
||||
case self::CARD_VALIDITY_KEY:
|
||||
$validityYear = '';
|
||||
$validityMonth = '';
|
||||
foreach ($label->getContent() as $cardValidityItem) {
|
||||
if (strlen((string)$cardValidityItem) === 1 || strlen((string)$cardValidityItem) === 2) {
|
||||
$validityMonth = $cardValidityItem;
|
||||
} elseif (strlen((string)$cardValidityItem) === 4) {
|
||||
$validityYear = $cardValidityItem;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($validityYear) || empty($validityMonth)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
'cardExpireMonth' => $validityMonth,
|
||||
'cardExpireYear' => $validityYear,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $salesChannelId
|
||||
* @param int $transactionId
|
||||
* @return ChargeAttempt|null
|
||||
*/
|
||||
private function getChargeAttempt(string $salesChannelId, int $transactionId): ?ChargeAttempt
|
||||
{
|
||||
/** @noinspection PhpParamsInspection */
|
||||
$entityQueryFilter = (new EntityQueryFilter())
|
||||
->setType(EntityQueryFilterType::LEAF)
|
||||
->setOperator(CriteriaOperator::EQUALS)
|
||||
->setFieldName('charge.transaction')
|
||||
->setValue($transactionId);
|
||||
|
||||
$query = (new EntityQuery())->setFilter($entityQueryFilter);
|
||||
|
||||
$settings = $this->settingsService->getSettings($salesChannelId);
|
||||
|
||||
$chargeAttempts = $settings->getApiClient()->getChargeAttemptService()->search($settings->getSpaceId(), $query);
|
||||
|
||||
return $chargeAttempts ? $chargeAttempts[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LineItem $productData
|
||||
* @return LineItemCreate
|
||||
*/
|
||||
private function createTempLineItem(LineItem $productData): LineItemCreate
|
||||
{
|
||||
$lineItem = new LineItemCreate();
|
||||
$lineItem->setName($productData->getLabel());
|
||||
$lineItem->setUniqueId($productData->getId());
|
||||
$lineItem->setSku($productData->getId());
|
||||
$lineItem->setQuantity($productData->getQuantity());
|
||||
$lineItem->setAmountIncludingTax($productData->getPrice()->getUnitPrice());
|
||||
$lineItem->setType(LineItemType::PRODUCT);
|
||||
|
||||
return $lineItem;
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\WebHooks\Command;
|
||||
|
||||
use Symfony\Component\{
|
||||
Console\Command\Command,
|
||||
Console\Attribute\AsCommand,
|
||||
Console\Input\InputInterface,
|
||||
Console\Output\OutputInterface};
|
||||
use VRPaymentPayment\Core\Api\WebHooks\Service\WebHooksService;
|
||||
|
||||
/**
|
||||
* Class WebHooksCommand
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\WebHooks\Command
|
||||
*/
|
||||
#[AsCommand(name: 'vrpayment:webhooks:install')]
|
||||
class WebHooksCommand extends Command {
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Api\WebHooks\Service\WebHooksService
|
||||
*/
|
||||
protected $webHooksService;
|
||||
|
||||
/**
|
||||
* WebHooksCommand constructor.
|
||||
*
|
||||
* @param \VRPaymentPayment\Core\Api\WebHooks\Service\WebHooksService $webHooksService
|
||||
*/
|
||||
public function __construct(WebHooksService $webHooksService)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->webHooksService = $webHooksService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
*
|
||||
* @return int
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$output->writeln('Install VRPaymentPayment webhooks...');
|
||||
$this->webHooksService->install();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the current command.
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDescription('Install VRPaymentPayment webhooks.')
|
||||
->setHelp('This command installs VRPaymentPayment webhooks.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,727 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\WebHooks\Controller;
|
||||
|
||||
use Doctrine\DBAL\{
|
||||
Connection,
|
||||
TransactionIsolationLevel};
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shopware\Core\{
|
||||
Checkout\Cart\CartException,
|
||||
Checkout\Order\Aggregate\OrderDelivery\OrderDeliveryEntity,
|
||||
Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity,
|
||||
Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler,
|
||||
Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStates,
|
||||
Checkout\Order\OrderEntity,
|
||||
Checkout\Order\SalesChannel\OrderService,
|
||||
Framework\Context,
|
||||
Framework\DataAbstractionLayer\Search\Criteria,
|
||||
Framework\DataAbstractionLayer\Search\Sorting\FieldSorting,
|
||||
Framework\Routing\Annotation\RouteScope,
|
||||
System\StateMachine\Aggregation\StateMachineTransition\StateMachineTransitionActions,
|
||||
System\StateMachine\Exception\IllegalTransitionException};
|
||||
use Shopware\Core\Checkout\Order\OrderStates;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Shopware\Core\Framework\Log\Package;
|
||||
use Symfony\Component\{
|
||||
HttpFoundation\JsonResponse,
|
||||
HttpFoundation\ParameterBag,
|
||||
HttpFoundation\Request,
|
||||
HttpFoundation\Response,
|
||||
Routing\Attribute\Route};
|
||||
use VRPayment\Sdk\{
|
||||
Model\RefundState,
|
||||
Model\Transaction,
|
||||
Model\TransactionInvoiceState,
|
||||
Model\TransactionState,
|
||||
Model\TransactionInvoice,};
|
||||
use VRPaymentPayment\Core\{Api\OrderDeliveryState\Handler\OrderDeliveryStateHandler,
|
||||
Api\PaymentMethodConfiguration\Service\PaymentMethodConfigurationService,
|
||||
Api\Refund\Service\RefundService,
|
||||
Api\Transaction\Service\OrderMailService,
|
||||
Api\Transaction\Service\TransactionService,
|
||||
Api\WebHooks\Strategy\WebHookPaymentMethodConfigurationStrategy,
|
||||
Api\WebHooks\Strategy\WebHookRefundStrategy,
|
||||
Api\WebHooks\Strategy\WebHookStrategyManager,
|
||||
Api\WebHooks\Strategy\WebHookTransactionInvoiceStrategy,
|
||||
Api\WebHooks\Strategy\WebHookTransactionStrategy,
|
||||
Api\WebHooks\Struct\WebHookRequest,
|
||||
Settings\Service\SettingsService,
|
||||
Util\Payload\TransactionPayload};
|
||||
|
||||
/**
|
||||
* Class WebHookController
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\WebHooks\Controller
|
||||
*
|
||||
*/
|
||||
#[Package('sales-channel')]
|
||||
#[Route(defaults: ['_routeScope' => ['api']])]
|
||||
class WebHookController extends AbstractController {
|
||||
|
||||
/**
|
||||
* @var \Doctrine\DBAL\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Api\Transaction\Service\OrderMailService
|
||||
*/
|
||||
protected $orderMailService;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler
|
||||
*/
|
||||
protected $orderTransactionStateHandler;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Service\PaymentMethodConfigurationService
|
||||
*/
|
||||
protected $paymentMethodConfigurationService;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Settings\Struct\Settings
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Settings\Service\SettingsService
|
||||
*/
|
||||
protected $settingsService;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Api\Refund\Service\RefundService
|
||||
*/
|
||||
protected $refundService;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService
|
||||
*/
|
||||
protected $transactionService;
|
||||
|
||||
/**
|
||||
* Transaction Final States
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $transactionFinalStates = [
|
||||
OrderTransactionStates::STATE_CANCELLED,
|
||||
OrderTransactionStates::STATE_PAID,
|
||||
OrderTransactionStates::STATE_REFUNDED,
|
||||
];
|
||||
/**
|
||||
* Transaction Failed States
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $transactionFailedStates = [
|
||||
TransactionState::DECLINE,
|
||||
TransactionState::FAILED,
|
||||
TransactionState::VOIDED,
|
||||
];
|
||||
|
||||
protected $vrpaymentTransactionSuccessStates = [
|
||||
TransactionState::AUTHORIZED,
|
||||
TransactionState::COMPLETED,
|
||||
TransactionState::FULFILL,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Checkout\Order\OrderEntity
|
||||
*/
|
||||
private $orderEntity;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Checkout\Order\SalesChannel\OrderService
|
||||
*/
|
||||
private $orderService;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Api\WebHooks\Strategy\WebHookStrategyManager
|
||||
*/
|
||||
private $webHookStrategyManager;
|
||||
|
||||
const LINE_ITEM_TYPE_FEE = 'FEE';
|
||||
|
||||
/**
|
||||
* WebHookController constructor.
|
||||
*
|
||||
* @param \Doctrine\DBAL\Connection $connection
|
||||
* @param \Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler $orderTransactionStateHandler
|
||||
* @param \Shopware\Core\Checkout\Order\SalesChannel\OrderService $orderService
|
||||
* @param \VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Service\PaymentMethodConfigurationService $paymentMethodConfigurationService
|
||||
* @param \VRPaymentPayment\Core\Api\Refund\Service\RefundService $refundService
|
||||
* @param \VRPaymentPayment\Core\Api\Transaction\Service\OrderMailService $orderMailService
|
||||
* @param \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService $transactionService
|
||||
* @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService
|
||||
* @param \VRPaymentPayment\Core\Api\WebHooks\Strategy\WebHookStrategyManager $settingsService
|
||||
*/
|
||||
public function __construct(
|
||||
Connection $connection,
|
||||
OrderTransactionStateHandler $orderTransactionStateHandler,
|
||||
OrderService $orderService,
|
||||
PaymentMethodConfigurationService $paymentMethodConfigurationService,
|
||||
RefundService $refundService,
|
||||
OrderMailService $orderMailService,
|
||||
TransactionService $transactionService,
|
||||
SettingsService $settingsService,
|
||||
WebHookStrategyManager $webHookStrategyManager
|
||||
)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
$this->orderTransactionStateHandler = $orderTransactionStateHandler;
|
||||
$this->paymentMethodConfigurationService = $paymentMethodConfigurationService;
|
||||
$this->refundService = $refundService;
|
||||
$this->orderMailService = $orderMailService;
|
||||
$this->transactionService = $transactionService;
|
||||
$this->settingsService = $settingsService;
|
||||
$this->orderService = $orderService;
|
||||
$this->webHookStrategyManager = $webHookStrategyManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
*
|
||||
* @internal
|
||||
* @required
|
||||
*
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger): void
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the method VRPayment calls
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @param string $salesChannelId
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse|\Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
#[Route(
|
||||
path: "/api/_action/vrpayment/webHook/callback/{salesChannelId}",
|
||||
name: "api.action.vrpayment.webhook.update",
|
||||
options: ["seo" => false],
|
||||
defaults: [
|
||||
"csrf_protected" => false,
|
||||
"XmlHttpRequest" => true,
|
||||
"auth_required" => false,
|
||||
],
|
||||
methods: ["POST"],
|
||||
)]
|
||||
public function callback(Request $request, Context $context, string $salesChannelId): Response
|
||||
{
|
||||
$status = Response::HTTP_UNPROCESSABLE_ENTITY;
|
||||
$callBackData = new WebHookRequest();
|
||||
try {
|
||||
// Configuration
|
||||
$salesChannelId = $salesChannelId == 'null' ? null : $salesChannelId;
|
||||
$this->settings = $this->settingsService->getSettings($salesChannelId);
|
||||
$signature = $request->server->get('HTTP_X_SIGNATURE');
|
||||
$requestJson = json_decode($request->getContent(), true);
|
||||
$apiClient = $this->settings->getApiClient();
|
||||
$callBackData->assign($requestJson);
|
||||
|
||||
// Handling of payloads without a signature (legacy method).
|
||||
// Deprecated since 3.0.12
|
||||
if (empty($signature)) {
|
||||
switch ($callBackData->getListenerEntityTechnicalName()) {
|
||||
case WebHookRequest::PAYMENT_METHOD_CONFIGURATION:
|
||||
return $this->updatePaymentMethodConfiguration($context, $salesChannelId);
|
||||
case WebHookRequest::REFUND:
|
||||
return $this->updateRefund($callBackData, $context);
|
||||
case WebHookRequest::TRANSACTION:
|
||||
return $this->updateTransaction($callBackData, $context);
|
||||
case WebHookRequest::TRANSACTION_INVOICE:
|
||||
return $this->updateTransactionInvoice($callBackData, $context);
|
||||
default:
|
||||
$this->logger->warning(__CLASS__ . ' : ' . __FUNCTION__ . ' : Listener not implemented : ', $callBackData->jsonSerialize());
|
||||
}
|
||||
}
|
||||
|
||||
// Handling of payloads with a valid signature.
|
||||
// This payload signed has the transaction state
|
||||
if (!empty($signature) && $apiClient->getWebhookEncryptionService()->isContentValid($signature, $request->getContent())) {
|
||||
return $this->webHookStrategyManager->process($callBackData, $context, $salesChannelId);
|
||||
}
|
||||
|
||||
$status = Response::HTTP_OK;
|
||||
} catch (\Exception $exception) {
|
||||
$this->logger->critical(__CLASS__ . ' : ' . __FUNCTION__ . ' : ' . $exception->getMessage(), $callBackData->jsonSerialize());
|
||||
}
|
||||
return new JsonResponse(['data' => $callBackData], $status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle VRPayment Payment Method Configuration callback
|
||||
*
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @param string $salesChannelId
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
* @deprecated 6.1.8 No longer used by internal code and not recommended.
|
||||
* @see WebHookPaymentMethodConfigurationStrategy
|
||||
*/
|
||||
private function updatePaymentMethodConfiguration(Context $context, string $salesChannelId = null): Response
|
||||
{
|
||||
$result = $this->paymentMethodConfigurationService->setSalesChannelId($salesChannelId)->synchronize($context);
|
||||
|
||||
return new JsonResponse(['result' => $result]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle VRPayment Refund callback
|
||||
*
|
||||
* @param \VRPaymentPayment\Core\Api\WebHooks\Struct\WebHookRequest $callBackData
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @deprecated 6.1.8 No longer used by internal code and not recommended.
|
||||
* @see WebHookRefundStrategy
|
||||
*/
|
||||
public function updateRefund(WebHookRequest $callBackData, Context $context): Response
|
||||
{
|
||||
$status = Response::HTTP_UNPROCESSABLE_ENTITY;
|
||||
|
||||
try {
|
||||
/**
|
||||
* @var \VRPayment\Sdk\Model\Transaction $transaction
|
||||
*/
|
||||
$refund = $this->settings->getApiClient()->getRefundService()
|
||||
->read($callBackData->getSpaceId(), $callBackData->getEntityId());
|
||||
$orderId = $refund->getTransaction()->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_ID];
|
||||
|
||||
if(!empty($orderId)) {
|
||||
|
||||
$this->executeLocked($orderId, $context, function () use ($orderId, $refund, $context) {
|
||||
|
||||
$this->refundService->upsert($refund, $context);
|
||||
|
||||
$orderTransactionId = $refund->getTransaction()->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_TRANSACTION_ID];
|
||||
$orderTransaction = $this->getOrderTransaction($orderId, $context);
|
||||
if (
|
||||
in_array(
|
||||
$orderTransaction->getStateMachineState()?->getTechnicalName(),
|
||||
[
|
||||
OrderTransactionStates::STATE_PAID,
|
||||
OrderTransactionStates::STATE_PARTIALLY_PAID,
|
||||
]
|
||||
) &&
|
||||
($refund->getState() == RefundState::SUCCESSFUL)
|
||||
) {
|
||||
if ($refund->getAmount() == $orderTransaction->getAmount()->getTotalPrice()) {
|
||||
$this->orderTransactionStateHandler->refund($orderTransactionId, $context);
|
||||
} else {
|
||||
if ($refund->getAmount() < $orderTransaction->getAmount()->getTotalPrice()) {
|
||||
$this->orderTransactionStateHandler->refundPartially($orderTransactionId, $context);
|
||||
}
|
||||
}
|
||||
} elseif ($orderTransaction->getStateMachineState()?->getTechnicalName()
|
||||
=== OrderTransactionStates::STATE_PARTIALLY_REFUNDED &&
|
||||
($refund->getState() == RefundState::SUCCESSFUL)
|
||||
) {
|
||||
$transactionByOrderTransactionId = $this->transactionService->getByOrderTransactionId($orderTransactionId, $context);
|
||||
$totalRefundedAmount = $this->getTotalRefundedAmount($transactionByOrderTransactionId->getTransactionId(), $context);
|
||||
if (floatval($orderTransaction->getAmount()->getTotalPrice()) - $totalRefundedAmount <= 0) {
|
||||
$this->orderTransactionStateHandler->refund($orderTransactionId, $context);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
$status = Response::HTTP_OK;
|
||||
} catch (CartException $exception) {
|
||||
$status = Response::HTTP_OK;
|
||||
$this->logger->info(__CLASS__ . ' : ' . __FUNCTION__ . ' : ' . $exception->getMessage(), $callBackData->jsonSerialize());
|
||||
} catch (IllegalTransitionException $exception) {
|
||||
$status = Response::HTTP_OK;
|
||||
$this->logger->info(__CLASS__ . ' : ' . __FUNCTION__ . ' : ' . $exception->getMessage(), $callBackData->jsonSerialize());
|
||||
} catch (\Exception $exception) {
|
||||
$this->logger->critical(__CLASS__ . ' : ' . __FUNCTION__ . ' : ' . $exception->getMessage(), $callBackData->jsonSerialize());
|
||||
}
|
||||
|
||||
return new JsonResponse(['data' => $callBackData->jsonSerialize()], $status);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $transactionId
|
||||
* @param Context $context
|
||||
* @return float
|
||||
*/
|
||||
private function getTotalRefundedAmount(int $transactionId, Context $context): float
|
||||
{
|
||||
$amount = 0;
|
||||
$refunds = $this->transactionService->getRefundEntityCollectionByTransactionId($transactionId, $context);
|
||||
foreach ($refunds as $refund) {
|
||||
$amount += floatval($refund->getData()['amount'] ?? 0);
|
||||
}
|
||||
|
||||
return (float) (string) $amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $orderId
|
||||
* @param Context $context
|
||||
* @param callable $operation
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function executeLocked(string $orderId, Context $context, callable $operation)
|
||||
{
|
||||
//$this->connection->setTransactionIsolation(TransactionIsolationLevel::READ_COMMITTED);
|
||||
//$this->connection->beginTransaction();
|
||||
try {
|
||||
|
||||
$data = [
|
||||
'id' => $orderId,
|
||||
'vrpayment_lock' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
|
||||
$order = $this->container->get('order.repository')->search(new Criteria([$orderId]), $context)->first();
|
||||
|
||||
if(empty($order)){
|
||||
throw CartException::orderNotFound($orderId);
|
||||
}
|
||||
|
||||
$this->container->get('order.repository')->upsert([$data], $context);
|
||||
|
||||
$result = $operation();
|
||||
|
||||
//$this->connection->commit();
|
||||
return $result;
|
||||
} catch (\Exception $exception) {
|
||||
//$this->connection->rollBack();
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param String $orderId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return OrderTransactionEntity
|
||||
* @deprecated 6.1.8 No longer used by internal code and not recommended.
|
||||
* @see WebHookTransactionStrategy
|
||||
*/
|
||||
private function getOrderTransaction(String $orderId, Context $context): OrderTransactionEntity
|
||||
{
|
||||
return $this->getOrderEntity($orderId, $context)->getTransactions()->last();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get order
|
||||
*
|
||||
* @param String $orderId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return \Shopware\Core\Checkout\Order\OrderEntity
|
||||
* @deprecated 6.1.8 No longer used by internal code and not recommended.
|
||||
* @see WebHookTransactionStrategy
|
||||
*/
|
||||
private function getOrderEntity(string $orderId, Context $context): OrderEntity
|
||||
{
|
||||
if (is_null($this->orderEntity)) {
|
||||
$criteria = (new Criteria([$orderId]))
|
||||
->addAssociations(['deliveries', 'transactions']);
|
||||
$criteria->getAssociation('transactions')
|
||||
->addSorting(new FieldSorting('createdAt', FieldSorting::ASCENDING));
|
||||
|
||||
try {
|
||||
$this->orderEntity = $this->container->get('order.repository')->search(
|
||||
$criteria,
|
||||
$context
|
||||
)->first();
|
||||
if (is_null($this->orderEntity)) {
|
||||
throw CartException::orderNotFound($orderId);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
throw CartException::orderNotFound($orderId);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->orderEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle VRPayment Transaction callback
|
||||
*
|
||||
* @param \VRPaymentPayment\Core\Api\WebHooks\Struct\WebHookRequest $callBackData
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @deprecated 6.1.8 No longer used by internal code and not recommended.
|
||||
* @see WebHookTransactionStrategy
|
||||
*/
|
||||
private function updateTransaction(WebHookRequest $callBackData, Context $context): Response
|
||||
{
|
||||
$status = Response::HTTP_UNPROCESSABLE_ENTITY;
|
||||
|
||||
try {
|
||||
/**
|
||||
* @var \VRPayment\Sdk\Model\Transaction $transaction
|
||||
* @var \Shopware\Core\Checkout\Order\OrderEntity $order
|
||||
*/
|
||||
$transaction = $this->settings->getApiClient()
|
||||
->getTransactionService()
|
||||
->read($callBackData->getSpaceId(), $callBackData->getEntityId());
|
||||
$orderId = $transaction->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_ID];
|
||||
if(!empty($orderId) && !$transaction->getParent()) {
|
||||
$this->executeLocked($orderId, $context, function () use ($orderId, $transaction, $context, $callBackData) {
|
||||
$this->transactionService->upsert($transaction, $context);
|
||||
$orderTransactionId = $transaction->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_TRANSACTION_ID];
|
||||
$orderTransaction = $this->getOrderTransaction($orderId, $context);
|
||||
$this->logger->info("OrderId: {$orderId} Current state: {$orderTransaction->getStateMachineState()?->getTechnicalName()}");
|
||||
|
||||
if (!in_array(
|
||||
$orderTransaction->getStateMachineState()?->getTechnicalName(),
|
||||
$this->transactionFinalStates
|
||||
)) {
|
||||
switch ($transaction->getState()) {
|
||||
case TransactionState::FAILED:
|
||||
$this->orderTransactionStateHandler->fail($orderTransactionId, $context);
|
||||
$this->unholdAndCancelDelivery($orderId, $context);
|
||||
break;
|
||||
case TransactionState::DECLINE:
|
||||
case TransactionState::VOIDED:
|
||||
$this->orderTransactionStateHandler->cancel($orderTransactionId, $context);
|
||||
$this->unholdAndCancelDelivery($orderId, $context);
|
||||
break;
|
||||
case TransactionState::FULFILL:
|
||||
$this->unholdDelivery($orderId, $context);
|
||||
break;
|
||||
case TransactionState::AUTHORIZED:
|
||||
$this->orderTransactionStateHandler->process($orderTransactionId, $context);
|
||||
$this->sendEmail($transaction, $context);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
$status = Response::HTTP_OK;
|
||||
} catch (CartException $exception) {
|
||||
$status = Response::HTTP_OK;
|
||||
$this->logger->info(__CLASS__ . ' : ' . __FUNCTION__ . ' : ' . $exception->getMessage(), $callBackData->jsonSerialize());
|
||||
} catch (IllegalTransitionException $exception) {
|
||||
$status = Response::HTTP_OK;
|
||||
$this->logger->info(__CLASS__ . ' : ' . __FUNCTION__ . ' : ' . $exception->getMessage(), $callBackData->jsonSerialize());
|
||||
} catch (\Exception $exception) {
|
||||
$this->logger->critical(__CLASS__ . ' : ' . __FUNCTION__ . ' : ' . $exception->getMessage(), $callBackData->jsonSerialize());
|
||||
}
|
||||
|
||||
return new JsonResponse(['data' => $callBackData->jsonSerialize()], $status);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \VRPayment\Sdk\Model\Transaction $transaction
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @deprecated 6.1.8 No longer used by internal code and not recommended.
|
||||
*/
|
||||
protected function sendEmail(Transaction $transaction, Context $context): void
|
||||
{
|
||||
$orderId = $transaction->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_ID];
|
||||
if ($this->settings->isEmailEnabled() && in_array($transaction->getState(), $this->vrpaymentTransactionSuccessStates)) {
|
||||
$this->orderMailService->send($orderId, $context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle VRPayment TransactionInvoice callback
|
||||
*
|
||||
* @param \VRPaymentPayment\Core\Api\WebHooks\Struct\WebHookRequest $callBackData
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @deprecated 6.1.8 No longer used by internal code and not recommended.
|
||||
* @see WebHookTransactionInvoiceStrategy
|
||||
*/
|
||||
public function updateTransactionInvoice(WebHookRequest $callBackData, Context $context): Response
|
||||
{
|
||||
$status = Response::HTTP_UNPROCESSABLE_ENTITY;
|
||||
|
||||
try {
|
||||
/**
|
||||
* @var \VRPayment\Sdk\Model\Transaction $transaction
|
||||
* @var TransactionInvoice $transactionInvoice
|
||||
*/
|
||||
$transactionInvoice = $this->settings->getApiClient()->getTransactionInvoiceService()
|
||||
->read($callBackData->getSpaceId(), $callBackData->getEntityId());
|
||||
$orderId = $transactionInvoice->getCompletion()
|
||||
->getLineItemVersion()
|
||||
->getTransaction()
|
||||
->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_ID];
|
||||
if(!empty($orderId)) {
|
||||
$this->executeLocked($orderId, $context, function () use ($orderId, $transactionInvoice, $context) {
|
||||
|
||||
$orderTransactionId = $transactionInvoice->getCompletion()
|
||||
->getLineItemVersion()
|
||||
->getTransaction()
|
||||
->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_TRANSACTION_ID];
|
||||
$orderTransaction = $this->getOrderTransaction($orderId, $context);
|
||||
$this->updatePriceIfAdditionalItemsExist($transactionInvoice, $orderTransaction, $context);
|
||||
|
||||
if (!in_array(
|
||||
$orderTransaction->getStateMachineState()?->getTechnicalName(),
|
||||
$this->transactionFinalStates
|
||||
)) {
|
||||
switch ($transactionInvoice->getState()) {
|
||||
case TransactionInvoiceState::DERECOGNIZED:
|
||||
$this->orderTransactionStateHandler->cancel($orderTransactionId, $context);
|
||||
break;
|
||||
case TransactionInvoiceState::NOT_APPLICABLE:
|
||||
case TransactionInvoiceState::PAID:
|
||||
$this->orderTransactionStateHandler->paid($orderTransactionId, $context);
|
||||
$this->unholdDelivery($orderTransactionId, $context);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
$status = Response::HTTP_OK;
|
||||
} catch (CartException $exception) {
|
||||
$status = Response::HTTP_OK;
|
||||
$this->logger->info(__CLASS__ . ' : ' . __FUNCTION__ . ' : ' . $exception->getMessage(), $callBackData->jsonSerialize());
|
||||
} catch (IllegalTransitionException $exception) {
|
||||
$status = Response::HTTP_OK;
|
||||
$this->logger->info(__CLASS__ . ' : ' . __FUNCTION__ . ' : ' . $exception->getMessage(), $callBackData->jsonSerialize());
|
||||
} catch (\Exception $exception) {
|
||||
$this->logger->critical(__CLASS__ . ' : ' . __FUNCTION__ . ' : ' . $exception->getMessage(), $callBackData->jsonSerialize());
|
||||
}
|
||||
|
||||
return new JsonResponse(['data' => $callBackData->jsonSerialize()], $status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates order table field price only if there are additional items added from portal side
|
||||
*
|
||||
* @param TransactionInvoice $transactionInvoice
|
||||
* @param OrderTransactionEntity $orderTransaction
|
||||
* @param Context $context
|
||||
* @return void
|
||||
*/
|
||||
private function updatePriceIfAdditionalItemsExist(TransactionInvoice $transactionInvoice, OrderTransactionEntity $orderTransaction, Context $context): void
|
||||
{
|
||||
$completionLineItems = $transactionInvoice->getCompletion()->getLineItems();
|
||||
$lineItems = $transactionInvoice->getLineItems();
|
||||
|
||||
if (count($completionLineItems) !== count($lineItems)) {
|
||||
$this->transactionService->updateOrderTotalPriceByInvoiceTotal(
|
||||
$orderTransaction->getOrderId(),
|
||||
$transactionInvoice->getOutstandingAmount(),
|
||||
$context
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hold delivery
|
||||
*
|
||||
* @param string $orderId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*/
|
||||
private function unholdDelivery(string $orderId, Context $context): void
|
||||
{
|
||||
try {
|
||||
$criteria = new Criteria([$orderId]);
|
||||
$criteria->addAssociation('deliveries.stateMachineState');
|
||||
$order = $this->container->get('order.repository')
|
||||
->search($criteria, $context)
|
||||
->first();
|
||||
|
||||
if (!$order) {
|
||||
$this->logger->info('Order not found: ' . $orderId);
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var OrderDeliveryEntity|null $orderDelivery */
|
||||
$orderDelivery = $order->getDeliveries()?->last();
|
||||
|
||||
if (null === $orderDelivery) {
|
||||
$this->logger->info('No deliveries found for order: ' . $orderId);
|
||||
return;
|
||||
}
|
||||
|
||||
$orderDeliveryState = $orderDelivery->getStateMachineState();
|
||||
if (!$orderDeliveryState) {
|
||||
$this->logger->info('Order delivery state is null for order: ' . $orderId);
|
||||
return;
|
||||
}
|
||||
|
||||
$technicalName = $orderDeliveryState->getTechnicalName();
|
||||
$this->logger->info('Order delivery state: ' . $technicalName);
|
||||
|
||||
if ($technicalName !== OrderDeliveryStateHandler::STATE_HOLD) {
|
||||
$this->logger->info('Order delivery is not on hold, skipping unhold process.');
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var OrderDeliveryStateHandler $orderDeliveryStateHandler */
|
||||
$orderDeliveryStateHandler = $this->container->get(OrderDeliveryStateHandler::class);
|
||||
$orderDeliveryStateHandler->unhold($orderDelivery->getId(), $context);
|
||||
|
||||
$this->logger->info('Successfully unheld order delivery for order: ' . $orderId);
|
||||
} catch (\Exception $exception) {
|
||||
$this->logger->error('Error unholding order delivery: ' . $exception->getMessage(), $exception->getTrace());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unhold and cancel delivery
|
||||
*
|
||||
* @param string $orderId
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*/
|
||||
private function unholdAndCancelDelivery(string $orderId, Context $context): void
|
||||
{
|
||||
$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 {
|
||||
/**
|
||||
* @var OrderDeliveryStateHandler $orderDeliveryStateHandler
|
||||
*/
|
||||
$orderDeliveryStateHandler = $this->container->get(OrderDeliveryStateHandler::class);
|
||||
/**
|
||||
* @var OrderDeliveryEntity $orderDelivery
|
||||
*/
|
||||
$orderDelivery = $order->getDeliveries()?->last();
|
||||
|
||||
if (null === $orderDelivery) {
|
||||
return;
|
||||
}
|
||||
if ($orderDelivery->getStateMachineState()?->getTechnicalName() !== OrderDeliveryStateHandler::STATE_HOLD){
|
||||
return;
|
||||
}
|
||||
$orderDeliveryId = $orderDelivery->getId();
|
||||
$orderDeliveryStateHandler->unhold($orderDeliveryId, $context);
|
||||
$orderDeliveryStateHandler->cancel($orderDeliveryId, $context);
|
||||
} catch (\Exception $exception) {
|
||||
$this->logger->info($exception->getMessage(), $exception->getTrace());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,403 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\WebHooks\Service;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shopware\Core\PlatformRequest;
|
||||
use Symfony\Component\{
|
||||
Routing\Generator\UrlGeneratorInterface,
|
||||
Routing\RouterInterface,};
|
||||
use VRPayment\Sdk\{
|
||||
ApiClient,
|
||||
Model\CreationEntityState,
|
||||
Model\CriteriaOperator,
|
||||
Model\EntityQuery,
|
||||
Model\EntityQueryFilter,
|
||||
Model\EntityQueryFilterType,
|
||||
Model\RefundState,
|
||||
Model\TransactionInvoiceState,
|
||||
Model\TransactionState,
|
||||
Model\WebhookListener,
|
||||
Model\WebhookListenerCreate,
|
||||
Model\WebhookUrl,
|
||||
Model\WebhookUrlCreate,};
|
||||
use VRPaymentPayment\Core\{
|
||||
Api\WebHooks\Struct\Entity,
|
||||
Settings\Service\SettingsService};
|
||||
|
||||
/**
|
||||
* Class WebHooksService
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\WebHooks\Service
|
||||
*/
|
||||
class WebHooksService {
|
||||
|
||||
public const TRANSACTION = 1472041829003;
|
||||
public const TRANSACTION_INVOICE = 1472041816898;
|
||||
public const REFUND = 1472041839405;
|
||||
public const PAYMENT_METHOD_CONFIGURATION = 1472041857405;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Settings\Service\SettingsService
|
||||
*/
|
||||
protected $settingsService;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\Routing\RouterInterface
|
||||
*/
|
||||
protected $router;
|
||||
|
||||
/**
|
||||
* @var \VRPayment\Sdk\ApiClient
|
||||
*/
|
||||
protected $apiClient;
|
||||
|
||||
/**
|
||||
* Space Id
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $spaceId;
|
||||
|
||||
/**
|
||||
* WebHook configs
|
||||
*/
|
||||
protected $webHookEntitiesConfig = [];
|
||||
|
||||
/**
|
||||
* WebHook configs
|
||||
*/
|
||||
protected $webHookEntityArrayConfig = [
|
||||
/**
|
||||
* Transaction WebHook Entity Id
|
||||
*
|
||||
* @link https://www.vr-payment.de//doc/api/webhook-entity/view/1472041829003
|
||||
*/
|
||||
[
|
||||
'id' => WebHooksService::TRANSACTION,
|
||||
'name' => 'Shopware6::WebHook::Transaction',
|
||||
'states' => [
|
||||
TransactionState::AUTHORIZED,
|
||||
TransactionState::COMPLETED,
|
||||
TransactionState::CONFIRMED,
|
||||
TransactionState::DECLINE,
|
||||
TransactionState::FAILED,
|
||||
TransactionState::FULFILL,
|
||||
TransactionState::PROCESSING,
|
||||
TransactionState::VOIDED,
|
||||
],
|
||||
'notifyEveryChange' => false,
|
||||
],
|
||||
/**
|
||||
* Transaction Invoice WebHook Entity Id
|
||||
*
|
||||
* @link https://www.vr-payment.de//doc/api/webhook-entity/view/1472041816898
|
||||
*/
|
||||
[
|
||||
'id' => WebHooksService::TRANSACTION_INVOICE,
|
||||
'name' => 'Shopware6::WebHook::Transaction Invoice',
|
||||
'states' => [
|
||||
TransactionInvoiceState::NOT_APPLICABLE,
|
||||
TransactionInvoiceState::PAID,
|
||||
TransactionInvoiceState::DERECOGNIZED,
|
||||
],
|
||||
'notifyEveryChange' => false,
|
||||
],
|
||||
/**
|
||||
* Refund WebHook Entity Id
|
||||
*
|
||||
* @link https://www.vr-payment.de//doc/api/webhook-entity/view/1472041839405
|
||||
*/
|
||||
[
|
||||
'id' => WebHooksService::REFUND,
|
||||
'name' => 'Shopware6::WebHook::Refund',
|
||||
'states' => [
|
||||
RefundState::FAILED,
|
||||
RefundState::SUCCESSFUL,
|
||||
],
|
||||
'notifyEveryChange' => false,
|
||||
],
|
||||
/**
|
||||
* Payment Method Configuration Id
|
||||
*
|
||||
* @link https://www.vr-payment.de//doc/api/webhook-entity/view/1472041857405
|
||||
*/
|
||||
[
|
||||
'id' => WebHooksService::PAYMENT_METHOD_CONFIGURATION,
|
||||
'name' => 'Shopware6::WebHook::Payment Method Configuration',
|
||||
'states' => [
|
||||
CreationEntityState::ACTIVE,
|
||||
CreationEntityState::DELETED,
|
||||
CreationEntityState::DELETING,
|
||||
CreationEntityState::INACTIVE
|
||||
],
|
||||
'notifyEveryChange' => true,
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @var ?string $salesChannelId
|
||||
*/
|
||||
private $salesChannelId;
|
||||
|
||||
/**
|
||||
* WebHooksService constructor.
|
||||
*
|
||||
* @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService
|
||||
* @param \Symfony\Component\Routing\RouterInterface $router
|
||||
*/
|
||||
public function __construct(SettingsService $settingsService, RouterInterface $router)
|
||||
{
|
||||
$this->router = $router;
|
||||
$this->settingsService = $settingsService;
|
||||
$this->setWebHookEntitiesConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set webhook configs
|
||||
*/
|
||||
protected function setWebHookEntitiesConfig(): void
|
||||
{
|
||||
foreach ($this->webHookEntityArrayConfig as $item) {
|
||||
$this->webHookEntitiesConfig[] = (new Entity())
|
||||
->setId((int) $item['id'])
|
||||
->setName($item['name'])
|
||||
->setStates($item['states'])
|
||||
->setNotifyEveryChange($item['notifyEveryChange']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
*
|
||||
* @internal
|
||||
* @required
|
||||
*
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger): void
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \VRPayment\Sdk\ApiClient
|
||||
*/
|
||||
public function getApiClient(): ApiClient
|
||||
{
|
||||
return $this->apiClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \VRPayment\Sdk\ApiClient $apiClient
|
||||
*
|
||||
* @return \VRPaymentPayment\Core\Api\WebHooks\Service\WebHooksService
|
||||
*/
|
||||
public function setApiClient(ApiClient $apiClient): WebHooksService
|
||||
{
|
||||
$this->apiClient = $apiClient;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSpaceId(): int
|
||||
{
|
||||
return $this->spaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $spaceId
|
||||
*
|
||||
* @return \VRPaymentPayment\Core\Api\WebHooks\Service\WebHooksService
|
||||
*/
|
||||
public function setSpaceId(int $spaceId): WebHooksService
|
||||
{
|
||||
$this->spaceId = $spaceId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install WebHooks
|
||||
*
|
||||
* @return array
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*/
|
||||
public function install(): array
|
||||
{
|
||||
// Configuration
|
||||
$settings = $this->settingsService->getSettings($this->getSalesChannelId());
|
||||
$this->setSpaceId($settings->getSpaceId())->setApiClient($settings->getApiClient());
|
||||
|
||||
return $this->installListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sales channel id
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getSalesChannelId(): ?string
|
||||
{
|
||||
return $this->salesChannelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set sales channel id
|
||||
*
|
||||
* @param string|null $salesChannelId
|
||||
*
|
||||
* @return \VRPaymentPayment\Core\Api\WebHooks\Service\WebHooksService
|
||||
*/
|
||||
public function setSalesChannelId(?string $salesChannelId = null): WebHooksService
|
||||
{
|
||||
$this->salesChannelId = $salesChannelId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install Listeners
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function installListeners(): array
|
||||
{
|
||||
$this->logger->info('Installing webhooks.');
|
||||
$returnValue = [];
|
||||
try {
|
||||
$webHookUrlId = $this->getOrCreateWebHookUrl()->getId();
|
||||
$installedWebHooks = $this->getInstalledWebHookListeners($webHookUrlId);
|
||||
$webHookEntityIds = array_map(function (WebhookListener $webHook) {
|
||||
return $webHook->getEntity();
|
||||
}, $installedWebHooks);
|
||||
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Api\WebHooks\Struct\Entity $data
|
||||
*/
|
||||
foreach ($this->webHookEntitiesConfig as $data) {
|
||||
|
||||
if (in_array($data->getId(), $webHookEntityIds)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$entity = (new WebhookListenerCreate())
|
||||
->setName($data->getName())
|
||||
->setEntity($data->getId())
|
||||
->setNotifyEveryChange($data->isNotifyEveryChange())
|
||||
->setState(CreationEntityState::CREATE)
|
||||
->setEntityStates($data->getStates())
|
||||
->setEnablePayloadSignatureAndState( true )
|
||||
->setUrl($webHookUrlId);
|
||||
|
||||
$returnValue[] = $this->apiClient->getWebhookListenerService()->create($this->spaceId, $entity);
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
$this->logger->critical($exception->getTraceAsString());
|
||||
return $exception->getTrace();
|
||||
}
|
||||
|
||||
return $returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create WebHook URL
|
||||
*
|
||||
* @return WebhookUrl
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*/
|
||||
protected function getOrCreateWebHookUrl(): WebhookUrl
|
||||
{
|
||||
$url = $this->getWebHookCallBackUrl();
|
||||
/** @noinspection PhpParamsInspection */
|
||||
$entityQueryFilter = (new EntityQueryFilter())
|
||||
->setType(EntityQueryFilterType::_AND)
|
||||
->setChildren([
|
||||
$this->getEntityFilter('state', CreationEntityState::ACTIVE),
|
||||
$this->getEntityFilter('url', $url),
|
||||
]);
|
||||
|
||||
$query = (new EntityQuery())->setFilter($entityQueryFilter)->setNumberOfEntities(1);
|
||||
|
||||
$webHookUrls = $this->apiClient->getWebhookUrlService()->search($this->spaceId, $query);
|
||||
|
||||
if (!empty($webHookUrls[0])) {
|
||||
return $webHookUrls[0];
|
||||
}
|
||||
|
||||
/** @noinspection PhpParamsInspection */
|
||||
$entity = (new WebhookUrlCreate())
|
||||
->setName('Shopware6::WebHookURL')
|
||||
->setUrl($url)
|
||||
->setState(CreationEntityState::ACTIVE);
|
||||
|
||||
return $this->apiClient->getWebhookUrlService()->create($this->spaceId, $entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a new entity filter.
|
||||
*
|
||||
* @param string $fieldName
|
||||
* @param $value
|
||||
* @param string $operator
|
||||
*
|
||||
* @return \VRPayment\Sdk\Model\EntityQueryFilter
|
||||
*/
|
||||
protected function getEntityFilter(string $fieldName, $value, string $operator = CriteriaOperator::EQUALS): EntityQueryFilter
|
||||
{
|
||||
/** @noinspection PhpParamsInspection */
|
||||
return (new EntityQueryFilter())
|
||||
->setType(EntityQueryFilterType::LEAF)
|
||||
->setOperator($operator)
|
||||
->setFieldName($fieldName)
|
||||
->setValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get web hook callback url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getWebHookCallBackUrl(): string
|
||||
{
|
||||
return $this->router->generate(
|
||||
'api.action.vrpayment.webhook.update',
|
||||
['salesChannelId' => $this->getSalesChannelId() ?? 'null',],
|
||||
UrlGeneratorInterface::ABSOLUTE_URL
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $webHookUrlId
|
||||
*
|
||||
* @return array
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*/
|
||||
protected function getInstalledWebHookListeners(int $webHookUrlId): array
|
||||
{
|
||||
/** @noinspection PhpParamsInspection */
|
||||
$entityQueryFilter = (new EntityQueryFilter())
|
||||
->setType(EntityQueryFilterType::_AND)
|
||||
->setChildren([
|
||||
$this->getEntityFilter('state', CreationEntityState::ACTIVE),
|
||||
$this->getEntityFilter('url.id', $webHookUrlId),
|
||||
]);
|
||||
|
||||
$query = (new EntityQuery())->setFilter($entityQueryFilter);
|
||||
|
||||
return $this->apiClient->getWebhookListenerService()->search($this->spaceId, $query);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\WebHooks\Strategy;
|
||||
|
||||
use Exception;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use VRPaymentPayment\Core\Api\WebHooks\Service\WebHooksService;
|
||||
use VRPaymentPayment\Core\Api\WebHooks\Struct\WebHookRequest;
|
||||
|
||||
/**
|
||||
* Handles the strategy for processing webhook requests related to manual tasks.
|
||||
*
|
||||
* This class extends the base webhook strategy class and is tailored specifically for handling
|
||||
* webhooks that deal with manual task updates. These tasks could involve manual interventions required
|
||||
* for certain operations within the system, which are triggered by external webhook events.
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\WebHooks\Strategy
|
||||
*/
|
||||
class WebHookPaymentMethodConfigurationStrategy extends WebHookStrategyBase {
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function match(string $webhookEntityId): bool {
|
||||
return WebHooksService::PAYMENT_METHOD_CONFIGURATION == $webhookEntityId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the incoming webhook request that pertains to manual tasks.
|
||||
*
|
||||
* This method activates the manual task service to handle updates based on the data provided
|
||||
* in the webhook request. It could involve marking tasks as completed, updating their status, or
|
||||
* initiating sub-processes required as part of the task resolution.
|
||||
*
|
||||
* @param WebHookRequest $request The webhook request object containing all necessary data.
|
||||
* @return Response The method does not return a value but updates the state of manual tasks based on the webhook data.
|
||||
* @throws Exception Throws an exception if there is a failure in processing the manual task updates.
|
||||
*/
|
||||
public function process(WebHookRequest $request): Response {
|
||||
$result = $this->paymentMethodConfigurationService
|
||||
->setSalesChannelId($this->getSalesChannelId())
|
||||
->synchronize($this->getContext());
|
||||
|
||||
return new JsonResponse(['result' => $result]);
|
||||
}
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\WebHooks\Strategy;
|
||||
|
||||
use Exception;
|
||||
use Symfony\Component\HttpFoundation\{
|
||||
JsonResponse,
|
||||
Response,};
|
||||
use Shopware\Core\{
|
||||
Checkout\Cart\CartException,
|
||||
Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStates,
|
||||
Framework\Context,
|
||||
System\StateMachine\Exception\IllegalTransitionException};
|
||||
use VRPayment\Sdk\{
|
||||
Model\Refund,
|
||||
Model\RefundState,
|
||||
Model\Transaction,
|
||||
Model\TransactionInvoiceState,
|
||||
Model\TransactionState,
|
||||
Model\TransactionInvoice,};
|
||||
use VRPaymentPayment\Core\{
|
||||
Api\WebHooks\Service\WebHooksService,
|
||||
Api\WebHooks\Struct\WebHookRequest,
|
||||
Util\Payload\TransactionPayload};
|
||||
|
||||
/**
|
||||
* Handles the strategy for processing webhook requests related to manual tasks.
|
||||
*
|
||||
* This class extends the base webhook strategy class and is tailored specifically for handling
|
||||
* webhooks that deal with manual task updates. These tasks could involve manual interventions required
|
||||
* for certain operations within the system, which are triggered by external webhook events.
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\WebHooks\Strategy
|
||||
*/
|
||||
class WebHookRefundStrategy extends WebHookStrategyBase implements WebhookStrategyActionsInterface {
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function match(string $webhookEntityId): bool
|
||||
{
|
||||
return WebHooksService::REFUND == $webhookEntityId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the relevant entity from the API based on the webhook request.
|
||||
*
|
||||
* This method utilizes the TransactionService to fetch entity details (e.g., transaction data)
|
||||
* based on the space and entity ID provided in the webhook request.
|
||||
*
|
||||
* @param WebHookRequest $request request.
|
||||
* @return object|\VRPayment\Sdk\Model\Refund
|
||||
* @throws \VRPayment\Sdk\ApiException ApiException.
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException ConnectionException.
|
||||
* @throws \VRPayment\Sdk\VersioningException VersioningException.
|
||||
*/
|
||||
public function getTransaction(WebHookRequest $request)
|
||||
{
|
||||
return $this->settings->getApiClient()
|
||||
->getRefundService()
|
||||
->read($request->getSpaceId(), $request->getEntityId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getOrderIdByTransaction($transaction): string
|
||||
{
|
||||
/** @var \VRPayment\Sdk\Model\Refund $transaction */
|
||||
return $transaction->getTransaction()
|
||||
->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_ID];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the request state is applicable.
|
||||
*
|
||||
* This method checks if the state of the transaction from a webhook request
|
||||
* is one of the predefined applicable states.
|
||||
*
|
||||
* @param WebHookRequest $request The webhook request containing the transaction state.
|
||||
* @return bool Returns true if the state is applicable, false otherwise.
|
||||
*/
|
||||
public function isRequestStateApplicable(WebHookRequest $request): bool
|
||||
{
|
||||
$applicableStates = [
|
||||
RefundState::SUCCESSFUL,
|
||||
];
|
||||
|
||||
return in_array($request->getState(), $applicableStates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the incoming webhook request that pertains to manual tasks.
|
||||
*
|
||||
* This method activates the manual task service to handle updates based on the data provided
|
||||
* in the webhook request. It could involve marking tasks as completed, updating their status, or
|
||||
* initiating sub-processes required as part of the task resolution.
|
||||
*
|
||||
* @param WebHookRequest $request The webhook request object containing all necessary data.
|
||||
* @return Response The method does not return a value but updates the state of manual tasks based on the webhook data.
|
||||
* @throws Exception Throws an exception if there is a failure in processing the manual task updates.
|
||||
*/
|
||||
public function process(WebHookRequest $request): Response
|
||||
{
|
||||
return $this->updateRefund($request, $this->getContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the refund callback for a VRPayment transaction, updating the associated order transaction state based on the refund status.
|
||||
* This method handles different refund scenarios, including full and partial refunds, and adjusts the order transaction state accordingly.
|
||||
* It ensures transactional integrity by locking the order record during updates to prevent concurrent modifications.
|
||||
* Logs the outcome of the operation and any exceptions encountered.
|
||||
*
|
||||
* @param WebHookRequest $request The webhook request data encapsulating the refund details.
|
||||
* @param Context $context Shopware execution context, providing scope for operations like database access.
|
||||
*
|
||||
* @return Response Returns a JSON response indicating the outcome of the refund processing.
|
||||
*/
|
||||
public function updateRefund(WebHookRequest $request, Context $context): Response
|
||||
{
|
||||
$status = Response::HTTP_UNPROCESSABLE_ENTITY;
|
||||
|
||||
try {
|
||||
$refund = $this->getTransaction($request);
|
||||
$orderId = $this->getOrderIdByTransaction($refund);
|
||||
|
||||
if(!empty($orderId)) {
|
||||
$this->executeLocked($orderId, $context, function () use ($orderId, $refund, $context, $request) {
|
||||
if ($request->getListenerEntityTechnicalName() == WebHookRequest::REFUND && $request->getState() == RefundState::SUCCESSFUL) {
|
||||
$this->refundService->upsert($refund, $context);
|
||||
$orderTransactionId = $refund->getTransaction()->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_TRANSACTION_ID];
|
||||
$orderTransaction = $this->getOrderTransaction($orderId, $context);
|
||||
|
||||
$transactionByOrderTransactionId = $this->transactionService->getByOrderTransactionId($orderTransactionId, $context);
|
||||
$totalRefundedAmount = $this->getTotalRefundedAmount($transactionByOrderTransactionId->getTransactionId(), $context);
|
||||
$leftToRefund = floatval($orderTransaction->getAmount()->getTotalPrice()) - $totalRefundedAmount;
|
||||
if ($leftToRefund > 0) {
|
||||
$this->orderTransactionStateHandler->refundPartially($orderTransactionId, $context);
|
||||
} elseif ($leftToRefund === floatval(0)) { // This trick is used, because it's float type and 0 is int
|
||||
$this->orderTransactionStateHandler->refund($orderTransactionId, $context);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$status = Response::HTTP_OK;
|
||||
} catch (CartException $exception) {
|
||||
$status = Response::HTTP_OK;
|
||||
$this->logRequest($exception, $request, 'info');
|
||||
} catch (IllegalTransitionException $exception) {
|
||||
$status = Response::HTTP_OK;
|
||||
$this->logRequest($exception, $request, 'info');
|
||||
} catch (\Exception $exception) {
|
||||
$this->logRequest($exception, $request, 'critical');
|
||||
}
|
||||
|
||||
return new JsonResponse(['data' => $request->jsonSerialize()], $status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the total amount refunded for a specific transaction by summing up all refunds associated with it.
|
||||
* This method queries the database for all refund records related to the transaction and aggregates their amounts.
|
||||
* It ensures accurate financial calculations that are crucial for adjusting transaction states and reporting.
|
||||
*
|
||||
* @param int $transactionId The unique identifier of the transaction for which to calculate the total refunded amount.
|
||||
* @param Context $context Shopware execution context, providing scope for operations like database access.
|
||||
*
|
||||
* @return float The total amount refunded for the specified transaction, converted to a float to ensure precision in calculations.
|
||||
*/
|
||||
private function getTotalRefundedAmount(int $transactionId, Context $context): float
|
||||
{
|
||||
$amount = 0;
|
||||
$refunds = $this->transactionService->getRefundEntityCollectionByTransactionId($transactionId, $context);
|
||||
foreach ($refunds as $refund) {
|
||||
$amount += floatval($refund->getData()['amount'] ?? 0);
|
||||
}
|
||||
|
||||
return (float) (string) $amount;
|
||||
}
|
||||
}
|
||||
@@ -1,498 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\WebHooks\Strategy;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Psr\{
|
||||
Container\ContainerInterface,
|
||||
Log\LoggerInterface,};
|
||||
use Shopware\Core\{
|
||||
Checkout\Cart\CartException,
|
||||
Checkout\Order\Aggregate\OrderDelivery\OrderDeliveryEntity,
|
||||
Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity,
|
||||
Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler,
|
||||
Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStates,
|
||||
Checkout\Order\OrderEntity,
|
||||
Checkout\Order\SalesChannel\OrderService,
|
||||
Framework\Context,
|
||||
Framework\DataAbstractionLayer\Search\Criteria,
|
||||
Framework\DataAbstractionLayer\Search\Sorting\FieldSorting,
|
||||
System\StateMachine\Aggregation\StateMachineTransition\StateMachineTransitionActions,};
|
||||
use Symfony\Component\{
|
||||
HttpFoundation\ParameterBag};
|
||||
use VRPayment\Sdk\{
|
||||
Model\RefundState,
|
||||
Model\Transaction,
|
||||
Model\TransactionInvoiceState,
|
||||
Model\TransactionState,
|
||||
Model\TransactionInvoice,
|
||||
ApiException,
|
||||
Http\ConnectionException,
|
||||
VersioningException,};
|
||||
use VRPaymentPayment\Core\{
|
||||
Api\OrderDeliveryState\Handler\OrderDeliveryStateHandler,
|
||||
Api\PaymentMethodConfiguration\Service\PaymentMethodConfigurationService,
|
||||
Api\Refund\Service\RefundService,
|
||||
Api\Transaction\Service\OrderMailService,
|
||||
Api\Transaction\Service\TransactionService,
|
||||
Api\WebHooks\Struct\WebHookRequest,
|
||||
Settings\Service\SettingsService,
|
||||
Settings\Struct\Settings,};
|
||||
|
||||
/**
|
||||
* Abstract class WebHookStrategyBase
|
||||
*
|
||||
* Serves as a base class for all webhook strategy implementations. It provides common methods needed to process webhook requests,
|
||||
* such as loading entity data from the API, retrieving order details, and more.
|
||||
* Note: Not all standard methods are applicable in all derived strategy classes. In some strategies, certain operations
|
||||
* may intentionally be left unimplemented to reflect that they are not relevant to the specific type of webhook being handled.
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\WebHooks\Strategy
|
||||
*/
|
||||
abstract class WebHookStrategyBase implements WebHookStrategyInterface {
|
||||
|
||||
/**
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @var Context
|
||||
*/
|
||||
private $context;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $salesChannelId;
|
||||
|
||||
/**
|
||||
* @var OrderService
|
||||
*/
|
||||
protected $orderService;
|
||||
|
||||
/**
|
||||
* @var Settings
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @var Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* @var OrderMailService
|
||||
*/
|
||||
protected $orderMailService;
|
||||
|
||||
/**
|
||||
* @var OrderTransactionStateHandler
|
||||
*/
|
||||
protected $orderTransactionStateHandler;
|
||||
|
||||
/**
|
||||
* @var PaymentMethodConfigurationService
|
||||
*/
|
||||
protected $paymentMethodConfigurationService;
|
||||
|
||||
/**
|
||||
* @var SettingsService
|
||||
*/
|
||||
protected $settingsService;
|
||||
|
||||
/**
|
||||
* @var RefundService
|
||||
*/
|
||||
protected $refundService;
|
||||
|
||||
/**
|
||||
* @var TransactionService
|
||||
*/
|
||||
protected $transactionService;
|
||||
|
||||
/**
|
||||
* @var OrderEntity
|
||||
*/
|
||||
protected $orderEntity;
|
||||
|
||||
/**
|
||||
* Transaction Final States
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $transactionFinalStates = [
|
||||
OrderTransactionStates::STATE_CANCELLED,
|
||||
OrderTransactionStates::STATE_PAID,
|
||||
OrderTransactionStates::STATE_REFUNDED,
|
||||
];
|
||||
|
||||
public $vrpaymentTransactionSuccessStates = [
|
||||
TransactionState::AUTHORIZED,
|
||||
TransactionState::COMPLETED,
|
||||
TransactionState::FULFILL,
|
||||
];
|
||||
|
||||
/**
|
||||
* WebHookStrategyBase constructor.
|
||||
*
|
||||
* @param Connection $connection
|
||||
* @param OrderTransactionStateHandler $orderTransactionStateHandler
|
||||
* @param OrderService $orderService
|
||||
* @param PaymentMethodConfigurationService $paymentMethodConfigurationService
|
||||
* @param RefundService $refundService
|
||||
* @param OrderMailService $orderMailService
|
||||
* @param TransactionService $transactionService
|
||||
* @param SettingsService $settingsService
|
||||
* @param ContainerInterface $container
|
||||
* @param LoggerInterface $vrpaymentPaymentLogger
|
||||
* @see "$vrpaymentPaymentLogger", please read the documentation "How to Autowire Logger Channels"
|
||||
*/
|
||||
public function __construct(
|
||||
Connection $connection,
|
||||
OrderTransactionStateHandler $orderTransactionStateHandler,
|
||||
OrderService $orderService,
|
||||
PaymentMethodConfigurationService $paymentMethodConfigurationService,
|
||||
RefundService $refundService,
|
||||
OrderMailService $orderMailService,
|
||||
TransactionService $transactionService,
|
||||
SettingsService $settingsService,
|
||||
ContainerInterface $container,
|
||||
LoggerInterface $vrpaymentPaymentLogger
|
||||
) {
|
||||
$this->connection = $connection;
|
||||
$this->orderTransactionStateHandler = $orderTransactionStateHandler;
|
||||
$this->paymentMethodConfigurationService = $paymentMethodConfigurationService;
|
||||
$this->refundService = $refundService;
|
||||
$this->orderMailService = $orderMailService;
|
||||
$this->transactionService = $transactionService;
|
||||
$this->settingsService = $settingsService;
|
||||
$this->orderService = $orderService;
|
||||
$this->container = $container;
|
||||
$this->logger = $vrpaymentPaymentLogger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the context for the current operation.
|
||||
*
|
||||
* This method assigns a new context to be used in subsequent operations within this instance.
|
||||
* Passing a null value clears the current context.
|
||||
*
|
||||
* @param Context|null $context The new context to set, or null to clear the existing context.
|
||||
* @return self Returns this instance to allow for method chaining.
|
||||
*/
|
||||
public function setContext(?Context $context): self
|
||||
{
|
||||
$this->context = $context;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current context.
|
||||
*
|
||||
* This method returns the context that has been set for this instance, which may be used in various operations.
|
||||
* If no context has been set, it returns null.
|
||||
*
|
||||
* @return Context|null The current context if set; otherwise, null.
|
||||
*/
|
||||
public function getContext(): ?Context
|
||||
{
|
||||
return $this->context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sales channel ID for this instance.
|
||||
*
|
||||
* This method updates the sales channel ID. This ID is used in various operations that are specific to a sales channel.
|
||||
*
|
||||
* @param string|null $salesChannelId The sales channel ID to be set.
|
||||
* @return $this Provides a fluent interface by returning itself.
|
||||
*/
|
||||
public function setSalesChannelId(?string $salesChannelId): self
|
||||
{
|
||||
$this->salesChannelId = $salesChannelId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current sales channel ID.
|
||||
*
|
||||
* This method returns the sales channel ID that has been set for this instance. If no ID has been set, it returns null.
|
||||
*
|
||||
* @return string|null The current sales channel ID if set; otherwise, null.
|
||||
*/
|
||||
public function getSalesChannelId(): ?string
|
||||
{
|
||||
return $this->salesChannelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the settings based on the current sales channel.
|
||||
*
|
||||
* This method fetches and applies the settings specific to the sales channel ID currently set for this instance.
|
||||
* @return $this Provides a fluent interface by returning itself.
|
||||
*/
|
||||
public function setCurrentSettingsBySalesChannel(): self
|
||||
{
|
||||
$this->settings = $this->getSettingsBySalesChannel($this->getSalesChannelId());
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get settings for a specific sales channel.
|
||||
*
|
||||
* This method accesses settings from the settings service using the provided sales channel ID. It returns configuration settings
|
||||
* that are specific to the given sales channel.
|
||||
*
|
||||
* @param string|null $salesChannelId The ID of the sales channel for which settings are being requested. If null, it may default to system-wide settings or no settings.
|
||||
* @return Settings The settings object containing configuration details for the specified sales channel.
|
||||
*/
|
||||
protected function getSettingsBySalesChannel(?string $salesChannelId): Settings
|
||||
{
|
||||
return $this->settingsService->getSettings($salesChannelId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an order entity based on the order ID.
|
||||
*
|
||||
* This method fetches an order entity from the database using the provided order ID and context. If the order entity has not
|
||||
* been fetched before, it performs a database query to retrieve it and caches it for future use.
|
||||
*
|
||||
* @param string $orderId The unique identifier of the order.
|
||||
* @param Context $context The context of the current operation, including scope and permissions.
|
||||
* @return OrderEntity The order entity associated with the provided ID.
|
||||
* @throws CartException If the order cannot be found.
|
||||
*/
|
||||
protected function getOrderEntity(string $orderId, Context $context): OrderEntity
|
||||
{
|
||||
if (is_null($this->orderEntity)) {
|
||||
$criteria = (new Criteria([$orderId]))
|
||||
->addAssociations(['deliveries', 'transactions']);
|
||||
$criteria->getAssociation('transactions')
|
||||
->addSorting(new FieldSorting('createdAt', FieldSorting::ASCENDING));
|
||||
|
||||
try {
|
||||
$this->orderEntity = $this->container
|
||||
->get('order.repository')
|
||||
->search($criteria, $context)
|
||||
->first();
|
||||
if (is_null($this->orderEntity)) {
|
||||
throw CartException::orderNotFound($orderId);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
throw CartException::orderNotFound($orderId);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->orderEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last transaction associated with an order.
|
||||
*
|
||||
* This method accesses the last transaction of an order based on the provided order ID and context.
|
||||
*
|
||||
* @param string $orderId The unique identifier of the order.
|
||||
* @param Context $context The context of the current operation, including scope and permissions.
|
||||
* @return OrderTransactionEntity The last transaction entity of the specified order.
|
||||
*/
|
||||
protected function getOrderTransaction(String $orderId, Context $context): OrderTransactionEntity
|
||||
{
|
||||
return $this->getOrderEntity($orderId, $context)->getTransactions()->last();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unholds the delivery of an order.
|
||||
*
|
||||
* This method changes the state of an order's last delivery from 'held' to 'released', allowing further processing like shipping.
|
||||
*
|
||||
* @param string $orderId The unique identifier of the order.
|
||||
* @param Context $context The context of the current operation, including scope and permissions.
|
||||
*/
|
||||
/**
|
||||
* Unholds the delivery of an order.
|
||||
*
|
||||
* This method changes the state of an order's last delivery from 'held' to 'released', allowing further processing like shipping.
|
||||
*
|
||||
* @param string $orderId The unique identifier of the order.
|
||||
* @param Context $context The context of the current operation, including scope and permissions.
|
||||
*/
|
||||
protected function unholdDelivery(string $orderId, Context $context): void
|
||||
{
|
||||
try {
|
||||
$criteria = new Criteria([$orderId]);
|
||||
$criteria->addAssociation('deliveries.stateMachineState');
|
||||
$order = $this->container->get('order.repository')
|
||||
->search($criteria, $context)
|
||||
->first();
|
||||
|
||||
if (!$order) {
|
||||
$this->logger->info('Order not found: ' . $orderId);
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var OrderDeliveryEntity|null $orderDelivery */
|
||||
$orderDelivery = $order->getDeliveries()?->last();
|
||||
|
||||
if (null === $orderDelivery) {
|
||||
$this->logger->info('No deliveries found for order: ' . $orderId);
|
||||
return;
|
||||
}
|
||||
|
||||
$orderDeliveryState = $orderDelivery->getStateMachineState();
|
||||
if (!$orderDeliveryState) {
|
||||
$this->logger->info('Order delivery state is null for order: ' . $orderId);
|
||||
return;
|
||||
}
|
||||
|
||||
$technicalName = $orderDeliveryState->getTechnicalName();
|
||||
$this->logger->info('Order delivery state: ' . $technicalName);
|
||||
|
||||
if ($technicalName !== OrderDeliveryStateHandler::STATE_HOLD) {
|
||||
$this->logger->info('Order delivery is not on hold, skipping unhold process.');
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var OrderDeliveryStateHandler $orderDeliveryStateHandler */
|
||||
$orderDeliveryStateHandler = $this->container->get(OrderDeliveryStateHandler::class);
|
||||
$orderDeliveryStateHandler->unhold($orderDelivery->getId(), $context);
|
||||
|
||||
$this->logger->info('Successfully unheld order delivery for order: ' . $orderId);
|
||||
} catch (\Exception $exception) {
|
||||
$this->logger->error('Error unholding order delivery: ' . $exception->getMessage(), $exception->getTrace());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases any holds and cancels the delivery of an order. If the order's delivery is not on hold, this method does nothing.
|
||||
* Any exceptions encountered during the process are logged for debugging.
|
||||
*
|
||||
* @param string $orderId The ID of the order to process.
|
||||
* @param Context $context Shopware execution context for the current operation.
|
||||
*/
|
||||
protected function unholdAndCancelDelivery(string $orderId, Context $context): void
|
||||
{
|
||||
$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 {
|
||||
|
||||
$orderDeliveryStateHandler = $this->container->get(OrderDeliveryStateHandler::class);
|
||||
/** @var OrderDeliveryEntity $orderDelivery */
|
||||
$orderDelivery = $order->getDeliveries()?->last();
|
||||
|
||||
if (null === $orderDelivery) {
|
||||
return;
|
||||
}
|
||||
if ($orderDelivery->getStateMachineState()?->getTechnicalName() !== OrderDeliveryStateHandler::STATE_HOLD){
|
||||
return;
|
||||
}
|
||||
$orderDeliveryId = $orderDelivery->getId();
|
||||
$orderDeliveryStateHandler->unhold($orderDeliveryId, $context);
|
||||
$orderDeliveryStateHandler->cancel($orderDeliveryId, $context);
|
||||
} catch (\Exception $exception) {
|
||||
$this->logger->info($exception->getMessage(), $exception->getTrace());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a locked operation on an order.
|
||||
*
|
||||
* This method ensures that the operation on the order is executed in a locked context, preventing other processes from interfering.
|
||||
* It locks the order, performs the operation, and then commits or rolls back the transaction based on the success of the operation.
|
||||
*
|
||||
* @param string $orderId The unique identifier of the order.
|
||||
* @param Context $context The context of the current operation, including scope and permissions.
|
||||
* @param callable $operation The operation to execute on the order.
|
||||
* @return mixed The result of the operation.
|
||||
* @throws Exception If the operation fails.
|
||||
*/
|
||||
protected function executeLocked(string $orderId, Context $context, callable $operation)
|
||||
{
|
||||
try {
|
||||
|
||||
$data = [
|
||||
'id' => $orderId,
|
||||
'vrpayment_lock' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
|
||||
$order = $this->container->get('order.repository')->search(new Criteria([$orderId]), $context)->first();
|
||||
|
||||
if(empty($order)){
|
||||
throw CartException::orderNotFound($orderId);
|
||||
}
|
||||
|
||||
$this->container->get('order.repository')->upsert([$data], $context);
|
||||
|
||||
$result = $operation();
|
||||
|
||||
return $result;
|
||||
} catch (\Exception $exception) {
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an email based on the transaction state.
|
||||
*
|
||||
* This method checks if the transaction state matches any of the successful states and sends an email if enabled in the settings.
|
||||
*
|
||||
* @param Transaction $transaction The transaction object containing the state and metadata.
|
||||
* @param Context $context The context of the current operation, including scope and permissions.
|
||||
* @param string $orderId The unique identifier of the order associated with the transaction.
|
||||
*/
|
||||
protected function sendEmail(Transaction $transaction, Context $context, string $orderId): void
|
||||
{
|
||||
$salesChannelId = $this->getSalesChannelId();
|
||||
$this->settings = $this->getSettingsBySalesChannel($salesChannelId);
|
||||
if ($this->settings->isEmailEnabled()
|
||||
&& in_array($transaction->getState(), $this->vrpaymentTransactionSuccessStates)) {
|
||||
$this->orderMailService->send($orderId, $context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a message with dynamic retrieval of the class and method names from where it is called, and allows specifying the log level.
|
||||
*
|
||||
* This method captures the class and method that called it using a backtrace, which automates the process
|
||||
* of logging without needing to manually specify the source of the log entry. It enhances error tracking
|
||||
* and informational logging by providing precise source identification for a variety of log levels.
|
||||
*
|
||||
* @param \Throwable $exception The exception to log, providing the error details.
|
||||
* @param WebHookRequest $request The HTTP request context, used for additional logging data.
|
||||
* @param string $logLevel The level of the log entry ('info', 'critical', etc.), controlling how the log is processed.
|
||||
*/
|
||||
protected function logRequest(\Throwable $exception, WebHookRequest $request, string $logLevel = 'info'): void
|
||||
{
|
||||
$class = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['class'];
|
||||
$function = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function'];
|
||||
$message = $class . ' : ' . $function . ' : ' . $exception->getMessage();
|
||||
|
||||
switch ($logLevel) {
|
||||
case 'critical':
|
||||
$this->logger->critical($message, $request->jsonSerialize());
|
||||
break;
|
||||
case 'debug':
|
||||
$this->logger->debug($message, $request->jsonSerialize());
|
||||
break;
|
||||
case 'info':
|
||||
default:
|
||||
$this->logger->info($message, $request->jsonSerialize());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\WebHooks\Strategy;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use VRPaymentPayment\Core\Api\WebHooks\Struct\WebHookRequest;
|
||||
|
||||
/**
|
||||
* Class Entity
|
||||
* Defines a strategy interface for processing webhook requests.
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\WebHooks\Strategy
|
||||
*/
|
||||
interface WebHookStrategyInterface {
|
||||
|
||||
/**
|
||||
* Checks if the provided webhook entity ID matches the expected ID.
|
||||
*
|
||||
* This method is intended to verify whether the entity ID from a webhook request matches
|
||||
* a specific ID configured within the WebHooksService. This can be used to validate that the
|
||||
* webhook is relevant and should be processed further.
|
||||
*
|
||||
* @param string $webhookEntityId The entity ID from the webhook request.
|
||||
* @return bool Returns true if the ID matches the system's criteria, false otherwise.
|
||||
*/
|
||||
public function match(string $webhookEntityId): bool;
|
||||
|
||||
/**
|
||||
* Process the webhook request.
|
||||
*
|
||||
* @param WebHookRequest $request The webhook request object.
|
||||
* @return Response
|
||||
*/
|
||||
public function process(WebHookRequest $request): Response;
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\WebHooks\Strategy;
|
||||
|
||||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shopware\Core\Framework\Context;
|
||||
use Symfony\Component\{
|
||||
HttpFoundation\JsonResponse,
|
||||
HttpFoundation\Response,};
|
||||
use VRPaymentPayment\Core\Api\WebHooks\Struct\WebHookRequest;
|
||||
|
||||
/**
|
||||
* Handles the management and processing of different webhook strategies.
|
||||
*
|
||||
* This manager class holds references to different webhook strategies and delegates
|
||||
* the processing of incoming webhook requests to the appropriate strategy based on
|
||||
* the type of the webhook. Each strategy corresponds to a specific type of webhook
|
||||
* and contains the logic needed to handle that specific webhook type.
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\WebHooks\Strategy
|
||||
*/
|
||||
class WebHookStrategyManager {
|
||||
|
||||
/**
|
||||
* @var iterable Holds instances of webhook strategies.
|
||||
*/
|
||||
protected $strategies;
|
||||
|
||||
/**
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* Constructor for the webhook manager.
|
||||
*
|
||||
* Initializes instances of each specific webhook strategy and stores them in an associative array.
|
||||
* Each key in this array corresponds to a webhook type, and each value is an instance of a strategy
|
||||
* class that handles that specific type of webhook.
|
||||
* @param iterable $strategies
|
||||
* @param LoggerInterface $vrpaymentPaymentLogger
|
||||
* @see "$vrpaymentPaymentLogger", please read the documentation "How to Autowire Logger Channels"
|
||||
*/
|
||||
public function __construct(
|
||||
iterable $strategies,
|
||||
LoggerInterface $vrpaymentPaymentLogger
|
||||
) {
|
||||
$this->strategies = $strategies;
|
||||
$this->logger = $vrpaymentPaymentLogger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the appropriate strategy for handling the given webhook request based on webhook type.
|
||||
*
|
||||
* This method fetches the webhook entity using the listener entity ID from the request, checks if a corresponding
|
||||
* strategy exists, and returns the strategy if found.
|
||||
*
|
||||
* @param WebHookRequest $request The incoming webhook request.
|
||||
* @param Context $context The shopware context.
|
||||
* @param string $salesChannelId The sales channel ID.
|
||||
* @return WebhookStrategyInterface The strategy to handle the request.
|
||||
* @throws Exception If no strategy can be resolved.
|
||||
*/
|
||||
private function resolveStrategy(WebHookRequest $request, Context $context, ?string $salesChannelId): ?WebHookStrategyInterface
|
||||
{
|
||||
// Check if the strategy exists for the retrieved transaction ID.
|
||||
foreach ($this->strategies as $strategy) {
|
||||
/** @var WebhookStrategyInterface $strategy */
|
||||
if ($strategy->match($request->getListenerEntityId())) {
|
||||
$strategy
|
||||
->setContext($context)
|
||||
->setSalesChannelId($salesChannelId)
|
||||
->setCurrentSettingsBySalesChannel();
|
||||
return $strategy;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the incoming webhook by delegating to the appropriate strategy.
|
||||
*
|
||||
* This method determines the type of the incoming webhook request and uses it
|
||||
* to look up the corresponding strategy. If a strategy is found, it delegates the
|
||||
* request processing to that strategy. If no strategy is found for the type, it
|
||||
* throws an exception.
|
||||
*
|
||||
* @param WebHookRequest $request The incoming webhook request object.
|
||||
* @param Context $context
|
||||
* @param string $salesChannelId
|
||||
* @return Response
|
||||
* @throws Exception If no strategy is available for the webhook type provided in the request.
|
||||
*/
|
||||
public function process(WebHookRequest $request, Context $context, ?string $salesChannelId): Response
|
||||
{
|
||||
try {
|
||||
$strategy = $this->resolveStrategy($request, $context, $salesChannelId);
|
||||
|
||||
//If there is no strategy available
|
||||
if (empty($strategy)) {
|
||||
$this->logger->warning("No strategy available for the transaction ID: {transactionId}", [
|
||||
'transactionId' => $request->getListenerEntityId(),
|
||||
]);
|
||||
return new JsonResponse(['data' => $request->jsonSerialize()], Response::HTTP_OK);
|
||||
}
|
||||
|
||||
//This reduces the number of unnecessary api calls.
|
||||
if (method_exists($strategy, "isRequestStateApplicable") && !$strategy->isRequestStateApplicable($request)) {
|
||||
return new JsonResponse(['data' => $request->jsonSerialize()], Response::HTTP_OK);
|
||||
}
|
||||
|
||||
//If the request state applies for current strategy, then it will be processed.
|
||||
return $strategy->process($request);
|
||||
} catch ( Exception $e) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\WebHooks\Strategy;
|
||||
|
||||
use Shopware\Core\{
|
||||
Checkout\Cart\CartException,
|
||||
Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity,
|
||||
Framework\Context,
|
||||
System\StateMachine\Exception\IllegalTransitionException};
|
||||
use Exception;
|
||||
use Symfony\Component\{
|
||||
HttpFoundation\JsonResponse,
|
||||
HttpFoundation\Response,};
|
||||
use VRPayment\Sdk\{
|
||||
Model\RefundState,
|
||||
Model\Transaction,
|
||||
Model\TransactionInvoiceState,
|
||||
Model\TransactionState,
|
||||
Model\TransactionInvoice,};
|
||||
use VRPaymentPayment\Core\{
|
||||
Api\WebHooks\Service\WebHooksService,
|
||||
Api\WebHooks\Struct\WebHookRequest,
|
||||
Util\Payload\TransactionPayload};
|
||||
|
||||
|
||||
/**
|
||||
* Handles the strategy for processing webhook requests related to manual tasks.
|
||||
*
|
||||
* This class extends the base webhook strategy class and is tailored specifically for handling
|
||||
* webhooks that deal with manual task updates. These tasks could involve manual interventions required
|
||||
* for certain operations within the system, which are triggered by external webhook events.
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\WebHooks\Strategy
|
||||
*/
|
||||
class WebHookTransactionInvoiceStrategy extends WebHookStrategyBase implements WebhookStrategyActionsInterface {
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function match(string $webhookEntityId): bool {
|
||||
return WebHooksService::TRANSACTION_INVOICE == $webhookEntityId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the relevant entity from the API based on the webhook request.
|
||||
*
|
||||
* This method utilizes the TransactionService to fetch entity details (e.g., transaction data)
|
||||
* based on the space and entity ID provided in the webhook request.
|
||||
*
|
||||
* @param WebHookRequest $request request.
|
||||
* @return object|\VRPayment\Sdk\Model\TransactionInvoice
|
||||
* @throws \VRPayment\Sdk\ApiException ApiException.
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException ConnectionException.
|
||||
* @throws \VRPayment\Sdk\VersioningException VersioningException.
|
||||
*/
|
||||
public function getTransaction(WebHookRequest $request)
|
||||
{
|
||||
return $this->settings->getApiClient()
|
||||
->getTransactionInvoiceService()
|
||||
->read($request->getSpaceId(), $request->getEntityId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getOrderIdByTransaction($transaction): string
|
||||
{
|
||||
/** @var \VRPayment\Sdk\Model\TransactionInvoice $transaction */
|
||||
return $transaction->getCompletion()
|
||||
->getLineItemVersion()
|
||||
->getTransaction()
|
||||
->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_ID];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the request state is applicable.
|
||||
*
|
||||
* This method checks if the state of the transaction from a webhook request
|
||||
* is one of the predefined applicable states.
|
||||
*
|
||||
* @param WebHookRequest $request The webhook request containing the transaction state.
|
||||
* @return bool Returns true if the state is applicable, false otherwise.
|
||||
*/
|
||||
public function isRequestStateApplicable(WebHookRequest $request): bool
|
||||
{
|
||||
$applicableStates = [
|
||||
TransactionInvoiceState::DERECOGNIZED,
|
||||
TransactionInvoiceState::NOT_APPLICABLE,
|
||||
TransactionInvoiceState::PAID,
|
||||
];
|
||||
|
||||
return in_array($request->getState(), $applicableStates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the incoming webhook request that pertains to manual tasks.
|
||||
*
|
||||
* This method activates the manual task service to handle updates based on the data provided
|
||||
* in the webhook request. It could involve marking tasks as completed, updating their status, or
|
||||
* initiating sub-processes required as part of the task resolution.
|
||||
*
|
||||
* @param WebHookRequest $request The webhook request object containing all necessary data.
|
||||
* @return Response The method does not return a value but updates the state of manual tasks based on the webhook data.
|
||||
* @throws Exception Throws an exception if there is a failure in processing the manual task updates.
|
||||
*/
|
||||
public function process(WebHookRequest $request): Response
|
||||
{
|
||||
return $this->updateTransactionInvoice($request, $this->getContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the VRPayment TransactionInvoice webhook request by updating transaction and order states based on the invoice state.
|
||||
* This method handles the entire lifecycle of the invoice processing within the system, from fetching transaction data,
|
||||
* locking operations for safety, updating transaction statuses based on invoice changes, and handling order delivery states.
|
||||
*
|
||||
* @param WebHookRequest $request The data received from the webhook.
|
||||
* @param Context $context The context within which this operation is performed, encapsulating scope-specific information like permissions and current store details.
|
||||
*
|
||||
* @return Response Returns a JSON response indicating the status of the operation, whether it was successful or resulted in an error.
|
||||
*/
|
||||
public function updateTransactionInvoice(WebHookRequest $request, Context $context): Response
|
||||
{
|
||||
$status = Response::HTTP_UNPROCESSABLE_ENTITY;
|
||||
|
||||
try {
|
||||
$transactionInvoice = $this->getTransaction($request);
|
||||
$orderId = $this->getOrderIdByTransaction($transactionInvoice);
|
||||
if(!empty($orderId)) {
|
||||
$this->executeLocked($orderId, $context, function () use ($orderId, $transactionInvoice, $context, $request) {
|
||||
|
||||
$orderTransactionId = $transactionInvoice->getCompletion()
|
||||
->getLineItemVersion()
|
||||
->getTransaction()
|
||||
->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_TRANSACTION_ID];
|
||||
$orderTransaction = $this->getOrderTransaction($orderId, $context);
|
||||
$this->updatePriceIfAdditionalItemsExist($transactionInvoice, $orderTransaction, $context);
|
||||
|
||||
if (!in_array(
|
||||
$orderTransaction->getStateMachineState()?->getTechnicalName(),
|
||||
$this->transactionFinalStates
|
||||
)) {
|
||||
switch ($request->getState()) {
|
||||
case TransactionInvoiceState::DERECOGNIZED:
|
||||
$this->orderTransactionStateHandler->cancel($orderTransactionId, $context);
|
||||
break;
|
||||
case TransactionInvoiceState::NOT_APPLICABLE:
|
||||
case TransactionInvoiceState::PAID:
|
||||
$this->orderTransactionStateHandler->paid($orderTransactionId, $context);
|
||||
$this->unholdDelivery($orderTransactionId, $context);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
$status = Response::HTTP_OK;
|
||||
} catch (CartException $exception) {
|
||||
$status = Response::HTTP_OK;
|
||||
$this->logRequest($exception, $request, 'info');
|
||||
} catch (IllegalTransitionException $exception) {
|
||||
$status = Response::HTTP_OK;
|
||||
$this->logRequest($exception, $request, 'info');
|
||||
} catch (\Exception $exception) {
|
||||
$this->logRequest($exception, $request, 'critical');
|
||||
}
|
||||
|
||||
return new JsonResponse(['data' => $request->jsonSerialize()], $status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the order's total price if there are additional items added to the transaction invoice compared to the completion invoice.
|
||||
* This method checks for discrepancies between the line items listed in the transaction invoice and its completion part,
|
||||
* adjusting the order's total price to reflect any additional items added on the portal side.
|
||||
*
|
||||
* @param TransactionInvoice $transactionInvoice The transaction invoice object containing detailed line items and completion details.
|
||||
* @param OrderTransactionEntity $orderTransaction The order transaction entity linked to the invoice, used for updating order details.
|
||||
* @param Context $context The operational context providing settings and environment for the operation.
|
||||
*/
|
||||
private function updatePriceIfAdditionalItemsExist(
|
||||
TransactionInvoice $transactionInvoice,
|
||||
OrderTransactionEntity $orderTransaction,
|
||||
Context $context
|
||||
): void {
|
||||
$completionLineItems = $transactionInvoice->getCompletion()->getLineItems();
|
||||
$lineItems = $transactionInvoice->getLineItems();
|
||||
|
||||
if (count($completionLineItems) !== count($lineItems)) {
|
||||
$this->transactionService->updateOrderTotalPriceByInvoiceTotal(
|
||||
$orderTransaction->getOrderId(),
|
||||
$transactionInvoice->getOutstandingAmount(),
|
||||
$context
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\WebHooks\Strategy;
|
||||
|
||||
use Symfony\Component\HttpFoundation\{
|
||||
JsonResponse,
|
||||
Response,};
|
||||
use Shopware\Core\{
|
||||
Checkout\Cart\CartException,
|
||||
Framework\Context,
|
||||
System\StateMachine\Exception\IllegalTransitionException};
|
||||
use VRPayment\Sdk\{
|
||||
Model\RefundState,
|
||||
Model\Transaction,
|
||||
Model\TransactionInvoiceState,
|
||||
Model\TransactionState,
|
||||
Model\TransactionInvoice,};
|
||||
use VRPaymentPayment\Core\{
|
||||
Api\WebHooks\Service\WebHooksService,
|
||||
Api\WebHooks\Struct\WebHookRequest,
|
||||
Util\Payload\TransactionPayload};
|
||||
|
||||
/**
|
||||
* Class WebHookTransactionStrategy
|
||||
*
|
||||
* This class provides the implementation for processing transaction webhooks.
|
||||
* It includes methods for handling specific actions that need to be taken when
|
||||
* transaction-related webhook notifications are received, such as updating order
|
||||
* statuses, recording transaction logs, or triggering further business logic.
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\WebHooks\Strategy
|
||||
*/
|
||||
class WebHookTransactionStrategy extends WebHookStrategyBase implements WebhookStrategyActionsInterface {
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function match(string $webhookEntityId): bool {
|
||||
return WebHooksService::TRANSACTION == $webhookEntityId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the relevant entity from the API based on the webhook request.
|
||||
*
|
||||
* This method utilizes the TransactionService to fetch entity details (e.g., transaction data)
|
||||
* based on the space and entity ID provided in the webhook request.
|
||||
*
|
||||
* @param WebHookRequest $request request.
|
||||
* @return object|\VRPayment\Sdk\Model\Transaction
|
||||
* @throws \VRPayment\Sdk\ApiException ApiException.
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException ConnectionException.
|
||||
* @throws \VRPayment\Sdk\VersioningException VersioningException.
|
||||
*/
|
||||
public function getTransaction(WebHookRequest $request) {
|
||||
return $this->settings->getApiClient()
|
||||
->getTransactionService()
|
||||
->read($request->getSpaceId(), $request->getEntityId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getOrderIdByTransaction($transaction): string
|
||||
{
|
||||
/** @var \VRPayment\Sdk\Model\Transaction $transaction */
|
||||
return $transaction->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_ID];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the request state is applicable.
|
||||
*
|
||||
* This method checks if the state of the transaction from a webhook request
|
||||
* is one of the predefined applicable states.
|
||||
*
|
||||
* @param WebHookRequest $request The webhook request containing the transaction state.
|
||||
* @return bool Returns true if the state is applicable, false otherwise.
|
||||
*/
|
||||
public function isRequestStateApplicable(WebHookRequest $request): bool
|
||||
{
|
||||
$applicableStates = [
|
||||
TransactionState::FAILED,
|
||||
TransactionState::DECLINE,
|
||||
TransactionState::VOIDED,
|
||||
TransactionState::FULFILL,
|
||||
TransactionState::AUTHORIZED,
|
||||
];
|
||||
|
||||
return in_array($request->getState(), $applicableStates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the webhook request.
|
||||
*
|
||||
* @param WebHookRequest $request The webhook request object.
|
||||
* @return Response.
|
||||
*/
|
||||
public function process(WebHookRequest $request): Response
|
||||
{
|
||||
return $this->updateTransaction($request, $this->getContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the processing of webhook callbacks related to VRPayment transactions.
|
||||
* This method updates or handles transaction states based on the webhook data received.
|
||||
*
|
||||
* @param WebHookRequest $request The data received from the webhook, encapsulating the transaction details.
|
||||
* @param Context $context The operational context providing settings and environment for transaction processing.
|
||||
* @return Response Returns a JSON response indicating the result of the transaction update operation.
|
||||
*/
|
||||
private function updateTransaction(WebHookRequest $request, Context $context): Response
|
||||
{
|
||||
$status = Response::HTTP_UNPROCESSABLE_ENTITY;
|
||||
|
||||
try {
|
||||
/** @var \Shopware\Core\Checkout\Order\OrderEntity $order */
|
||||
$transaction = $this->getTransaction($request);
|
||||
$orderId = $transaction->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_ID];
|
||||
if(!empty($orderId) && !$transaction->getParent()) {
|
||||
$this->executeLocked($orderId, $context, function () use ($orderId, $transaction, $context, $request) {
|
||||
$this->transactionService->upsert($transaction, $context);
|
||||
$orderTransactionId = $transaction->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_TRANSACTION_ID];
|
||||
$orderTransaction = $this->getOrderTransaction($orderId, $context);
|
||||
$this->logger->info("OrderId: {orderId} Current state: {state}", [
|
||||
'orderId' => $orderId,
|
||||
'state' => $orderTransaction->getStateMachineState()?->getTechnicalName(),
|
||||
]);
|
||||
|
||||
if (!in_array(
|
||||
$orderTransaction->getStateMachineState()?->getTechnicalName(),
|
||||
$this->transactionFinalStates
|
||||
)) {
|
||||
switch ($request->getState()) {
|
||||
case TransactionState::FAILED:
|
||||
$this->orderTransactionStateHandler->fail($orderTransactionId, $context);
|
||||
$this->unholdAndCancelDelivery($orderId, $context);
|
||||
break;
|
||||
case TransactionState::DECLINE:
|
||||
case TransactionState::VOIDED:
|
||||
$this->orderTransactionStateHandler->cancel($orderTransactionId, $context);
|
||||
$this->unholdAndCancelDelivery($orderId, $context);
|
||||
break;
|
||||
case TransactionState::FULFILL:
|
||||
$this->unholdDelivery($orderId, $context);
|
||||
break;
|
||||
case TransactionState::AUTHORIZED:
|
||||
$this->orderTransactionStateHandler->process($orderTransactionId, $context);
|
||||
$this->sendEmail($transaction, $context, $orderId);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
$status = Response::HTTP_OK;
|
||||
} catch (CartException $exception) {
|
||||
$status = Response::HTTP_OK;
|
||||
$this->logRequest($exception, $request, 'info');
|
||||
} catch (IllegalTransitionException $exception) {
|
||||
$status = Response::HTTP_OK;
|
||||
$this->logRequest($exception, $request, 'info');
|
||||
} catch (\Exception $exception) {
|
||||
$this->logRequest($exception, $request, 'critical');
|
||||
}
|
||||
|
||||
return new JsonResponse(['data' => $request->jsonSerialize()], $status);
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\WebHooks\Strategy;
|
||||
|
||||
use VRPaymentPayment\Core\Api\WebHooks\Struct\WebHookRequest;
|
||||
use VRPayment\Sdk\{
|
||||
Model\Refund,
|
||||
Model\RefundState,
|
||||
Model\Transaction,
|
||||
Model\TransactionInvoiceState,
|
||||
Model\TransactionState,
|
||||
Model\TransactionInvoice,
|
||||
ApiException,
|
||||
Http\ConnectionException,
|
||||
VersioningException,};
|
||||
|
||||
/**
|
||||
* Class Entity
|
||||
* Defines a strategy interface for processing webhook requests.
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\WebHooks\Strategy
|
||||
*/
|
||||
interface WebhookStrategyActionsInterface {
|
||||
|
||||
/**
|
||||
* Check if the request state is applicable.
|
||||
*
|
||||
* This method checks if the state of the transaction from a webhook request
|
||||
* is one of the predefined applicable states.
|
||||
*
|
||||
* @param WebHookRequest $request The webhook request containing the transaction state.
|
||||
* @return bool Returns true if the state is applicable, false otherwise.
|
||||
*/
|
||||
public function isRequestStateApplicable(WebHookRequest $request): bool;
|
||||
|
||||
/**
|
||||
* Loads the relevant entity from the API based on the webhook request.
|
||||
*
|
||||
* This method utilizes the TransactionService to fetch entity details
|
||||
* based on the space and entity ID provided in the webhook request.
|
||||
*
|
||||
* @param WebHookRequest $request request.
|
||||
* @return object|Transaction
|
||||
* @throws ApiException ApiException.
|
||||
* @throws ConnectionException ConnectionException.
|
||||
* @throws VersioningException VersioningException.
|
||||
*/
|
||||
public function getTransaction(WebHookRequest $request);
|
||||
|
||||
/**
|
||||
* Get the order ID associated with a transaction.
|
||||
*
|
||||
* This method abstracts the retrieval of an order ID based on a provided metadata transaction object.
|
||||
* Implementing classes must define the specific logic to extract the order ID from the transaction.
|
||||
* The transaction object can be of type Transaction, TransactionInvoiceState, or Refund.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
public function getOrderIdByTransaction($transaction): string;
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\WebHooks\Struct;
|
||||
|
||||
use Shopware\Core\Framework\Struct\Struct;
|
||||
|
||||
/**
|
||||
* Class Entity
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\WebHooks\Struct
|
||||
*/
|
||||
class Entity extends Struct {
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $states;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $notifyEveryChange = false;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getId(): int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return \VRPaymentPayment\Core\Api\WebHooks\Struct\Entity
|
||||
*/
|
||||
public function setId(int $id): Entity
|
||||
{
|
||||
$this->id = $id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return \VRPaymentPayment\Core\Api\WebHooks\Struct\Entity
|
||||
*/
|
||||
public function setName(string $name): Entity
|
||||
{
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getStates(): array
|
||||
{
|
||||
return $this->states;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $states
|
||||
* @return \VRPaymentPayment\Core\Api\WebHooks\Struct\Entity
|
||||
*/
|
||||
public function setStates(array $states): Entity
|
||||
{
|
||||
$this->states = $states;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isNotifyEveryChange(): bool
|
||||
{
|
||||
return $this->notifyEveryChange;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $notifyEveryChange
|
||||
* @return \VRPaymentPayment\Core\Api\WebHooks\Struct\Entity
|
||||
*/
|
||||
public function setNotifyEveryChange(bool $notifyEveryChange): Entity
|
||||
{
|
||||
$this->notifyEveryChange = $notifyEveryChange;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace VRPaymentPayment\Core\Api\WebHooks\Struct;
|
||||
|
||||
use Shopware\Core\Framework\Struct\Struct;
|
||||
|
||||
/**
|
||||
* Class WebHookRequest
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Api\WebHooks\Struct
|
||||
*/
|
||||
class WebHookRequest extends Struct {
|
||||
|
||||
public const PAYMENT_METHOD_CONFIGURATION = 'PaymentMethodConfiguration';
|
||||
public const REFUND = 'Refund';
|
||||
public const TRANSACTION = 'Transaction';
|
||||
public const TRANSACTION_INVOICE = 'TransactionInvoice';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $eventId;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $entityId;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $listenerEntityId;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $listenerEntityTechnicalName;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $spaceId;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $webhookListenerId;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $timestamp;
|
||||
|
||||
/**
|
||||
* Entity state.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getEventId(): int
|
||||
{
|
||||
return $this->eventId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $eventId
|
||||
* @return WebHookRequest
|
||||
*/
|
||||
public function setEventId(int $eventId): WebHookRequest
|
||||
{
|
||||
$this->eventId = $eventId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getEntityId(): int
|
||||
{
|
||||
return $this->entityId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $entityId
|
||||
* @return WebHookRequest
|
||||
*/
|
||||
public function setEntityId(int $entityId): WebHookRequest
|
||||
{
|
||||
$this->entityId = $entityId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getListenerEntityId(): int
|
||||
{
|
||||
return $this->listenerEntityId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $listenerEntityId
|
||||
* @return WebHookRequest
|
||||
*/
|
||||
public function setListenerEntityId(int $listenerEntityId): WebHookRequest
|
||||
{
|
||||
$this->listenerEntityId = $listenerEntityId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getListenerEntityTechnicalName(): string
|
||||
{
|
||||
return $this->listenerEntityTechnicalName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $listenerEntityTechnicalName
|
||||
* @return WebHookRequest
|
||||
*/
|
||||
public function setListenerEntityTechnicalName(string $listenerEntityTechnicalName): WebHookRequest
|
||||
{
|
||||
$this->listenerEntityTechnicalName = $listenerEntityTechnicalName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSpaceId(): int
|
||||
{
|
||||
return $this->spaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $spaceId
|
||||
* @return WebHookRequest
|
||||
*/
|
||||
public function setSpaceId(int $spaceId): WebHookRequest
|
||||
{
|
||||
$this->spaceId = $spaceId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getWebhookListenerId(): int
|
||||
{
|
||||
return $this->webhookListenerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $webhookListenerId
|
||||
* @return WebHookRequest
|
||||
*/
|
||||
public function setWebhookListenerId(int $webhookListenerId): WebHookRequest
|
||||
{
|
||||
$this->webhookListenerId = $webhookListenerId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTimestamp(): string
|
||||
{
|
||||
return $this->timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $timestamp
|
||||
* @return WebHookRequest
|
||||
*/
|
||||
public function setTimestamp(string $timestamp): WebHookRequest
|
||||
{
|
||||
$this->timestamp = $timestamp;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getState(): string
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $state
|
||||
* @return WebHookRequest
|
||||
*/
|
||||
public function setState(string $state): WebHookRequest
|
||||
{
|
||||
$this->state = $state;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Checkout\Cart;
|
||||
|
||||
use Shopware\Core\Checkout\Cart\AbstractCartPersister;
|
||||
use Shopware\Core\Checkout\Cart\Cart;
|
||||
use Shopware\Core\Checkout\Payment\PaymentMethodEntity;
|
||||
use Shopware\Core\System\SalesChannel\SalesChannelContext;
|
||||
use VRPaymentPayment\Core\Checkout\PaymentHandler\VRPaymentPaymentHandler;
|
||||
|
||||
class CustomCartPersister extends AbstractCartPersister
|
||||
{
|
||||
private AbstractCartPersister $inner;
|
||||
|
||||
public function __construct(AbstractCartPersister $inner)
|
||||
{
|
||||
$this->inner = $inner;
|
||||
}
|
||||
|
||||
public function delete(string $token, SalesChannelContext $context): void
|
||||
{
|
||||
if (!$context->getContext()->hasState('do-cart-delete') && $this->isWhiteLabelPayment($context)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->inner->delete($token, $context);
|
||||
}
|
||||
|
||||
public function load(string $token, SalesChannelContext $context): Cart
|
||||
{
|
||||
return $this->inner->load($token, $context);
|
||||
}
|
||||
|
||||
public function save(Cart $cart, SalesChannelContext $context): void
|
||||
{
|
||||
$this->inner->save($cart, $context);
|
||||
}
|
||||
|
||||
public function replace(string $oldToken, string $newToken, SalesChannelContext $context): void
|
||||
{
|
||||
$this->inner->replace($oldToken, $newToken, $context);
|
||||
}
|
||||
|
||||
public function getDecorated(): AbstractCartPersister
|
||||
{
|
||||
return $this->inner;
|
||||
}
|
||||
|
||||
private function isWhiteLabelPayment(SalesChannelContext $context): bool
|
||||
{
|
||||
$paymentMethod = $context->getPaymentMethod();
|
||||
|
||||
if (!$paymentMethod instanceof PaymentMethodEntity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $paymentMethod->getHandlerIdentifier() === VRPaymentPaymentHandler::class;
|
||||
}
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Checkout\PaymentHandler;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shopware\Core\{
|
||||
Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity,
|
||||
Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler,
|
||||
Checkout\Payment\Cart\PaymentTransactionStruct,
|
||||
Checkout\Payment\Cart\PaymentHandler\AbstractPaymentHandler,
|
||||
Checkout\Payment\Cart\PaymentHandler\PaymentHandlerType,
|
||||
Checkout\Payment\PaymentException,
|
||||
Checkout\Payment\Exception\CustomerCanceledAsyncPaymentException,
|
||||
Framework\App\AppException,
|
||||
Framework\Api\Context\SalesChannelApiSource,
|
||||
Framework\Context,
|
||||
Framework\DataAbstractionLayer\EntityRepository,
|
||||
Framework\DataAbstractionLayer\Search\Criteria,
|
||||
Framework\DataAbstractionLayer\Search\Sorting\FieldSorting,
|
||||
Framework\Struct\Struct,
|
||||
Framework\Validation\DataBag\RequestDataBag,
|
||||
System\SalesChannel\Context\SalesChannelContextService,
|
||||
System\SalesChannel\Context\SalesChannelContextServiceParameters
|
||||
};
|
||||
use Shopware\Core\Framework\Util\Random;
|
||||
use VRPaymentPayment\Core\Checkout\Cart\CustomCartPersister;
|
||||
use Shopware\Core\Checkout\Cart\Cart;
|
||||
use Shopware\Core\Checkout\Cart\CartPersister;
|
||||
use Shopware\Core\System\SalesChannel\SalesChannelContext;
|
||||
|
||||
use Symfony\Component\{
|
||||
HttpFoundation\RedirectResponse,
|
||||
HttpFoundation\Request
|
||||
};
|
||||
use VRPayment\Sdk\Model\TransactionState;
|
||||
use VRPaymentPayment\Core\Api\Transaction\Service\TransactionService;
|
||||
|
||||
|
||||
/**
|
||||
* Class VRPaymentPaymentHandler
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Checkout\PaymentHandler
|
||||
*/
|
||||
class VRPaymentPaymentHandler extends AbstractPaymentHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* @var CustomCartPersister
|
||||
*/
|
||||
private CustomCartPersister $cartPersister;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService
|
||||
*/
|
||||
protected $transactionService;
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
/**
|
||||
* @var \Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler
|
||||
*/
|
||||
private $orderTransactionStateHandler;
|
||||
|
||||
protected SalesChannelContextService $salesChannelContextService;
|
||||
|
||||
protected EntityRepository $orderTransactionRepository;
|
||||
|
||||
/**
|
||||
* VRPaymentPaymentHandler constructor.
|
||||
*
|
||||
* @param \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService $transactionService
|
||||
* @param \Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler $orderTransactionStateHandler
|
||||
* @param SalesChannelContextService $salesChannelContextService
|
||||
* @param EntityRepository $orderTransactionRepository
|
||||
*/
|
||||
public function __construct(
|
||||
CustomCartPersister $cartPersister,
|
||||
TransactionService $transactionService,
|
||||
OrderTransactionStateHandler $orderTransactionStateHandler,
|
||||
SalesChannelContextService $salesChannelContextService,
|
||||
EntityRepository $orderTransactionRepository
|
||||
) {
|
||||
$this->cartPersister = $cartPersister;
|
||||
$this->transactionService = $transactionService;
|
||||
$this->orderTransactionStateHandler = $orderTransactionStateHandler;
|
||||
$this->salesChannelContextService = $salesChannelContextService;
|
||||
$this->orderTransactionRepository = $orderTransactionRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
*
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger): void
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* The pay function will be called after the customer completed the order.
|
||||
* Allows to process the order and store additional information.
|
||||
*
|
||||
* A redirect to the url will be performed
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request
|
||||
* @param \Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct $transaction
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @param \Shopware\Core\Framework\Struct\Struct $validateStruct
|
||||
* @return \Symfony\Component\HttpFoundation\RedirectResponse
|
||||
*/
|
||||
public function pay(
|
||||
Request $request,
|
||||
PaymentTransactionStruct $transaction,
|
||||
Context $context,
|
||||
?Struct $validateStruct
|
||||
): RedirectResponse
|
||||
{
|
||||
try {
|
||||
$orderTransactionId = $transaction->getOrderTransactionId();
|
||||
$orderTransaction = $this->orderTransactionRepository->search(
|
||||
(new Criteria([$orderTransactionId]))
|
||||
->addAssociation('order'), $context
|
||||
)->getEntities()->first();
|
||||
|
||||
$contextSource = $context->getSource();
|
||||
if ($contextSource instanceof SalesChannelApiSource) {
|
||||
$salesChannelContextId = $contextSource->getSalesChannelId();
|
||||
}
|
||||
|
||||
$parameters = new SalesChannelContextServiceParameters($salesChannelContextId, $request->getSession()->get("sw-context-token", Random::getAlphanumericString(32)), originalContext: $context);
|
||||
$salesChannelContext = $this->salesChannelContextService->get($parameters);
|
||||
$redirectUrl = $transaction->getReturnUrl();
|
||||
|
||||
if ($orderTransaction->getOrder()->getAmountTotal() > 0) {
|
||||
$transactionId = $_SESSION['transactionId'] ?? null;
|
||||
if ($transactionId === null) {
|
||||
$this->transactionService->createPendingTransaction($transaction, $salesChannelContext);
|
||||
}
|
||||
$redirectUrl = $this->transactionService->create($transaction, $salesChannelContext);
|
||||
}
|
||||
return new RedirectResponse($redirectUrl);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
unset($_SESSION['transactionId']);
|
||||
$errorMessage = 'An error occurred during the communication with external payment gateway : ' . $e->getMessage();
|
||||
$this->logger->critical($errorMessage);
|
||||
throw PaymentException::customerCanceled($transaction->getOrderTransaction()->getId(), $errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The finalize function will be called when the user is redirected back to shop from the payment gateway.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* @param \Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct $transaction
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
* @throws \Exception when the payment was canceled by the customer
|
||||
*/
|
||||
public function finalize(
|
||||
Request $request,
|
||||
PaymentTransactionStruct $transaction,
|
||||
Context $context
|
||||
): void
|
||||
{
|
||||
$orderTransactionId = $transaction->getOrderTransactionId();
|
||||
$orderTransaction = $this->orderTransactionRepository->search(
|
||||
(new Criteria([$orderTransactionId]))
|
||||
->addAssociation('order'), $context
|
||||
)->getEntities()->first();
|
||||
|
||||
if ($orderTransaction->getOrder()->getAmountTotal() > 0) {
|
||||
$transactionEntity = $this->transactionService->getByOrderId(
|
||||
$orderTransaction->getOrder()->getId(),
|
||||
$context
|
||||
);
|
||||
|
||||
$vRPaymentTransaction = $this->transactionService->read(
|
||||
$transactionEntity->getTransactionId(),
|
||||
$transactionEntity->getSalesChannelId()
|
||||
);
|
||||
|
||||
if (in_array($vRPaymentTransaction->getState(), [TransactionState::FAILED])) {
|
||||
$errorMessage = strtr('Customer canceled payment for :orderId on SalesChannel :salesChannelName', [
|
||||
':orderId' => $orderTransaction->getOrder()->getId(),
|
||||
':salesChannelName' => $transactionEntity->getSalesChannelId(),
|
||||
]);
|
||||
unset($_SESSION['transactionId']);
|
||||
$this->logger->info($errorMessage);
|
||||
throw PaymentException::customerCanceled($transaction->getOrderTransaction()->getId(), $errorMessage);
|
||||
}
|
||||
} else {
|
||||
$this->orderTransactionStateHandler->paid($orderTransaction->getId(), $context);
|
||||
}
|
||||
|
||||
$token = $request->getSession()->get('sw-context-token');
|
||||
if ($token) {
|
||||
$salesChannelId = $transactionEntity->getSalesChannelId();
|
||||
$parameters = new SalesChannelContextServiceParameters($salesChannelId, $token, originalContext: $context);
|
||||
$salesChannelContext = $this->salesChannelContextService->get($parameters);
|
||||
|
||||
$salesChannelContext->getContext()->addState('do-cart-delete');
|
||||
$this->logger->info('Clearing cart with token: ' . $token);
|
||||
$this->cartPersister->delete($salesChannelContext->getToken(), $salesChannelContext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function supports(
|
||||
PaymentHandlerType $type,
|
||||
string $paymentMethodId,
|
||||
Context $context
|
||||
): bool {
|
||||
if ($type === PaymentHandlerType::RECURRING) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Settings\Command;
|
||||
|
||||
use Symfony\Component\{
|
||||
Console\Command\Command,
|
||||
Console\Attribute\AsCommand,
|
||||
Console\Input\InputInterface,
|
||||
Console\Input\InputArgument,
|
||||
Console\Input\InputOption,
|
||||
Console\Output\OutputInterface,
|
||||
PasswordHasher\Hasher\UserPasswordHasherInterface};
|
||||
use Shopware\Core\Framework\{
|
||||
DataAbstractionLayer\EntityRepository,
|
||||
DataAbstractionLayer\EntityRepositoryInterface,
|
||||
DataAbstractionLayer\Search\Criteria,
|
||||
DataAbstractionLayer\Search\Filter\EqualsFilter,
|
||||
Uuid\Uuid,
|
||||
Context};
|
||||
use VRPaymentPayment\Core\{
|
||||
Settings\Options\Integration,
|
||||
Settings\Service\SettingsService};
|
||||
|
||||
/**
|
||||
* Class CreateMerchantCommand
|
||||
* @internal
|
||||
* @package VRPaymentPayment\Core\Settings\Command
|
||||
*/
|
||||
#[AsCommand(name: 'vrpayment:settings:create-merchant')]
|
||||
class CreateMerchantCommand extends Command {
|
||||
|
||||
/**
|
||||
* @var EntityRepositoryInterface Repository for user entities
|
||||
*/
|
||||
private $userRepository;
|
||||
|
||||
/**
|
||||
* @var EntityRepositoryInterface Repository for user role entities
|
||||
*/
|
||||
private $userRoleRepository;
|
||||
|
||||
/**
|
||||
* @var EntityRepositoryInterface Repository for locale entities
|
||||
*/
|
||||
private $localeRepository;
|
||||
|
||||
/**
|
||||
* CreateMerchantUserCommand constructor.
|
||||
*
|
||||
* @param EntityRepositoryInterface $userRepository
|
||||
* @param EntityRepositoryInterface $userRoleRepository
|
||||
* @param EntityRepositoryInterface $localeRepository
|
||||
*/
|
||||
public function __construct(
|
||||
EntityRepository $userRepository,
|
||||
EntityRepository $userRoleRepository,
|
||||
EntityRepository $localeRepository,
|
||||
) {
|
||||
parent::__construct();
|
||||
$this->userRepository = $userRepository;
|
||||
$this->userRoleRepository = $userRoleRepository;
|
||||
$this->localeRepository = $localeRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the command to create a new merchant user with a specific role.
|
||||
*
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int Command::SUCCESS on success, Command::FAILURE on failure
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$output->writeln('Creating VRPaymentPayment merchant with custom role...');
|
||||
|
||||
$firstName = $input->getOption('firstName');
|
||||
$lastName = $input->getOption('lastName');
|
||||
$email = $input->getOption('email') ?? 'merchant@merchant.com';
|
||||
$password = $input->getOption('password') ?? 'merchant123';
|
||||
|
||||
$context = Context::createDefaultContext();
|
||||
|
||||
// Check if user already exists
|
||||
$criteria = new Criteria();
|
||||
$criteria->addFilter(new EqualsFilter('email', $email));
|
||||
$existingUser = $this->userRepository->search($criteria, $context)->first();
|
||||
|
||||
if ($existingUser) {
|
||||
$output->writeln('User already exists.');
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
// Create role if it doesn't exist
|
||||
$roleId = $this->getOrCreateRoleId('VRPayment viewer', $context);
|
||||
|
||||
// Create user if it doesn't exist
|
||||
$this->userRepository->create([
|
||||
[
|
||||
'id' => Uuid::randomHex(),
|
||||
'username' => $email,
|
||||
'email' => $email,
|
||||
'firstName' => $firstName,
|
||||
'lastName' => $lastName,
|
||||
'password' => $password,
|
||||
'admin' => false,
|
||||
'localeId' => $this->getLocaleId($context),
|
||||
'aclRoles' => [
|
||||
[
|
||||
'id' => $roleId
|
||||
]
|
||||
],
|
||||
]
|
||||
], $context);
|
||||
|
||||
$output->writeln('Merchant user created successfully.');
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the default locale ID.
|
||||
*
|
||||
* @param Context $context
|
||||
* @return string Locale ID
|
||||
* @throws \RuntimeException If the default locale is not found
|
||||
*/
|
||||
private function getLocaleId(Context $context): string
|
||||
{
|
||||
// Fetch the default locale id
|
||||
$criteria = new Criteria();
|
||||
$criteria->addFilter(new EqualsFilter('code', 'en-GB'));
|
||||
$localeId = $this->localeRepository->searchIds($criteria, $context)->firstId();
|
||||
|
||||
if (!$localeId) {
|
||||
throw new \RuntimeException('Default locale not found');
|
||||
}
|
||||
|
||||
return $localeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the role ID for a given role name or creates the role if it does not exist.
|
||||
*
|
||||
* @param string $roleName
|
||||
* @param Context $context
|
||||
* @return string Role ID
|
||||
* @throws \RuntimeException If the role cannot be created or found
|
||||
*/
|
||||
private function getOrCreateRoleId(string $roleName, Context $context): string
|
||||
{
|
||||
$criteria = new Criteria();
|
||||
$criteria->addFilter(new EqualsFilter('name', $roleName));
|
||||
$roleId = $this->userRoleRepository->searchIds($criteria, $context)->firstId();
|
||||
|
||||
if (!$roleId) {
|
||||
$roleId = Uuid::randomHex();
|
||||
$this->userRoleRepository->create([
|
||||
[
|
||||
'id' => $roleId,
|
||||
'name' => $roleName,
|
||||
'privileges' => [
|
||||
'vrpayment.viewer',
|
||||
'vrpayment_sales_channel:read',
|
||||
'vrpayment_sales_channel_run:read',
|
||||
'vrpayment_sales_channel_run_log:read',
|
||||
'language:read',
|
||||
'locale:read',
|
||||
'system_config:read'
|
||||
]
|
||||
]
|
||||
], $context);
|
||||
}
|
||||
|
||||
return $roleId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the current command.
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setDescription('Creates a new merchant user with specific roles.')
|
||||
->addOption('firstName', null, InputOption::VALUE_OPTIONAL, 'First name of the merchant user', 'Merchant')
|
||||
->addOption('lastName', null, InputOption::VALUE_OPTIONAL, 'Last name of the merchant user', 'Merchant')
|
||||
->addOption('email', null, InputOption::VALUE_OPTIONAL, 'Email of the merchant user')
|
||||
->addOption('password', null, InputOption::VALUE_OPTIONAL, 'Password of the merchant user');
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Settings\Command;
|
||||
|
||||
use Symfony\Component\{
|
||||
Console\Command\Command,
|
||||
Console\Attribute\AsCommand,
|
||||
Console\Input\InputInterface,
|
||||
Console\Input\InputOption,
|
||||
Console\Output\OutputInterface};
|
||||
use VRPaymentPayment\Core\{
|
||||
Settings\Options\Integration,
|
||||
Settings\Service\SettingsService};
|
||||
|
||||
/**
|
||||
* Class SettingsCommand
|
||||
* @internal
|
||||
* @package VRPaymentPayment\Core\Settings\Command
|
||||
*/
|
||||
#[AsCommand(name: 'vrpayment:settings:install')]
|
||||
class SettingsCommand extends Command {
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Settings\Service\SettingsService
|
||||
*/
|
||||
protected $settingsService;
|
||||
|
||||
/**
|
||||
* SettingsCommand constructor.
|
||||
* @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService
|
||||
*/
|
||||
public function __construct(SettingsService $settingsService)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->settingsService = $settingsService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$output->writeln('Set VRPaymentPayment settings...');
|
||||
$this->settingsService->updateSettings([
|
||||
SettingsService::CONFIG_APPLICATION_KEY => $input->getOption(SettingsService::CONFIG_APPLICATION_KEY),
|
||||
SettingsService::CONFIG_EMAIL_ENABLED => $input->getOption(SettingsService::CONFIG_EMAIL_ENABLED),
|
||||
SettingsService::CONFIG_INTEGRATION => $input->getOption(SettingsService::CONFIG_INTEGRATION),
|
||||
SettingsService::CONFIG_LINE_ITEM_CONSISTENCY_ENABLED => $input->getOption(SettingsService::CONFIG_LINE_ITEM_CONSISTENCY_ENABLED),
|
||||
SettingsService::CONFIG_SPACE_ID => $input->getOption(SettingsService::CONFIG_SPACE_ID),
|
||||
SettingsService::CONFIG_SPACE_VIEW_ID => $input->getOption(SettingsService::CONFIG_SPACE_VIEW_ID),
|
||||
SettingsService::CONFIG_STOREFRONT_INVOICE_DOWNLOAD_ENABLED => $input->getOption(SettingsService::CONFIG_STOREFRONT_INVOICE_DOWNLOAD_ENABLED),
|
||||
SettingsService::CONFIG_USER_ID => $input->getOption(SettingsService::CONFIG_USER_ID),
|
||||
SettingsService::CONFIG_STOREFRONT_WEBHOOKS_UPDATE_ENABLED => $input->getOption(SettingsService::CONFIG_STOREFRONT_WEBHOOKS_UPDATE_ENABLED),
|
||||
SettingsService::CONFIG_STOREFRONT_PAYMENTS_UPDATE_ENABLED => $input->getOption(SettingsService::CONFIG_STOREFRONT_PAYMENTS_UPDATE_ENABLED),
|
||||
]);
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the current command.
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDescription('Sets VRPaymentPayment settings.')
|
||||
->setHelp('This command updates VRPaymentPayment settings for all SalesChannels.')
|
||||
->addOption(
|
||||
SettingsService::CONFIG_APPLICATION_KEY,
|
||||
SettingsService::CONFIG_APPLICATION_KEY,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
SettingsService::CONFIG_APPLICATION_KEY
|
||||
)
|
||||
->addOption(
|
||||
SettingsService::CONFIG_SPACE_ID,
|
||||
SettingsService::CONFIG_SPACE_ID,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
SettingsService::CONFIG_SPACE_ID
|
||||
)
|
||||
->addOption(
|
||||
SettingsService::CONFIG_USER_ID,
|
||||
SettingsService::CONFIG_USER_ID,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
SettingsService::CONFIG_USER_ID
|
||||
)
|
||||
->addOption(
|
||||
SettingsService::CONFIG_EMAIL_ENABLED,
|
||||
SettingsService::CONFIG_EMAIL_ENABLED,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
SettingsService::CONFIG_EMAIL_ENABLED,
|
||||
true
|
||||
)
|
||||
->addOption(
|
||||
SettingsService::CONFIG_INTEGRATION,
|
||||
SettingsService::CONFIG_INTEGRATION,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
SettingsService::CONFIG_INTEGRATION,
|
||||
Integration::PAYMENT_PAGE
|
||||
)
|
||||
->addOption(
|
||||
SettingsService::CONFIG_LINE_ITEM_CONSISTENCY_ENABLED,
|
||||
SettingsService::CONFIG_LINE_ITEM_CONSISTENCY_ENABLED,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
SettingsService::CONFIG_LINE_ITEM_CONSISTENCY_ENABLED,
|
||||
true
|
||||
)
|
||||
->addOption(
|
||||
SettingsService::CONFIG_STOREFRONT_INVOICE_DOWNLOAD_ENABLED,
|
||||
SettingsService::CONFIG_STOREFRONT_INVOICE_DOWNLOAD_ENABLED,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
SettingsService::CONFIG_STOREFRONT_INVOICE_DOWNLOAD_ENABLED,
|
||||
true
|
||||
)
|
||||
->addOption(
|
||||
SettingsService::CONFIG_SPACE_VIEW_ID,
|
||||
SettingsService::CONFIG_SPACE_VIEW_ID,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
SettingsService::CONFIG_SPACE_VIEW_ID,
|
||||
''
|
||||
)
|
||||
->addOption(
|
||||
SettingsService::CONFIG_STOREFRONT_WEBHOOKS_UPDATE_ENABLED,
|
||||
SettingsService::CONFIG_STOREFRONT_WEBHOOKS_UPDATE_ENABLED,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
SettingsService::CONFIG_STOREFRONT_WEBHOOKS_UPDATE_ENABLED,
|
||||
true
|
||||
) ->addOption(
|
||||
SettingsService::CONFIG_STOREFRONT_PAYMENTS_UPDATE_ENABLED,
|
||||
SettingsService::CONFIG_STOREFRONT_PAYMENTS_UPDATE_ENABLED,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
SettingsService::CONFIG_STOREFRONT_PAYMENTS_UPDATE_ENABLED,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Settings\Options;
|
||||
|
||||
/**
|
||||
* Class Integration
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Settings\Options
|
||||
*/
|
||||
class Integration {
|
||||
|
||||
/**
|
||||
* Possible values of this enum
|
||||
*/
|
||||
public const CHARGE_FLOW = 'charge_flow';
|
||||
public const DIRECT_CARD_PROCESSING = 'direct_card_processing';
|
||||
public const IFRAME = 'iframe';
|
||||
public const LIGHTBOX = 'lightbox';
|
||||
public const MOBILE_WEB = 'mobile_web_view';
|
||||
public const PAYMENT_LINK = 'payment_link';
|
||||
public const PAYMENT_PAGE = 'payment_page';
|
||||
public const TERMINAL = 'terminal';
|
||||
|
||||
|
||||
/**
|
||||
* Gets allowable values of the enum
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getAllowableEnumValues(): array
|
||||
{
|
||||
return [
|
||||
self::CHARGE_FLOW,
|
||||
self::DIRECT_CARD_PROCESSING,
|
||||
self::IFRAME,
|
||||
self::LIGHTBOX,
|
||||
self::MOBILE_WEB,
|
||||
self::PAYMENT_LINK,
|
||||
self::PAYMENT_PAGE,
|
||||
self::TERMINAL,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Settings\Service;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shopware\Core\System\SystemConfig\SystemConfigService;
|
||||
use VRPaymentPayment\Core\Settings\Struct\Settings;
|
||||
|
||||
|
||||
/**
|
||||
* Class SettingsService
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Settings\Service
|
||||
*/
|
||||
class SettingsService {
|
||||
|
||||
/**
|
||||
* Prefix to VRPayment configs
|
||||
*/
|
||||
public const SYSTEM_CONFIG_DOMAIN = 'VRPaymentPayment.config.';
|
||||
public const CONFIG_APPLICATION_KEY = 'applicationKey';
|
||||
public const CONFIG_EMAIL_ENABLED = 'emailEnabled';
|
||||
public const CONFIG_INTEGRATION = 'integration';
|
||||
public const CONFIG_LINE_ITEM_CONSISTENCY_ENABLED = 'lineItemConsistencyEnabled';
|
||||
public const CONFIG_SPACE_ID = 'spaceId';
|
||||
public const CONFIG_SPACE_VIEW_ID = 'spaceViewId';
|
||||
public const CONFIG_STOREFRONT_INVOICE_DOWNLOAD_ENABLED = 'storefrontInvoiceDownloadEnabled';
|
||||
public const CONFIG_USER_ID = 'userId';
|
||||
public const CONFIG_STOREFRONT_WEBHOOKS_UPDATE_ENABLED = 'storefrontWebhooksUpdateEnabled';
|
||||
public const CONFIG_STOREFRONT_PAYMENTS_UPDATE_ENABLED = 'storefrontPaymentsUpdateEnabled';
|
||||
|
||||
/**
|
||||
* List of config properties whose values allowed to be empty without triggering a warning in logger.
|
||||
*
|
||||
* This list is derived from testing of all config properties. The plugin fails only when either spaceId, userId, applicationKey and/or integration is empty.
|
||||
* On top of that, spaceId, userId, applicationKey are marked as "required" input fields in admin interface.
|
||||
*
|
||||
* It is worth considering updating this list whenever a new config is introduced in settings.
|
||||
* If new config is optional, left empty by design and not required for transactions to work, this list should be updated to avoid false-positive warnings.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private const ALLOWED_EMPTY_CONFIGS = [
|
||||
// Options
|
||||
self::CONFIG_SPACE_VIEW_ID,
|
||||
self::CONFIG_LINE_ITEM_CONSISTENCY_ENABLED,
|
||||
self::CONFIG_EMAIL_ENABLED,
|
||||
|
||||
// Storefront Options
|
||||
self::CONFIG_STOREFRONT_INVOICE_DOWNLOAD_ENABLED,
|
||||
|
||||
// Advanced Options
|
||||
self::CONFIG_STOREFRONT_WEBHOOKS_UPDATE_ENABLED,
|
||||
self::CONFIG_STOREFRONT_PAYMENTS_UPDATE_ENABLED
|
||||
];
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\System\SystemConfig\SystemConfigService
|
||||
*/
|
||||
private $systemConfigService;
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* SettingsService constructor.
|
||||
*
|
||||
* @param \Shopware\Core\System\SystemConfig\SystemConfigService $systemConfigService
|
||||
*/
|
||||
public function __construct(SystemConfigService $systemConfigService)
|
||||
{
|
||||
$this->systemConfigService = $systemConfigService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
* @internal
|
||||
* @required
|
||||
*
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger): void
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update setting
|
||||
*
|
||||
* @param array $settings
|
||||
* @param string|null $salesChannelId
|
||||
*/
|
||||
public function updateSettings(array $settings, ?string $salesChannelId = null): void
|
||||
{
|
||||
foreach ($settings as $key => $value) {
|
||||
$this->systemConfigService->set(
|
||||
self::SYSTEM_CONFIG_DOMAIN . $key,
|
||||
$value,
|
||||
$salesChannelId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get valid settings
|
||||
*
|
||||
* @param string|null $salesChannelId
|
||||
* @return \VRPaymentPayment\Core\Settings\Struct\Settings|null
|
||||
*/
|
||||
public function getValidSettings(?string $salesChannelId = null): ?Settings
|
||||
{
|
||||
$settings = $this->getSettings($salesChannelId);
|
||||
|
||||
if (empty($settings->getSpaceId())) {
|
||||
$this->logger->critical('Empty spaceId setting');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (empty($settings->getUserId())) {
|
||||
$this->logger->critical('Empty userId setting');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (empty($settings->getIntegration())) {
|
||||
$this->logger->critical('Empty integration setting');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (empty($settings->getApplicationKey())) {
|
||||
$this->logger->critical('Empty applicationKey setting');
|
||||
return null;
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get settings
|
||||
*
|
||||
* @param string|null $salesChannelId
|
||||
* @return \VRPaymentPayment\Core\Settings\Struct\Settings
|
||||
*/
|
||||
public function getSettings(?string $salesChannelId = null): Settings
|
||||
{
|
||||
$values = $this->systemConfigService->getDomain(
|
||||
self::SYSTEM_CONFIG_DOMAIN,
|
||||
$salesChannelId,
|
||||
true
|
||||
);
|
||||
|
||||
$propertyValuePairs = [];
|
||||
|
||||
/** @var string $key */
|
||||
foreach ($values as $key => $value) {
|
||||
$property = (string) \mb_substr($key, \mb_strlen(self::SYSTEM_CONFIG_DOMAIN));
|
||||
if ($property === '') {
|
||||
continue;
|
||||
}
|
||||
// Space view id is only numeric setting which can be 0. If it is, rest of the loop is skipped.
|
||||
if ($property === self::CONFIG_SPACE_VIEW_ID && $value === 0) {
|
||||
$propertyValuePairs[$property] = $value;
|
||||
continue;
|
||||
}
|
||||
// Check if $value is empty and is not in the list of configs which are allowed to be empty
|
||||
if (empty($value) && !in_array($property, self::ALLOWED_EMPTY_CONFIGS, true)) {
|
||||
$this->logger->warning(strtr('Empty value :value for settings :property.', [':property' => $property, ':value' => $value]));
|
||||
}
|
||||
$propertyValuePairs[$property] = $value;
|
||||
}
|
||||
|
||||
return (new Settings())->assign($propertyValuePairs);
|
||||
}
|
||||
}
|
||||
@@ -1,271 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Settings\Struct;
|
||||
|
||||
use Shopware\Core\Framework\Struct\Struct;
|
||||
use VRPayment\Sdk\ApiClient;
|
||||
use VRPaymentPayment\Core\Util\Analytics\Analytics;
|
||||
|
||||
/**
|
||||
* Class Settings
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Settings\Struct
|
||||
*/
|
||||
class Settings extends Struct {
|
||||
|
||||
/**
|
||||
* @var \VRPayment\Sdk\ApiClient
|
||||
*/
|
||||
protected $apiClient;
|
||||
|
||||
/**
|
||||
* Application Key
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $applicationKey;
|
||||
|
||||
/**
|
||||
* Enable emails
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $emailEnabled = true;
|
||||
|
||||
/**
|
||||
* Preferred integration
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $integration;
|
||||
|
||||
/**
|
||||
* Enforce line item consistency
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $lineItemConsistencyEnabled;
|
||||
|
||||
/**
|
||||
* Enable storefront invoice download
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $storefrontInvoiceDownloadEnabled = true;
|
||||
|
||||
/**
|
||||
* Space Id
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $spaceId;
|
||||
|
||||
/**
|
||||
* Space View Id
|
||||
*
|
||||
* @var ?int
|
||||
*/
|
||||
protected $spaceViewId;
|
||||
|
||||
/**
|
||||
* Enable webhooks update
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $webhooksUpdate = true;
|
||||
|
||||
/**
|
||||
* Enable payments update
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $paymentsUpdate = true;
|
||||
|
||||
/**
|
||||
* User id
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $userId;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailEnabled(): bool
|
||||
{
|
||||
return boolval($this->emailEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $emailEnabled
|
||||
*/
|
||||
public function setEmailEnabled(bool $emailEnabled): void
|
||||
{
|
||||
$this->emailEnabled = $emailEnabled;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getIntegration(): string
|
||||
{
|
||||
return strval($this->integration);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $integration
|
||||
*/
|
||||
public function setIntegration(string $integration): void
|
||||
{
|
||||
$this->integration = $integration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isLineItemConsistencyEnabled(): bool
|
||||
{
|
||||
return boolval($this->lineItemConsistencyEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $lineItemConsistencyEnabled
|
||||
*/
|
||||
public function setLineItemConsistencyEnabled(bool $lineItemConsistencyEnabled): void
|
||||
{
|
||||
$this->lineItemConsistencyEnabled = $lineItemConsistencyEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isStorefrontInvoiceDownloadEnabled(): bool
|
||||
{
|
||||
return boolval($this->storefrontInvoiceDownloadEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $storefrontInvoiceDownloadEnabled
|
||||
*/
|
||||
public function setStorefrontInvoiceDownloadEnabled(bool $storefrontInvoiceDownloadEnabled): void
|
||||
{
|
||||
$this->storefrontInvoiceDownloadEnabled = $storefrontInvoiceDownloadEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSpaceId(): int
|
||||
{
|
||||
return intval($this->spaceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $spaceId
|
||||
*/
|
||||
public function setSpaceId(int $spaceId): void
|
||||
{
|
||||
$this->spaceId = $spaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
public function getSpaceViewId(): ?int
|
||||
{
|
||||
if (!empty($this->spaceViewId) && is_numeric($this->spaceViewId)) {
|
||||
return intval($this->spaceViewId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $spaceViewId
|
||||
*/
|
||||
public function setSpaceViewId(int $spaceViewId): void
|
||||
{
|
||||
$this->spaceViewId = $spaceViewId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isWebhooksUpdateEnabled(): bool
|
||||
{
|
||||
return boolval($this->webhooksUpdate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $webhooksUpdate
|
||||
*/
|
||||
public function setWebhooksEnabled(bool $webhooksUpdate): void
|
||||
{
|
||||
$this->webhooksUpdate = $webhooksUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isPaymentsUpdateEnabled(): bool
|
||||
{
|
||||
return boolval($this->paymentsUpdate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $paymentsUpdate
|
||||
*/
|
||||
public function setPaymentsEnabled(bool $paymentsUpdate): void
|
||||
{
|
||||
$this->paymentsUpdate = $paymentsUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get SDK ApiClient
|
||||
*
|
||||
* @return \VRPayment\Sdk\ApiClient
|
||||
*/
|
||||
public function getApiClient(): ApiClient
|
||||
{
|
||||
if (is_null($this->apiClient)) {
|
||||
$this->apiClient = new ApiClient($this->getUserId(), $this->getApplicationKey());
|
||||
$apiClientBasePath = getenv('VRPAYMENT_API_BASE_PATH') ? getenv('VRPAYMENT_API_BASE_PATH') : $this->apiClient->getBasePath();
|
||||
$this->apiClient->setBasePath($apiClientBasePath);
|
||||
Analytics::addHeaders($this->apiClient);
|
||||
}
|
||||
return $this->apiClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getUserId(): int
|
||||
{
|
||||
return intval($this->userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $userId
|
||||
*/
|
||||
public function setUserId(int $userId): void
|
||||
{
|
||||
$this->userId = $userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getApplicationKey(): string
|
||||
{
|
||||
return strval($this->applicationKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $applicationKey
|
||||
*/
|
||||
public function setApplicationKey(string $applicationKey): void
|
||||
{
|
||||
$this->applicationKey = $applicationKey;
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Storefront\Account\Controller;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shopware\Core\{
|
||||
Checkout\Cart\Exception\CustomerNotLoggedInException,
|
||||
Checkout\Customer\CustomerEntity,
|
||||
PlatformRequest,
|
||||
System\SalesChannel\SalesChannelContext
|
||||
};
|
||||
use Shopware\Storefront\Controller\StorefrontController;
|
||||
use Shopware\Core\Framework\Log\Package;
|
||||
use Symfony\Component\{
|
||||
HttpFoundation\HeaderUtils,
|
||||
HttpFoundation\RequestStack,
|
||||
HttpFoundation\Response,
|
||||
Routing\Attribute\Route,
|
||||
Security\Core\Exception\AccessDeniedException
|
||||
};
|
||||
use VRPaymentPayment\Core\{
|
||||
Api\Transaction\Service\TransactionService,
|
||||
Settings\Service\SettingsService
|
||||
};
|
||||
|
||||
#[Package('storefront')]
|
||||
#[Route(defaults: ['_routeScope' => ['storefront']])]
|
||||
class AccountOrderController extends StorefrontController
|
||||
{
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Settings\Service\SettingsService
|
||||
*/
|
||||
protected $settingsService;
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService
|
||||
*/
|
||||
protected $transactionService;
|
||||
|
||||
/**
|
||||
* @var RequestStack
|
||||
*/
|
||||
protected $requestStack;
|
||||
|
||||
/**
|
||||
* AccountOrderController constructor.
|
||||
* @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService
|
||||
* @param \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService $transactionService
|
||||
* @param RequestStack $requestStack
|
||||
*/
|
||||
public function __construct(SettingsService $settingsService, TransactionService $transactionService, RequestStack $requestStack)
|
||||
{
|
||||
$this->settingsService = $settingsService;
|
||||
$this->transactionService = $transactionService;
|
||||
$this->requestStack = $requestStack;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
* @internal
|
||||
* @required
|
||||
*
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger): void
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Download invoice document
|
||||
*
|
||||
* @param string $orderId
|
||||
* @param \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*/
|
||||
#[Route("/vrpayment/account/order/download/invoice/document/{orderId}",
|
||||
name: "frontend.vrpayment.account.order.download.invoice.document",
|
||||
methods: ['GET'])]
|
||||
public function downloadInvoiceDocument(string $orderId, SalesChannelContext $salesChannelContext): Response
|
||||
{
|
||||
$customer = $this->getLoggedInCustomer();
|
||||
$settings = $this->settingsService->getSettings($salesChannelContext->getSalesChannel()->getId());
|
||||
$transactionEntity = $this->transactionService->getByOrderId($orderId, $salesChannelContext->getContext());
|
||||
if (strcasecmp($customer->getCustomerNumber(), $transactionEntity->getData()['customerId']) != 0) {
|
||||
throw new AccessDeniedException();
|
||||
}
|
||||
$invoiceDocument = $settings->getApiClient()->getTransactionService()->getInvoiceDocument($settings->getSpaceId(), $transactionEntity->getTransactionId());
|
||||
$forceDownload = true;
|
||||
$filename = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '_', $invoiceDocument->getTitle()) . '.pdf';
|
||||
$disposition = HeaderUtils::makeDisposition(
|
||||
$forceDownload ? HeaderUtils::DISPOSITION_ATTACHMENT : HeaderUtils::DISPOSITION_INLINE,
|
||||
$filename,
|
||||
$filename
|
||||
);
|
||||
$response = new Response(base64_decode($invoiceDocument->getData()));
|
||||
$response->headers->set('Content-Type', $invoiceDocument->getMimeType());
|
||||
$response->headers->set('Content-Disposition', $disposition);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CustomerEntity
|
||||
*/
|
||||
protected function getLoggedInCustomer(): CustomerEntity
|
||||
{
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
|
||||
if (!$request) {
|
||||
throw new CustomerNotLoggedInException();
|
||||
}
|
||||
|
||||
$context = $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT);
|
||||
|
||||
if ($context && $context->getCustomer() && $context->getCustomer()->getGuest() === false) {
|
||||
return $context->getCustomer();
|
||||
}
|
||||
|
||||
throw new CustomerNotLoggedInException();
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Storefront\Account\Subscriber;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shopware\Core\Framework\Struct\ArrayStruct;
|
||||
use Shopware\Storefront\Page\Account\Order\AccountOrderPageLoadedEvent;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use VRPaymentPayment\Core\Settings\Service\SettingsService;
|
||||
|
||||
/**
|
||||
* Class AccountOrderSubscriber
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Storefront\Account\Subscriber
|
||||
*/
|
||||
class AccountOrderSubscriber implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Settings\Service\SettingsService
|
||||
*/
|
||||
private $settingsService;
|
||||
|
||||
/**
|
||||
* CheckoutSubscriber constructor.
|
||||
*
|
||||
* @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService
|
||||
*/
|
||||
public function __construct(SettingsService $settingsService)
|
||||
{
|
||||
$this->settingsService = $settingsService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
* @internal
|
||||
* @required
|
||||
*
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger): void
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
AccountOrderPageLoadedEvent::class => ['onAccountOrderPageLoaded', 1],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pass settings to template
|
||||
*
|
||||
* @param \Shopware\Storefront\Page\Account\Order\AccountOrderPageLoadedEvent $event
|
||||
*/
|
||||
public function onAccountOrderPageLoaded(AccountOrderPageLoadedEvent $event): void
|
||||
{
|
||||
$vrpaymentSettings = new ArrayStruct();
|
||||
$vrpaymentSettings->set(SettingsService::CONFIG_STOREFRONT_INVOICE_DOWNLOAD_ENABLED, false);
|
||||
try {
|
||||
$settings = $this->settingsService->getValidSettings($event->getSalesChannelContext()->getSalesChannel()->getId());
|
||||
if (is_null($settings)) {
|
||||
$this->logger->notice('Disabling invoice downloads');
|
||||
} else {
|
||||
$vrpaymentSettings->set(SettingsService::CONFIG_STOREFRONT_INVOICE_DOWNLOAD_ENABLED, $settings->isStorefrontInvoiceDownloadEnabled());
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error($e->getMessage());
|
||||
}
|
||||
|
||||
$event->getPage()->addExtension('vrpaymentSettings', $vrpaymentSettings);
|
||||
}
|
||||
}
|
||||
@@ -1,568 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Storefront\Checkout\Controller;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shopware\Core\{
|
||||
Checkout\Cart\Cart,
|
||||
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,
|
||||
Framework\Context,
|
||||
Framework\DataAbstractionLayer\Search\Criteria,
|
||||
Framework\DataAbstractionLayer\Search\Filter\EqualsFilter,
|
||||
Framework\DataAbstractionLayer\Search\Sorting\FieldSorting,
|
||||
Framework\Log\Package,
|
||||
Framework\Routing\Exception\MissingRequestParameterException,
|
||||
Framework\Uuid\Uuid,
|
||||
Framework\Uuid\Exception\InvalidUuidException,
|
||||
Framework\Validation\DataBag\RequestDataBag,
|
||||
System\SalesChannel\SalesChannelContext
|
||||
};
|
||||
use Shopware\Storefront\{
|
||||
Controller\StorefrontController,
|
||||
Page\Checkout\Finish\CheckoutFinishPage,
|
||||
Page\GenericPageLoaderInterface
|
||||
};
|
||||
use Symfony\Component\{
|
||||
HttpFoundation\Request,
|
||||
HttpFoundation\Response,
|
||||
Routing\Attribute\Route,
|
||||
Routing\Generator\UrlGeneratorInterface
|
||||
};
|
||||
use VRPayment\Sdk\{
|
||||
Model\Transaction,
|
||||
Model\TransactionState
|
||||
};
|
||||
use VRPaymentPayment\Core\{
|
||||
Api\Transaction\Service\TransactionService,
|
||||
Settings\Options\Integration,
|
||||
Settings\Service\SettingsService,
|
||||
Storefront\Checkout\Struct\CheckoutPageData,
|
||||
Util\Payload\CustomProducts\CustomProductsLineItemTypes
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class CheckoutController
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Storefront\Checkout\Controller
|
||||
*
|
||||
*/
|
||||
#[Package('checkout')]
|
||||
#[Route(defaults: ['_routeScope' => ['storefront']])]
|
||||
class CheckoutController extends StorefrontController {
|
||||
|
||||
/**
|
||||
* @var \Shopware\Storefront\Page\GenericPageLoader
|
||||
*/
|
||||
protected $genericLoader;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Checkout\Cart\SalesChannel\CartService
|
||||
*/
|
||||
protected $cartService;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Settings\Service\SettingsService
|
||||
*/
|
||||
protected $settingsService;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Settings\Struct\Settings
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService
|
||||
*/
|
||||
protected $transactionService;
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Checkout\Cart\LineItemFactoryRegistry
|
||||
*/
|
||||
private $lineItemFactoryRegistry;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Checkout\Order\SalesChannel\AbstractOrderRoute
|
||||
*/
|
||||
private $orderRoute;
|
||||
|
||||
/**
|
||||
* PaymentController constructor.
|
||||
*
|
||||
* @param \Shopware\Core\Checkout\Cart\LineItemFactoryRegistry $lineItemFactoryRegistry
|
||||
* @param \Shopware\Core\Checkout\Cart\SalesChannel\CartService $cartService
|
||||
* @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService
|
||||
* @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(
|
||||
LineItemFactoryRegistry $lineItemFactoryRegistry,
|
||||
CartService $cartService,
|
||||
SettingsService $settingsService,
|
||||
TransactionService $transactionService,
|
||||
GenericPageLoaderInterface $genericLoader,
|
||||
AbstractOrderRoute $orderRoute
|
||||
)
|
||||
{
|
||||
$this->cartService = $cartService;
|
||||
$this->genericLoader = $genericLoader;
|
||||
$this->settingsService = $settingsService;
|
||||
$this->transactionService = $transactionService;
|
||||
$this->lineItemFactoryRegistry = $lineItemFactoryRegistry;
|
||||
$this->orderRoute = $orderRoute;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
*
|
||||
* @internal
|
||||
* @required
|
||||
*
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger): void
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @throws \VRPayment\Sdk\ApiException
|
||||
* @throws \VRPayment\Sdk\Http\ConnectionException
|
||||
* @throws \VRPayment\Sdk\VersioningException
|
||||
*
|
||||
*/
|
||||
#[Route(
|
||||
path: "/vrpayment/checkout/pay",
|
||||
name: "frontend.vrpayment.checkout.pay",
|
||||
options: ["seo" => false],
|
||||
methods: ["GET"],
|
||||
)]
|
||||
public function pay(SalesChannelContext $salesChannelContext, Request $request): Response
|
||||
{
|
||||
$orderId = $request->query->get('orderId');
|
||||
|
||||
if (empty($orderId)) {
|
||||
throw new MissingRequestParameterException('orderId');
|
||||
}
|
||||
|
||||
// Configuration
|
||||
$this->settings = $this->settingsService->getSettings($salesChannelContext->getSalesChannel()->getId());
|
||||
|
||||
$transaction = $this->getTransaction($orderId, $salesChannelContext->getContext());
|
||||
$recreateCartUrl = $this->generateUrl(
|
||||
'frontend.vrpayment.checkout.recreate-cart',
|
||||
['orderId' => $orderId,],
|
||||
UrlGeneratorInterface::ABSOLUTE_URL
|
||||
);
|
||||
|
||||
if (in_array(
|
||||
$transaction->getState(),
|
||||
[
|
||||
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()
|
||||
->getTransactionService()
|
||||
->fetchPaymentMethods(
|
||||
$this->settings->getSpaceId(),
|
||||
$transaction->getId(),
|
||||
$this->settings->getIntegration()
|
||||
);
|
||||
|
||||
if (empty($possiblePaymentMethods)) {
|
||||
$this->addFlash('danger', $this->trans('vrpayment.paymentMethod.notAvailable'));
|
||||
return $this->redirect($recreateCartUrl, Response::HTTP_MOVED_PERMANENTLY);
|
||||
}
|
||||
|
||||
$javascriptUrl = $this->getTransactionJavaScriptUrl($transaction->getId());
|
||||
|
||||
// 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);
|
||||
|
||||
return $this->renderStorefront(
|
||||
'@VRPaymentPayment/storefront/page/checkout/order/vrpayment.html.twig',
|
||||
['page' => $page]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transaction Javascript URL
|
||||
*
|
||||
* @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
|
||||
*
|
||||
*/
|
||||
#[Route(
|
||||
path: "/vrpayment/checkout/recreate-cart",
|
||||
name: "frontend.vrpayment.checkout.recreate-cart",
|
||||
options: ["seo" => false],
|
||||
methods: ["GET"],
|
||||
)]
|
||||
public function recreateCart(Request $request, SalesChannelContext $salesChannelContext)
|
||||
{
|
||||
$orderId = $request->query->get('orderId');
|
||||
|
||||
if (empty($orderId)) {
|
||||
throw new MissingRequestParameterException('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 {
|
||||
$this->cartService->deleteCart($salesChannelContext);
|
||||
$cart = $this->cartService->createNew($salesChannelContext->getToken());
|
||||
|
||||
// Configuration
|
||||
$this->settings = $this->settingsService->getSettings($salesChannelContext->getSalesChannel()->getId());
|
||||
$orderEntity = $this->getOrder($request, $salesChannelContext);
|
||||
$lastTransaction = $orderEntity->getTransactions()->last();
|
||||
if ($lastTransaction && !$lastTransaction->getPaymentMethod()->getAfterOrderEnabled()) {
|
||||
return $this->redirectToRoute('frontend.home.page');
|
||||
}
|
||||
|
||||
$transaction = $this->getTransaction($orderId, $salesChannelContext->getContext());
|
||||
if (!empty($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);
|
||||
|
||||
}
|
||||
|
||||
} catch (\Exception $exception) {
|
||||
$this->addFlash('danger', $this->trans('error.addToCartError'));
|
||||
$this->logger->critical($exception->getMessage());
|
||||
return $this->redirectToRoute('frontend.home.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,200 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Storefront\Checkout\Struct;
|
||||
|
||||
use Shopware\Core\Framework\Struct\Struct;
|
||||
|
||||
/**
|
||||
* Class CheckoutPageData
|
||||
*
|
||||
* @package VRPaymentPayment\Storefront\Checkout\Struct
|
||||
*/
|
||||
class CheckoutPageData extends Struct {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $cartRecreateUrl;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $checkoutUrl;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $deviceJavascriptUrl;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $integration;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $javascriptUrl;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $paymentMethodId;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $possiblePaymentMethodsArray = [];
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $transactionPossiblePaymentMethods = [];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCartRecreateUrl(): string
|
||||
{
|
||||
return $this->cartRecreateUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $cartRecreateUrl
|
||||
* @return CheckoutPageData
|
||||
*/
|
||||
public function setCartRecreateUrl(string $cartRecreateUrl): CheckoutPageData
|
||||
{
|
||||
$this->cartRecreateUrl = $cartRecreateUrl;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCheckoutUrl(): string
|
||||
{
|
||||
return $this->checkoutUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $checkoutUrl
|
||||
* @return CheckoutPageData
|
||||
*/
|
||||
public function setCheckoutUrl(string $checkoutUrl): CheckoutPageData
|
||||
{
|
||||
$this->checkoutUrl = $checkoutUrl;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDeviceJavascriptUrl(): string
|
||||
{
|
||||
return $this->deviceJavascriptUrl;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param int $spaceId
|
||||
* @param string $sessionId
|
||||
* @return \VRPaymentPayment\Core\Storefront\Checkout\Struct\CheckoutPageData
|
||||
*/
|
||||
public function setDeviceJavascriptUrl(int $spaceId, string $sessionId): CheckoutPageData
|
||||
{
|
||||
$this->deviceJavascriptUrl = strtr('https://gateway.vr-payment.de/s/{spaceId}/payment/device.js?sessionIdentifier={sessionId}', [
|
||||
'{spaceId}' => $spaceId,
|
||||
'{sessionId}' => $sessionId,
|
||||
]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getJavascriptUrl(): string
|
||||
{
|
||||
return $this->javascriptUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* JavaScript URL
|
||||
*
|
||||
* @param string $javascriptUrl
|
||||
* @return \VRPaymentPayment\Core\Storefront\Checkout\Struct\CheckoutPageData
|
||||
*/
|
||||
public function setJavascriptUrl(string $javascriptUrl): CheckoutPageData
|
||||
{
|
||||
$this->javascriptUrl = $javascriptUrl;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getPossiblePaymentMethodsArray(): array
|
||||
{
|
||||
return $this->possiblePaymentMethodsArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $possiblePaymentMethodsArray
|
||||
* @return \VRPaymentPayment\Core\Storefront\Checkout\Struct\CheckoutPageData
|
||||
*/
|
||||
public function setPossiblePaymentMethodsArray(array $possiblePaymentMethodsArray): CheckoutPageData
|
||||
{
|
||||
$this->possiblePaymentMethodsArray = $possiblePaymentMethodsArray;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getTransactionPossiblePaymentMethods(): array
|
||||
{
|
||||
return $this->transactionPossiblePaymentMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $transactionPossiblePaymentMethods
|
||||
* @return \VRPaymentPayment\Core\Storefront\Checkout\Struct\CheckoutPageData
|
||||
*/
|
||||
public function setTransactionPossiblePaymentMethods(array $transactionPossiblePaymentMethods): CheckoutPageData
|
||||
{
|
||||
$this->transactionPossiblePaymentMethods = $transactionPossiblePaymentMethods;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getIntegration(): string
|
||||
{
|
||||
return $this->integration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $integration
|
||||
* @return \VRPaymentPayment\Core\Storefront\Checkout\Struct\CheckoutPageData
|
||||
*/
|
||||
public function setIntegration(string $integration): CheckoutPageData
|
||||
{
|
||||
$this->integration = $integration;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPaymentMethodId(): string
|
||||
{
|
||||
return $this->paymentMethodId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Payment method id from Shopware database
|
||||
*
|
||||
* @param string $paymentMethodId
|
||||
* @return \VRPaymentPayment\Core\Storefront\Checkout\Struct\CheckoutPageData
|
||||
*/
|
||||
public function setPaymentMethodId(string $paymentMethodId): CheckoutPageData
|
||||
{
|
||||
$this->paymentMethodId = $paymentMethodId;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -1,260 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Storefront\Checkout\Subscriber;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shopware\Core\{Checkout\Order\Aggregate\OrderTransaction\OrderTransactionCollection,
|
||||
Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStates,
|
||||
Checkout\Order\OrderEntity,
|
||||
Content\MailTemplate\Service\Event\MailBeforeValidateEvent};
|
||||
use Shopware\Core\System\SalesChannel\SalesChannelContext;
|
||||
use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use VRPaymentPayment\Core\{Api\Transaction\Service\OrderMailService,
|
||||
Api\Transaction\Service\TransactionService,
|
||||
Checkout\PaymentHandler\VRPaymentPaymentHandler,
|
||||
Settings\Service\SettingsService,
|
||||
Settings\Struct\Settings,
|
||||
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};
|
||||
|
||||
/**
|
||||
* Class CheckoutSubscriber
|
||||
*
|
||||
* @package VRPaymentPayment\Storefront\Checkout\Subscriber
|
||||
*/
|
||||
class CheckoutSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Service\PaymentMethodConfigurationService
|
||||
*/
|
||||
private $paymentMethodConfigurationService;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService
|
||||
*/
|
||||
private $transactionService;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Settings\Service\SettingsService
|
||||
*/
|
||||
private $settingsService;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Util\PaymentMethodUtil
|
||||
*/
|
||||
private $paymentMethodUtil;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function __construct(PaymentMethodConfigurationService $paymentMethodConfigurationService, TransactionService $transactionService, SettingsService $settingsService, PaymentMethodUtil $paymentMethodUtil)
|
||||
{
|
||||
$this->paymentMethodConfigurationService = $paymentMethodConfigurationService;
|
||||
$this->transactionService = $transactionService;
|
||||
$this->settingsService = $settingsService;
|
||||
$this->paymentMethodUtil = $paymentMethodUtil;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
*
|
||||
* @internal
|
||||
* @required
|
||||
*
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger): void
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
CheckoutConfirmPageLoadedEvent::class => ['onConfirmPageLoaded', 1],
|
||||
MailBeforeValidateEvent::class => ['onMailBeforeValidate', 1],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop order emails being sent out
|
||||
*
|
||||
* @param \Shopware\Core\Content\MailTemplate\Service\Event\MailBeforeValidateEvent $event
|
||||
*/
|
||||
public function onMailBeforeValidate(MailBeforeValidateEvent $event): void
|
||||
{
|
||||
$templateData = $event->getTemplateData();
|
||||
|
||||
/**
|
||||
* @var $order \Shopware\Core\Checkout\Order\OrderEntity
|
||||
*/
|
||||
$order = !empty($templateData['order']) && $templateData['order'] instanceof OrderEntity ? $templateData['order'] : null;
|
||||
|
||||
if (!empty($order) && $order->getAmountTotal() > 0) {
|
||||
|
||||
$isVRPaymentEmailSettingEnabled = $this->settingsService->getSettings($order->getSalesChannelId())->isEmailEnabled();
|
||||
|
||||
if (!$isVRPaymentEmailSettingEnabled) { //setting is disabled
|
||||
return;
|
||||
}
|
||||
|
||||
$orderTransactions = $order->getTransactions();
|
||||
if (!($orderTransactions instanceof OrderTransactionCollection)) {
|
||||
return;
|
||||
}
|
||||
$orderTransactionLast = $orderTransactions->last();
|
||||
if (empty($orderTransactionLast) || empty($orderTransactionLast->getPaymentMethod())) { // no payment method available
|
||||
return;
|
||||
}
|
||||
|
||||
$isVRPaymentPM = VRPaymentPaymentHandler::class == $orderTransactionLast->getPaymentMethod()->getHandlerIdentifier();
|
||||
if (!$isVRPaymentPM) { // not our payment method
|
||||
return;
|
||||
}
|
||||
|
||||
$isOrderTransactionStateOpen = in_array(
|
||||
$orderTransactionLast->getStateMachineState()->getTechnicalName(), [
|
||||
OrderTransactionStates::STATE_OPEN,
|
||||
OrderTransactionStates::STATE_IN_PROGRESS,
|
||||
]);
|
||||
|
||||
if (!$isOrderTransactionStateOpen) { // order payment status is open or in progress
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent $event
|
||||
*/
|
||||
public function onConfirmPageLoaded(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);
|
||||
$this->setPossiblePaymentMethods($settings->getSpaceId(), $event);
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error($e->getMessage());
|
||||
$this->removeVRPaymentPaymentMethodFromConfirmPage($event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent $event
|
||||
*/
|
||||
private function removeVRPaymentPaymentMethodFromConfirmPage(CheckoutConfirmPageLoadedEvent $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): void
|
||||
{
|
||||
$transactionService = $settings->getApiClient()->getTransactionService();
|
||||
$possiblePaymentMethods = $transactionService->fetchPaymentMethods(
|
||||
$settings->getSpaceId(),
|
||||
$createdTransactionId,
|
||||
$settings->getIntegration()
|
||||
);
|
||||
$arrayOfPossibleMethods = [];
|
||||
foreach ($possiblePaymentMethods as $possiblePaymentMethod) {
|
||||
$arrayOfPossibleMethods[] = $possiblePaymentMethod->getid();
|
||||
}
|
||||
$_SESSION['arrayOfPossibleMethods'] = $arrayOfPossibleMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $spaceId
|
||||
* @param CheckoutConfirmPageLoadedEvent $event
|
||||
* @return void
|
||||
*/
|
||||
private function setPossiblePaymentMethods(int $spaceId, CheckoutConfirmPageLoadedEvent $event): void
|
||||
{
|
||||
$localPaymentMethods = [];
|
||||
$paymentMethodConfigurations = $this->paymentMethodConfigurationService->getAllPaymentMethodConfigurations($spaceId, $event->getSalesChannelContext()->getContext());
|
||||
foreach ($paymentMethodConfigurations as $paymentMethodConfiguration) {
|
||||
$localPaymentMethods[$paymentMethodConfiguration->getId()] = $paymentMethodConfiguration->getPaymentMethodConfigurationId();
|
||||
}
|
||||
|
||||
$paymentMethodCollection = $event->getPage()->getPaymentMethods();
|
||||
foreach ($paymentMethodCollection as $paymentMethodCollectionItem) {
|
||||
$isVRPaymentPM = VRPaymentPaymentHandler::class == $paymentMethodCollectionItem->getHandlerIdentifier();
|
||||
if (!$isVRPaymentPM) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$paymentMethodConfigurationId = $localPaymentMethods[$paymentMethodCollectionItem->getId()];
|
||||
if (!\in_array($paymentMethodConfigurationId, $_SESSION['arrayOfPossibleMethods'])) {
|
||||
$paymentMethodCollection->remove($paymentMethodCollectionItem->getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SalesChannelContext $salesChannelContext
|
||||
* @param int $createdTransactionId
|
||||
* @return void
|
||||
*/
|
||||
private function updateTempTransactionIfNeeded(SalesChannelContext $salesChannelContext, int $createdTransactionId): void
|
||||
{
|
||||
$addressCheck = $_SESSION['addressCheck'] ?? null;
|
||||
$currencyCheck = $_SESSION['currencyCheck'] ?? null;
|
||||
|
||||
$customer = $salesChannelContext->getCustomer();
|
||||
$addressHash = md5(json_encode((array)$customer));
|
||||
$currency = $salesChannelContext->getCurrency()->getIsoCode();
|
||||
if (($addressCheck && $currencyCheck) && $addressCheck !== $addressHash || $currencyCheck !== $currency) {
|
||||
if ($createdTransactionId) {
|
||||
$this->transactionService->updateTempTransaction($salesChannelContext, $createdTransactionId);
|
||||
}
|
||||
$_SESSION['arrayOfPossibleMethods'] = null;
|
||||
$_SESSION['addressCheck'] = $addressHash;
|
||||
$_SESSION['currencyCheck'] = $currency;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Storefront\Framework\Cookie;
|
||||
|
||||
use Shopware\Storefront\Framework\Cookie\CookieProviderInterface;
|
||||
|
||||
/**
|
||||
* Class VRPaymentCookieProvider
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Storefront\Framework\Cookie
|
||||
*/
|
||||
class VRPaymentCookieProvider implements CookieProviderInterface {
|
||||
/**
|
||||
* @var CookieProviderInterface
|
||||
*/
|
||||
private $original;
|
||||
|
||||
public function __construct(CookieProviderInterface $cookieProvider)
|
||||
{
|
||||
$this->original = $cookieProvider;
|
||||
}
|
||||
|
||||
public function getCookieGroups(): array
|
||||
{
|
||||
$cookies = $this->original->getCookieGroups();
|
||||
|
||||
foreach ($cookies as &$cookie) {
|
||||
if (!\is_array($cookie)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->isRequiredCookieGroup($cookie)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!\array_key_exists('entries', $cookie)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$cookie['entries'][] = [
|
||||
'snippet_name' => 'vrpayment.cookie.name',
|
||||
'cookie' => 'vrpayment-cookie-key',
|
||||
];
|
||||
}
|
||||
|
||||
return $cookies;
|
||||
}
|
||||
|
||||
private function isRequiredCookieGroup(array $cookie): bool
|
||||
{
|
||||
return (\array_key_exists('isRequired', $cookie) && $cookie['isRequired'] === true)
|
||||
&& (\array_key_exists('snippet_name', $cookie) && $cookie['snippet_name'] === 'cookie.groupRequired');
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Util\Analytics;
|
||||
|
||||
use VRPayment\Sdk\ApiClient;
|
||||
|
||||
/**
|
||||
* Class Analytics
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Util\Analytics
|
||||
*/
|
||||
class Analytics {
|
||||
|
||||
public const SHOP_SYSTEM = 'x-meta-shop-system';
|
||||
public const SHOP_SYSTEM_VERSION = 'x-meta-shop-system-version';
|
||||
public const SHOP_SYSTEM_AND_VERSION = 'x-meta-shop-system-and-version';
|
||||
public const PLUGIN_SYSTEM_VERSION = 'x-meta-plugin-version';
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getDefaultData()
|
||||
{
|
||||
return [
|
||||
self::SHOP_SYSTEM => 'shopware',
|
||||
self::SHOP_SYSTEM_VERSION => '6',
|
||||
self::SHOP_SYSTEM_AND_VERSION => 'shopware-6',
|
||||
self::PLUGIN_SYSTEM_VERSION => '7.0.1',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \VRPayment\Sdk\ApiClient $apiClient
|
||||
*/
|
||||
public static function addHeaders(ApiClient &$apiClient)
|
||||
{
|
||||
$data = self::getDefaultData();
|
||||
foreach ($data as $key => $value) {
|
||||
$apiClient->addDefaultHeader($key, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
|
||||
namespace VRPaymentPayment\Core\Util\Exception;
|
||||
|
||||
|
||||
class InvalidPayloadException extends \InvalidArgumentException{
|
||||
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Util;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shopware\Core\{
|
||||
Defaults,
|
||||
Framework\Adapter\Translation\Translator,
|
||||
Framework\Context,
|
||||
Framework\DataAbstractionLayer\EntityRepositoryInterface,
|
||||
Framework\DataAbstractionLayer\Search\Criteria,
|
||||
System\Language\LanguageCollection,
|
||||
System\Language\LanguageEntity};
|
||||
|
||||
|
||||
/**
|
||||
* Class LocaleCodeProvider
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Util
|
||||
*/
|
||||
class LocaleCodeProvider {
|
||||
|
||||
public const LOCALE_GREAT_BRITAIN_ENGLISH = 'en-GB';
|
||||
public const LOCALE_GERMANY_GERMAN = 'de-DE';
|
||||
public const LOCALE_FRANCE_FRENCH = 'fr-FR';
|
||||
public const LOCALE_ITALY_ITALIAN = 'it-IT';
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
/**
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
/**
|
||||
* @var \Shopware\Core\Framework\Adapter\Translation\Translator
|
||||
*/
|
||||
protected $translator;
|
||||
/**
|
||||
* @var EntityRepositoryInterface
|
||||
*/
|
||||
private $languageRepository;
|
||||
|
||||
/**
|
||||
* LocaleCodeProvider constructor.
|
||||
*
|
||||
* @param \Psr\Container\ContainerInterface $container
|
||||
* @param \Shopware\Core\Framework\Adapter\Translation\Translator $translator
|
||||
*/
|
||||
public function __construct(ContainerInterface $container, Translator $translator)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->translator = $translator;
|
||||
$this->languageRepository = $this->container->get('language.repository');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
*
|
||||
* @internal
|
||||
* @required
|
||||
*
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger): void
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocaleCodeFromContext(Context $context): string
|
||||
{
|
||||
$defaultLocale = self::LOCALE_GREAT_BRITAIN_ENGLISH;
|
||||
$languageId = $context->getLanguageId();
|
||||
/** @var \Shopware\Core\System\Language\LanguageCollection $languageCollection */
|
||||
$languageCollection = $this->languageRepository->search(
|
||||
(new Criteria([$languageId]))->addAssociation('locale'),
|
||||
$context
|
||||
)->getEntities();
|
||||
|
||||
$language = $languageCollection->get($languageId);
|
||||
if (is_null($language)) {
|
||||
return $defaultLocale;
|
||||
}
|
||||
|
||||
return $language->getLocale() ? $language->getLocale()->getCode() : $defaultLocale;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDefaultLocaleCode(Context $context): string
|
||||
{
|
||||
$defaultLocale = self::LOCALE_GREAT_BRITAIN_ENGLISH;
|
||||
$languageId = Defaults::LANGUAGE_SYSTEM;
|
||||
/** @var \Shopware\Core\System\Language\LanguageCollection $languageCollection */
|
||||
$languageCollection = $this->languageRepository->search(
|
||||
(new Criteria([$languageId]))->addAssociation('locale'),
|
||||
$context
|
||||
)->getEntities();
|
||||
|
||||
$language = $languageCollection->get($languageId);
|
||||
if (is_null($language)) {
|
||||
return $defaultLocale;
|
||||
}
|
||||
|
||||
return $language->getLocale() ? $language->getLocale()->getCode() : $defaultLocale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available translations
|
||||
*
|
||||
* @param string $snippet
|
||||
* @param string $fallback
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAvailableTranslations(string $snippet, string $fallback, Context $context): array
|
||||
{
|
||||
$locales = $this->getAvailableLocales($context);
|
||||
$translations = [];
|
||||
|
||||
foreach ($locales as $locale) {
|
||||
$translation = $this->translator->trans($snippet, [], null, $locale);
|
||||
$pattern = '/^vrpayment\./';
|
||||
|
||||
// there is a bug/lack of documentation on Shopware translations, sometimes the translation does not work
|
||||
|
||||
if (preg_match($pattern, $translation)) { // string not translated
|
||||
$translation = $this->translator->trans($snippet, [], 'storefront', $locale);
|
||||
}
|
||||
|
||||
if (preg_match($pattern, $translation)) { // string not translated
|
||||
$translation = $fallback;
|
||||
}
|
||||
|
||||
$translations[$locale]['name'] = $translation;
|
||||
}
|
||||
|
||||
return $translations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all locales available
|
||||
*
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAvailableLocales(Context $context): array
|
||||
{
|
||||
$availableLanguages = $this->getAvailableLanguages($context);
|
||||
$locales = array_map(function (LanguageEntity $language) {
|
||||
return $language->getLocale()->getCode();
|
||||
},
|
||||
$availableLanguages->jsonSerialize()
|
||||
);
|
||||
$locales[] = $this->getDefaultLocaleCode($context);
|
||||
$locales[] = self::LOCALE_GERMANY_GERMAN;
|
||||
$locales[] = self::LOCALE_GREAT_BRITAIN_ENGLISH;
|
||||
$locales[] = self::LOCALE_FRANCE_FRENCH;
|
||||
$locales[] = self::LOCALE_ITALY_ITALIAN;
|
||||
$locales = array_unique($locales);
|
||||
return $locales;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available languages
|
||||
*
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
* @return \Shopware\Core\System\Language\LanguageCollection
|
||||
*/
|
||||
public function getAvailableLanguages(Context $context): LanguageCollection
|
||||
{
|
||||
return $this->languageRepository->search((new Criteria())->addAssociations([
|
||||
'locale',
|
||||
]), $context)->getEntities();
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Util\Payload;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Class AbstractPayload
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Util\Payload
|
||||
*/
|
||||
abstract class AbstractPayload {
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
* @internal
|
||||
* @required
|
||||
*
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger): void
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix string length string to specific length.
|
||||
*
|
||||
* @param string $string
|
||||
* @param int $maxLength
|
||||
* @return string
|
||||
*/
|
||||
protected function fixLength(string $string, int $maxLength): string
|
||||
{
|
||||
return mb_substr($string, 0, $maxLength, 'UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $amount
|
||||
* @param int $precision
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public static function round(float $amount, int $precision = 2): float {
|
||||
return \round($amount, $precision);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Util\Payload\CustomProducts;
|
||||
|
||||
class CustomProductsLineItemTypes {
|
||||
|
||||
public const LINE_ITEM_TYPE_PRODUCT = 'product';
|
||||
public const LINE_ITEM_TYPE_CUSTOMIZED_PRODUCTS = 'customized-products';
|
||||
public const LINE_ITEM_TYPE_CUSTOMIZED_PRODUCTS_OPTION = 'customized-products-option';
|
||||
|
||||
public const PRODUCT_OPTION_TYPE_DATETIME = 'datetime';
|
||||
public const PRODUCT_OPTION_TYPE_TIMESTAMP = 'timestamp';
|
||||
public const PRODUCT_OPTION_TYPE_IMAGE_UPLOAD = 'imageupload';
|
||||
public const PRODUCT_OPTION_TYPE_IMAGE_SELECT = 'imageselect';
|
||||
public const PRODUCT_OPTION_TYPE_FILE_UPLOAD = 'fileupload';
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Util\Payload\CustomProducts;
|
||||
|
||||
use Shopware\Core\{
|
||||
Checkout\Cart\Tax\Struct\CalculatedTaxCollection,
|
||||
Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity
|
||||
};
|
||||
use VRPayment\Sdk\{
|
||||
Model\LineItemAttributeCreate,
|
||||
Model\TaxCreate
|
||||
};
|
||||
use VRPaymentPayment\Core\Util\Exception\InvalidPayloadException;
|
||||
|
||||
trait CustomProductsLineItems {
|
||||
|
||||
/**
|
||||
* Get Custom Product attributes
|
||||
*
|
||||
* @param \Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity $shopLineItem
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCustomProductLineItemAttribute(OrderLineItemEntity $shopLineItem)
|
||||
{
|
||||
$customProductsOptions = $this->transaction->getOrder()
|
||||
->getLineItems()
|
||||
->filterByType(CustomProductsLineItemTypes::LINE_ITEM_TYPE_CUSTOMIZED_PRODUCTS_OPTION)
|
||||
->filterByProperty('parentId', $shopLineItem->getId());
|
||||
$productAttributes = [];
|
||||
foreach ($customProductsOptions as $option) {
|
||||
$label = $option->getLabel();
|
||||
$value = $this->extractValueFromPayload($option);
|
||||
|
||||
if ($value === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$lineItemAttributeCreate = (new LineItemAttributeCreate())
|
||||
->setLabel($this->fixLength($label, 512))
|
||||
->setValue($this->fixLength($value, 512));
|
||||
|
||||
if ($lineItemAttributeCreate->valid()) {
|
||||
$key = $this->fixLength('option_' . md5($label), 40);
|
||||
$productAttributes[$key] = $lineItemAttributeCreate;
|
||||
} else {
|
||||
$this->logger->critical('LineItemAttributeCreate payload invalid:', $lineItemAttributeCreate->listInvalidProperties());
|
||||
throw new InvalidPayloadException('LineItemAttributeCreate payload invalid:' . json_encode($lineItemAttributeCreate->listInvalidProperties()));
|
||||
}
|
||||
}
|
||||
return $productAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Checkout\Cart\Tax\Struct\CalculatedTaxCollection $calculatedTaxes
|
||||
* @param string $title
|
||||
* @param $amount
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCustomProductTaxes(CalculatedTaxCollection $calculatedTaxes, string $title, $amount)
|
||||
{
|
||||
$taxes = [];
|
||||
$sumOfTaxes = $this->getSumOfTaxes($calculatedTaxes);
|
||||
|
||||
foreach ($calculatedTaxes as $calculatedTax) {
|
||||
$taxRate = ($calculatedTax->getTax() * 100) / ($amount - $sumOfTaxes);
|
||||
$taxRate = (float) number_format($taxRate, 8, '.', '');
|
||||
$tax = (new TaxCreate())
|
||||
->setRate($taxRate)
|
||||
->setTitle($this->fixLength($title . ' : ' . $calculatedTax->getTaxRate(), 40));
|
||||
|
||||
if (!$tax->valid()) {
|
||||
$this->logger->critical('Tax payload invalid:', $tax->listInvalidProperties());
|
||||
throw new InvalidPayloadException('Tax payload invalid:' . json_encode($tax->listInvalidProperties()));
|
||||
}
|
||||
|
||||
$taxes [] = $tax;
|
||||
}
|
||||
|
||||
return $taxes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract Custom Product Attribute value
|
||||
*
|
||||
* @param \Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity $option
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function extractValueFromPayload(OrderLineItemEntity $option): ?string
|
||||
{
|
||||
$payload = $option->getPayload() ?? [];
|
||||
|
||||
$type = $payload['type'] ?? null;
|
||||
|
||||
$value = $payload['value'] ?? 'on';
|
||||
|
||||
if (!$type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($type === CustomProductsLineItemTypes::PRODUCT_OPTION_TYPE_DATETIME) {
|
||||
return $value ? \date('d.m.Y', \strtotime($value)) : null;
|
||||
}
|
||||
|
||||
if ($type === CustomProductsLineItemTypes::PRODUCT_OPTION_TYPE_TIMESTAMP) {
|
||||
return $value ? \date('H:i', \strtotime($value)) : null;
|
||||
}
|
||||
|
||||
if ($type === CustomProductsLineItemTypes::PRODUCT_OPTION_TYPE_IMAGE_UPLOAD || $type === CustomProductsLineItemTypes::PRODUCT_OPTION_TYPE_FILE_UPLOAD) {
|
||||
return \implode(', ', \array_column($option->getPayload()['media'] ?? [], 'filename'));
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CalculatedTaxCollection $calculatedTaxes
|
||||
* @return float
|
||||
*/
|
||||
private function getSumOfTaxes(CalculatedTaxCollection $calculatedTaxes): float
|
||||
{
|
||||
$sumOfTaxes = 0;
|
||||
foreach ($calculatedTaxes as $calculatedTax) {
|
||||
$sumOfTaxes += $calculatedTax->getTax();
|
||||
}
|
||||
|
||||
return $sumOfTaxes;
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Util\Payload;
|
||||
|
||||
use VRPayment\Sdk\{
|
||||
Model\LineItem,
|
||||
Model\RefundCreate,
|
||||
Model\RefundType,
|
||||
Model\Transaction,
|
||||
Model\TransactionState
|
||||
};
|
||||
use VRPaymentPayment\Core\Util\Exception\InvalidPayloadException;
|
||||
|
||||
/**
|
||||
* Class RefundPayload
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Util\Payload
|
||||
*/
|
||||
class RefundPayload extends AbstractPayload
|
||||
{
|
||||
|
||||
/**
|
||||
* @param \VRPayment\Sdk\Model\Transaction $transaction
|
||||
* @param string $lineItemId
|
||||
* @param int $quantity
|
||||
* @return \VRPayment\Sdk\Model\RefundCreate|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function get(Transaction $transaction, string $lineItemId, int $quantity): ?RefundCreate
|
||||
{
|
||||
$lineItem = $this->findLineItemByUniqueId($transaction['line_items'], $lineItemId);
|
||||
|
||||
if ($lineItem === null) {
|
||||
$errorMessage = sprintf('Line item doesn\'t exist: %s', $lineItemId);
|
||||
$this->logger->critical($errorMessage);
|
||||
throw new InvalidPayloadException($errorMessage);
|
||||
}
|
||||
|
||||
$price = 0;
|
||||
|
||||
// If refund the whole line item
|
||||
if ($quantity === 0) {
|
||||
$quantity = $lineItem['quantity'];
|
||||
$price = $lineItem['unit_price_including_tax'];
|
||||
}
|
||||
|
||||
$amount = floatval($quantity * $lineItem['unit_price_including_tax']);
|
||||
|
||||
if (
|
||||
($transaction->getState() == TransactionState::FULFILL) &&
|
||||
($amount <= floatval($transaction->getAuthorizationAmount()))
|
||||
) {
|
||||
$reduction = new \VRPayment\Sdk\Model\LineItemReductionCreate();
|
||||
$reduction->setLineItemUniqueId($lineItem['unique_id']);
|
||||
$reduction->setQuantityReduction($quantity);
|
||||
$reduction->setUnitPriceReduction($price);
|
||||
|
||||
$refund = (new RefundCreate())
|
||||
->setReductions([$reduction])
|
||||
->setTransaction($transaction->getId())
|
||||
->setMerchantReference($this->fixLength($transaction->getMerchantReference(), 100))
|
||||
->setExternalId($this->fixLength(uniqid('refund_', true), 100))
|
||||
/** @noinspection PhpParamsInspection */
|
||||
->setType(RefundType::MERCHANT_INITIATED_ONLINE);
|
||||
|
||||
if (!$refund->valid()) {
|
||||
$this->logger->critical('Refund payload invalid:', $refund->listInvalidProperties());
|
||||
throw new InvalidPayloadException('Refund payload invalid:' . json_encode($refund->listInvalidProperties()));
|
||||
}
|
||||
|
||||
return $refund;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \VRPayment\Sdk\Model\Transaction $transaction
|
||||
* @param float $amount
|
||||
* @return \VRPayment\Sdk\Model\RefundCreate|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getByAmount(Transaction $transaction, float $amount): ?RefundCreate
|
||||
{
|
||||
if (
|
||||
($transaction->getState() == TransactionState::FULFILL) &&
|
||||
($amount <= floatval($transaction->getAuthorizationAmount()))
|
||||
) {
|
||||
$refund = (new RefundCreate())
|
||||
->setAmount(self::round($amount))
|
||||
->setTransaction($transaction->getId())
|
||||
->setMerchantReference($this->fixLength($transaction->getMerchantReference(), 100))
|
||||
->setExternalId($this->fixLength(uniqid('refund_', true), 100))
|
||||
->setType(RefundType::MERCHANT_INITIATED_ONLINE);
|
||||
|
||||
if (!$refund->valid()) {
|
||||
$this->logger->critical('Refund payload invalid:', $refund->listInvalidProperties());
|
||||
throw new InvalidPayloadException('Refund payload invalid:' . json_encode($refund->listInvalidProperties()));
|
||||
}
|
||||
|
||||
return $refund;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \VRPayment\Sdk\Model\Transaction $transaction
|
||||
* @param string $lineItemId
|
||||
* @param int $quantity
|
||||
* @return \VRPayment\Sdk\Model\RefundCreate|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getForPartial(Transaction $transaction, string $lineItemId, float $amount): ?RefundCreate
|
||||
{
|
||||
$lineItem = $this->findLineItemByUniqueId($transaction['line_items'], $lineItemId);
|
||||
|
||||
if ($lineItem === null) {
|
||||
$errorMessage = sprintf('Line item doesn\'t exist: %s', $lineItemId);
|
||||
$this->logger->critical($errorMessage);
|
||||
throw new InvalidPayloadException($errorMessage);
|
||||
}
|
||||
|
||||
$unitPrice = floatval($lineItem['unit_price_including_tax']);
|
||||
$quantityAvailable = intval($lineItem['quantity']);
|
||||
$totalItemAmount = $unitPrice * $quantityAvailable;
|
||||
|
||||
if (
|
||||
($transaction->getState() == TransactionState::FULFILL) &&
|
||||
($amount <= $totalItemAmount)
|
||||
) {
|
||||
$reduction = new \VRPayment\Sdk\Model\LineItemReductionCreate();
|
||||
$reduction->setLineItemUniqueId($lineItemId);
|
||||
$reduction->setQuantityReduction(0);
|
||||
$reduction->setUnitPriceReduction($amount / $quantityAvailable);
|
||||
|
||||
$refund = (new RefundCreate())
|
||||
->setReductions([$reduction])
|
||||
->setTransaction($transaction->getId())
|
||||
->setMerchantReference($this->fixLength($transaction->getMerchantReference(), 100))
|
||||
->setExternalId($this->fixLength(uniqid('refund_', true), 100))
|
||||
/** @noinspection PhpParamsInspection */
|
||||
->setType(RefundType::MERCHANT_INITIATED_ONLINE);
|
||||
|
||||
if (!$refund->valid()) {
|
||||
$this->logger->critical('Refund payload invalid:', $refund->listInvalidProperties());
|
||||
throw new InvalidPayloadException('Refund payload invalid:' . json_encode($refund->listInvalidProperties()));
|
||||
}
|
||||
|
||||
return $refund;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $lineItems
|
||||
* @param string $uniqueId
|
||||
* @return LineItem|null
|
||||
*/
|
||||
private function findLineItemByUniqueId(array $lineItems, string $uniqueId): ?LineItem
|
||||
{
|
||||
$lineItems = \array_values(
|
||||
\array_filter($lineItems, function ($item) use ($uniqueId) {
|
||||
return $item['unique_id'] === $uniqueId;
|
||||
})
|
||||
);
|
||||
|
||||
return $lineItems[0] ?? null;
|
||||
}
|
||||
}
|
||||
@@ -1,921 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Util\Payload;
|
||||
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Shopware\Core\{Checkout\Cart\Tax\Struct\CalculatedTaxCollection,
|
||||
Checkout\Customer\Aggregate\CustomerAddress\CustomerAddressEntity,
|
||||
Checkout\Customer\CustomerEntity,
|
||||
Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity,
|
||||
Checkout\Order\OrderEntity,
|
||||
Checkout\Payment\Cart\PaymentTransactionStruct,
|
||||
Framework\DataAbstractionLayer\Search\Criteria,
|
||||
System\SalesChannel\SalesChannelContext
|
||||
};
|
||||
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
|
||||
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
|
||||
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use VRPayment\Sdk\{Model\AddressCreate,
|
||||
Model\ChargeAttempt,
|
||||
Model\CreationEntityState,
|
||||
Model\CriteriaOperator,
|
||||
Model\EntityQuery,
|
||||
Model\EntityQueryFilter,
|
||||
Model\EntityQueryFilterType,
|
||||
Model\LineItemAttributeCreate,
|
||||
Model\LineItemCreate,
|
||||
Model\LineItemType,
|
||||
Model\TaxCreate,
|
||||
Model\TransactionCreate,
|
||||
Model\TransactionPending
|
||||
};
|
||||
use VRPaymentPayment\Core\{Api\PaymentMethodConfiguration\Entity\PaymentMethodConfigurationEntity,
|
||||
Settings\Struct\Settings,
|
||||
Util\Exception\InvalidPayloadException,
|
||||
Util\LocaleCodeProvider,
|
||||
Util\Payload\CustomProducts\CustomProductsLineItems,
|
||||
Util\Payload\CustomProducts\CustomProductsLineItemTypes
|
||||
};
|
||||
|
||||
use Shopware\Core\System\SystemConfig\SystemConfigService;
|
||||
use Shopware\Core\Framework\Context;
|
||||
use Shopware\Core\System\Tax\TaxEntity;
|
||||
|
||||
/**
|
||||
* Class TransactionPayload
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Util\Payload
|
||||
*/
|
||||
class TransactionPayload extends AbstractPayload
|
||||
{
|
||||
|
||||
use CustomProductsLineItems;
|
||||
|
||||
public const ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_SPACE_ID = 'vrpayment_space_id';
|
||||
public const ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_TRANSACTION_ID = 'vrpayment_transaction_id';
|
||||
|
||||
public const VRPAYMENT_METADATA_SALES_CHANNEL_ID = 'salesChannelId';
|
||||
public const VRPAYMENT_METADATA_ORDER_ID = 'orderId';
|
||||
public const VRPAYMENT_METADATA_ORDER_TRANSACTION_ID = 'orderTransactionId';
|
||||
public const VRPAYMENT_METADATA_CUSTOMER_NAME = 'customerName';
|
||||
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\System\SalesChannel\SalesChannelContext
|
||||
*/
|
||||
protected $salesChannelContext;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct
|
||||
*/
|
||||
protected $transaction;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Settings\Struct\Settings
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* @var \Psr\Container\ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @var \VRPaymentPayment\Core\Util\LocaleCodeProvider
|
||||
*/
|
||||
private $localeCodeProvider;
|
||||
|
||||
/**
|
||||
* @var TranslatorInterface
|
||||
*/
|
||||
protected $translator;
|
||||
|
||||
protected EntityRepository $orderTransactionRepository;
|
||||
|
||||
protected OrderEntity $order;
|
||||
|
||||
/**
|
||||
* TransactionPayload constructor.
|
||||
*
|
||||
* @param \Psr\Container\ContainerInterface $container
|
||||
* @param \VRPaymentPayment\Core\Util\LocaleCodeProvider $localeCodeProvider
|
||||
* @param \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext
|
||||
* @param \VRPaymentPayment\Core\Settings\Struct\Settings $settings
|
||||
* @param \Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct $transaction
|
||||
*/
|
||||
public function __construct(
|
||||
ContainerInterface $container,
|
||||
LocaleCodeProvider $localeCodeProvider,
|
||||
SalesChannelContext $salesChannelContext,
|
||||
Settings $settings,
|
||||
PaymentTransactionStruct $transaction
|
||||
)
|
||||
{
|
||||
$this->localeCodeProvider = $localeCodeProvider;
|
||||
$this->salesChannelContext = $salesChannelContext;
|
||||
$this->settings = $settings;
|
||||
$this->transaction = $transaction;
|
||||
$this->container = $container;
|
||||
$this->translator = $this->container->get('translator');
|
||||
$this->orderTransactionRepository = $this->container->get('order_transaction.repository');
|
||||
|
||||
$criteria = (new Criteria());
|
||||
$criteria->addFilter(new EqualsFilter('id', $this->transaction->getOrderTransactionId()));
|
||||
|
||||
$orders = $this->orderTransactionRepository->search($criteria, $this->salesChannelContext->getContext())->getEntities();
|
||||
$orderId = $orders->first()->getOrderId();
|
||||
|
||||
$criteria = new Criteria([$orderId]);
|
||||
$criteria
|
||||
->addAssociation('lineItems')
|
||||
->addAssociation('orderCustomer')
|
||||
->addAssociation('transactions')
|
||||
->addAssociation('currency')
|
||||
;
|
||||
|
||||
$this->order = $this->container->get('order.repository')->search($criteria, $this->salesChannelContext->getContext())->getEntities()->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Transaction Payload
|
||||
*
|
||||
* @return \VRPayment\Sdk\Model\TransactionPending
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function get(int $version): TransactionPending
|
||||
{
|
||||
$customerId = $this->order->getOrderCustomer()->getCustomerId();
|
||||
$criteria = new Criteria([$customerId]);
|
||||
$criteria->addAssociation('activeBillingAddress')
|
||||
->addAssociation('activeShippingAddress')
|
||||
->addAssociation('activeShippingAddress')
|
||||
->addAssociation('defaultBillingAddress')
|
||||
->addAssociation('defaultShippingAddress')
|
||||
->addAssociation('salutation');
|
||||
$customer = $this->container->get('customer.repository')->search($criteria, $this->salesChannelContext->getContext())->getEntities()->first();
|
||||
|
||||
$lineItems = $this->getLineItems();
|
||||
|
||||
$billingAddress = $this->getAddressPayload($customer, $customer->getActiveBillingAddress());
|
||||
$shippingAddress = $this->getAddressPayload($customer, $customer->getActiveShippingAddress(), false);
|
||||
|
||||
$customerId = null;
|
||||
$customerName = null;
|
||||
if ($customer->getGuest() === false) {
|
||||
$customerId = $customer->getCustomerNumber();
|
||||
$customerName = '';
|
||||
if ($customer->getGuest() === false) {
|
||||
$customerId = $customer->getCustomerNumber();
|
||||
$customerName = $customer->getSalutation()->getDisplayName() . ' ' . $customer->getFirstName() . ' ' . $customer->getLastName();
|
||||
}
|
||||
}
|
||||
|
||||
$transactionData = [
|
||||
'currency' => $this->order->getCurrency()->getIsoCode(),
|
||||
'customer_email_address' => $customer->getEmail(),
|
||||
'customer_id' => $customerId,
|
||||
'language' => $this->localeCodeProvider->getLocaleCodeFromContext($this->salesChannelContext->getContext()) ?? null,
|
||||
'merchant_reference' => $this->fixLength($this->order->getOrderNumber(), 100),
|
||||
'meta_data' => [
|
||||
self::VRPAYMENT_METADATA_ORDER_ID => $this->order->getId(),
|
||||
self::VRPAYMENT_METADATA_ORDER_TRANSACTION_ID => $this->order->getTransactions()->first()->getId(),
|
||||
self::VRPAYMENT_METADATA_SALES_CHANNEL_ID => $this->salesChannelContext->getSalesChannel()->getId(),
|
||||
self::VRPAYMENT_METADATA_CUSTOMER_NAME => $customerName,
|
||||
],
|
||||
'shipping_method' => $this->salesChannelContext->getShippingMethod()->getName() ? $this->fixLength($this->salesChannelContext->getShippingMethod()->getName(), 200) : null,
|
||||
'space_view_id' => $this->settings->getSpaceViewId() ?? null,
|
||||
];
|
||||
|
||||
// we have to manually check for these additional fields as they might not be active
|
||||
if (!empty($additionalAddress1 = $customer->getDefaultBillingAddress()->getAdditionalAddressLine1())) {
|
||||
$transactionData['meta_data']['additionalAddress1'] = $additionalAddress1;
|
||||
}
|
||||
|
||||
if (!empty($additionalAddress2 = $customer->getDefaultBillingAddress()->getAdditionalAddressLine2())) {
|
||||
$transactionData['meta_data']['additionalAddress2'] = $additionalAddress2;
|
||||
}
|
||||
|
||||
if (!empty($this->order->getCustomerComment())) {
|
||||
$transactionData['meta_data']['customer_comment'] = $this->order->getCustomerComment();
|
||||
}
|
||||
|
||||
$vatIds = $customer->getVatIds();
|
||||
if (!empty($vatIds)) {
|
||||
$taxNumber = $vatIds[0];
|
||||
$transactionData['meta_data']['taxNumber'] = $taxNumber;
|
||||
}
|
||||
|
||||
if (!empty($companyDepartment = $customer->getDefaultBillingAddress()->getDepartment())) {
|
||||
$transactionData['meta_data']['billingCompanyDepartment'] = $companyDepartment;
|
||||
}
|
||||
|
||||
if (!empty($companyDepartment = $customer->getDefaultShippingAddress()->getDepartment())) {
|
||||
$transactionData['meta_data']['shippingCompanyDepartment'] = $companyDepartment;
|
||||
}
|
||||
|
||||
$transactionPayload = (new TransactionPending())
|
||||
->setId($_SESSION['transactionId'])
|
||||
->setVersion($version)
|
||||
->setBillingAddress($billingAddress)
|
||||
->setCurrency($transactionData['currency'])
|
||||
->setCustomerEmailAddress($transactionData['customer_email_address'])
|
||||
->setCustomerId($transactionData['customer_id'])
|
||||
->setLanguage($transactionData['language'])
|
||||
->setLineItems($lineItems)
|
||||
->setMerchantReference($transactionData['merchant_reference'])
|
||||
->setMetaData($transactionData['meta_data'])
|
||||
->setShippingAddress($shippingAddress)
|
||||
->setShippingMethod($transactionData['shipping_method']);
|
||||
|
||||
$paymentConfiguration = $this->getPaymentConfiguration($this->salesChannelContext->getPaymentMethod()->getId());
|
||||
|
||||
$transactionPayload->setAllowedPaymentMethodConfigurations([$paymentConfiguration->getPaymentMethodConfigurationId()]);
|
||||
|
||||
$successUrl = $this->transaction->getReturnUrl() . '&status=paid';
|
||||
$failedUrl = $this->getFailUrl($this->order->getId()) . '&status=fail';
|
||||
$transactionPayload->setSuccessUrl($successUrl)
|
||||
->setFailedUrl($failedUrl);
|
||||
|
||||
if (!$transactionPayload->valid()) {
|
||||
$this->logger->critical('Transaction payload invalid:', $transactionPayload->listInvalidProperties());
|
||||
throw new InvalidPayloadException('Transaction payload invalid:' . json_encode($transactionPayload->listInvalidProperties()));
|
||||
}
|
||||
|
||||
return $transactionPayload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transaction line items
|
||||
*
|
||||
* @return \VRPayment\Sdk\Model\LineItemCreate[]
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function getLineItems(): array
|
||||
{
|
||||
$lineItems = [];
|
||||
$items = $this->order->getLineItems() ?? [];
|
||||
|
||||
foreach ($items as $shopLineItem) {
|
||||
if ($this->shouldSkipLineItem($shopLineItem)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->isCustomProductOption($shopLineItem)) {
|
||||
$shopLineItem = $this->updateCustomProductOptionLabel($shopLineItem);
|
||||
}
|
||||
|
||||
$lineItem = $this->createLineItem($shopLineItem);
|
||||
$this->validateLineItem($lineItem);
|
||||
|
||||
$lineItems[] = $lineItem;
|
||||
}
|
||||
|
||||
$this->processDiscounts($items, $lineItems);
|
||||
$this->sortLineItemsByName($lineItems);
|
||||
|
||||
$this->addOptionalLineItems($lineItems);
|
||||
|
||||
return $lineItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a line item should be skipped.
|
||||
*/
|
||||
protected function shouldSkipLineItem($shopLineItem): bool
|
||||
{
|
||||
return in_array($shopLineItem->getType(), [
|
||||
CustomProductsLineItemTypes::LINE_ITEM_TYPE_CUSTOMIZED_PRODUCTS,
|
||||
'promotion'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the line item is a custom product option.
|
||||
*/
|
||||
protected function isCustomProductOption($shopLineItem): bool
|
||||
{
|
||||
return $shopLineItem->getType() === CustomProductsLineItemTypes::LINE_ITEM_TYPE_CUSTOMIZED_PRODUCTS_OPTION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the label of a custom product option.
|
||||
*/
|
||||
protected function updateCustomProductOptionLabel($shopLineItem)
|
||||
{
|
||||
$customProductOptionParentLabel = $this->getCustomProductOptionLabel($shopLineItem->getParentId());
|
||||
$shopLineItem->setLabel($customProductOptionParentLabel . ': ' . $shopLineItem->getLabel());
|
||||
return $shopLineItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the created line item.
|
||||
*/
|
||||
protected function validateLineItem($lineItem): void
|
||||
{
|
||||
if (!$lineItem->valid()) {
|
||||
$this->logger->critical('LineItem payload invalid:', $lineItem->listInvalidProperties());
|
||||
throw new InvalidPayloadException('LineItem payload invalid: ' . json_encode($lineItem->listInvalidProperties()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process discounts from the order items and add them to the line items array.
|
||||
*/
|
||||
protected function processDiscounts($items, array &$lineItems): void
|
||||
{
|
||||
$itemsArray = is_array($items) ? $items : iterator_to_array($items);
|
||||
$discounts = array_filter($itemsArray, function ($orderItem) {
|
||||
return $orderItem->getType() === 'promotion';
|
||||
});
|
||||
|
||||
if ($discounts) {
|
||||
foreach ($discounts as $discount) {
|
||||
$this->addDiscountLineItem($discount, $lineItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add discount line item.
|
||||
*/
|
||||
protected function addDiscountLineItem($discount, array &$lineItems): void
|
||||
{
|
||||
$calculatedPrice = $discount->getPrice();
|
||||
$discountName = $discount->getLabel() ?? 'Unnamed';
|
||||
$definition = $discount->getPriceDefinition();
|
||||
|
||||
if ($this->order->getTaxStatus() === 'net' || $definition instanceof \Shopware\Core\Checkout\Cart\Price\Struct\AbsolutePriceDefinition) {
|
||||
$calculatedTaxesCollection = $calculatedPrice->getCalculatedTaxes();
|
||||
foreach ($calculatedTaxesCollection as $calculatedTax) {
|
||||
$rate = $calculatedTax->getTaxRate();
|
||||
$amount = $this->calculateDiscountAmount($calculatedTax);
|
||||
|
||||
$lineItems[] = $this->createDiscountLineItem($discountName, $amount, $rate);
|
||||
}
|
||||
} else {
|
||||
$taxRules = $calculatedPrice->getTaxRules();
|
||||
|
||||
if ($taxRules && $taxRules->count() > 0) {
|
||||
foreach ($taxRules as $taxRule) {
|
||||
$rate = $taxRule->getTaxRate();
|
||||
$amount = $calculatedPrice->getTotalPrice();
|
||||
$lineItems[] = $this->createDiscountLineItem($discountName, $amount, $rate);
|
||||
}
|
||||
} else {
|
||||
$rate = $this->getDefaultTaxRate();
|
||||
$amount = $calculatedPrice->getTotalPrice();
|
||||
$lineItems[] = $this->createDiscountLineItem($discountName, $amount, $rate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $discountName
|
||||
* @param float $amount
|
||||
* @param float $rate
|
||||
* @return LineItemCreate
|
||||
*/
|
||||
private function createDiscountLineItem(string $discountName, float $amount, float $rate): LineItemCreate
|
||||
{
|
||||
$lineItem = new LineItemCreate();
|
||||
|
||||
$discountSkuName = 'sku-discount-' . $rate . '-' . $discountName;
|
||||
$discountTitle = sprintf('DISCOUNT: %s (%s%% tax)', $discountName, $rate);
|
||||
if ($this->order->getTaxStatus() === 'tax-free') {
|
||||
$discountSkuName = 'sku-discount-' . $discountName;
|
||||
$discountTitle = sprintf('DISCOUNT: %s', $discountName);
|
||||
}
|
||||
|
||||
$lineItem->setAmountIncludingTax($amount)
|
||||
->setName($discountTitle)
|
||||
->setQuantity(1)
|
||||
->setShippingRequired(false)
|
||||
->setSku($discountSkuName, 200)
|
||||
->setType(LineItemType::DISCOUNT)
|
||||
->setUniqueId('coupon-' . $discountSkuName);
|
||||
|
||||
$taxRate = new TaxCreate([
|
||||
'title' => 'Discount Tax: ' . $rate,
|
||||
'rate' => $rate,
|
||||
]);
|
||||
|
||||
if ($this->order->getTaxStatus() !== 'tax-free') {
|
||||
$lineItem->setTaxes([$taxRate]);
|
||||
}
|
||||
|
||||
return $lineItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
private function getDefaultTaxRate(): float
|
||||
{
|
||||
/** @var SystemConfigService $systemConfigService */
|
||||
$systemConfigService = $this->container->get(SystemConfigService::class);
|
||||
$taxId = $systemConfigService->get('core.tax.defaultTaxRate');
|
||||
|
||||
if (!$taxId || !is_string($taxId)) {
|
||||
return 21.0;
|
||||
}
|
||||
|
||||
$criteria = new Criteria([$taxId]);
|
||||
/** @var TaxRepository $taxRepository */
|
||||
$taxRepository = $this->container->get('tax.repository');
|
||||
$tax = $taxRepository->search($criteria, Context::createDefaultContext())->get($taxId);
|
||||
|
||||
return $tax instanceof TaxEntity ? $tax->getTaxRate() : 21.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate discount amount including tax if necessary.
|
||||
*/
|
||||
protected function calculateDiscountAmount($calculatedTax): float
|
||||
{
|
||||
$amount = self::round($calculatedTax->getPrice());
|
||||
if ($this->order->getTaxStatus() === 'net') {
|
||||
$amount = self::round($amount + $calculatedTax->getTax());
|
||||
}
|
||||
return $amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort line items by name.
|
||||
*/
|
||||
protected function sortLineItemsByName(array &$lineItems): void
|
||||
{
|
||||
usort($lineItems, function ($lineItem1, $lineItem2) {
|
||||
return strcmp($lineItem1->getName(), $lineItem2->getName());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add optional shipping and adjustment line items.
|
||||
*/
|
||||
protected function addOptionalLineItems(array &$lineItems): void
|
||||
{
|
||||
if (count($this->order->getShippingCosts()->getCalculatedTaxes()) === 1) {
|
||||
if ($shippingLineItem = $this->getShippingLineItem()) {
|
||||
$lineItems[] = $shippingLineItem;
|
||||
}
|
||||
} else {
|
||||
if ($multipleShippingLineItems = $this->getMultipleShippingLineItems()) {
|
||||
$lineItems = array_merge($lineItems, $multipleShippingLineItems);
|
||||
}
|
||||
}
|
||||
|
||||
if ($adjustmentLineItem = $this->getAdjustmentLineItem($lineItems)) {
|
||||
$lineItems[] = $adjustmentLineItem;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $lineItemParentId
|
||||
* @return string
|
||||
*/
|
||||
protected function getCustomProductOptionLabel(string $lineItemParentId): string
|
||||
{
|
||||
$label = '';
|
||||
foreach ($this->order->getLineItems() as $shopLineItem) {
|
||||
if ($shopLineItem->getParentId() === $lineItemParentId && $shopLineItem->getType() === CustomProductsLineItemTypes::LINE_ITEM_TYPE_PRODUCT) {
|
||||
$label = $shopLineItem->getLabel();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $label;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return \VRPayment\Sdk\Model\LineItemCreate|null
|
||||
* @throws \Exception
|
||||
* @var \Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity $shopLineItem
|
||||
*/
|
||||
protected function createLineItem(OrderLineItemEntity $shopLineItem): ?LineItemCreate
|
||||
{
|
||||
$uniqueId = $shopLineItem->getId();
|
||||
$sku = $shopLineItem->getProductId() ? $shopLineItem->getProductId() : $uniqueId;
|
||||
$payLoad = $shopLineItem->getPayload();
|
||||
if (!empty($payLoad) && !empty($payLoad['productNumber'])) {
|
||||
$sku = $payLoad['productNumber'];
|
||||
}
|
||||
$sku = $this->fixLength($sku, 200);
|
||||
|
||||
$amount = $shopLineItem->getTotalPrice() ? self::round($shopLineItem->getTotalPrice()) : 0;
|
||||
|
||||
//include Tax Excluded for Net Tax display customer group
|
||||
if ($this->order->getTaxStatus() === 'net') {
|
||||
$amount = self::round($amount + $shopLineItem->getPrice()->getCalculatedTaxes()->getAmount());
|
||||
}
|
||||
|
||||
$lineItem = (new LineItemCreate())
|
||||
->setName($this->fixLength($shopLineItem->getLabel(), 150))
|
||||
->setUniqueId($uniqueId)
|
||||
->setSku($sku)
|
||||
->setQuantity($shopLineItem->getQuantity() ?? 1)
|
||||
->setAmountIncludingTax($amount);
|
||||
|
||||
|
||||
if (!empty($shopLineItem->getType()) && $shopLineItem->getType() == CustomProductsLineItemTypes::LINE_ITEM_TYPE_CUSTOMIZED_PRODUCTS) {
|
||||
|
||||
$productAttributes = $this->getCustomProductLineItemAttribute($shopLineItem);
|
||||
$taxes = $this->getCustomProductTaxes(
|
||||
$shopLineItem->getPrice()->getCalculatedTaxes(),
|
||||
$this->translator->trans('vrpayment.payload.taxes'),
|
||||
$amount
|
||||
);
|
||||
|
||||
} else {
|
||||
$productAttributes = $this->getProductAttributes($shopLineItem);
|
||||
|
||||
$taxes = $this->getTaxes(
|
||||
$shopLineItem->getPrice()->getCalculatedTaxes(),
|
||||
$this->translator->trans('vrpayment.payload.taxes')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (!empty($productAttributes)) {
|
||||
$lineItem->setAttributes($productAttributes);
|
||||
}
|
||||
|
||||
if (!empty($taxes)) {
|
||||
if ($this->order->getTaxStatus() !== 'tax-free') {
|
||||
$lineItem->setTaxes($taxes);
|
||||
}
|
||||
}
|
||||
|
||||
if ($shopLineItem->getTotalPrice() >= 0) {
|
||||
$lineItem->setType(LineItemType::PRODUCT);
|
||||
} else {
|
||||
$lineItem->setType(LineItemType::DISCOUNT);
|
||||
}
|
||||
|
||||
return $lineItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Checkout\Cart\Tax\Struct\CalculatedTaxCollection $calculatedTaxes
|
||||
* @param string $title
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTaxes(CalculatedTaxCollection $calculatedTaxes, string $title): array
|
||||
{
|
||||
$taxes = [];
|
||||
foreach ($calculatedTaxes as $calculatedTax) {
|
||||
|
||||
$tax = (new TaxCreate())
|
||||
->setRate($calculatedTax->getTaxRate())
|
||||
->setTitle($this->fixLength($title . ' : ' . $calculatedTax->getTaxRate(), 40));
|
||||
|
||||
if (!$tax->valid()) {
|
||||
$this->logger->critical('Tax payload invalid:', $tax->listInvalidProperties());
|
||||
throw new InvalidPayloadException('Tax payload invalid:' . json_encode($tax->listInvalidProperties()));
|
||||
}
|
||||
|
||||
$taxes [] = $tax;
|
||||
}
|
||||
|
||||
return $taxes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity $shopLineItem
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
protected function getProductAttributes(OrderLineItemEntity $shopLineItem): ?array
|
||||
{
|
||||
$productAttributes = [];
|
||||
$lineItemPayload = $shopLineItem->getPayload();
|
||||
|
||||
if (is_array($lineItemPayload) && !empty($lineItemPayload['options'])) {
|
||||
foreach ($lineItemPayload['options'] as $option) {
|
||||
|
||||
$label = $option['group'];
|
||||
$lineItemAttributeCreate = (new LineItemAttributeCreate())
|
||||
->setLabel($this->fixLength($label, 512))
|
||||
->setValue($this->fixLength((string)$option['option'], 512));
|
||||
|
||||
if ($lineItemAttributeCreate->valid()) {
|
||||
$key = $this->fixLength('option_' . md5($label), 40);
|
||||
$productAttributes[$key] = $lineItemAttributeCreate;
|
||||
} else {
|
||||
$this->logger->critical('LineItemAttributeCreate payload invalid:', $lineItemAttributeCreate->listInvalidProperties());
|
||||
throw new InvalidPayloadException('LineItemAttributeCreate payload invalid:' . json_encode($lineItemAttributeCreate->listInvalidProperties()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return empty($productAttributes) ? null : $productAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \VRPayment\Sdk\Model\LineItemCreate|null
|
||||
*/
|
||||
protected function getShippingLineItem(): ?LineItemCreate
|
||||
{
|
||||
try {
|
||||
|
||||
$amount = $this->order->getShippingTotal();
|
||||
$amount = self::round($amount);
|
||||
|
||||
if ($amount > 0) {
|
||||
|
||||
$shippingName = $this->salesChannelContext->getShippingMethod()->getName() ?? $this->translator->trans('vrpayment.payload.shipping.name');
|
||||
$taxes = $this->getTaxes(
|
||||
$this->order->getShippingCosts()->getCalculatedTaxes(),
|
||||
$shippingName
|
||||
);
|
||||
if ($this->order->getTaxStatus() === 'net') {
|
||||
$amount = self::round($amount + $this->order->getShippingCosts()->getCalculatedTaxes()->getAmount());
|
||||
}
|
||||
|
||||
|
||||
$lineItem = (new LineItemCreate())
|
||||
->setAmountIncludingTax($amount)
|
||||
->setName($this->fixLength($shippingName . ' ' . $this->translator->trans('vrpayment.payload.shipping.lineItem'), 150))
|
||||
->setQuantity($this->order->getShippingCosts()->getQuantity() ?? 1)
|
||||
->setSku($this->fixLength($shippingName . '-Shipping', 200))
|
||||
/** @noinspection PhpParamsInspection */
|
||||
->setType(LineItemType::SHIPPING)
|
||||
->setUniqueId($this->fixLength($shippingName . '-Shipping', 200));
|
||||
|
||||
if ($this->order->getTaxStatus() !== 'tax-free') {
|
||||
$lineItem->setTaxes($taxes);
|
||||
}
|
||||
|
||||
if (!$lineItem->valid()) {
|
||||
$this->logger->critical('Shipping LineItem payload invalid:', $lineItem->listInvalidProperties());
|
||||
throw new InvalidPayloadException('Shipping LineItem payload invalid:' . json_encode($lineItem->listInvalidProperties()));
|
||||
}
|
||||
|
||||
return $lineItem;
|
||||
}
|
||||
|
||||
} catch (\Exception $exception) {
|
||||
$this->logger->critical(__CLASS__ . ' : ' . __FUNCTION__ . ' : ' . $exception->getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getMultipleShippingLineItems(): array
|
||||
{
|
||||
try {
|
||||
if ($this->order->getShippingTotal() > 0) {
|
||||
$lineItems = [];
|
||||
$shippingName = $this->salesChannelContext->getShippingMethod()->getName() ?? $this->translator->trans('vrpayment.payload.shipping.name');
|
||||
|
||||
$isFirst = true;
|
||||
|
||||
foreach ($this->order->getShippingCosts()->getCalculatedTaxes() as $taxItem) {
|
||||
$amount = self::round($taxItem->getPrice());
|
||||
if ($this->order->getTaxStatus() === 'net') {
|
||||
$amount = self::round($amount + $taxItem->getTax());
|
||||
}
|
||||
$taxRate = $taxItem->getTaxRate();
|
||||
$tax = (new TaxCreate())
|
||||
->setRate($taxRate)
|
||||
->setTitle('Tax rate: '.$taxRate);
|
||||
|
||||
$name = $taxRate . '%-' . $shippingName;
|
||||
$lineItem = (new LineItemCreate())
|
||||
->setAmountIncludingTax($amount)
|
||||
->setName($this->fixLength($name . ' ' . $this->translator->trans('vrpayment.payload.shipping.lineItem'), 150))
|
||||
->setQuantity($this->order->getShippingCosts()->getQuantity() ?? 1)
|
||||
->setSku($this->fixLength($name . '-Shipping', 200))
|
||||
->setType($isFirst ? LineItemType::SHIPPING : LineItemType::FEE) // First item as SHIPPING, rest as FEE
|
||||
->setUniqueId($this->fixLength($name . '-Shipping', 200));
|
||||
|
||||
if ($this->order->getTaxStatus() !== 'tax-free') {
|
||||
$lineItem->setTaxes([$tax]);
|
||||
}
|
||||
|
||||
if (!$lineItem->valid()) {
|
||||
$this->logger->critical('Shipping LineItem payload invalid:', $lineItem->listInvalidProperties());
|
||||
throw new InvalidPayloadException('Shipping LineItem payload invalid:' . json_encode($lineItem->listInvalidProperties()));
|
||||
}
|
||||
|
||||
$lineItems[] = $lineItem;
|
||||
$isFirst = false;
|
||||
}
|
||||
return $lineItems;
|
||||
}
|
||||
|
||||
} catch (\Exception $exception) {
|
||||
$this->logger->critical(__CLASS__ . ' : ' . __FUNCTION__ . ' : ' . $exception->getMessage());
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Adjustment Line Item
|
||||
*
|
||||
* @param \VRPayment\Sdk\Model\LineItemCreate[] $lineItems
|
||||
*
|
||||
* @return \VRPayment\Sdk\Model\LineItemCreate|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function getAdjustmentLineItem(array &$lineItems): ?LineItemCreate
|
||||
{
|
||||
$lineItem = null;
|
||||
|
||||
$lineItemPriceTotal = array_sum(array_map(static function (LineItemCreate $lineItem) {
|
||||
return $lineItem->getAmountIncludingTax();
|
||||
}, $lineItems));
|
||||
|
||||
$adjustmentPrice = $this->order->getAmountTotal() - $lineItemPriceTotal;
|
||||
$adjustmentPrice = self::round($adjustmentPrice);
|
||||
|
||||
if (abs($adjustmentPrice) != 0) {
|
||||
if ($this->settings->isLineItemConsistencyEnabled()) {
|
||||
$error = strtr('LineItems total :lineItemTotal does not add up to order total :orderTotal', [
|
||||
':lineItemTotal' => $lineItemPriceTotal,
|
||||
':orderTotal' => $this->order->getAmountTotal(),
|
||||
]);
|
||||
$this->logger->critical($error);
|
||||
throw new \Exception($error);
|
||||
|
||||
} else {
|
||||
$lineItem = (new LineItemCreate())
|
||||
->setName($this->translator->trans('vrpayment.payload.adjustmentLineItem'))
|
||||
->setUniqueId('Adjustment-Line-Item')
|
||||
->setSku('Adjustment-Line-Item')
|
||||
->setQuantity(1);
|
||||
/** @noinspection PhpParamsInspection */
|
||||
$lineItem->setAmountIncludingTax($adjustmentPrice)
|
||||
->setType(($adjustmentPrice > 0) ? LineItemType::FEE : LineItemType::DISCOUNT);
|
||||
|
||||
if (!$lineItem->valid()) {
|
||||
$this->logger->critical('Adjustment LineItem payload invalid:', $lineItem->listInvalidProperties());
|
||||
throw new InvalidPayloadException('Adjustment LineItem payload invalid:' . json_encode($lineItem->listInvalidProperties()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $lineItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get address payload
|
||||
*
|
||||
* @param \Shopware\Core\Checkout\Customer\CustomerEntity $customer
|
||||
* @param \Shopware\Core\Checkout\Customer\Aggregate\CustomerAddress\CustomerAddressEntity $customerAddressEntity
|
||||
*
|
||||
* @return \VRPayment\Sdk\Model\AddressCreate
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function getAddressPayload(CustomerEntity $customer, CustomerAddressEntity $customerAddressEntity, bool $returnSalesTaxNumber = true): AddressCreate
|
||||
{
|
||||
// Family name
|
||||
$family_name = null;
|
||||
if (!empty($customerAddressEntity->getLastName())) {
|
||||
$family_name = $customerAddressEntity->getLastName();
|
||||
} else {
|
||||
if (!empty($customer->getLastName())) {
|
||||
$family_name = $customer->getLastName();
|
||||
}
|
||||
}
|
||||
$family_name = !empty($family_name) ? $this->fixLength($family_name, 100) : null;
|
||||
|
||||
// Given name
|
||||
$given_name = null;
|
||||
if (!empty($customerAddressEntity->getFirstName())) {
|
||||
$given_name = $customerAddressEntity->getFirstName();
|
||||
} else {
|
||||
if (!empty($customer->getFirstName())) {
|
||||
$given_name = $customer->getFirstName();
|
||||
}
|
||||
}
|
||||
$given_name = !empty($given_name) ? $this->fixLength($given_name, 100) : null;
|
||||
|
||||
// Organization name
|
||||
$organization_name = null;
|
||||
if (!empty($customerAddressEntity->getCompany())) {
|
||||
$organization_name = $customerAddressEntity->getCompany();
|
||||
}
|
||||
|
||||
$organization_name = !empty($organization_name) ? $this->fixLength($organization_name, 100) : null;
|
||||
|
||||
$salesTaxNumber = null;
|
||||
if ($returnSalesTaxNumber) {
|
||||
// salesTaxNumber
|
||||
$vatIds = $customer->getVatIds();
|
||||
if (!empty($vatIds)) {
|
||||
$salesTaxNumber = $vatIds[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Salutation
|
||||
$salutation = null;
|
||||
if (!(
|
||||
empty($customerAddressEntity->getSalutation()) ||
|
||||
empty($customerAddressEntity->getSalutation()->getDisplayName())
|
||||
)) {
|
||||
$salutation = $customerAddressEntity->getSalutation()->getDisplayName();
|
||||
} else {
|
||||
if (!empty($customer->getSalutation())) {
|
||||
$salutation = $customer->getSalutation()->getDisplayName();
|
||||
|
||||
}
|
||||
}
|
||||
$salutation = !empty($salutation) ? $this->fixLength($salutation, 20) : null;
|
||||
|
||||
$birthday = null;
|
||||
if (!empty($customer->getBirthday())) {
|
||||
$birthday = new \DateTime();
|
||||
$birthday->setTimestamp($customer->getBirthday()->getTimestamp());
|
||||
$birthday = $birthday->format('Y-m-d');
|
||||
}
|
||||
|
||||
$postalState = $customerAddressEntity?->getCountryState()?->getName() ?? '';
|
||||
if (empty($postalState)) {
|
||||
$postalState = $customerAddressEntity?->getCountryState()?->getShortCode() ?? '';
|
||||
}
|
||||
|
||||
$addressData = [
|
||||
'city' => $customerAddressEntity->getCity() ? $this->fixLength($customerAddressEntity->getCity(), 100) : null,
|
||||
'country' => $customerAddressEntity->getCountry() ? $customerAddressEntity->getCountry()->getIso() : null,
|
||||
'email_address' => $customer->getEmail() ? $this->fixLength($customer->getEmail(), 254) : null,
|
||||
'family_name' => $family_name,
|
||||
'given_name' => $given_name,
|
||||
'organization_name' => $organization_name,
|
||||
'phone_number' => $customerAddressEntity->getPhoneNumber() ? $this->fixLength($customerAddressEntity->getPhoneNumber(), 100) : null,
|
||||
'postcode' => $customerAddressEntity->getZipcode() ? $this->fixLength($customerAddressEntity->getZipcode(), 40) : null,
|
||||
'postal_state' => $postalState,
|
||||
'salutation' => $salutation,
|
||||
'street' => $customerAddressEntity->getStreet() ? $this->fixLength($customerAddressEntity->getStreet(), 300) : null,
|
||||
'birthday' => $birthday
|
||||
];
|
||||
|
||||
if ($returnSalesTaxNumber) {
|
||||
$addressData['sales_tax_number'] = $salesTaxNumber;
|
||||
}
|
||||
|
||||
$addressPayload = (new AddressCreate())
|
||||
->setCity($addressData['city'])
|
||||
->setCountry($addressData['country'])
|
||||
->setEmailAddress($addressData['email_address'])
|
||||
->setFamilyName($addressData['family_name'])
|
||||
->setGivenName($addressData['given_name'])
|
||||
->setOrganizationName($addressData['organization_name'])
|
||||
->setPhoneNumber($addressData['phone_number'])
|
||||
->setPostCode($addressData['postcode'])
|
||||
->setPostalState($addressData['postal_state'])
|
||||
->setSalutation($addressData['salutation'])
|
||||
->setStreet($addressData['street']);
|
||||
|
||||
if ($returnSalesTaxNumber) {
|
||||
$addressPayload->setSalesTaxNumber($addressData['sales_tax_number']);
|
||||
}
|
||||
|
||||
if (!empty($addressData['birthday'])) {
|
||||
$addressPayload->setDateOfBirth($addressData['birthday']);
|
||||
}
|
||||
|
||||
if (!$addressPayload->valid()) {
|
||||
$this->logger->critical('Address payload invalid:', $addressPayload->listInvalidProperties());
|
||||
throw new InvalidPayloadException('Address payload invalid:' . json_encode($addressPayload->listInvalidProperties()));
|
||||
}
|
||||
|
||||
return $addressPayload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
*
|
||||
* @return \VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Entity\PaymentMethodConfigurationEntity
|
||||
*/
|
||||
protected function getPaymentConfiguration(string $id): PaymentMethodConfigurationEntity
|
||||
{
|
||||
$criteria = (new Criteria([$id]));
|
||||
|
||||
return $this->container->get('vrpayment_payment_method_configuration.repository')
|
||||
->search($criteria, $this->salesChannelContext->getContext())
|
||||
->getEntities()->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get failure URL
|
||||
*
|
||||
* @param string $orderId
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getFailUrl(string $orderId): string
|
||||
{
|
||||
return $this->container->get('router')->generate(
|
||||
'frontend.vrpayment.checkout.recreate-cart',
|
||||
['orderId' => $orderId,],
|
||||
UrlGeneratorInterface::ABSOLUTE_URL
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,205 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Util;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shopware\Core\{
|
||||
Framework\Context,
|
||||
Framework\DataAbstractionLayer\Search\Criteria,
|
||||
Framework\DataAbstractionLayer\Search\Filter\EqualsFilter,
|
||||
Framework\DataAbstractionLayer\Search\Filter\NotFilter,
|
||||
Framework\DataAbstractionLayer\Search\Sorting\FieldSorting,
|
||||
System\SalesChannel\SalesChannelCollection,};
|
||||
use VRPaymentPayment\Core\Checkout\PaymentHandler\VRPaymentPaymentHandler;
|
||||
|
||||
/**
|
||||
* Class PaymentMethodUtil
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Util
|
||||
*/
|
||||
class PaymentMethodUtil {
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @var \Psr\Container\ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface
|
||||
*/
|
||||
private $paymentRepository;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface
|
||||
*/
|
||||
private $salesChannelRepository;
|
||||
|
||||
/**
|
||||
* @var \Shopware\Core\System\SalesChannel\Aggregate\SalesChannelPaymentMethod\SalesChannelPaymentMethodDefinition
|
||||
*/
|
||||
private $salesChannelPaymentMethodRepository;
|
||||
|
||||
/**
|
||||
* PaymentMethodUtil constructor.
|
||||
*
|
||||
* @param \Psr\Container\ContainerInterface $container
|
||||
*/
|
||||
public function __construct(ContainerInterface $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->paymentRepository = $this->container->get('payment_method.repository');
|
||||
$this->salesChannelRepository = $this->container->get('sales_channel.repository');
|
||||
$this->salesChannelPaymentMethodRepository = $this->container->get('sales_channel_payment_method.repository');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
* @internal
|
||||
* @required
|
||||
*
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger): void
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @param string|null $salesChannelId
|
||||
*/
|
||||
public function setVRPaymentAsDefaultPaymentMethod(Context $context, ?string $salesChannelId = null): void
|
||||
{
|
||||
$paymentMethodIds = $this->getVRPaymentPaymentMethodIds($context);
|
||||
if (empty($paymentMethodIds)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$salesChannelsToChange = $this->getSalesChannelsToChange($context, $salesChannelId);
|
||||
$updateData = [];
|
||||
|
||||
foreach ($salesChannelsToChange as $salesChannel) {
|
||||
foreach ($paymentMethodIds as $paymentMethodId) {
|
||||
$salesChannelUpdateData = [
|
||||
'id' => $salesChannel->getId(),
|
||||
'paymentMethodId' => $paymentMethodId,
|
||||
];
|
||||
|
||||
$paymentMethodCollection = $salesChannel->getPaymentMethods();
|
||||
if (is_null($paymentMethodCollection) || is_null($paymentMethodCollection->get($paymentMethodId))) {
|
||||
$salesChannelUpdateData['paymentMethods'][] = [
|
||||
'id' => $paymentMethodId,
|
||||
];
|
||||
}
|
||||
|
||||
$updateData[] = $salesChannelUpdateData;
|
||||
}
|
||||
}
|
||||
|
||||
$this->salesChannelRepository->update($updateData, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @return array
|
||||
*/
|
||||
public function getVRPaymentPaymentMethodIds(Context $context): array
|
||||
{
|
||||
$criteria = (new Criteria())
|
||||
->addFilter(new EqualsFilter('handlerIdentifier', VRPaymentPaymentHandler::class))
|
||||
->addSorting(new FieldSorting('position'));
|
||||
|
||||
return $this->paymentRepository->searchIds($criteria, $context)->getIds();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @param string|null $salesChannelId
|
||||
* @return \Shopware\Core\System\SalesChannel\SalesChannelCollection
|
||||
*/
|
||||
private function getSalesChannelsToChange(Context $context, ?string $salesChannelId = null): SalesChannelCollection
|
||||
{
|
||||
$criteria = is_null($salesChannelId) ? new Criteria() : new Criteria([$salesChannelId]);
|
||||
$criteria->addAssociation('paymentMethods');
|
||||
|
||||
return $this->salesChannelRepository->search($criteria, $context)->getEntities();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable System Payment Methods
|
||||
*
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
*/
|
||||
public function disableSystemPaymentMethods(Context $context): void
|
||||
{
|
||||
$paymentMethodIds = $this->getSystemPaymentMethodIds($context);
|
||||
$this->setPaymentMethodIsActive($paymentMethodIds, false, $context);
|
||||
$this->disableSalesChannelPaymentMethods($paymentMethodIds, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getSystemPaymentMethodIds(Context $context): array
|
||||
{
|
||||
$criteria = (new Criteria())
|
||||
->addFilter(new NotFilter(
|
||||
NotFilter::CONNECTION_AND,
|
||||
[
|
||||
new EqualsFilter('handlerIdentifier', VRPaymentPaymentHandler::class),
|
||||
]
|
||||
));
|
||||
|
||||
return $this->paymentRepository->searchIds($criteria, $context)->getIds();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $paymentMethodIds
|
||||
* @param bool $active
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
*/
|
||||
protected function setPaymentMethodIsActive(array $paymentMethodIds, bool $active, Context $context): void
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($paymentMethodIds as $paymentMethodId) {
|
||||
$data[] = [
|
||||
'id' => $paymentMethodId,
|
||||
'active' => $active,
|
||||
];
|
||||
}
|
||||
|
||||
$this->paymentRepository->update($data, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $paymentMethodIds
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*
|
||||
*/
|
||||
protected function disableSalesChannelPaymentMethods(array $paymentMethodIds, Context $context)
|
||||
{
|
||||
$data = [];
|
||||
|
||||
$salesChannels = $this->getSalesChannelsToChange($context);
|
||||
|
||||
foreach ($salesChannels as $salesChannel) {
|
||||
foreach ($paymentMethodIds as $paymentMethodId) {
|
||||
$data[] = [
|
||||
'paymentMethodId' => $paymentMethodId,
|
||||
'salesChannelId' => $salesChannel->getId(),
|
||||
];
|
||||
}
|
||||
}
|
||||
$this->salesChannelPaymentMethodRepository->delete($data, $context);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Core\Util\Traits;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Shopware\Core\{
|
||||
Framework\Context,
|
||||
Framework\DataAbstractionLayer\EntityRepositoryInterface,
|
||||
Framework\DataAbstractionLayer\Search\Criteria,
|
||||
Framework\DataAbstractionLayer\Search\Filter\ContainsFilter,
|
||||
Framework\DataAbstractionLayer\Search\Filter\EqualsFilter,
|
||||
Framework\Plugin\Context\UninstallContext};
|
||||
use VRPaymentPayment\Core\{
|
||||
Checkout\PaymentHandler\VRPaymentPaymentHandler,
|
||||
Settings\Service\SettingsService};
|
||||
|
||||
/**
|
||||
* Trait VRPaymentPaymentPluginTrait
|
||||
*
|
||||
* We use a trait keep the plugin class clean and free of auxiliary functions.
|
||||
*
|
||||
* @package VRPaymentPayment\Core\Util\Traits
|
||||
*/
|
||||
trait VRPaymentPaymentPluginTrait {
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
*/
|
||||
protected function enablePaymentMethods(Context $context)
|
||||
{
|
||||
$paymentMethodIds = $this->getPaymentMethodIds($context);
|
||||
foreach ($paymentMethodIds as $paymentMethodId) {
|
||||
$this->setPaymentMethodIsActive($paymentMethodId, true, $context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getPaymentMethodIds(Context $context): array
|
||||
{
|
||||
/** @var EntityRepositoryInterface $paymentRepository */
|
||||
$paymentRepository = $this->container->get('payment_method.repository');
|
||||
$criteria = (new Criteria())
|
||||
->addFilter(new EqualsFilter('handlerIdentifier', VRPaymentPaymentHandler::class));
|
||||
|
||||
return $paymentRepository->searchIds($criteria, $context)->getIds();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $paymentMethodId
|
||||
* @param bool $active
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @return void
|
||||
*/
|
||||
protected function setPaymentMethodIsActive(string $paymentMethodId, bool $active, Context $context): void
|
||||
{
|
||||
$paymentMethod = [
|
||||
'id' => $paymentMethodId,
|
||||
'active' => $active,
|
||||
];
|
||||
|
||||
/** @var EntityRepositoryInterface $paymentRepository */
|
||||
$paymentRepository = $this->container->get('payment_method.repository');
|
||||
$paymentRepository->update([$paymentMethod], $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @return void
|
||||
*/
|
||||
protected function disablePaymentMethods(Context $context): void
|
||||
{
|
||||
$paymentMethodIds = $this->getPaymentMethodIds($context);
|
||||
foreach ($paymentMethodIds as $paymentMethodId) {
|
||||
$this->setPaymentMethodIsActive($paymentMethodId, false, $context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Shopware\Core\Framework\Context $context
|
||||
* @return void
|
||||
*/
|
||||
private function removeConfiguration(Context $context): void
|
||||
{
|
||||
$criteria = (new Criteria())
|
||||
->addFilter(new ContainsFilter('configurationKey', SettingsService::SYSTEM_CONFIG_DOMAIN));
|
||||
|
||||
$systemConfigRepository = $this->container->get('system_config.repository');
|
||||
$idSearchResult = $systemConfigRepository->searchIds($criteria, $context);
|
||||
|
||||
foreach ($idSearchResult->getIds() as $id) {
|
||||
$systemConfigRepository->delete([['id' => $id]], $context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete user data when plugin is uninstalled
|
||||
*
|
||||
* @internal
|
||||
* @param \Shopware\Core\Framework\Plugin\Context\UninstallContext $uninstallContext
|
||||
* @return void
|
||||
*/
|
||||
protected function deleteUserData(UninstallContext $uninstallContext): void
|
||||
{
|
||||
$connection = $this->container->get(Connection::class);
|
||||
// Check if the column exists before attempting to drop it
|
||||
$columns = $connection->fetchAllAssociative("SHOW COLUMNS FROM `order` LIKE 'vrpayment_lock'");
|
||||
if (!empty($columns)) {
|
||||
$query = 'ALTER TABLE `order` DROP COLUMN `vrpayment_lock`;';
|
||||
$connection->executeStatement($query);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Migration;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Shopware\Core\Framework\Migration\MigrationStep;
|
||||
|
||||
/**
|
||||
* Class Migration1590156974PaymentMethodConfigurationEntity
|
||||
*
|
||||
* @package VRPaymentPayment\Migration
|
||||
*/
|
||||
class Migration1590156974PaymentMethodConfigurationEntity extends MigrationStep {
|
||||
/**
|
||||
* get creation timestamp
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCreationTimestamp(): int
|
||||
{
|
||||
return 1590156974;
|
||||
}
|
||||
|
||||
/**
|
||||
* update non-destructive changes
|
||||
*
|
||||
* @param \Doctrine\DBAL\Connection $connection
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function update(Connection $connection): void
|
||||
{
|
||||
$connection->executeStatement('
|
||||
CREATE TABLE IF NOT EXISTS `vrpayment_payment_method_configuration` (
|
||||
`id` BINARY(16) NOT NULL,
|
||||
`data` JSON NOT NULL,
|
||||
`payment_method_configuration_id` INT UNSIGNED NOT NULL,
|
||||
`payment_method_id` BINARY(16) NOT NULL,
|
||||
`sort_order` TINYINT UNSIGNED NOT NULL,
|
||||
`space_id` INT UNSIGNED NOT NULL,
|
||||
`state` VARCHAR(255) NOT NULL,
|
||||
`created_at` DATETIME(3) NOT NULL,
|
||||
`updated_at` DATETIME(3) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `payment_method_configuration_id_space_id_UNIQUE` (`payment_method_configuration_id`,`space_id`),
|
||||
KEY `fk.vrp_payment_method_configuration.payment_method_id` (`payment_method_id`),
|
||||
CONSTRAINT `fk.vrp_payment_method_configuration.payment_method_id` FOREIGN KEY (`payment_method_id`) REFERENCES `payment_method` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
');
|
||||
}
|
||||
|
||||
/**
|
||||
* update destructive changes
|
||||
*
|
||||
* @param \Doctrine\DBAL\Connection $connection
|
||||
*/
|
||||
public function updateDestructive(Connection $connection): void
|
||||
{
|
||||
// implement update destructive
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace VRPaymentPayment\Migration;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Shopware\Core\Framework\Migration\MigrationStep;
|
||||
|
||||
/**
|
||||
* Class Migration1590156974TransactionEntity
|
||||
*
|
||||
* @package VRPaymentPayment\Migration
|
||||
*/
|
||||
class Migration1590156974TransactionEntity extends MigrationStep {
|
||||
|
||||
/**
|
||||
* get creation timestamp
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCreationTimestamp(): int
|
||||
{
|
||||
return 1590156974;
|
||||
}
|
||||
|
||||
/**
|
||||
* update non-destructive changes
|
||||
*
|
||||
* @param \Doctrine\DBAL\Connection $connection
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function update(Connection $connection): void
|
||||
{
|
||||
$connection->executeStatement('
|
||||
CREATE TABLE IF NOT EXISTS `vrpayment_transaction` (
|
||||
`id` BINARY(16) NOT NULL,
|
||||
`data` JSON NOT NULL,
|
||||
`payment_method_id` BINARY(16) NOT NULL,
|
||||
`order_id` BINARY(16) NOT NULL,
|
||||
`order_transaction_id` BINARY(16) NOT NULL,
|
||||
`space_id` INT UNSIGNED NOT NULL,
|
||||
`state` VARCHAR(255) NOT NULL,
|
||||
`sales_channel_id` BINARY(16) NOT NULL,
|
||||
`transaction_id` INT UNSIGNED NOT NULL,
|
||||
`created_at` DATETIME(3) NOT NULL,
|
||||
`updated_at` DATETIME(3) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `transaction_id_UNIQUE` (`transaction_id`),
|
||||
KEY `fk.vrp_transaction.order_id` (`order_id`),
|
||||
KEY `fk.vrp_transaction.order_transaction_id` (`order_transaction_id`),
|
||||
KEY `fk.vrp_transaction.payment_method_id` (`payment_method_id`),
|
||||
KEY `fk.vrp_transaction.sales_channel_id` (`sales_channel_id`),
|
||||
CONSTRAINT `fk.vrp_transaction.order_id` FOREIGN KEY (`order_id`) REFERENCES `order` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk.vrp_transaction.order_transaction_id` FOREIGN KEY (`order_transaction_id`) REFERENCES `order_transaction` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk.vrp_transaction.payment_method_id` FOREIGN KEY (`payment_method_id`) REFERENCES `payment_method` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk.vrp_transaction.sales_channel_id` FOREIGN KEY (`sales_channel_id`) REFERENCES `sales_channel` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
');
|
||||
}
|
||||
|
||||
/**
|
||||
* update destructive changes
|
||||
*
|
||||
* @param \Doctrine\DBAL\Connection $connection
|
||||
*/
|
||||
public function updateDestructive(Connection $connection): void
|
||||
{
|
||||
// implement update destructive
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user