diff --git a/.htaccess b/.htaccess
new file mode 100644
index 0000000..3d84ab1
--- /dev/null
+++ b/.htaccess
@@ -0,0 +1,21 @@
+
+ Order deny,allow
+ Deny from all
+ Allow from 127.0.0.1
+
+
+
+ Order deny,allow
+ Deny from all
+ Allow from 127.0.0.1
+
+
+
+ Order allow,deny
+ Deny from all
+
+
+
+ Order allow,deny
+ Deny from all
+
diff --git a/_config.php b/_config.php
new file mode 100644
index 0000000..5ece431
--- /dev/null
+++ b/_config.php
@@ -0,0 +1,15 @@
+
+ Zugelassene Staaten: DE, AT, CH
+ Zugelassene Währung: EUR
+ Mindestbetrag der Bestellung: 9,99 EUR or more
+ Mindestalter: 18 years or more
+ Rechnungsadresse und Lieferadresse müssen übereinstimmen
'
+ GuaranteeMinOrderAmount: 'Mindestbestellbetrag für Zahlungsgarantie'
+ GuaranteeMinOrderAmountDesc: 'Geben Sie den Mindestbetrag (in Cent) für die zu bearbeitende Transaktion mit Zahlungsgarantie ein. Geben Sie z.B. 100 ein, was 1,00 entspricht. Der Standbetrag ist 9,99 EUR'
+ PaymentGuaranteeForce: 'Zahlung ohne Zahlungsgarantie erzwingen'
+ PaymentGuaranteeForceDesc: 'Falls die Zahlungsgarantie zwar aktiviert ist, jedoch die Voraussetzungen für Zahlungsgarantie nicht erfüllt sind, wird die Zahlung ohne Zahlungsgarantie verarbeitet. Die Voraussetzungen finden Sie in der Installationsanleitung unter "Zahlungsgarantie aktivieren'
+ novalnetinvoiceConfiguration: 'Kauf auf Rechnung Konfiguration'
+ InvoicePaymentDueDate: 'Fälligkeit der Rechnung (in Tagen)'
+ InvoicePaymentDueDateDesc: 'Geben Sie die Anzahl der Tage ein, binnen derer die Zahlung bei Novalnet eingehen soll (muss größer als 7 Tage sein). Falls dieses Feld leer ist, werden 14 Tage als Standard-Zahlungsfrist gesetzt'
+ novalnetprepaymentConfiguration: 'Vorauskasse Konfiguration'
+ PrepaymentDueDate: 'Fälligkeitsdatum (in Tagen)'
+ PrepaymentDueDateDesc: 'Anzahl der Tage, die der Käufer Zeit hat, um den Betrag an Novalnet zu überweisen (muss zwischen 7 und 28 Tagen liegen). Wenn Sie dieses Feld leer lassen, werden standardmäßig 14 Tage als Fälligkeitsdatum festgelegt. '
+ GuaranteePaymentPendingStatus: 'Status für Bestellungen mit ausstehender Zahlung'
+ GuaranteePaymentPendingStatusDesc: 'Wählen Sie, welcher Status für Bestellungen mit ausstehender Zahlung verwendet wird'
+ PendingStatus: 'Status für Bestellungen mit ausstehender Zahlung'
+ PendingStatusDesc: 'Für schwebende Geschäfte zu verwendender Status'
+ OrderCompletionStatus: 'Status für erfolgreichen Auftragsabschluss'
+ OrderCompletionStatusDesc: 'Wählen Sie, welcher Status für erfolgreich abgeschlossene Bestellungen verwendet wird'
+ CallbackStatus: 'Callback-Bestellstatus'
+ CallbackStatusDesc: 'Status, der zu verwendet wird, wenn das Callback-Skript für eine bei Novalnet eingegangene Zahlung ausgeführt wird'
+ novalnetcashpaymentConfiguration: 'Barzahlen/viacash Konfiguration'
+ CashPaymentSlipExpiryDate: 'Ablauffrist des Zahlscheins (in Tagen)'
+ CashPaymentSlipExpiryDateDesc: 'eben Sie die Anzahl der Tage ein, binnen derer der Betrag in einer Barzahlen-Partnerfiliale bezahlt werden muss. Wenn das Feld leer ist, werden standardmäßig 14 Tage als Fälligkeitsdatum gesetzt, danach verfällt der Zahlschein'
+ novalnetpaypalConfiguration: 'PayPal Konfiguration'
+ novalnetsofortConfiguration: 'Sofort Konfiguration'
+ novalnetgiropayConfiguration: 'giropay Konfiguration'
+ novalnetepsConfiguration: 'eps Konfiguration'
+ novalnetidealConfiguration: 'iDEAL Konfiguration'
+ novalnetprzelewy24Configuration: 'Przelewy24 Konfiguration'
+ InvoiceComment: 'Überweisen Sie bitte den Betrag an die unten aufgeführte Bankverbindung unseres Zahlungsdienstleisters Novalnet'
+ InvoiceDueDate: 'Fälligkeitsdatum: '
+ InvoiceAccountHolder: 'Kontoinhaber: '
+ InvoiceIban: 'IBAN: '
+ InvoiceBic: 'BIC: '
+ InvoiceBank: 'Bank: '
+ InvoiceAmount: 'Betrag: '
+ InvoiceMultiRefDescription: 'Bitte verwenden Sie einen der unten angegebenen Verwendungszwecke für die Überweisung. Nur so kann Ihr Geldeingang Ihrer Bestellung zugeordnet werden:'
+ InvoicePaymentRef1: 'Verwendungszweck 1: '
+ InvoicePaymentRef2: 'Verwendungszweck 2: '
+ GuaranteeComments: 'Diese Transaktion wird mit Zahlungsgarantie verarbeitet'
+ GuaranteeErrorMsg: 'Die Zahlung kann nicht verarbeitet werden, da die grundlegenden Anforderungen für die Zahlungsgarantie nicht erfüllt wurden'
+ GuaranteeErrorMsgAmount: 'Der Mindestbestellwert beträgt '
+ GuguaranteeErrorMsgCountry: 'Als Land ist nur Deutschland, Österreich oder Schweiz erlaubt'
+ GuaranteeErrorMsgAddress: 'Die Lieferadresse muss mit der Rechnungsadresse übereinstimmen'
+ GuaranteeErrorMsgCurrency: 'Als Währung ist nur EUR erlaubt'
+ StatusCancelled: 'Abgebrochen'
+ MinimumOrdeAmount: 'Mindesttransaktionsbetrag für die Autorisierung'
+ MinimumOrdeAmountDesc: 'Transaktionen über diesem Betrag werden bis zum Capture als "nur autorisiert" gekennzeichnet. Lassen Sie das Feld leer, um alle Transaktionen zu autorisieren'
+ PaymentAction: 'Aktion für vom Besteller autorisierte Zahlungen'
+ PaymentActionDesc: 'Wählen Sie, ob die Zahlung sofort belastet werden soll oder nicht.Zahlung einziehen: Betrag sofort belasten. Zahlung autorisieren: Die Zahlung wird überprüft und autorisiert, aber erst zu einem späteren Zeitpunkt belastet. So haben Sie Zeit, über die Bestellung zu entscheiden'
+ SlipExpiryDate: 'Verfallsdatum des Zahlscheins '
+ Capture: 'Zahlung einziehen'
+ Authorize: 'Zahlung autorisieren'
+ SepaDueDateError: 'SEPA Fälligkeitsdatum Ungültiger'
+ GuaranteeMinOrderAmountError: 'Der Mindestbetrag sollte bei mindestens 9,99 EUR liegen'
+ SilverCart\NovalnetGateway\Model\NovalnetGatewayTranslation:
+ PLURALNAME: "Translations"
+ SINGULARNAME: "Translation"
+ SilverCart\NovalnetGateway\Model\NovalnetCallback:
+ CriticalErrorMessage1: 'Critical error on shop system '
+ CriticalErrorMessage2: ' : order not found for TID: '
+ CriticalMessageSubject: 'Dear Technic team,
Please evaluate this transaction and contact our payment module team at Novalnet.
'
+ MerchantId: 'Merchant ID: '
+ ProjectId: 'Project ID: '
+ TidStatus: 'TID status: '
+ OrderNo: 'Order no: '
+ PaymentType: 'Payment type: '
+ Email: 'E-mail: '
+ Regards: '
Regards,
Novalnet Team'
+ TransactionDetails: 'Novalnet-Transaktionsdetails'
+ TransactionID: 'Novalnet-Transaktions-ID: '
+ TestMode: 'Testbestellung'
+ GuaranteePayment: 'Diese Transaktion wird mit Zahlungsgarantie verarbeitet'
+ OrderConfirmation: 'Bestellbestätigung - Ihre Bestellung '
+ OrderConfirmation1: ' bei '
+ OrderConfirmation2: ' wurde bestätigt'
+ OrderConfirmation3: 'Wir freuen uns Ihnen mitteilen zu können, dass Ihre Bestellung bestätigt wurde'
+ PaymentInformation: 'Zahlung Informationen:'
+
diff --git a/lang/en.yml b/lang/en.yml
new file mode 100644
index 0000000..bff00f9
--- /dev/null
+++ b/lang/en.yml
@@ -0,0 +1,133 @@
+en:
+ SilverCart\Model\ShopEmail:
+ TemplateName_PaymentNovalnetTransactionInfo: 'Informations for Novalnet payment method (customer)'
+ ThankYouForYourOrder: 'Thank you for your order at our shop.'
+ SilverCart\NovalnetGateway\Model\NovalnetGateway:
+ NAME: 'Novalnet Gateway'
+ PAYMENT_CHANNEL: 'Payment Channel'
+ PAYMENT_CHANNEL_NOVALNETGLOBALCONFIGURATION: Novalnet Global Configuration
+ PAYMENT_CHANNEL_NOVALNETCREDITCARD: Credit/Debit Cards
+ PAYMENT_CHANNEL_NOVALNETSEPA: Direct Debit SEPA
+ PAYMENT_CHANNEL_NOVALNETINVOICE: Invoice
+ PAYMENT_CHANNEL_NOVALNETPREPAYMENT: Prepayment
+ PAYMENT_CHANNEL_NOVALNETCASHPAYMENT: Barzahlen/viacash
+ PAYMENT_CHANNEL_NOVALNETSOFORT: Sofort
+ PAYMENT_CHANNEL_NOVALNETPAYPAL: PayPal
+ PAYMENT_CHANNEL_NOVALNETEPS: eps
+ PAYMENT_CHANNEL_NOVALNETIDEAL: iDEAL
+ PAYMENT_CHANNEL_NOVALNETGIROPAY: giropay
+ PAYMENT_CHANNEL_NOVALNETPRZELEWY24: Przelewy24
+ VendorId: 'Merchant ID'
+ VendorIdDesc: 'Enter Novalnet merchant ID'
+ AuthCode: 'Authentication Code'
+ AuthCodeDesc: 'Enter Novalnet authentication code'
+ ProductId: 'Project ID'
+ ProductIdDesc: 'Enter Novalnet project ID'
+ TariffId: 'Tariff ID'
+ TariffIdDesc: 'Enter Tariff ID to match the preferred tariff plan you created at the Novalnet Merchant Administration portal for this project. Refer Novalnet Payment Gateway Installation Guide for further details'
+ AccessKey: 'Payment access key'
+ AccessKeyDesc: 'Enter Novalnet payment access key'
+ ManualTestingVendorScript: 'Allow manual testing of the Notification / Webhook URL'
+ ManualTestingVendorScriptDesc: 'Enable this to test the Novalnet Notification / Webhook URL manually. Disable this before setting your shop live to block unauthorized calls from external parties'
+ SendTo: 'Send e-mail to'
+ SendToDesc: 'Notification / Webhook URL execution messages will be sent to this e-mail'
+ VendorScriptConfiguration: 'Notification / Webhook URL Setup'
+ VencorConfiguration: 'Novalnet Global Configuration'
+ OnholdStatusManagement: 'Order status management for on-hold transactions'
+ OnholdOrderStatus: 'Onhold order status'
+ OnholdOrderStatusDesc: 'Status to be used for on-hold orders until the transaction is confirmed or cancelled'
+ OnholdOrderCancelStatus: 'Order cancellation status'
+ OnholdOrderCancelStatusDesc: 'Status to be used when order is cancelled or fully refunded'
+ OrderConfirmationSubmitButtonTitle: "Pay with Novalnet (over 100 payment methods worldwide)"
+ TransactionDetails: 'Novalnet transaction details'
+ TransactionID: 'Novalnet transaction ID: '
+ TestMode: 'Test Order'
+ novalnetcreditcardConfiguration: 'Credit Card Configuration'
+ Creditcard3dEnforce: 'Enforce 3D secure payment outside EU'
+ Creditcard3dEnforceDesc: 'By enabling this option, all payments from cards issued outside the EU will be authenticated via 3DS 2.0 SCA.'
+ novalnetsepaConfiguration: 'Direct Debit SEPA Configuration'
+ SepaDueDate: 'Payment due date (in days)'
+ SepaDueDateDesc: 'Number of days after which the payment is debited (must be between 2 and 14 days)'
+ EnableGuarantee: 'Enable payment guarantee'
+ EnableGuaranteeDesc: 'Payment guarantee requirements:
+ Allowed countries: DE, AT, CH
+ Allowed currency: EUR
+ Minimum order amount: 9,99 EUR or more
+ Age limit: 18 years or more
+ The billing address must be the same as shipping address
'
+ GuaranteeMinOrderAmount: 'Minimum order amount for payment guarantee'
+ GuaranteeMinOrderAmountDesc: 'Enter the minimum amount (in cents) for the transaction to be processed with payment guarantee. For example, enter 100 which is equal to 1,00. By default, the amount will be 9,99 EUR'
+ PaymentGuaranteeForce: 'Force non-guarantee payment'
+ PaymentGuaranteeForceDesc: 'Even if payment guarantee is enabled, payments will still be processed as non-guarantee payment if payment guarantee requirements are not met. Review the requirements under "Enable Payment Guarantee" in the installation guide'
+ novalnetinvoiceConfiguration: 'Invoice Configuration'
+ InvoicePaymentDueDate: 'Payment due date (in days)'
+ InvoicePaymentDueDateDesc: 'Number of days given to the buyer to transfer the amount to Novalnet (must be greater than 7 days). In case this field is empty, 14 days will be set as due date by default'
+ novalnetprepaymentConfiguration: 'Prepayment Configuration'
+ GuaranteePaymentPendingStatus: 'Payment pending order status'
+ GuaranteePaymentPendingStatusDesc: 'Status to be used for pending transactions'
+ PendingStatus: 'Pending payment order status'
+ PendingStatusDesc: 'Status to be used for pending transactions'
+ OrderCompletionStatus: 'Completed order status'
+ OrderCompletionStatusDesc: 'Status to be used for successful orders'
+ CallbackStatus: 'Callback / webhook order status'
+ CallbackStatusDesc: 'Status to be used when callback script is executed for payment received by Novalnet'
+ PrepaymentDueDate: 'Payment due date (in days)'
+ PrepaymentDueDateDesc: 'Number of days given to the buyer to transfer the amount to Novalnet (must be between 7 and 28 days). If this field is left blank, 14 days will be set as due date by default.'
+ novalnetcashpaymentConfiguration: 'Barzahlen/viacash Configuration'
+ CashPaymentSlipExpiryDate: 'Slip expiry date (in days)'
+ CashPaymentSlipExpiryDateDesc: 'Number of days given to the buyer to pay at a store. In case this field is empty, 14 days will be set as slip expiry date by default'
+ novalnetpaypalConfiguration: 'PayPal Configuration'
+ novalnetsofortConfiguration: 'Sofort Configuration'
+ novalnetgiropayConfiguration: 'giropay Configuration'
+ novalnetepsConfiguration: 'eps Configuration'
+ novalnetidealConfiguration: 'iDEAL Configuration'
+ novalnetprzelewy24Configuration: 'Przelewy24 Configuration'
+ InvoiceComment: 'Please transfer the amount to the below mentioned account details of our payment processor Novalnet'
+ InvoiceAccountHolder: 'Account holder: '
+ InvoiceDueDate: 'Due date: '
+ InvoiceIban: 'IBAN: '
+ InvoiceBic: 'BIC: '
+ InvoiceBank: 'Bank: '
+ InvoiceAmount: 'Amount: '
+ InvoiceMultiRefDescription: 'Please use any one of the following references as the payment reference, as only through this way your payment is matched and assigned to the order:'
+ InvoicePaymentRef1: 'Payment Reference 1: '
+ InvoicePaymentRef2: 'Payment Reference 2: '
+ GuaranteeComments: 'This is processed as a guarantee payment'
+ GuaranteeErrorMsg: 'The payment cannot be processed, because the basic requirements for the payment guarantee haven't been met'
+ GuaranteeErrorMsgAmount: 'Minimum order amount must be'
+ GuguaranteeErrorMsgCountry: 'Only Germany, Austria or Switzerland are allowed'
+ GuaranteeErrorMsgAddress: 'The shipping address must be the same as the billing address'
+ GuaranteeErrorMsgCurrency: 'Only EUR currency allowed'
+ StatusCancelled: 'Cancelled'
+ MinimumOrdeAmount: 'Minimum transaction amount for authorization'
+ MinimumOrdeAmountDesc: 'Transactions above this amount will be authorized only until you capture. Leave the field blank to authorize all transactions'
+ PaymentAction: 'Payment confirmation'
+ PaymentActionDesc: 'Choose whether or not the payment should be charged immediately. Capture: completes the transaction by transferring the funds from buyer to merchant. Authorize: verifies payment details and reserve funds to capture it later, giving time for the merchant to decide on the order.'
+ SlipExpiryDate: 'Slip expiry date '
+ Capture: 'Capture'
+ Authorize: 'Authorize'
+ SepaDueDateError: 'SEPA Due date is not valid'
+ GuaranteeMinOrderAmountError: 'The minimum amount should be at least 9,99 EUR'
+ SilverCart\NovalnetGateway\Model\NovalnetGatewayTranslation:
+ PLURALNAME: "Translations"
+ SINGULARNAME: "Translation"
+ SilverCart\NovalnetGateway\Model\NovalnetCallback:
+ CriticalErrorMessage1: 'Critical error on shop system '
+ CriticalErrorMessage2: ' : order not found for TID: '
+ CriticalMessageSubject: 'Dear Technic team,
Please evaluate this transaction and contact our payment module team at Novalnet.
'
+ MerchantId: 'Merchant ID: '
+ ProjectId: 'Project ID: '
+ TidStatus: 'TID status: '
+ OrderNo: 'Order no: '
+ PaymentType: 'Payment type: '
+ Email: 'E-mail: '
+ Regards: '
Regards,
Novalnet Team'
+ TransactionDetails: 'Novalnet transaction details'
+ TransactionID: 'Novalnet transaction ID: '
+ TestMode: 'Test order'
+ GuaranteePayment: 'This is processed as a guarantee payment'
+ OrderConfirmation: 'Order Confirmation - Your Order '
+ OrderConfirmation1: ' with '
+ OrderConfirmation2: ' has been confirmed'
+ OrderConfirmation3: 'We are pleased to inform you that your order has been confirmed'
+ PaymentInformation: 'Payment Information:'
diff --git a/src/Model/NovalnetCallback.php b/src/Model/NovalnetCallback.php
new file mode 100644
index 0000000..a8a7658
--- /dev/null
+++ b/src/Model/NovalnetCallback.php
@@ -0,0 +1,661 @@
+ 'Varchar(128)',
+ 'Amount' => 'Int',
+ 'CallbackTId' => 'Varchar(50)',
+ 'OrgTId' => 'Varchar(50)',
+ 'PaymentType' => 'Varchar(50)',
+ ];
+
+ /**
+ * DB table name
+ *
+ * @var string
+ */
+ private static $table_name = 'SilvercartPaymentNovalnetCallback';
+
+
+ protected $_captureParams; // Get REQUEST param
+
+ /**
+ * Type of payment available - Level : 0
+ *
+ * @var array
+ */
+ private $aryPayments = ['CREDITCARD', 'INVOICE_START', 'DIRECT_DEBIT_SEPA', 'GUARANTEED_DIRECT_DEBIT_SEPA',
+ 'GUARANTEED_INVOICE', 'PAYPAL', 'ONLINE_TRANSFER', 'IDEAL', 'EPS', 'GIROPAY', 'PRZELEWY24', 'CASHPAYMENT'];
+
+ /**
+ * Type of Chargebacks available - Level : 1
+ *
+ * @var array
+ */
+ private $aryChargebacks = ['RETURN_DEBIT_SEPA', 'CREDITCARD_BOOKBACK', 'CREDITCARD_CHARGEBACK',
+ 'REFUND_BY_BANK_TRANSFER_EU', 'PAYPAL_BOOKBACK', 'PRZELEWY24_REFUND', 'REVERSAL', 'CASHPAYMENT_REFUND',
+ 'GUARANTEED_INVOICE_BOOKBACK', 'GUARANTEED_SEPA_BOOKBACK'];
+
+ /**
+ * Type of Credit entry payment and Collections available - Level : 2
+ *
+ * @var array
+ */
+ private $aryCollection = ['INVOICE_CREDIT', 'CREDIT_ENTRY_CREDITCARD', 'CREDIT_ENTRY_SEPA', 'DEBT_COLLECTION_SEPA',
+ 'DEBT_COLLECTION_CREDITCARD', 'ONLINE_TRANSFER_CREDIT', 'CASHPAYMENT_CREDIT',
+ 'CREDIT_ENTRY_DE', 'DEBT_COLLECTION_DE'];
+
+ /**
+ * Types of payment available
+ *
+ * @var array
+ */
+ private $paymentTypes = [
+ 'novalnetcreditcard' => ['CREDITCARD', 'CREDITCARD_BOOKBACK', 'CREDITCARD_CHARGEBACK', 'CREDIT_ENTRY_CREDITCARD',
+ 'DEBT_COLLECTION_CREDITCARD'],
+ 'novalnetsepa' => ['DIRECT_DEBIT_SEPA', 'GUARANTEED_SEPA_BOOKBACK', 'RETURN_DEBIT_SEPA', 'DEBT_COLLECTION_SEPA',
+ 'CREDIT_ENTRY_SEPA', 'REFUND_BY_BANK_TRANSFER_EU', 'GUARANTEED_DIRECT_DEBIT_SEPA'],
+ 'novalnetideal' => ['IDEAL', 'REFUND_BY_BANK_TRANSFER_EU', 'ONLINE_TRANSFER_CREDIT', 'REVERSAL', 'CREDIT_ENTRY_DE',
+ 'DEBT_COLLECTION_DE'],
+ 'novalnetsofort' => ['ONLINE_TRANSFER', 'REFUND_BY_BANK_TRANSFER_EU', 'ONLINE_TRANSFER_CREDIT', 'REVERSAL',
+ 'CREDIT_ENTRY_DE', 'DEBT_COLLECTION_DE'],
+ 'novalnetpaypal' => ['PAYPAL', 'PAYPAL_BOOKBACK'],
+ 'novalnetprepayment' => ['INVOICE_START', 'INVOICE_CREDIT', 'REFUND_BY_BANK_TRANSFER_EU'],
+ 'novalnetcashpayment' => ['CASHPAYMENT', 'CASHPAYMENT_CREDIT', 'CASHPAYMENT_REFUND'],
+ 'novalnetinvoice' => ['INVOICE_START', 'INVOICE_CREDIT', 'GUARANTEED_INVOICE', 'REFUND_BY_BANK_TRANSFER_EU',
+ 'GUARANTEED_INVOICE_BOOKBACK', 'CREDIT_ENTRY_DE', 'DEBT_COLLECTION_DE'],
+ 'novalneteps' => ['EPS', 'ONLINE_TRANSFER_CREDIT', 'REFUND_BY_BANK_TRANSFER_EU', 'REVERSAL', 'CREDIT_ENTRY_DE',
+ 'DEBT_COLLECTION_DE'],
+ 'novalnetgiropay' => ['GIROPAY', 'ONLINE_TRANSFER_CREDIT', 'REFUND_BY_BANK_TRANSFER_EU', 'REVERSAL',
+ 'CREDIT_ENTRY_DE', 'DEBT_COLLECTION_DE'],
+ 'novalnetprzelewy24' => ['PRZELEWY24', 'PRZELEWY24_REFUND']];
+
+
+ /**
+ * i18n for field labels
+ *
+ * @param boolean $includerelations a boolean value to indicate if the labels returned include relation fields
+ *
+ * @return array
+ *
+ * @author Novalnet AG
+ */
+ public function fieldLabels($includerelations = true) : array
+ {
+ return array_merge(
+ parent::fieldLabels($includerelations),
+ array(
+
+ 'CriticalErrorMessage1' => _t(self::class . '.CriticalErrorMessage1', 'Critical error on shop system '),
+ 'CriticalErrorMessage2' => _t(self::class . '.CriticalErrorMessage2', ' : order not found for TID: '),
+ 'CriticalMessageSubject' => _t(self::class . '.CriticalMessageSubject', 'Dear Technic team,
Please evaluate this transaction and contact our payment module team at Novalnet.
'),
+ 'MerchantId' => _t(self::class . '.MerchantId', 'Merchant ID: '),
+ 'ProjectId' => _t(self::class . '.ProjectId', 'Project ID: '),
+ 'TidStatus' => _t(self::class . '.TidStatus', 'TID status: '),
+ 'OrderNo' => _t(self::class . '.OrderNo', 'Order no: '),
+ 'PaymentType' => _t(self::class . '.PaymentType', 'Payment type: '),
+ 'Email' => _t(self::class . '.Email', 'E-mail: '),
+ 'Regards' => _t(self::class . '.Regards', '
Regards,
Novalnet Team'),
+ 'TransactionDetails' => _t(self::class . '.TransactionDetails', 'Novalnet transaction details'),
+ 'TransactionID' => _t(self::class . '.TransactionID', 'Novalnet transaction ID: '),
+ 'TestMode' => _t(self::class . '.TestMode', 'Test order'),
+ 'GuaranteePayment' => _t(self::class . 'GuaranteePayment','This is processed as a guarantee payment'),
+ 'OrderConfirmation' => _t(self::class . 'OrderConfirmation','Order Confirmation - Your Order '),
+ 'OrderConfirmation1' => _t(self::class . 'OrderConfirmation1',' with '),
+ 'OrderConfirmation2' => _t(self::class . 'OrderConfirmation2',' has been confirmed'),
+ 'OrderConfirmation3' => _t(self::class . 'OrderConfirmation3','We are pleased to inform you that your order has been confirmed'),
+ 'PaymentInformation' => _t(self::class . 'PaymentInformation','Payment Information:'),
+ )
+ );
+ }
+
+ /**
+ * Processes the callback script
+ *
+ * @param array $aryCaptureParams
+ * @return null
+ */
+ public function startProcess($aryCaptureParams)
+ {
+ $this->_captureParams = array_map('trim', $aryCaptureParams);
+
+ // Validate the request params
+ if (!$this->validateCaptureParams()) {
+ return;
+ }
+
+ $nntransHistory = $this->getOrderReference();
+ // Check getOrderReference returns false value
+ if (!$nntransHistory) {
+ return;
+ }
+
+ // Handle transaction cancellation
+ if ($this->transactionCancellation($nntransHistory))
+ return false;
+
+ if (!empty($this->_captureParams['order_no']) && $nntransHistory->OrderNumber != $this->_captureParams['order_no']) {
+ return $this->debugMessage('Novalnet callback received. Order no is not valid', $this->_captureParams['order_no']);
+ } elseif (empty($nntransHistory->PaymentType) || !in_array($this->_captureParams['payment_type'], $this->paymentTypes[$nntransHistory->PaymentType])) {
+ return $this->debugMessage('Novalnet callback received. Payment Type [' . $this->_captureParams['payment_type'] . '] is not valid');
+ }
+ $callbacklogParams = [
+ 'callback_tid' => $this->_captureParams['tid'],
+ 'org_tid' => $nntransHistory->transactionId,
+ 'callback_amount' => $aryCaptureParams['amount'],
+ 'order_no' => $nntransHistory->OrderNumber,
+ 'payment_type' => $this->_captureParams['payment_type'],
+ 'gatewayStatus' => $this->_captureParams['tid_status'],
+ 'date' => date('Y-m-d H:i:s'),
+ ];
+
+ if (in_array($aryCaptureParams['payment_type'], $this->aryPayments)) {
+ $this->availablePayments($nntransHistory, $callbacklogParams);
+ } elseif (in_array($aryCaptureParams['payment_type'], $this->aryChargebacks)) {
+ $this->chargebackPayments($nntransHistory);
+ } elseif (in_array($aryCaptureParams['payment_type'], $this->aryCollection)) {
+ $this->collectionsPayments($nntransHistory, $callbacklogParams);
+ }
+ }
+
+ /**
+ * Validate the required params to process callback
+ *
+ * @return boolean
+ */
+ protected function validateCaptureParams()
+ {
+ // Validate Authenticated IP
+ $realHostIp = gethostbyname('pay-nn.de');
+ if (empty($realHostIp)) {
+ return $this->debugMessage('Novalnet HOST IP missing');
+ }
+
+ $gatewayClass = new NovalnetGateway();
+ $callerIp = $gatewayClass->getRemoteAddress();
+
+ $manualTestingVendorScript = NovalnetGateway::get()->filter('PaymentChannel', 'novalnetglobalconfiguration')->first()->ManualTestingVendorScript;
+
+ if ($callerIp != $realHostIp && !$manualTestingVendorScript) {
+ return $this->debugMessage('Unauthorised access from the IP [' . $callerIp . ']');
+ }
+
+ if (!array_filter($this->_captureParams)) {
+ return $this->debugMessage('No params passed over!');
+ }
+
+ $hParamsRequired = array('vendor_id', 'tid', 'payment_type', 'status', 'tid_status');
+
+ $this->_captureParams['shop_tid'] = $this->_captureParams['tid'];
+ if (in_array($this->_captureParams['payment_type'], array_merge($this->aryChargebacks, $this->aryCollection))) {
+ array_push($hParamsRequired, 'tid_payment');
+ $this->_captureParams['shop_tid'] = $this->_captureParams['tid_payment'];
+ }
+
+ $error = '';
+ foreach ($hParamsRequired as $v) {
+ if (!isset($this->_captureParams[$v]) || ($this->_captureParams[$v] == '')) {
+ $error .= 'Required param (' . $v . ') missing!
';
+ } elseif (in_array($v, ['tid', 'signup_tid', 'tid_payment'])
+ && !preg_match('/^[0-9]{17}$/', $this->_captureParams[$v])) {
+ $error .= 'Novalnet callback received. TID [' . $this->_captureParams[$v] . '] is not valid.';
+ }
+ }
+ if (!empty($error)) {
+ return $this->debugMessage($error, '');
+ }
+ return true;
+ }
+
+ /**
+ * Get order reference from the novalnetGatewayOrder table on shop database
+ *
+ * @return array
+ */
+ protected function getOrderReference()
+ {
+ if (!empty($this->_captureParams['order_no'])) {
+ $nntransHistory = NovalnetTransaction::get()->filter('OrderNumber', $this->_captureParams['order_no'])->first();
+ } else {
+ $nntransHistory = NovalnetTransaction::get()->filter('Tid', $this->_captureParams['shop_tid'])->first();
+ }
+
+ if (empty($nntransHistory)) {
+ list($subject, $message) = $this->NotificationMessage();
+ ShopEmail::send_email('technic@novalnet.de', $subject, $message);
+ $this->debugMessage($message);
+ return false;
+ }
+
+ $amount = Order::get()->filter('OrderNumber', $nntransHistory->OrderNumber)->first()->AmountTotalAmount;
+
+ $query = "SELECT SUM(amount) AS paid_amount FROM SilvercartPaymentNovalnetCallback WHERE OrderNumber = '". $nntransHistory->OrderNumber."'";
+ $result = DB::query($query);
+ $paid_amount = $result->first()['paid_amount'];
+ $nntransHistory->amount = $amount*100;
+ $nntransHistory->paidAmount = $paid_amount;
+
+ return $nntransHistory;
+
+ }
+
+ /**
+ * Initial level payment process
+ *
+ * @param object $nntransHistory
+ * @param array $callbacklogParams
+ * @return string
+ */
+ protected function availablePayments($nntransHistory, $callbacklogParams)
+ {
+ $gatewayClass = new NovalnetGateway();
+ $formatedAmount = $gatewayClass->formatedAmount($this->_captureParams, true);
+
+ if ($this->_captureParams['payment_type'] == 'PAYPAL') {
+ return $this->handleInitialLevelPaypal($nntransHistory, $callbacklogParams);
+ } elseif ($this->_captureParams['payment_type'] == 'PRZELEWY24') {
+ if ($this->_captureParams['tid_status'] == '100') {
+ if ($nntransHistory->paidAmount == 0) {
+ // Full Payment paid Update order status due to full payment
+ $callbackComments = 'Novalnet Callback Script executed successfully for the TID: ' . $this->_captureParams['tid'] . ' with amount ' . $formatedAmount. ' on ' . date('Y-m-d H:i:s');
+ $this->setCallbackComments(['comments' => $callbackComments, 'order_no' => $nntransHistory->OrderNumber]);
+ $this->insertCallbackOrder($callbacklogParams);
+ $this->sendNotifyMail(['comments' => $callbackComments, 'order_no' => $nntransHistory->OrderNumber]);
+ return $this->debugMessage($callbackComments, $nntransHistory->OrderNumber);
+ } else {
+ return $this->debugMessage("Novalnet callback received. Order already paid.", $nntransHistory->OrderNumber);
+ }
+
+ } elseif ($this->_captureParams['tid_status'] != '86') {
+ $message = (($this->_captureParams['status_message'])
+ ? $this->_captureParams['status_message'] : (($this->_captureParams['status_text'])
+ ? $this->_captureParams['status_text'] : (($this->_captureParams['status_desc'])
+ ? $this->_captureParams['status_desc']
+ : 'Payment was not successful. An error occurred.')));
+ $callbackComments = 'The transaction has been cancelled due to: ' . $message;
+
+ $this->setCallbackComments(['comments' => $callbackComments, 'order_no' => $nntransHistory->OrderNumber]);
+ // Logcallback process
+ $this->insertCallbackOrder($callbacklogParams);
+ // Send notification mail to Merchant
+ $this->sendNotifyMail(['comments' => $callbackComments, 'order_no' => $nntransHistory->OrderNumber]);
+ return $this->debugMessage($callbackComments, $nntransHistory->OrderNumber);
+ }
+ } elseif (in_array(
+ $this->_captureParams['payment_type'], ['INVOICE_START', 'GUARANTEED_INVOICE', 'DIRECT_DEBIT_SEPA', 'GUARANTEED_DIRECT_DEBIT_SEPA', 'CREDITCARD']) && in_array($nntransHistory->GatewayStatus, [75, 91, 99, 98]) && in_array($this->_captureParams['tid_status'], [91, 99, 100]) && $this->_captureParams['status'] == '100') {
+ return $this->handleInitialLevelPayments($nntransHistory, $callbacklogParams);
+ } else {
+ return $this->debugMessage('Novalnet callback received. Payment type ( ' . $this->_captureParams['payment_type'] . ' ) is
+ not applicable for this process!', $nntransHistory->OrderNumber);
+ }
+ }
+
+ /**
+ * Level 2 payment process
+ *
+ * @param object $nntransHistory
+ * @param array $callbacklogParams
+ * @return string
+ */
+ protected function collectionsPayments($nntransHistory, $callbacklogParams)
+ {
+ $gatewayClass = new NovalnetGateway();
+ $formatedAmount = $gatewayClass->formatedAmount($this->_captureParams, true);
+ // Credit entry payment and Collections available
+ if ($nntransHistory->paidAmount < $nntransHistory->amount && in_array($this->_captureParams['payment_type'], ['INVOICE_CREDIT', 'CASHPAYMENT_CREDIT', 'ONLINE_TRANSFER_CREDIT'])
+ ) {
+ $paidAmount = (empty($nntransHistory->paidAmount) ? 0 : $nntransHistory->paidAmount) + $this->_captureParams['amount'];
+ $callbackComments = PHP_EOL. 'Novalnet Callback Script executed successfully for the TID: ' . $this->_captureParams['tid_payment'] . ' with amount ' . $formatedAmount. ' on ' . date('Y-m-d H:i:s') . '. Please refer PAID transaction in our Novalnet Merchant Administration with the TID: ' . $this->_captureParams['tid'];
+ $this->setCallbackComments(['comments' => $callbackComments, 'order_no' => $nntransHistory->OrderNumber]);
+ $callbacklogParams['callback_amount'] = $paidAmount;
+ $this->insertCallbackOrder($callbacklogParams);
+ $CallbackStatus = NovalnetGateway::get()->filter('PaymentChannel', $nntransHistory->PaymentType)->first()->CallbackStatus;
+ if ($paidAmount >= $nntransHistory->amount
+ && $this->_captureParams['payment_type'] != 'ONLINE_TRANSFER_CREDIT') {
+ $this->setPaymentStatus($nntransHistory, $CallbackStatus);
+ $message = $this->getTransactionDetails();
+ DB::query(
+ sprintf(
+ "UPDATE SilvercartOrder SET PaymentReferenceMessage= '".$message."' WHERE OrderNumber = '%s'",
+ $nntransHistory->OrderNumber
+ ));
+ }
+ $this->sendNotifyMail(['comments' => $callbackComments, 'order_no' => $nntransHistory->OrderNumber]);
+ return $this->debugMessage($callbackComments, $nntransHistory->OrderNumber);
+ } else {
+ return $this->debugMessage('Novalnet Callback script received. Order already Paid', $nntransHistory->OrderNumber);
+ }
+ }
+
+ /**
+ * Handle initial level payments
+ *
+ * @param object $nntransHistory
+ * @param array $callbacklogParams
+ * @return string
+ */
+ protected function handleInitialLevelPayments($nntransHistory, $callbacklogParams)
+ {
+ $callbackComments = '';
+ if (in_array($this->_captureParams['tid_status'], [99, 91]) && $nntransHistory->GatewayStatus == 75) {
+ $callbackComments = PHP_EOL.'The transaction status has been changed from pending to on hold for the TID:'. $this->_captureParams['shop_tid']. ' on '. date('Y-m-d H:i:s');
+ $OnholdOrderStatus = NovalnetGateway::get()->filter('PaymentChannel', $nntransHistory->PaymentType)->first()->OnholdOrderStatus;
+ $this->setPaymentStatus($nntransHistory, $OnholdOrderStatus);
+ } elseif ($this->_captureParams['tid_status'] == 100 && in_array($nntransHistory->GatewayStatus, [75, 91, 99, 98])) {
+ $callbackComments = PHP_EOL. 'The transaction has been confirmed on '. date('Y-m-d H:i:s');
+ $CallbackStatus = ($nntransHistory->PaymentType == 'novalnetinvoice' && $this->_captureParams['tid_status'] == 100 && $nntransHistory->GatewayStatus ==75) ? NovalnetGateway::get()->filter('PaymentChannel', $nntransHistory->PaymentType)->first()->CallbackStatus : NovalnetGateway::get()->filter('PaymentChannel', $nntransHistory->PaymentType)->first()->OrderCompletionStatus;
+ $this->setPaymentStatus($nntransHistory, $CallbackStatus);
+ }
+ if (!empty($callbackComments)) {
+ if ($this->_captureParams['tid_status'] == '100' && $this->_captureParams['payment_type'] == 'GUARANTEED_INVOICE') {
+ $gatewayClass = new NovalnetGateway();
+ $message = '';
+ $message .= $this->getTransactionDetails();
+ $message .= PHP_EOL.$this->fieldLabel('GuaranteePayment').PHP_EOL;
+ $this->_captureParams['amount'] = sprintf('%0.2f', $this->_captureParams['amount'] / 100);
+ $this->_captureParams['invoice_account_holder'] = !empty($this->_captureParams['invoice_account_holder']) ? $this->_captureParams['invoice_account_holder'] : 'Novalnet AG';
+ $message .= $gatewayClass->prepareInvoiceComments($this->_captureParams);
+ DB::query(
+ sprintf(
+ "UPDATE SilvercartOrder SET PaymentReferenceMessage= '".$message."' WHERE OrderNumber = '%s'",
+ $nntransHistory->OrderNumber
+ ));
+ $this->sendNotificationMail(['order_no' => $nntransHistory->OrderNumber]);
+ }
+
+ $this->insertCallbackOrder($callbacklogParams);
+ $this->setCallbackComments(['comments' => $callbackComments, 'order_no' => $nntransHistory->OrderNumber]);
+ // Send notification mail to Merchant
+ $this->sendNotifyMail(['comments' => $callbackComments, 'order_no' => $nntransHistory->OrderNumber]);
+ return $this->debugMessage($callbackComments, $nntransHistory->OrderNumber);
+ } else {
+ return $this->debugMessage(
+ 'Novalnet callback received. Payment type ( ' . $this->_captureParams['payment_type'] . ' ) is
+ not applicable for this process!',
+ $nntransHistory->OrderNumber
+ );
+ }
+ }
+
+ /**
+ * Level 1 payment process
+ *
+ * @param object $nntransHistory
+ * @return string
+ */
+ protected function chargebackPayments($nntransHistory)
+ {
+ $gatewayClass = new NovalnetGateway();
+ $formatedAmount = $gatewayClass->formatedAmount($this->_captureParams, true);
+
+ //Level 1 payments - Type of Chargebacks
+ $chargebackComments = PHP_EOL.'Chargeback executed successfully for the TID: ' . $this->_captureParams['tid_payment'] . ' amount ' . $formatedAmount . ' on ' . date('Y-m-d H:i:s') . '. The subsequent TID: ' . $this->_captureParams['tid'];
+
+ $bookbackComments = PHP_EOL.'Refund/Bookback executed successfully for the TID: '. $this->_captureParams['tid_payment']. ' amount ' . $formatedAmount . '. The subsequent TID: ' . $this->_captureParams['tid'];
+
+ $callbackComments = (in_array(
+ $this->_captureParams['payment_type'],
+ ['PAYPAL_BOOKBACK',
+ 'CREDITCARD_BOOKBACK',
+ 'PRZELEWY24_REFUND',
+ 'GUARANTEED_INVOICE_BOOKBACK',
+ 'GUARANTEED_SEPA_BOOKBACK',
+ 'CASHPAYMENT_REFUND',
+ 'REFUND_BY_BANK_TRANSFER_EU']
+ ))
+ ? $bookbackComments : $chargebackComments;
+ $this->setCallbackComments(['comments' => $callbackComments, 'order_no' => $nntransHistory->OrderNumber]);
+
+ $this->sendNotifyMail(['comments' => $callbackComments, 'order_no' => $nntransHistory->OrderNumber]);
+
+ return $this->debugMessage($callbackComments, $nntransHistory->OrderNumber);
+ }
+
+ /**
+ * Initial level Payment
+ *
+ * @param object $nntransHistory
+ * @param array $callbacklogParams
+ * @return string
+ */
+ protected function handleInitialLevelPaypal($nntransHistory, $callbacklogParams)
+ {
+ if ($this->_captureParams['tid_status'] == 100 && in_array($nntransHistory->GatewayStatus, [85, 90])) {
+ $callbackComments = 'Novalnet Callback Script executed successfully for the TID: ' . $this->_captureParams['tid'] . ' with amount ' . sprintf('%0.2f', $this->_captureParams['amount'] / 100) . ' on ' . date('Y-m-d H:i:s');
+ $this->setPaymentStatus($nntransHistory, $callbacklogParams['completion_status']);
+ $this->setCallbackComments(['comments' => $callbackComments, 'order_no' => $nntransHistory->OrderNumber]);
+ $this->sendNotifyMail(['comments' => $callbackComments, 'order_no' => $nntransHistory->OrderNumber]);
+ return $this->debugMessage($callbackComments, $nntransHistory->OrderNumber);
+ } else {
+ return $this->debugMessage('Novalnet callback received. Order already paid.', $nntransHistory->OrderNumber);
+ }
+ }
+
+ /**
+ * Set callback comments
+ *
+ * @param string $comments
+ * @return null
+ */
+ protected function setCallbackComments($data)
+ {
+ DB::query(
+ sprintf(
+ "UPDATE SilvercartOrder SET PaymentReferenceMessage=CONCAT(IF(PaymentReferenceMessage IS NULL, '', PaymentReferenceMessage), '".$data['comments']."' ) WHERE OrderNumber = '%s'",
+ $data['order_no']
+ )
+ );
+ $nnOrder = NovalnetTransaction::get()->filter('OrderNumber', $data['order_no'])->first();
+ if ($nnOrder instanceof NovalnetTransaction
+ && $nnOrder->exists()
+ ) {
+ $nnOrder->updateOrder(
+ $this->_captureParams,
+ false
+ );
+ }
+ }
+
+ /**
+ * Set transaction comments
+ *
+ * @return string
+ */
+ protected function getTransactionDetails()
+ {
+ $message = '';
+ $message .= $this->fieldLabel('TransactionDetails').PHP_EOL;
+ $message .= $this->fieldLabel('TransactionID'). $this->_captureParams['tid']. PHP_EOL;
+ $message .= ($this->_captureParams['test_mode'] == 1) ? $this->fieldLabel('TestMode') : ''.PHP_EOL;
+ return $message;
+ }
+
+ /**
+ * Handle TRANSACTION_CANCELLATION payment
+ *
+ * @param object $nntransHistory
+ * @return boolean
+ *
+ */
+ protected function transactionCancellation($nntransHistory)
+ {
+ if ($this->_captureParams['payment_type'] == 'TRANSACTION_CANCELLATION') {
+ $transactionCancelComments = PHP_EOL.'The transaction has been cancelled on '.date('Y-m-d H:i:s');
+ $this->setCallbackComments(['comments' => $transactionCancelComments, 'order_no' => $nntransHistory->OrderNumber]);
+ $this->sendNotifyMail(['comments' => $transactionCancelComments, 'order_no' => $nntransHistory->OrderNumber]);
+ $onholdCancelStatus = NovalnetGateway::get()->filter('PaymentChannel', 'novalnetglobalconfiguration')->first()->OnholdOrderCancelStatus;
+ $this->setPaymentStatus($nntransHistory, $onholdCancelStatus);
+ $this->debugMessage($transactionCancelComments);
+ return true;
+ }
+ }
+
+ /**
+ * Display the error message
+ *
+ * @param string $errorMsg
+ * @param string $orderNo
+ * @return null
+ */
+ protected function debugMessage($errorMsg, $orderNo = null)
+ {
+ if ($orderNo) {
+ $errorMsg = 'message=' . $errorMsg . '&ordernumber=' . $orderNo;
+ } else {
+ $errorMsg = 'message=' . $errorMsg;
+ }
+ echo $errorMsg;
+ return;
+ }
+
+ /**
+ * Insert callback details
+ *
+ * @param int $requestData
+ * @param array $requestData array with order data
+ *
+ * @return void
+ *
+ */
+ protected function insertCallbackOrder($requestData)
+ {
+ $this->OrderNumber = $requestData['order_no'];
+ $this->Amount = $requestData['callback_amount'];
+ $this->CallbackTId = $requestData['callback_tid'];
+ $this->OrgTId = $requestData['org_tid'];
+ $this->PaymentType = $requestData['payment_type'];
+ $this->write();
+ }
+
+ /**
+ * Set Payment status
+ *
+ * @param object $nntransHistory
+ * @param int $status
+ *
+ * @return void
+ *
+ */
+ protected function setPaymentStatus($nntransHistory, $status)
+ {
+ $order = Order::get()->byID($nntransHistory->OrderId);
+ if ($order instanceof Order
+ && $order->exists()
+ ) {
+ $order->setPaymentStatus(PaymentStatus::get()->byID($status));
+ }
+ }
+
+ /**
+ * Critical message
+ *
+ * @return void
+ *
+ */
+ protected function NotificationMessage()
+ {
+ $sShopName = 'Notification message';
+ $sSubject = $this->fieldLabel('CriticalErrorMessage1').$sShopName. $this->fieldLabel('CriticalErrorMessage2') . $this->_captureParams['shop_tid'];
+ $sMessage = $this->fieldLabel('CriticalMessageSubject');
+ $sMessage .= $this->fieldLabel('MerchantId') . $this->_captureParams['vendor_id'] . PHP_EOL;
+ $sMessage .= $this->fieldLabel('ProjectId') . $this->_captureParams['product_id'] . PHP_EOL;
+ $sMessage .= 'TID: ' . $this->_captureParams['shop_tid'] . PHP_EOL;
+ $sMessage .= $this->fieldLabel('TidStatus') . $this->_captureParams['tid_status'] . PHP_EOL;
+ $sMessage .= $this->fieldLabel('OrderNo') . $this->_captureParams['order_no'] . PHP_EOL;
+ $sMessage .= $this->fieldLabel('PaymentType') . $this->_captureParams['payment_type'] . PHP_EOL;
+ $sMessage .= $this->fieldLabel('Email') . $this->_captureParams['email'] . PHP_EOL;
+ $sMessage .= $this->fieldLabel('Regards');
+ return [$sSubject, $sMessage];
+ }
+
+ /**
+ * Send Notification mail
+ *
+ * @parama array $data
+ * @return void
+ *
+ */
+ protected function sendNotifyMail($data)
+ {
+ $mailSubject = 'Novalnet Callback script notification - Order No : ' . $data['order_no'];
+ $recipient = NovalnetGateway::get()->filter('PaymentChannel', 'novalnetglobalconfiguration')->first()->SendTo;
+ $content = $data['comments'];
+ if (Email::is_valid_address($recipient)) {
+ ShopEmail::send_email($recipient, $mailSubject, $content);
+ } else {
+ return 'Mail not sent';
+ }
+ }
+
+ /**
+ * Send payment notification mail
+ *
+ * @param string $data
+ *
+ */
+ protected function sendNotificationMail($data)
+ {
+ $config = Config::getConfig();
+ $customerEmail = Order::get()->filter('OrderNumber', $data['order_no'])->first()->CustomersEmail;
+
+ $firstName = Member::get()->filter('Email', $customerEmail)->first()->FirstName;
+ $lastName = Member::get()->filter('Email', $customerEmail)->first()->Surname;
+ $gatewayClass = new NovalnetGateway();
+ $break = '
';
+ $message = '';
+ $message .= $this->fieldLabel('TransactionDetails').$break;
+ $message .= $this->fieldLabel('TransactionID'). $this->_captureParams['tid']. $break;
+ $message .= ($this->_captureParams['test_mode'] == 1) ? $this->fieldLabel('TestMode') : ''.$break;
+ $message .= $break.$this->fieldLabel('GuaranteePayment').$break;
+ $message .= $gatewayClass->prepareInvoiceComments($this->_captureParams, $break);
+
+ $subject = $this->fieldLabel('OrderConfirmation') . $this->_captureParams['order_no'] . $this->fieldLabel('OrderConfirmation1') .$config->ShopName. $this->fieldLabel('OrderConfirmation2') ;
+ $emailContent = '
+
+
+
+
+ Dear Mr./Ms./Mrs. '.$firstName.' '.$lastName.'
+
+ '.$this->fieldLabel('OrderConfirmation3').'
+ '. $this->fieldLabel('PaymentInformation') .'
+ '.$message.'
+
+
+
+
+
+
+ ';
+ ShopEmail::send_email($customerEmail, $subject, $emailContent);
+ }
+}
diff --git a/src/Model/NovalnetGateway.php b/src/Model/NovalnetGateway.php
new file mode 100644
index 0000000..1650b87
--- /dev/null
+++ b/src/Model/NovalnetGateway.php
@@ -0,0 +1,1396 @@
+ 'Novalnet Global Configuration',
+ 'novalnetcreditcard' => 'Credit/Debit Cards',
+ 'novalnetsepa' => 'Direct Debit SEPA',
+ 'novalnetinvoice' => 'Invoice',
+ 'novalnetprepayment' => 'Prepayment',
+ 'novalnetcashpayment' => 'Barzahlen/viacash',
+ 'novalnetsofort' => 'Sofort',
+ 'novalnetpaypal' => 'PayPal',
+ 'novalneteps' => 'eps',
+ 'novalnetideal' => 'iDEAL',
+ 'novalnetgiropay' => 'giropay',
+ 'novalnetprzelewy24' => 'Prezelewyz24',
+ ];
+
+ /**
+ * classes attributes
+ *
+ * @var array
+ */
+ private static $db = [
+ 'PaymentChannel' => 'Enum("novalnetglobalconfiguration,novalnetcreditcard,novalnetsepa,novalnetinvoice,novalnetprepayment,novalnetcashpayment,novalnetsofort,novalnetpaypal, novalneteps,novalnetideal,novalnetgiropay,novalnetprzelewy24", "novalnetglobalconfiguration")',
+ 'VendorId' => 'Int',
+ 'AuthCode' => 'Text',
+ 'ProductId' => 'Int',
+ 'TariffId' => 'Int',
+ 'AccessKey' => 'Text',
+ 'ManualTestingVendorScript' => 'Int',
+ 'SendTo' => 'Text',
+ 'OnholdOrderStatus' => 'Int',
+ 'OnholdOrderCancelStatus' => 'Int',
+ 'Cc3dEnforce' => 'Int',
+ 'PaymentAction' => 'Enum("capture,authorize","capture")',
+ 'OnholdMinOrderAmount' => 'Int',
+ 'InvoiceDueDate' => 'Int',
+ 'EnableGuarantee' => 'Int',
+ 'GuaranteeMinOrderAmount' => 'Int',
+ 'PaymentGuaranteeForce' => 'Int',
+ 'GuaranteePendingStatus' => 'Int',
+ 'SepaDueDate' => 'Int',
+ 'PrepaymentDueDate' => 'Int',
+ 'CashPaymentDuration' => 'Int',
+ 'OrderCompletionStatus' => 'Int',
+ 'CallbackStatus' => 'Int',
+ 'PendingStatus' => 'Int',
+ ];
+
+ /**
+ * 1:n relationships.
+ *
+ * @var array
+ */
+ private static $has_many = [
+ 'NovalnetGatewayTranslations' => NovalnetGatewayTranslation::class,
+ ];
+
+ /**
+ * DB table name
+ *
+ * @var string
+ */
+ private static $table_name = 'SilvercartPaymentNovalnetGateway';
+
+ /**
+ * contains module name for display in the admin backend
+ *
+ * @var string
+ */
+ protected $moduleName = 'NovalnetGateway';
+
+ /** @var array */
+ private $nnSecuredParams = ['auth_code', 'product', 'tariff', 'amount', 'test_mode'];
+
+ /** @var array */
+ public $redirectPayments = ['novalnetsofort', 'novalnetideal', 'novalnetpaypal', 'novalneteps', 'novalnetgiropay', 'novalnetprzelewy24'];
+
+ /**
+ * i18n for field labels
+ *
+ * @param boolean $includerelations a boolean value to indicate if the labels returned include relation fields
+ *
+ * @return array
+ *
+ * @author Novalnet AG
+ */
+ public function fieldLabels($includerelations = true) : array
+ {
+ $this->beforeUpdateFieldLabels(function(&$labels) {
+ $labels = array_merge(
+ $labels,
+ Tools::field_labels_for(self::class),
+ [
+ 'NovalnetGatewayTranslations' => NovalnetGatewayTranslation::singleton()->plural_name(),
+ 'PaymentChannel' => _t(self::class . '.PAYMENT_CHANNEL', 'Payment Channel'),
+ 'InfoMailSubject' => _t(self::class . '.InfoMailSubject', 'payment information regarding your order'),
+ 'VendorId' => _t(self::class . '.VendorId', 'Merchant ID'),
+ 'VendorIdDesc' => _t(self::class . '.VendorIdDesc', 'Enter Novalnet merchant ID'),
+ 'AuthCode' => _t(self::class . '.AuthCode', 'Authentication Code'),
+ 'AuthCodeDesc' => _t(self::class . '.AuthCodeDesc', 'Enter Novalnet authentication code'),
+ 'ProductId' => _t(self::class . '.ProductId', 'Project ID'),
+ 'ProductIdDesc' => _t(self::class . '.ProductIdDesc', 'Enter Novalnet project ID'),
+ 'TariffId' => _t(self::class . '.TariffId', 'Tariff ID'),
+ 'TariffIdDesc' => _t(self::class . '.TariffIdDesc', 'Enter Tariff ID to match the preferred tariff plan you created at the Novalnet Merchant Administration portal for this project. Refer Novalnet Payment Gateway Installation Guide for further details'),
+ 'AccessKey' => _t(self::class . '.AccessKey', 'Payment access key'),
+ 'AccessKeyDesc' => _t(self::class . '.AccessKeyDesc', 'Enter the Novalnet payment access key'),
+ 'ManualTestingVendorScript' => _t(self::class . '.ManualTestingVendorScript', 'Allow manual testing of the Notification / Webhook URL'),
+ 'ManualTestingVendorScriptDesc' => _t(self::class . '.ManualTestingVendorScriptDesc', 'Enable this to test the Novalnet Notification / Webhook URL manually. Disable this before setting your shop live to block unauthorized calls from external parties'),
+ 'SendTo' => _t(self::class . '.SendTo', 'Send e-mail to'),
+ 'SendToDesc' => _t(self::class . '.SendToDesc', 'Notification / Webhook URL execution messages will be sent to this e-mail'),
+ 'VendorScriptConfiguration' => _t(self::class . '.VendorScriptConfiguration', 'Notification / Webhook URL Setup'),
+ 'VencorConfiguration' => _t(self::class . '.VencorConfiguration', 'Novalnet Global Configuration'),
+ 'OnholdStatusManagement' => _t(self::class . '.OnholdStatusManagement', 'Order status management for on-hold transactions'),
+ 'OnholdOrderStatus' => _t(self::class . '.OnholdOrderStatus', 'Onhold order status'),
+ 'OnholdOrderStatusDesc' => _t(self::class . '.OnholdOrderStatusDesc', 'Status to be used for on-hold orders until the transaction is confirmed or cancelled'),
+ 'OnholdOrderCancelStatus' => _t(self::class . '.OnholdOrderCancelStatus', 'Order cancellation status'),
+ 'OnholdOrderCancelStatusDesc' => _t(self::class . '.OnholdOrderCancelStatusDesc', 'Status to be used when order is cancelled or fully refunded'),
+ 'OrderConfirmationSubmitButtonTitle' => _t(self::class . '.OrderConfirmationSubmitButtonTitle', 'Pay with Novalnet (over 100 payment methods worldwide)'),'novalnetcreditcardConfiguration' => _t(self::class . '.novalnetcreditcardConfiguration', 'Credit/Debit Cards Configuration'),
+ 'Creditcard3dEnforce' => _t(self::class . '.Creditcard3dEnforce', 'Enforce 3D secure payment outside EU'),
+ 'Creditcard3dEnforceDesc' => _t(self::class . '.Creditcard3dEnforceDesc', 'By enabling this option, all payments from cards issued outside the EU will be authenticated via 3DS 2.0 SCA'),
+ 'novalnetsepaConfiguration' => _t(self::class . '.novalnetsepaConfiguration', 'Direct Debit SEPA Configuration'),
+ 'SepaDueDate' => _t(self::class . '.SepaDueDate', 'Payment due date (in days)'),
+ 'SepaDueDateDesc' => _t(self::class . '.SepaDueDateDesc', 'Number of days after which the payment is debited (must be between 2 and 14 days)'),
+ 'EnableGuarantee' => _t(self::class . '.EnableGuarantee', 'Enable payment guarantee'),
+ 'EnableGuaranteeDesc' => _t(self::class . '.EnableGuaranteeDesc', 'Payment guarantee requirements:
+ Allowed countries: DE, AT, CH
+ Allowed currency: EUR
+ Minimum order amount: 9,99 EUR or more
+ Age limit: 18 years or more
+ The billing address must be the same as shipping address
'),
+ 'GuaranteeMinOrderAmount' => _t(self::class . '.GuaranteeMinOrderAmount', 'Minimum order amount for payment guarantee'),
+ 'GuaranteeMinOrderAmountDesc' => _t(self::class . '.GuaranteeMinOrderAmountDesc', 'Enter the minimum amount (in cents) for the transaction to be processed with payment guarantee. For example, enter 100 which is equal to 1,00. By default, the amount will be 9,99 EUR. '),
+ 'PaymentGuaranteeForce' => _t(self::class . '.PaymentGuaranteeForce', 'Force non-guarantee payment'),
+ 'PaymentGuaranteeForceDesc' => _t(self::class . '.PaymentGuaranteeForceDesc', 'Even if payment guarantee is enabled, payments will still be processed as non-guarantee payment if payment guarantee requirements are not met. Review the requirements under "Enable Payment Guarantee" in the installation guide.'),
+ 'novalnetinvoiceConfiguration' => _t(self::class . '.novalnetinvoiceConfiguration', 'Invoice Configuration'),
+ 'InvoicePaymentDueDate' => _t(self::class .'InvoicePaymentDueDate','Payment due date (in days)'),
+ 'InvoicePaymentDueDateDesc' => _t(self::class .'InvoicePaymentDueDateDesc','Number of days given to the buyer to transfer the amount to Novalnet (must be greater than 7 days). In case this field is empty, 14 days will be set as due date by default'),
+ 'GuaranteePaymentPendingStatus' => _t(self::class .'GuaranteePaymentPendingStatus','Payment pending order status'),
+ 'GuaranteePaymentPendingStatusDesc' => _t(self::class .'GuaranteePaymentPendingStatusDesc','Status to be used for pending transactions'),
+ 'CallbackStatus' => _t(self::class .'CallbackStatus','Callback / webhook order status'),
+ 'CallbackStatusDesc' => _t(self::class .'CallbackStatusDesc','Status to be used when callback script is executed for payment received by Novalnet'),
+ 'novalnetprepaymentConfiguration' => _t(self::class . '.novalnetprepaymentConfiguration', 'Prepayment Configuration'),
+ 'PendingStatus' => _t(self::class .'PendingStatus','Pending payment order status'),
+ 'PendingStatusDesc' => _t(self::class .'PendingStatusDesc','Status to be used for pending transactions'),
+ 'OrderCompletionStatus' => _t(self::class .'OrderCompletionStatus', 'Completed order status'),
+ 'OrderCompletionStatusDesc' => _t(self::class .'OrderCompletionStatusDesc', 'Status to be used for successful orders'),
+ 'PrepaymentDueDate' => _t(self::class . '.PrepaymentDueDate', 'Payment due date (in days)'),
+ 'PrepaymentDueDateDesc' => _t(self::class . '.PrepaymentDueDateDesc', 'Number of days after which the payment is debited (must be between 2 and 14 days)'),
+ 'novalnetcashpaymentConfiguration' => _t(self::class . '.novalnetcashpaymentConfiguration', 'Barzahlen/viacash Configuration'),
+ 'CashPaymentSlipExpiryDate' => _t(self::class .'CashPaymentSlipExpiryDate','Slip expiry date (in days)'),
+ 'CashPaymentSlipExpiryDateDesc' => _t(self::class .'CashPaymentSlipExpiryDateDesc','Number of days given to the buyer to pay at a store. In case this field is empty, 14 days will be set as slip expiry date by default.'),
+ 'novalnetpaypalConfiguration' => _t(self::class . '.novalnetpaypalConfiguration', 'PayPal Configuration'),
+ 'novalnetsofortConfiguration' => _t(self::class . '.novalnetsofortConfiguration', 'Sofort Configuration'),
+ 'novalnetgiropayConfiguration' => _t(self::class . '.novalnetgiropayConfiguration', 'giropay Configuration'),
+ 'novalnetepsConfiguration' => _t(self::class . '.novalnetepsConfiguration', 'eps Configuration'),
+ 'novalnetidealConfiguration' => _t(self::class . '.novalnetidealConfiguration', 'iDEAL Configuration'),
+ 'novalnetprzelewy24Configuration' => _t(self::class . '.novalnetprzelewy24Configuration', 'Przelewy24 Configuration'),
+ 'TransactionDetails' => _t(self::class .'TransactionDetails','Novalnet transaction details'),
+ 'TransactionID' => _t(self::class .'TransactionID','Novalnet transaction ID: '),
+ 'TestMode' => _t(self::class .'TestMode','Test order'),
+ 'InvoiceComment' => _t(self::class .'InvoiceComment','Please transfer the amount to the below mentioned account details of our payment processor Novalnet'),
+ 'InvoiceDueDate' => _t(self::class .'InvoiceDueDate','Due date: '),
+ 'InvoiceAccountHolder' => _t(self::class .'InvoiceAccountHolder','Account holder: '),
+ 'InvoiceIban' => _t(self::class .'InvoiceIban','IBAN: '),
+ 'InvoiceBic' => _t(self::class .'InvoiceBic','BIC: '),
+ 'InvoiceBank' => _t(self::class .'InvoiceBank','Bank: '),
+ 'InvoiceAmount' => _t(self::class .'InvoiceAmount','Amount: '),
+ 'InvoiceMultiRefDescription' => _t(self::class .'InvoiceMultiRefDescription','Please use any one of the following references as the payment reference, as only through this way your payment is matched and assigned to the order:'),
+ 'InvoicePaymentRef1' => _t(self::class .'InvoicePaymentRef1','Payment Reference 1: '),
+ 'InvoicePaymentRef2' => _t(self::class .'InvoicePaymentRef2','Payment Reference 2: '),
+ 'GuaranteeText' => _t(self::class .'GuaranteeText','Your order is under verification and once confirmed, we will send you our bank details to where the order amount should be transferred. Please note that this may take upto 24 hours'),
+ 'GuaranteeSepaText' => _t(self::class .'GuaranteeSepaText','Your order is under verification and we will soon update you with the order status. Please note that this may take upto 24 hours'),
+ 'PaymentAction' => _t(self::class .'PaymentAction','Payment Action'),
+ 'PaymentActionDesc' => _t(self::class .'PaymentActionDesc','Choose whether or not the payment should be charged immediately. Capture completes the transaction by transferring the funds from buyer account to merchant account. Authorize verifies payment details and reserves funds to capture it later, giving time for the merchant to decide on the order'),
+ 'SlipExpiryDate' => _t(self::class .'SlipExpiryDate','Slip expiry date '),
+ 'CashpaymentStore' => _t(self::class .'CashpaymentStore','Store(s) near you'),
+ 'GuaranteeComments' => _t(self::class .'GuaranteeComments','This is processed as a guarantee payment'),
+ 'GuaranteeErrorMsg' => _t(self::class .'GuaranteeErrorMsg','The payment cannot be processed, because the basic requirements for the payment guarantee haven’t been met'),
+ 'GuaranteeErrorMsgAmount' => _t(self::class .'GuaranteeErrorMsgAmount','Minimum order amount must be '),
+ 'GuguaranteeErrorMsgCountry' => _t(self::class .'GuguaranteeErrorMsgCountry','Only Germany, Austria or Switzerland are allowed'),
+ 'GuaranteeErrorMsgAddress' => _t(self::class .'GuaranteeErrorMsgAddress','The shipping address must be the same as the billing address'),
+ 'GuaranteeErrorMsgCurrency' => _t(self::class .'GuaranteeErrorMsgCurrency','Only EUR currency allowed'),
+ 'StatusCancelled' => _t(self::class .'StatusCancelled','Cancelled'),
+ 'MinimumOrderAmount' => _t(self::class . '.MinimumOrderAmount', 'Minimum transaction amount for authorization'),
+ 'MinimumOrderAmountDesc' => _t(self::class . '.MinimumOrderAmountDesc', 'Transactions above this amount will be "authorized only" until you capture. Leave the field blank to authorize all transactions'),
+ 'Capture' => _t(self::class . '.Capture', 'Capture'),
+ 'Authorize' => _t(self::class . '.Authorize', 'Authorize'),
+ 'SepaDueDateError' => _t(self::class . '.SepaDueDateError', 'SEPA Due date is not valid'),
+ 'GuaranteeMinOrderAmountError' => _t(self::class . '.GuaranteeMinOrderAmountError', 'The minimum amount should be at least 9,99 EUR'),
+ ]
+ );
+ });
+ return parent::fieldLabels($includerelations);
+ }
+
+ /**
+ * returns CMS fields
+ *
+ * @return \SilverStripe\Forms\FieldList
+ */
+ public function getCMSFields() : FieldList
+ {
+ $this->beforeUpdateCMSFields(function(FieldList $fields) {
+ if ($this->PaymentChannel == 'novalnetglobalconfiguration') {
+ $this->getFieldsForNnGlobalConfig($fields);
+ $this->getFieldsForNnVendorScriptConfig($fields);
+ $this->getFieldsForNnOnholdOrderConfig($fields);
+ } else {
+ $this->getFieldsForNnPaymentConfiguation($fields);
+ }
+ $translations = GridField::create(
+ 'NovalnetGatewayTranslations',
+ $this->fieldLabel('NovalnetGatewayTranslations'),
+ $this->NovalnetGatewayTranslations(),
+ GridFieldConfig_ExclusiveRelationEditor::create()
+ );
+ $fields->addFieldToTab('Root.Translations', $translations);
+
+ });
+ return parent::getCMSFieldsForModules();
+ }
+ /**
+ * Adds the fields for the Novalnet global configuration
+ *
+ * @param FieldList $fields FieldList to add fields to
+ *
+ * @return void
+ */
+ protected function getFieldsForNnGlobalConfig($fields) : void
+ {
+ $tabBasicFieldList = FieldList::create();
+
+ $filedDetails = ['VendorId' => 'VendorIdDesc', 'AuthCode' => 'AuthCodeDesc', 'ProductId' => 'ProductIdDesc', 'TariffId' => 'TariffIdDesc', 'AccessKey' => 'AccessKeyDesc'];
+
+ foreach ( $filedDetails as $field => $desc) {
+ $tabBasicFieldList->push(TextField::create($field,$this->fieldLabel($field))
+ ->setDescription($this->fieldLabel($desc)));
+ }
+
+ $apiDataToggle = ToggleCompositeField::create(
+ 'VencorConfiguration',
+ $this->fieldLabel('VencorConfiguration'),
+ $tabBasicFieldList
+ )->setHeadingLevel(4)->setStartClosed(true);
+
+ $fields->addFieldToTab('Root.Basic', $apiDataToggle);
+ }
+
+ /**
+ * Adds the fields for the Novalnet vendorscript configuration
+ *
+ * @param FieldList $fields FieldList to add fields to
+ *
+ * @return void
+ */
+ protected function getFieldsForNnVendorScriptConfig($fields) : void
+ {
+ $tabBasicFieldList = FieldList::create();
+
+ $filedDetails = ['ManualTestingVendorScript' => 'ManualTestingVendorScriptDesc'];
+
+ foreach ( $filedDetails as $field => $desc) {
+ $tabBasicFieldList->push(CheckboxField::create($field, $this->fieldLabel($field))
+ ->setDescription($this->fieldLabel($desc)));
+ }
+
+ $tabBasicFieldList->push(TextField::create('SendTo',$this->fieldLabel('SendTo'))
+ ->setDescription($this->fieldLabel('SendToDesc'))
+ );
+
+ $apiDataToggle = ToggleCompositeField::create(
+ 'VendorScriptConfiguration',
+ $this->fieldLabel('VendorScriptConfiguration'),
+ $tabBasicFieldList
+ )->setHeadingLevel(4)->setStartClosed(true);
+
+ $fields->addFieldToTab('Root.Basic', $apiDataToggle);
+ }
+
+ /**
+ * Adds the fields for the Novalnet onhold configuration
+ *
+ * @param FieldList $fields FieldList to add fields to
+ *
+ * @return void
+ */
+ protected function getFieldsForNnOnholdOrderConfig($fields) : void
+ {
+ $paymentStatus = PaymentStatus::get();
+ $tabBasicFieldList = FieldList::create();
+
+ $filedDetails = ['OnholdOrderStatus' => 'OnholdOrderStatusDesc', 'OnholdOrderCancelStatus' => 'OnholdOrderCancelStatusDesc'];
+
+ foreach ( $filedDetails as $field => $desc) {
+ $tabBasicFieldList->push(DropdownField::create($field, $this->fieldLabel($field), $paymentStatus->map('ID', 'Title'), $field)
+ ->setDescription($this->fieldLabel($desc)));
+ }
+
+ $fields->removeByName('PaymentStatusID');
+ $fields->removeByName('maxAmountForActivation');
+ $fields->removeByName('minAmountForActivation');
+ $fields->removeByName('LongPaymentDescription');
+ $fields->removeByName('SumModifiers');
+ $apiDataToggle = ToggleCompositeField::create(
+ 'OnholdStatusManagement',
+ $this->fieldLabel('OnholdStatusManagement'),
+ $tabBasicFieldList
+ )->setHeadingLevel(4)->setStartClosed(true);
+
+ $fields->addFieldToTab('Root.Basic', $apiDataToggle);
+ }
+ /**
+ * Adds the fields for the Novalnet payment configuration
+ *
+ * @param FieldList $fields FieldList to add fields to
+ *
+ * @return void
+ */
+ protected function getFieldsForNnPaymentConfiguation($fields) : void
+ {
+ $paymentStatus = PaymentStatus::get();
+
+ $tabBasicFieldList = FieldList::create();
+
+ $tabBasicFieldList->push(DropdownField::create('OrderCompletionStatus', $this->fieldLabel('OrderCompletionStatus'), $paymentStatus->map('ID', 'Title'), $this->OrderCompletionStatus)
+ ->setDescription($this->fieldLabel('OrderCompletionStatusDesc'))
+ );
+
+ if (in_array($this->PaymentChannel, ['novalnetprepayment', 'novalnetinvoice', 'novalnetcashpayment'])) {
+
+ $tabBasicFieldList->push(DropdownField::create('CallbackStatus', $this->fieldLabel('CallbackStatus'), $paymentStatus->map('ID', 'Title'), $this->CallbackStatus)
+ ->setDescription($this->fieldLabel('CallbackStatusDesc')));
+ }
+
+ $paymentAction = [
+ 'capture' => $this->fieldLabel('Capture'),
+ 'authorize' => $this->fieldLabel('Authorize'),
+ ];
+
+ if (in_array($this->PaymentChannel, ['novalnetinvoice', 'novalnetcreditcard', 'novalnetsepa', 'novalnetpaypal'])) {
+ $tabBasicFieldList->push( DropdownField::create('PaymentAction', $this->fieldLabel('PaymentAction'), $paymentAction, $this->PaymentAction)
+ ->setDescription($this->fieldLabel('PaymentActionDesc'))
+ );
+ $tabBasicFieldList->push(TextField::create('OnholdMinOrderAmount',$this->fieldLabel('MinimumOrderAmount'))
+ ->setDescription($this->fieldLabel('MinimumOrderAmountDesc'))
+ );
+ }
+
+ if (in_array($this->PaymentChannel, ['novalnetpaypal', 'novalnetprzelewy24'])) {
+ $tabBasicFieldList->push(DropdownField::create('PendingStatus', $this->fieldLabel('PendingStatus'), $paymentStatus->map('ID', 'Title'), $this->PendingStatus)
+ ->setDescription($this->fieldLabel('PendingStatusDesc'))
+ );
+ }
+
+ if ($this->PaymentChannel == 'novalnetinvoice') {
+ $tabBasicFieldList->push(TextField::create('InvoiceDueDate',$this->fieldLabel('InvoicePaymentDueDate'))
+ ->setDescription($this->fieldLabel('InvoicePaymentDueDateDesc'))
+ );
+ }
+
+ if ($this->PaymentChannel == 'novalnetcreditcard') {
+ $tabBasicFieldList->push(CheckboxField::create('Cc3dEnforce', $this->owner->fieldLabel('Creditcard3dEnforce'))
+ ->setDescription($this->fieldLabel('Creditcard3dEnforceDesc'))
+ );
+ }
+
+ if ($this->PaymentChannel == 'novalnetsepa') {
+ $tabBasicFieldList->push(TextField::create('SepaDueDate',$this->fieldLabel('SepaDueDate'))
+ ->setDescription($this->fieldLabel('SepaDueDateDesc'))
+ );
+ }
+
+ if ($this->PaymentChannel == 'novalnetprepayment') {
+ $tabBasicFieldList->push(TextField::create('PrepaymentDueDate',$this->fieldLabel('PrepaymentDueDate'))
+ ->setDescription($this->fieldLabel('PrepaymentDueDateDesc'))
+ );
+ }
+
+ if (in_array($this->PaymentChannel, ['novalnetinvoice', 'novalnetsepa'])) {
+ $tabBasicFieldList->push(CheckboxField::create('EnableGuarantee',$this->fieldLabel('EnableGuarantee'))
+ ->setDescription($this->fieldLabel('EnableGuaranteeDesc'))
+ );
+ $tabBasicFieldList->push(TextField::create('GuaranteeMinOrderAmount',$this->fieldLabel('GuaranteeMinOrderAmount'))
+ ->setDescription($this->fieldLabel('GuaranteeMinOrderAmountDesc'))
+ );
+ $tabBasicFieldList->push(CheckboxField::create('PaymentGuaranteeForce',$this->fieldLabel('PaymentGuaranteeForce'))
+ ->setDescription($this->fieldLabel('PaymentGuaranteeForceDesc'))
+ );
+
+ $tabBasicFieldList->push(DropdownField::create('GuaranteePendingStatus', $this->fieldLabel('GuaranteePaymentPendingStatus'), $paymentStatus->map('ID', 'Title'), $this->GuaranteePendingStatus)
+ ->setDescription($this->fieldLabel('GuaranteePaymentPendingStatusDesc'))
+ );
+ }
+
+ if ($this->PaymentChannel == 'novalnetcashpayment') {
+ $tabBasicFieldList->push(TextField::create('CashPaymentDuration',$this->fieldLabel('CashPaymentSlipExpiryDate'))
+ ->setDescription($this->fieldLabel('CashPaymentSlipExpiryDateDesc'))
+ );
+ }
+
+ $paymentStatusDataToggle = ToggleCompositeField::create(
+ 'PaymentStatus',
+ $this->fieldLabel($this->PaymentChannel. 'Configuration'),
+ $tabBasicFieldList
+ )->setHeadingLevel(4)->setStartClosed(true);
+ $fields->removeByName('PaymentStatusID');
+
+ $fields->addFieldToTab('Root.Basic', $paymentStatusDataToggle);
+ }
+
+ /**
+ * Creates and relates required order status and logo images.
+ *
+ * @return void
+ *
+ * @author Novalnet AG
+ */
+ public function requireDefaultRecords() : void
+ {
+ parent::requireDefaultRecords();
+ $novalnetPayments = NovalnetGateway::get()->filter('OrderCompletionStatus', 0);
+ if ($novalnetPayments->exists()) {
+ foreach ($novalnetPayments as $novalnetPayment) {
+ $novalnetPayment->OnholdOrderStatus = PaymentStatus::get()->filter('Code', 'open')->first()->ID;
+ $novalnetPayment->OrderCompletionStatus = PaymentStatus::get()->filter('Code', 'open')->first()->ID;
+ $novalnetPayment->GuaranteePendingStatus = PaymentStatus::get()->filter('Code', 'open')->first()->ID;
+ $novalnetPayment->PendingStatus = PaymentStatus::get()->filter('Code', 'open')->first()->ID;
+ $novalnetPayment->CallbackStatus = PaymentStatus::get()->filter('Code', 'paid')->first()->ID;
+ $novalnetPayment->OnholdOrderCancelStatus = PaymentStatus::get()->filter('Code', 'canceled')->first()->ID;
+ $novalnetPayment->write();
+ }
+ }
+
+ $infoMail = ShopEmail::get()->filter('TemplateName', 'PaymentNovalnetTransactionInfo')->first();
+ if (is_null($infoMail)
+ || !$infoMail->exists()
+ ) {
+ $infoMail = ShopEmail::create();
+ $infoMail->TemplateName = 'PaymentNovalnetTransactionInfo';
+ $infoMail->Subject = $this->fieldLabel('InfoMailSubject');
+ $infoMail->write();
+ }
+
+ }
+
+ /**
+ * Called on before write.
+ *
+ * @return void
+ */
+ public function onBeforeWrite() : void
+ {
+ parent::onBeforeWrite();
+ }
+
+ /**
+ * Set the title for the submit button on the order confirmation step.
+ *
+ * @return string
+ */
+ public function getOrderConfirmationSubmitButtonTitle() : string
+ {
+ return $this->fieldLabel('OrderConfirmationSubmitButtonTitle');
+ }
+
+ /**
+ * Returns whether the checkout is ready to call self::processBeforePaymentProvider().
+ *
+ * @param array $checkoutData Checkout data
+ *
+ * @return bool
+ *
+ * @author Novalnet AG
+ */
+ public function canProcessBeforePaymentProvider(array $checkoutData) : bool
+ {
+ return !$this->beforePaymentProviderIsProcessed();
+ }
+
+ /**
+ * Returns whether the checkout is ready to call self::processAfterPaymentProvider().
+ *
+ * @param array $checkoutData Checkout data
+ *
+ * @return bool
+ *
+ * @author Novalnet AG
+ */
+ public function canProcessAfterPaymentProvider(array $checkoutData) : bool
+ {
+ $request = $this->getController()->getRequest();
+ $getResponseData = $request->postVars();
+ $processed = false;
+ if ((isset($getResponseData['tid']) && !is_null($getResponseData['tid'])) || (isset($getResponseData['status']) && !is_null($getResponseData['status']))) {
+ $processed = true;
+ }
+ return $processed && $this->beforePaymentProviderIsProcessed() && !$this->afterPaymentProviderIsProcessed();
+ }
+
+ /**
+ * Is called by default checkout right before placing an order.
+ * If this returns false, the order won't be placed and the checkout won't be finalized.
+ *
+ * @param array $checkoutData Checkout data
+ *
+ * @return bool
+ *
+ * @author Novalnet AG
+ */
+ public function canPlaceOrder(array $checkoutData) : bool
+ {
+ return $this->beforePaymentProviderIsProcessed() && $this->afterPaymentProviderIsProcessed();
+ }
+
+ /**
+ * Returns whether the checkout is ready to call self::processAfterOrder().
+ *
+ * @param \SilverCart\Model\Order\Order $order Order
+ * @param array $checkoutData Checkout data
+ *
+ * @return bool
+ *
+ * @author Novalnet AG
+ */
+ public function canProcessAfterOrder(Order $order, array $checkoutData) : bool
+ {
+ return $this->canPlaceOrder($checkoutData) && $order instanceof Order;
+ }
+
+ /**
+ * Is called by default checkout right before placing an order.
+ *
+ * @param array $checkoutData Checkout data
+ *
+ * @return void
+ *
+ * @author Novalnet AG
+ */
+ protected function processBeforePaymentProvider(array $checkoutData) : void
+ {
+ if (in_array($this->PaymentChannel, ['novalnetinvoice','novalnetsepa'])) {
+ if ($this->PaymentChannel == 'novalnetsepa') {
+ if (!empty($this->SepaDueDate) && (!is_numeric($this->SepaDueDate) || $this->SepaDueDate < 2 || $this->SepaDueDate > 14)) {
+ $this->errorOccured = true;
+ $this->addError($this->fieldLabel('SepaDueDateError'));
+ return;
+ }
+ }
+
+ $condition = $this->EnableGuarantee && !$this->PaymentGuaranteeForce;
+ if ($condition) {
+ if ($this->GuaranteeMinOrderAmount != '' && (!is_numeric($this->GuaranteeMinOrderAmount) || $this->GuaranteeMinOrderAmount < 999)) {
+ $this->errorOccured = true;
+ $this->addError($this->fieldLabel('GuaranteeMinOrderAmountError'));
+ return;
+ }
+ $isGuaranteePayment = $this->checkGuaranteePayment();
+ if ($isGuaranteePayment !== true) {
+ $this->errorOccured = true;
+ $this->addError($isGuaranteePayment);
+ return;
+ }
+ }
+ }
+
+ $parameters = $this->getBasicParameters($checkoutData);
+
+ $response = $this->sendServerRequest($parameters, "https://paygate.novalnet.de/paygate.jsp");
+
+ if (isset($response['status']) && $response['status'] != 100) {
+ $this->errorOccured = true;
+ $errorMessage = ($response['status_desc']) ? $response['status_desc'] : $response['status_text'];
+ $this->addError($errorMessage);
+ return;
+ } else {
+ $this->getController()->redirect($response['url']);
+ Tools::Session()->set(self::BEFORE_PAYMENT_PROVIDER_IS_PROCESSED_SESSION_KEY, true);
+ Tools::saveSession();
+ }
+ }
+
+ /**
+ * Is called right after returning to the checkout after being redirected to Novalnet.
+ * During this step which will be saved to the session.
+ *
+ * @param array $checkoutData Checkout data
+ *
+ * @return void
+ *
+ * @author Novalnet AG
+ */
+ public function processAfterPaymentProvider(array $checkoutData) : void
+ {
+ $request = $this->getController()->getRequest();
+ $getResponseData = $request->postVars();
+ if (isset($getResponseData['status']) && $getResponseData['status'] != 100) {
+ $this->errorOccured = true;
+ $errorMessage = ($getResponseData['status_desc']) ? $getResponseData['status_desc'] : $getResponseData['status_text'];
+ $this->addError($errorMessage);
+ return;
+ } else {
+ if (($getResponseData['status'] != '100')|| (isset($getResponseData['tid']) && is_null($getResponseData['tid'])) || (isset($getResponseData['status']) && is_null($getResponseData['status']))) {
+ $this->errorOccured = true;
+ $errorMessage = ($getResponseData['status_desc']) ? $getResponseData['status_desc'] : $getResponseData['status_text'];
+ $this->addError($errorMessage);
+ return;
+ } else {
+ Tools::Session()->set(self::AFTER_PAYMENT_PROVIDER_IS_PROCESSED_SESSION_KEY, true);
+ Tools::saveSession();
+ }
+ }
+ }
+
+ /**
+ * Is called by default checkout right after placing an order.
+ *
+ * @param \SilverCart\Model\Order\Order $order Order
+ * @param array $checkoutData Checkout data
+ *
+ * @return void
+ *
+ * @author Novalnet AG
+ */
+ protected function processAfterOrder(Order $order, array $checkoutData) : void
+ {
+ $request = $this->getController()->getRequest();
+ $response = $request->postVars();
+
+ if (in_array($this->PaymentChannel, $this->redirectPayments) || !empty($response['cc_3d'])) {
+ $response = $this->decodeData($response);
+ }
+
+ $transaction = NovalnetTransaction::create();
+ $response['orderId'] = $order->ID;
+ $response['orderNumber'] = $order->OrderNumber;
+ $response['paymentChannel'] = $this->PaymentChannel;
+ $transaction->updateOrder($response);
+ $this->updatePaymentDetails($response, $order);
+ $this->updatePostBackCall($response, $order);
+ $this->clearSession();
+ }
+
+ /**
+ * Resets the payment progress hold in session.
+ *
+ * @return void
+ *
+ * @author Novalnet AG
+ */
+ public function resetProgress() : void
+ {
+ Tools::Session()->set(self::BEFORE_PAYMENT_PROVIDER_IS_PROCESSED_SESSION_KEY, false);
+ Tools::Session()->set(self::AFTER_PAYMENT_PROVIDER_IS_PROCESSED_SESSION_KEY, false);
+ Tools::saveSession();
+ }
+
+ /**
+ * Clears the Novalnet session data.
+ *
+ * @return void
+ *
+ * @author Novalnet AG
+ */
+ public function clearSession() : void
+ {
+ Tools::Session()->set(self::SESSION_KEY, null);
+ Tools::saveSession();
+ }
+
+ /**
+ * Returns whether self::processBeforePaymentProvider() is already processed.
+ *
+ * @return bool
+ */
+ protected function beforePaymentProviderIsProcessed() : bool
+ {
+ return (bool) Tools::Session()->get(self::BEFORE_PAYMENT_PROVIDER_IS_PROCESSED_SESSION_KEY);
+ }
+
+ /**
+ * Returns whether self::processAfterPaymentProvider() is already processed.
+ *
+ * @return bool
+ */
+ protected function afterPaymentProviderIsProcessed() : bool
+ {
+ return (bool) Tools::Session()->get(self::AFTER_PAYMENT_PROVIDER_IS_PROCESSED_SESSION_KEY);
+ }
+
+ /**
+ * Update the payment details
+ *
+ * @param array $requestData
+ * @param object $order
+ *
+ * @author Novalnet AG
+ */
+ protected function updatePaymentDetails($requestData, $order)
+ {
+ if ($requestData['status'] == 100) {
+ if (in_array($requestData['tid_status'], [91, 99, 98, 85])) {
+ $onholdOrderStatus = NovalnetGateway::get()->filter('PaymentChannel', 'novalnetglobalconfiguration')->first()->OnholdOrderStatus;
+ $order->setPaymentStatus(PaymentStatus::get()->byID($onholdOrderStatus));
+ } elseif($requestData['tid_status'] == 75) {
+ $order->setPaymentStatus(PaymentStatus::get()->byID($this->GuaranteePendingStatus));
+ } elseif (in_array($requestData['payment_type'], ['GUARANTEED_INVOICE', 'GUARANTEED_DIRECT_DEBIT_SEPA'])) {
+ $status = ($requestData['tid_status'] == 100 && $requestData['payment_type'] == 'GUARANTEED_INVOICE') ? $this->CallbackStatus : $this->OrderCompletionStatus;
+ $order->setPaymentStatus(PaymentStatus::get()->byID($status));
+ } elseif (in_array($requestData['tid_status'], [86, 90])) {
+ $order->setPaymentStatus(PaymentStatus::get()->byID($this->PendingStatus));
+ } else {
+ $order->setPaymentStatus(PaymentStatus::get()->byID($this->OrderCompletionStatus));
+ }
+ }
+
+ $message = '';
+ $message .= $this->fieldLabel('TransactionDetails').PHP_EOL;
+ $message .= $this->fieldLabel('TransactionID'). $requestData['tid']. PHP_EOL;
+ $message .= ($requestData['test_mode'] == 1) ? $this->fieldLabel('TestMode'): ''.PHP_EOL;
+
+ if (in_array($requestData['key'], ['40', '41'])) {
+ $message .= PHP_EOL.$this->fieldLabel('GuaranteeComments');
+ }
+
+ if (in_array($requestData['payment_type'], ['INVOICE_START', 'GUARANTEED_INVOICE']) && $requestData['tid_status'] !=75) {
+ $requestData['order_no'] = $order->OrderNumber;
+ $requestData['product_id'] = NovalnetGateway::get()->filter('PaymentChannel', 'novalnetglobalconfiguration')->first()->ProductId;
+ $message .= $this->prepareInvoiceComments($requestData);
+ }
+
+ if ($requestData['tid_status'] == 75) {
+ $message .= ($requestData['key'] == '41') ? PHP_EOL.$this->fieldLabel('GuaranteeText') : PHP_EOL.$this->fieldLabel('GuaranteeSepaText');
+ }
+
+ if (isset($requestData['key']) && $requestData['key'] =='59') {
+ $message .= $this->getBarzahlenComments($requestData);
+ }
+
+ // send email with payment information to the customer
+ ShopEmail::send(
+ 'PaymentNovalnetTransactionInfo',
+ $order->CustomersEmail,
+ [
+ 'Order' => $order,
+ 'Message' => ['comments' => $message],
+ ]
+ );
+
+ DB::query(
+ sprintf(
+ "UPDATE SilvercartOrder SET PaymentReferenceID= '".$requestData['tid']."', PaymentReferenceMessage ='".$message."' WHERE OrderNumber = '%s'",
+ $order->OrderNumber
+ )
+ );
+ }
+
+ /**
+ * Prepare invoice prepayment bank details
+ *
+ * @param array $requestData
+ * @return string
+ *
+ * @author Novalnet AG
+ */
+ public function prepareInvoiceComments($requestData, $break = '')
+ {
+ $message = '';
+ $break = !empty($break) ? $break : PHP_EOL;
+ $message .= $break.$break.$this->fieldLabel('InvoiceComment') . $break;
+ if (!empty($requestData['due_date'])) {
+ $message .= $break.$this->fieldLabel('InvoiceDueDate') . date('Y-m-d', strtotime($requestData['due_date'])). $break;
+ }
+ $message .= $this->fieldLabel('InvoiceAccountHolder') . $requestData['invoice_account_holder']. $break;
+ $message .= $this->fieldLabel('InvoiceIban') . $requestData['invoice_iban']. $break;
+ $message .= $this->fieldLabel('InvoiceBic') . $requestData['invoice_bic']. $break;
+ $message .= $this->fieldLabel('InvoiceBank'). $requestData['invoice_bankname'].' '.$requestData['invoice_bankplace']. $break;
+ $message .= $this->fieldLabel('InvoiceAmount') . $this->formatedAmount($requestData).$break.$break;
+ $message .= $this->fieldLabel('InvoiceMultiRefDescription').$break;
+ $message .= $this->fieldLabel('InvoicePaymentRef1') . 'BNR-'.$requestData['product_id'].'-'.$requestData['order_no'].$break;
+ $message .= $this->fieldLabel('InvoicePaymentRef2') . $requestData['tid'];
+
+ return $message;
+ }
+
+ /**
+ * Returns the Price formatted by locale.
+ *
+ * @param array $requestData
+ * @return string
+ *
+ * @author Novalnet AG
+ */
+ public function formatedAmount($requestData, $callback = false)
+ {
+ $formatedAmount = DBMoney::create();
+ if ($callback == true)
+ $requestData['amount'] = sprintf('%0.2f', $requestData['amount'] / 100);
+
+ $formatedAmount->setAmount($requestData['amount']);
+ $formatedAmount->setCurrency($requestData['currency']);
+
+ return $formatedAmount->Nice();
+ }
+
+ /**
+ * Decode the basic parameters
+ *
+ * @param array $response
+ * @return string
+ *
+ * @author Novalnet AG
+ */
+ public function decodeData($response)
+ {
+ $accessKey = NovalnetGateway::get()->filter('PaymentChannel', 'novalnetglobalconfiguration')->first()->AccessKey;
+ foreach ($this->nnSecuredParams as $key) {
+ $response[$key] = openssl_decrypt(base64_decode($response[$key]),"aes-256-cbc", $accessKey,true, $response['uniqid']);
+ }
+ return $response;
+ }
+
+ /**
+ * Generate Novalnet Gateway parameters.
+ *
+ * @param array $checkoutData Checkout data
+ *
+ * @return array
+ *
+ * @author Novalnet AG
+ */
+ protected function getBasicParameters(array $checkoutData) : array
+ {
+ $paygateParams = [];
+ $this->getVendorParams($paygateParams);
+ $this->getBillingParams($checkoutData, $paygateParams);
+ $this->getCommonParams($paygateParams);
+ $this->getPaymentParams($paygateParams);
+ $this->getSeamlessFormParams($paygateParams);
+ $accesskey = NovalnetGateway::get()->filter('PaymentChannel', 'novalnetglobalconfiguration')->first()->AccessKey;
+ $this->encodeParams($paygateParams, $accesskey);
+ $paygateParams['hash'] = $this->generateHash($paygateParams, $accesskey);
+ return $paygateParams;
+ }
+
+ /**
+ * Assign Novalnet authentication Data
+ *
+ * @param array $paygateParams
+ * @return null
+ *
+ * @author Novalnet AG
+ */
+ public function getVendorParams(&$paygateParams)
+ {
+ $paygateParams['vendor'] = NovalnetGateway::get()->filter('PaymentChannel', 'novalnetglobalconfiguration')->first()->VendorId;
+ $paygateParams['auth_code'] = NovalnetGateway::get()->filter('PaymentChannel', 'novalnetglobalconfiguration')->first()->AuthCode;
+ $paygateParams['product'] = NovalnetGateway::get()->filter('PaymentChannel', 'novalnetglobalconfiguration')->first()->ProductId;
+ $paygateParams['tariff'] = NovalnetGateway::get()->filter('PaymentChannel', 'novalnetglobalconfiguration')->first()->TariffId;$paygateParams['test_mode'] = ($this->mode == 'Dev') ? '1' : '0';
+ }
+
+ /**
+ * Get end-customer billing informations
+ *
+ * @param array $checkoutData
+ * @param array $paygateParams
+ * @return null
+ *
+ * @author Novalnet AG
+ */
+ public function getBillingParams($checkoutData, &$paygateParams)
+ {
+ $invoiceAddress = $this->getInvoiceAddress();
+
+ $paygateParams['gender'] = 'u';
+ $paygateParams['firstname'] = $invoiceAddress->FirstName;
+ $paygateParams['lastname'] = $invoiceAddress->Surname;
+ $customer = Security::getCurrentUser();
+ $paygateParams['email'] = $customer->Email;
+ $paygateParams['customer_no'] = $customer->CustomerNumber;
+ $paygateParams['street'] = $invoiceAddress->Street;
+ $paygateParams['house_no'] = $invoiceAddress->StreetNumber;
+ $paygateParams['country_code'] = $invoiceAddress->Country()->ISO2;
+ $paygateParams['city'] = $invoiceAddress->City;
+ $paygateParams['zip'] = $invoiceAddress->Postcode;
+ if ($invoiceAddress->Phone)
+ $paygateParams['tel'] = $invoiceAddress->Phone;
+
+ if ($invoiceAddress->Fax)
+ $paygateParams['fax'] = $invoiceAddress->Fax;
+
+ if ($customer->Birthday)
+ $paygateParams['birth_date'] = $customer->Birthday;
+
+ if ($invoiceAddress->Company)
+ $paygateParams['company'] = $invoiceAddress->Company;
+ }
+
+ /**
+ * Get common params for Novalnet payment API request
+ *
+ * @param array $paygateParams
+ * @return null
+ *
+ * @author Novalnet AG
+ */
+ public function getCommonParams(&$paygateParams)
+ {
+ $shoppingCart = $this->getShoppingCart();
+ $currentLocale = Tools::current_locale();
+ $parts = explode('_', $currentLocale);
+ $paygateParams['amount'] = round((float) $shoppingCart->getAmountTotal()->getAmount(), 2)*100;
+ $paygateParams['currency'] = $shoppingCart->getAmountTotal()->getCurrency();
+ $paygateParams['lang'] = $parts[0];
+ $paygateParams['system_name'] = 'silvercart';
+ $paygateParams['remote_ip'] = $this->getRemoteAddress();
+ $paygateParams['system_ip'] = $_SERVER['SERVER_ADDR'];
+ $paygateParams['uniqid'] = $this->getRandomString();
+ $paygateParams['implementation'] = 'ENC';
+ $paygateParams['return_method'] = 'POST';
+ $paygateParams['error_return_method'] = 'POST';
+ $paygateParams['return_url'] = $this->getReturnLink();
+ $paygateParams['error_return_url'] = $this->getReturnLink();
+ $paygateParams['hook_url'] = $this->getNotificationLink();
+ $paygateParams['input1'] = 'nn_sid';
+ $paygateParams['inputval1'] = Cookie::get(session_name());
+ }
+
+
+ /**
+ * Assign Novalnet payment data
+ *
+ * @param array $paygateParams
+ * @return null
+ *
+ * @author Novalnet AG
+ */
+ public function getPaymentParams(&$paygateParams)
+ {
+ $paygateParams['key'] = $this->getPaymentId($this->PaymentChannel);
+ $paygateParams['payment_type'] = $this->getPaymentType($this->PaymentChannel);
+
+ if(in_array($this->PaymentChannel, ['novalnetcreditcard', 'novalnetsepa', 'novalnetinvoice', 'novalnetpaypal']) && $this->PaymentAction == 'authorize' && (empty($this->OnholdMinOrderAmount) || (is_numeric($this->OnholdMinOrderAmount) && $paygateParams['amount'] >= $this->OnholdMinOrderAmount))) {
+ $paygateParams['on_hold'] = '1';
+ }
+ if ($this->PaymentChannel == 'novalnetcreditcard') {
+ if (!empty($this->Cc3dEnforce))
+ $paygateParams['cc_3d'] = $this->Cc3dEnforce;
+
+ } elseif($this->PaymentChannel == 'novalnetsepa') {
+ if ($this->EnableGuarantee) {
+ $guaranteePayment = $this->checkGuaranteePayment();
+ if ($guaranteePayment === true) {
+ $paygateParams['key'] = '40';
+ $paygateParams['payment_type'] = 'GUARANTEED_DIRECT_DEBIT_SEPA';
+ }
+ }
+ $paygateParams['sepa_due_date'] = (trim($this->SepaDueDate))
+ ? date('Y-m-d', strtotime('+' . trim($this->SepaDueDate) . ' days')) : '';
+
+ } elseif ($this->PaymentChannel == 'novalnetinvoice') {
+ $paygateParams['invoice_type'] = 'INVOICE';
+ if ($this->EnableGuarantee) {
+ $guaranteePayment = $this->checkGuaranteePayment();
+ if ($guaranteePayment === true) {
+ $paygateParams['key'] = '41';
+ $paygateParams['payment_type'] = 'GUARANTEED_INVOICE';
+ unset($paygateParams['invoice_type']);
+ }
+ }
+ $paygateParams['due_date'] = (trim($this->InvoiceDueDate)) ? (trim($this->InvoiceDueDate)) : '';
+ } elseif($this->PaymentChannel == 'novalnetprepayment') {
+ $paygateParams['invoice_type'] = 'PREPAYMENT';
+ $paygateParams['due_date'] = (!empty($this->PrepaymentDueDate) || $this->PrepaymentDueDate <= 28 || $this->PrepaymentDueDate >= 7) ? $this->PrepaymentDueDate : '';
+ } elseif($this->PaymentChannel == 'novalnetcashpayment') {
+
+ $paygateParams['cp_due_date'] = (trim($this->CashPaymentDuration))
+ ? date('Y-m-d', strtotime('+' . trim($this->CashPaymentDuration) . ' days')) : '';
+ }
+ }
+
+ /**
+ * Check guarantee payment requirements
+ *
+ * @return boolean
+ *
+ * @author Novalnet AG
+ */
+ public function checkGuaranteePayment()
+ {
+ $shoppingCart = $this->getShoppingCart();
+ $amount = round((float) $shoppingCart->getAmountTotal()->getAmount(), 2)*100;
+
+ $minAmount = $this->GuaranteeMinOrderAmount;
+
+ $minAmount = ($minAmount) ? $minAmount : 999;
+
+ $invoiceAddress = $this->getInvoiceAddress();
+ $shippingAddress = $this->getShippingAddress();
+ $currency = $shoppingCart->getAmountTotal()->getCurrency();
+
+ $billingAddress = [
+ $invoiceAddress->Street,
+ $invoiceAddress->StreetNumber,
+ $invoiceAddress->City,
+ $invoiceAddress->Postcode,
+ $invoiceAddress->Country()->ISO2
+ ];
+ $shippingAddress = [
+ $shippingAddress->Street,
+ $shippingAddress->StreetNumber,
+ $shippingAddress->City,
+ $shippingAddress->Postcode,
+ $shippingAddress->Country()->ISO2
+ ];
+
+ if (in_array($invoiceAddress->Country()->ISO2, ['AT', 'CH', 'DE'])
+ && ($shippingAddress === $billingAddress)
+ && $amount >= $minAmount && $currency == 'EUR') {
+ return true;
+ } else {
+
+ $errorMsg = $this->fieldLabel('GuaranteeErrorMsg') . PHP_EOL;
+ if (!($amount >= $minAmount)) {
+ $errorMsg .= $this->fieldLabel('GuaranteeErrorMsgAmount'). $minAmount. ' ' .$currency;
+ }
+ if (!in_array($invoiceAddress->Country()->ISO2, ['AT', 'CH', 'DE'])) {
+ $errorMsg .= $this->fieldLabel('GuguaranteeErrorMsgCountry');
+ }
+ if ($shippingAddress !== $billingAddress) {
+ $errorMsg .= $this->fieldLabel('GuaranteeErrorMsgAddress');
+ }
+ if ($currency != 'EUR') {
+ $errorMsg .= $this->fieldLabel('GuaranteeErrorMsgCurrency');
+ }
+
+ return $errorMsg;
+ }
+ }
+
+ /**
+ * Get Seamless payment form customization params
+ *
+ * @param array $paygateParams
+ * @return null
+ *
+ * @author Novalnet AG
+ */
+ public function getSeamlessFormParams(&$paygateParams)
+ {
+ $paygateParams['hfooter'] = '0';
+ $paygateParams['skip_cfm'] = '1';
+ $paygateParams['skip_suc'] = '1';
+ $paygateParams['skip_sp'] = '1';
+ $paygateParams['thide'] = '1';
+ $paygateParams['purl'] = '1';
+ $paygateParams['address_form'] = '0';
+ $paygateParams['shide'] = '1';
+ $paygateParams['lhide'] = '1';
+ $paygateParams['chosen_only'] = '1';
+ }
+
+ /**
+ * Is this payment method allowed for a total amount?
+ *
+ * @param int $amount Amount to be checked
+ *
+ * @return bool
+ *
+ * @author Novalnet AG
+ */
+ public function isAvailableForAmount($amount)
+ {
+ $isAvailable = parent::isAvailableForAmount($amount);
+
+ if ($this->PaymentChannel == 'novalnetglobalconfiguration') {
+ $isAvailable = false;
+ }
+ $vendorId = NovalnetGateway::get()->filter('PaymentChannel', 'novalnetglobalconfiguration')->first()->VendorId;
+ $authCode = NovalnetGateway::get()->filter('PaymentChannel', 'novalnetglobalconfiguration')->first()->AuthCode;
+ $productId = NovalnetGateway::get()->filter('PaymentChannel', 'novalnetglobalconfiguration')->first()->ProductId;
+ $tariffId = NovalnetGateway::get()->filter('PaymentChannel', 'novalnetglobalconfiguration')->first()->TariffId;
+ $accessKey = NovalnetGateway::get()->filter('PaymentChannel', 'novalnetglobalconfiguration')->first()->AccessKey;
+
+ if (empty($vendorId) || empty($authCode) || empty($productId) || empty($tariffId) || empty($accessKey)) {
+ $this->Log('isAvailableForAmount','');
+ $this->Log('isAvailableForAmount','Please fill in all the mandatory fields');
+ $isAvailable = false;
+ }
+
+ return $isAvailable;
+
+ }
+
+ /**
+ * Performs CURL request
+ *
+ * @param array $paymentData
+ * @param string $url
+ * @return mixed
+ *
+ * @author Novalnet AG
+ */
+ public function sendServerRequest($paymentData, $url)
+ {
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, $url);
+ curl_setopt($ch, CURLOPT_VERBOSE, 1);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($ch, CURLOPT_POST, 1);
+ curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($paymentData));
+ $GatewayTimeout = NovalnetGateway::get()->filter('PaymentChannel', 'novalnetglobalconfiguration')->first()->GatewayTimeout;
+ // Custom CURL time-out.
+ curl_setopt($ch, CURLOPT_TIMEOUT, 240);
+
+ $response = curl_exec($ch);
+ $responseData = [];
+ parse_str($response, $responseData);
+
+ return $responseData;
+ }
+
+ /**
+ * Generate unique string.
+ *
+ * @return string
+ *
+ * @author Novalnet AG
+ */
+ public function getRandomString()
+ {
+ $randomwordarray = ['8','7','6','5','4','3','2','1','9','0','9','7','6','1','2','3','4','5','6','7','8','9','0'];
+ shuffle($randomwordarray);
+ return substr(implode('', $randomwordarray), 0, 16);
+ }
+
+ /**
+ * Encode the config parameters before transaction.
+ *
+ * @param array $paygateParams
+ * @param string $key
+ * @return null
+ *
+ * @author Novalnet AG
+ */
+ public function encodeParams(&$paygateParams, $key)
+ {
+ foreach ($this->nnSecuredParams as $value) {
+ try {
+ $paygateParams[$value] = htmlentities(base64_encode(openssl_encrypt(
+ $paygateParams[$value],
+ "aes-256-cbc",
+ $key,
+ true,
+ $paygateParams['uniqid']
+ )));
+ } catch (\Exception $e) {
+ throw new \Exception($e->getMessage());
+ }
+ }
+ }
+
+ /**
+ * Generate the 32 digit hash code
+ *
+ * @param array $data
+ * @param string $key
+ * @return string
+ *
+ * @author Novalnet AG
+ */
+ public function generateHash($data, $key)
+ {
+ $str = '';
+ $hashFields = [
+ 'auth_code',
+ 'product',
+ 'tariff',
+ 'amount',
+ 'test_mode',
+ 'uniqid'
+ ];
+ foreach ($hashFields as $value) {
+ $str .= $data[$value];
+ }
+ return hash('sha256', $str . strrev($key));
+ }
+
+ /**
+ * Send postback to Novalnet server
+ *
+ * @param array $request
+ * @param object $order
+ * @return
+ *
+ * @author Novalnet AG
+ */
+ public function updatePostBackCall($request, $order)
+ {
+ $params = [
+ 'vendor' => NovalnetGateway::get()->filter('PaymentChannel', 'novalnetglobalconfiguration')->first()->VendorId,
+ 'auth_code' => NovalnetGateway::get()->filter('PaymentChannel', 'novalnetglobalconfiguration')->first()->AuthCode,
+ 'product' => NovalnetGateway::get()->filter('PaymentChannel', 'novalnetglobalconfiguration')->first()->ProductId,
+ 'tariff' => NovalnetGateway::get()->filter('PaymentChannel', 'novalnetglobalconfiguration')->first()->TariffId,
+ 'key' => !empty($request['key']) ? $request['key']: $request['payment_id'],
+ 'tid' => $request['tid'],
+ 'status' => $request['status'],
+ 'order_no' => $order->OrderNumber,
+ ];
+
+ if ($params['key'] == 27){
+ $params['invoice_ref'] = 'BNR-'.$params['product'].'-'.$params['order_no'];
+ }
+ $this->sendServerRequest($params, 'https://payport.novalnet.de/paygate.jsp');
+ }
+
+ /**
+ * Get Novalnet payment method id/key
+ *
+ * @param string $code
+ * @return string
+ *
+ * @author Novalnet AG
+ */
+ public function getPaymentId($code)
+ {
+ $paymentId = [
+ 'novalnetcreditcard' => '6',
+ 'novalnetsepa' => '37',
+ 'novalnetprepayment' => '27',
+ 'novalnetinvoice' => '27',
+ 'novalnetsofort' => '33',
+ 'novalnetpaypal' => '34',
+ 'novalnetideal' => '49',
+ 'novalneteps' => '50',
+ 'novalnetcashpayment' => '59',
+ 'novalnetgiropay' => '69',
+ 'novalnetprzelewy24' => '78',
+ ];
+ return $paymentId[$code];
+ }
+
+ /**
+ * Get Novalnet payment method type
+ *
+ * @param string $code
+ * @return string
+ *
+ * @author Novalnet AG
+ */
+ public function getPaymentType($code)
+ {
+ $paymentType = [
+ 'novalnetcreditcard' => 'CREDITCARD',
+ 'novalnetsepa' => 'DIRECT_DEBIT_SEPA',
+ 'novalnetprepayment' => 'INVOICE_START',
+ 'novalnetinvoice' => 'INVOICE_START',
+ 'novalnetsofort' => 'ONLINE_TRANSFER',
+ 'novalnetpaypal' => 'PAYPAL',
+ 'novalnetideal' => 'IDEAL',
+ 'novalneteps' => 'EPS',
+ 'novalnetcashpayment' => 'CASHPAYMENT',
+ 'novalnetgiropay' => 'GIROPAY',
+ 'novalnetprzelewy24' => 'PRZELEWY24',
+ ];
+ return $paymentType[$code];
+ }
+
+ /**
+ * Retrieves the original end-customer address with and without proxy
+ *
+ * @return string
+ *
+ * @author Novalnet AG
+ */
+ public function getRemoteAddress()
+ {
+ $ipKeys = ['HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR'];
+
+ foreach ($ipKeys as $key)
+ {
+ if (array_key_exists($key, $_SERVER) === true)
+ {
+ foreach (explode(',', $_SERVER[$key]) as $ip)
+ {
+ return $ip;
+ }
+ }
+ }
+ }
+
+ /**
+ * Forms comments for barzhalan nearest store details
+ *
+ * @param array $requestData
+ *
+ * @return string
+ *
+ * @author Novalnet AG
+ */
+ protected function getBarzahlenComments($requestData)
+ {
+ $storeCounts = 1;
+ foreach ($requestData as $sKey => $sValue)
+ {
+ if (strpos($sKey, 'nearest_store_street') !== false)
+ $storeCounts++;
+ }
+
+ $dueDate = !empty($requestData['cp_due_date']) ? $requestData['cp_due_date']: $requestData['due_date'];
+ $barzahlenComments = PHP_EOL. $this->fieldLabel('SlipExpiryDate') . date('d.m.Y', strtotime($dueDate));
+ if($storeCounts !=1)
+ $barzahlenComments .= PHP_EOL . $this->fieldLabel('CashpaymentStore') . PHP_EOL;
+
+ for ($i = 1; $i < $storeCounts; $i++)
+ {
+ $barzahlenComments .= $requestData['nearest_store_title_' . $i] . PHP_EOL;
+ $barzahlenComments .= $requestData['nearest_store_street_' . $i ] . PHP_EOL;
+ $barzahlenComments .= $requestData['nearest_store_city_' . $i ] . PHP_EOL;
+ $barzahlenComments .= $requestData['nearest_store_zipcode_' . $i ] . PHP_EOL;
+ $barzahlenComments .= $requestData['nearest_store_country_' . $i ].PHP_EOL;
+ $break = PHP_EOL;
+ if ( ($storeCounts -2) < $i )
+ $break ='';
+ }
+
+ return $barzahlenComments;
+ }
+
+ /**
+ * Is called when a payment provider sends a background notification to the shop.
+ *
+ * @param HTTPRequest $request Request data
+ *
+ * @return
+ *
+ * @author Novalnet AG
+ */
+ protected function processNotification(HTTPRequest $request) : void
+ {
+ $getRequestData = $request->postVars();
+ $callbackProcess = NovalnetCallback::create();
+ $callbackProcess->startProcess($getRequestData);
+ }
+}
diff --git a/src/Model/NovalnetGatewayTranslation.php b/src/Model/NovalnetGatewayTranslation.php
new file mode 100644
index 0000000..4511ef4
--- /dev/null
+++ b/src/Model/NovalnetGatewayTranslation.php
@@ -0,0 +1,60 @@
+ NovalnetGateway::class,
+ ];
+ /**
+ * DB table name
+ *
+ * @var string
+ */
+ private static $table_name = 'SilvercartPaymentNovalnetGatewayTranslation';
+
+ /**
+ * Returns the translated singular name of the object. If no translation exists
+ * the class name will be returned.
+ *
+ * @return string
+ */
+ public function singular_name() : string
+ {
+ return Tools::singular_name_for($this);
+ }
+
+ /**
+ * Returns the translated plural name of the object. If no translation exists
+ * the class name will be returned.
+ *
+ * @return string
+ */
+ public function plural_name() : string
+ {
+ return Tools::plural_name_for($this);
+ }
+
+}
diff --git a/src/Model/NovalnetTransaction.php b/src/Model/NovalnetTransaction.php
new file mode 100644
index 0000000..5bee0bb
--- /dev/null
+++ b/src/Model/NovalnetTransaction.php
@@ -0,0 +1,62 @@
+ 'Int',
+ 'OrderNumber' => 'Varchar(128)',
+ 'Tid' => 'Varchar(50)',
+ 'PaymentType' => 'Varchar(50)',
+ 'CustomerId' => 'Varchar(128)',
+ 'GatewayStatus' => 'Int'
+ ];
+
+ /**
+ * DB table name
+ *
+ * @var string
+ */
+ private static $table_name = 'SilvercartPaymentNovalnetTransaction';
+
+
+ /**
+ * update an order
+ *
+ * @param array $data array with order data
+ * @param array $status boolean
+ *
+ * @return void
+ *
+ */
+ public function updateOrder($data, $status = true) {
+ if ($status == false) {
+ $this->GatewayStatus = $data['tid_status'];
+ } else {
+ $this->OrderId = $data['orderId'];
+ $this->OrderNumber = $data['orderNumber'];
+ $this->Tid = $data['tid'];
+ $this->PaymentType = $data['paymentChannel'];
+ $this->GatewayStatus = $data['tid_status'];
+ $this->CustomerId = $data['customer_no'];
+ }
+ $this->write();
+ }
+}
diff --git a/templates/SilverCart/Email/Layout/PaymentNovalnetTransactionInfo.ss b/templates/SilverCart/Email/Layout/PaymentNovalnetTransactionInfo.ss
new file mode 100644
index 0000000..02b3b15
--- /dev/null
+++ b/templates/SilverCart/Email/Layout/PaymentNovalnetTransactionInfo.ss
@@ -0,0 +1,27 @@
+<% with $Order %>
+
+<%t SilverCart\Model\ShopEmail.ThankYouForYourOrder 'Thank you for your order at our shop.' %>
+
+
+
+
+
+
+ <%t SilverCart\Model\Pages\Page.ORDER_DATE 'Order date' %>: |
+ {$Created.Nice} |
+
+
+ <%t SilverCart\Model\Order\NumberRange.ORDERNUMBER 'Ordernumber' %>: |
+ {$OrderNumber} |
+
+
+
+<%t SilverCart\Model\ShopEmail.TRANSACTIONINFO 'Transaction Info' %>
+{$Message}
+
+<% end_with %>
+<% with $Message %>
+{$comments}
+<% end_with %>
+<%t SilverCart\Model\ShopEmail.REGARDS 'Best regards' %>,
+<%t SilverCart\Model\ShopEmail.YOUR_TEAM 'Your SilverCart ecommerce team' %>