Skip to content

Commit b9541d1

Browse files
PT-3173: Add Pay Now as Payment Method to JTL
1 parent 6457474 commit b9541d1

31 files changed

+740
-259
lines changed

AdminRender.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,17 @@ public function renderPage(string $tabName, JTLSmarty $smarty, $request): string
5555

5656
$smarty->assign('pluginURL', $this->plugin->getPaths()->getShopURL());
5757

58+
// Add JS script to Settings page for webhooks_secret field
59+
if ($tabName === 'Settings') {
60+
$jsUrl = $this->plugin->getPaths()->getAdminURL() . 'js/mondu_admin.js?v=3.0.8';
61+
$postUrl = Shop::getURL() . '/' . \PFAD_ADMIN . 'plugin.php?kPlugin=' . $this->plugin->getID();
62+
63+
pq('body')->append('
64+
<input type="hidden" name="mondu_post_url" id="mondu_post_url" value="' . $postUrl . '">
65+
<script type="text/javascript" src="' . $jsUrl . '"></script>
66+
');
67+
}
68+
5869
if ($tabName === 'Info') {
5970
return $smarty
6071
->assign('postUrl', Shop::getURL() . '/' . \PFAD_ADMIN . 'plugin.php?kPlugin=' . $this->plugin->getID())
@@ -93,6 +104,7 @@ private function handleRegisterWebhooksRequest()
93104
$response,
94105
Response::HTTP_UNPROCESSABLE_ENTITY
95106
);
107+
exit;
96108
}
97109
}
98110

Bootstrap.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,19 @@ public function prepareFrontend(LinkInterface $link, JTLSmarty $smarty): bool
8080
$routes = new RoutesService;
8181
$routes->frontEndRoutes($this->getPlugin());
8282

83+
// Register CSS and JS files via Smarty
84+
$pluginUrl = $this->getPlugin()->getPaths()->getBaseURL();
85+
$version = $this->getPlugin()->getMeta()->getVersion();
86+
87+
// Add CSS to head
88+
Shop::Smarty()->assign('monduPluginUrl', $pluginUrl);
89+
Shop::Smarty()->assign('monduPluginVersion', $version);
90+
91+
// Register in page header
92+
\pq('head')->append(
93+
'<link rel="stylesheet" href="' . $pluginUrl . 'frontend/css/style.css?v=' . $version . '" type="text/css" media="all">'
94+
);
95+
8396
return true;
8497
}
8598

Migrations/Migration20221130105300.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ class Migration20221130105300 extends Migration implements IMigration
99
{
1010
public function up()
1111
{
12+
// Clear all hint texts for Mondu payment methods instead of setting them
1213
$this->execute("
1314
UPDATE `tzahlungsartsprache` zs
14-
SET zs.`cHinweisTextShop` = 'Hinweise zur Verarbeitung Ihrer personenbezogenen Daten durch die Mondu GmbH finden Sie [url=https://www.mondu.ai/de/datenschutzgrundverordnung-kaeufer/]hier[/url].'
15-
WHERE zs.`cGebuehrname` = 'Mondu' and zs.`cISOSprache` IN ('ger', 'eng');
15+
SET zs.`cHinweisTextShop` = '', zs.`cHinweisText` = ''
16+
WHERE zs.`cGebuehrname` = 'Mondu';
1617
");
1718
}
1819

@@ -21,3 +22,4 @@ public function down()
2122

2223
}
2324
}
25+

README.md

Lines changed: 62 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ JTL 5 Integration plugin for Mondu Payment.
3535
2. Add following payment methods:
3636

3737
```
38-
Rechnungskauf - jetzt kaufen, später bezahlen
39-
SEPA-Lastschrift - jetzt kaufen, später per Bankeinzug bezahlen
40-
Ratenzahlung - Bequem in Raten per Bankeinzug zahlen
41-
Echtzeitüberweisung – Direkt von Ihrem Bankkonto bezahlen
38+
Ratenkauf (3, 6, 12 Monaten)
39+
SEPA-Lastschrift (30 Tage)
40+
Rechnungskauf (30 Tage)
41+
Echtzeitüberweisung
4242
```
4343

4444
**Note: In case Payment Method names are changed manually in the JTL Shop, please update accordingly in the JTL Wawi.**
@@ -50,10 +50,25 @@ Echtzeitüberweisung – Direkt von Ihrem Bankkonto bezahlen
5050
2. Select **Rechnungen** tab
5151
3. Select Rechnungen -> Erstellt -> Rechnungen_Erstellt workflow
5252
4. Configure condition with "One condition met" (Eine Bedingung erfüllt")
53-
1. Rechnungen\Auftrag\Zahlungsart\Name **Enthalt** Rechnungskauf - jetzt kaufen, später bezahlen
54-
2. Rechnungen\Auftrag\Zahlungsart\Name **Enthalt** SEPA-Lastschrift - jetzt kaufen, später per Bankeinzug bezahlen
55-
3. Rechnungen\Auftrag\Zahlungsart\Name **Enthalt** Ratenzahlung - Bequem in Raten per Bankeinzug zahlen
56-
4. Rechnungen\Auftrag\Zahlungsart\Name **Enthalt** Echtzeitüberweisung – Direkt von Ihrem Bankkonto bezahlen
53+
### New installation:
54+
Just create the workflows with the name of the payment methods which are mentioned in step 2 (add payment methods):
55+
* Ratenkauf (3, 6, 12 Monaten)
56+
* SEPA-Lastschrift (30 Tage)
57+
* Rechnungskauf (30 Tage)
58+
* Echtzeitüberweisung
59+
60+
### Update from existing installation:
61+
Update the conditions so that the rules will match for old AND new naming e.g.:
62+
* Ratenkauf (3, 6, 12 Monaten)
63+
* SEPA-Lastschrift (30 Tage)
64+
* Rechnungskauf (30 Tage)
65+
* Echtzeitüberweisung
66+
OR create additional rules so that all new payment methods will be covered
67+
68+
1. Rechnungen\Auftrag\Zahlungsart\Name **Enthalt** Rechnungskauf
69+
2. Rechnungen\Auftrag\Zahlungsart\Name **Enthalt** SEPA-Lastschrift
70+
3. Rechnungen\Auftrag\Zahlungsart\Name **Enthalt** Ratenkauf
71+
4. Rechnungen\Auftrag\Zahlungsart\Name **Enthalt** Echtzeitüberweisung
5772
5. Configure action
5873
1. Web-Request POST:
5974
1. URL:
@@ -76,10 +91,25 @@ Echtzeitüberweisung – Direkt von Ihrem Bankkonto bezahlen
7691
3. Select Rechnungen - Manuell, create new Event with "Ereignis anlegen" button
7792
4. Create new event
7893
4. Configure condition with "One condition met" (Eine Bedingung erfüllt")
79-
1. Auftrag\Zahlungsart\Name **Enthalt** Rechnungskauf - jetzt kaufen, später bezahlen
80-
2. Auftrag\Zahlungsart\Name **Enthalt** SEPA-Lastschrift - jetzt kaufen, später per Bankeinzug bezahlen
81-
3. Auftrag\Zahlungsart\Name **Enthalt** Ratenzahlung - Bequem in Raten per Bankeinzug zahlen
82-
4. Auftrag\Zahlungsart\Name **Enthalt** Echtzeitüberweisung – Direkt von Ihrem Bankkonto bezahlen
94+
### New installation:
95+
Just create the workflows with the name of the payment methods which are mentioned in step 2 (add payment methods):
96+
* Ratenkauf (3, 6, 12 Monaten)
97+
* SEPA-Lastschrift (30 Tage)
98+
* Rechnungskauf (30 Tage)
99+
* Echtzeitüberweisung
100+
101+
### Update from existing installation:
102+
Update the conditions so that the rules will match for old AND new naming e.g.:
103+
* Ratenkauf (3, 6, 12 Monaten)
104+
* SEPA-Lastschrift (30 Tage)
105+
* Rechnungskauf (30 Tage)
106+
* Echtzeitüberweisung
107+
OR create additional rules so that all new payment methods will be covered
108+
109+
1. Rechnungen\Auftrag\Zahlungsart\Name **Enthalt** Rechnungskauf
110+
2. Rechnungen\Auftrag\Zahlungsart\Name **Enthalt** SEPA-Lastschrift
111+
3. Rechnungen\Auftrag\Zahlungsart\Name **Enthalt** Ratenkauf
112+
4. Rechnungen\Auftrag\Zahlungsart\Name **Enthalt** Echtzeitüberweisung
83113
6. Configure action
84114
1. Web-Request POST:
85115
1. URL:
@@ -100,11 +130,26 @@ Echtzeitüberweisung – Direkt von Ihrem Bankkonto bezahlen
100130
1. Navigate to the Admin -> JTL-Workflows
101131
2. Select **Auftrage** tab
102132
3. Select Auftrag -> Storniert and create a workflow
103-
4. 4. Configure condition with "One condition met" (Eine Bedingung erfüllt")
104-
1. Zahlungen\Zahlungsart\Name **Enthalt** Rechnungskauf - jetzt kaufen, später bezahlen
105-
2. Zahlungen\Zahlungsart\Name **Enthalt** SEPA-Lastschrift - jetzt kaufen, später per Bankeinzug bezahlen
106-
3. Zahlungen\Zahlungsart\Name **Enthalt** Ratenzahlung - Bequem in Raten per Bankeinzug zahlen
107-
3. Zahlungen\Zahlungsart\Name **Enthalt** Echtzeitüberweisung – Direkt von Ihrem Bankkonto bezahlen
133+
4. Configure condition with "One condition met" (Eine Bedingung erfüllt")
134+
### New installation:
135+
Just create the workflows with the name of the payment methods which are mentioned in step 2 (add payment methods):
136+
* Ratenkauf (3, 6, 12 Monaten)
137+
* SEPA-Lastschrift (30 Tage)
138+
* Rechnungskauf (30 Tage)
139+
* Echtzeitüberweisung
140+
141+
### Update from existing installation:
142+
Update the conditions so that the rules will match for old AND new naming e.g.:
143+
* Ratenkauf (3, 6, 12 Monaten)
144+
* SEPA-Lastschrift (30 Tage)
145+
* Rechnungskauf (30 Tage)
146+
* Echtzeitüberweisung
147+
OR create additional rules so that all new payment methods will be covered
148+
149+
1. Rechnungen\Auftrag\Zahlungsart\Name **Enthalt** Rechnungskauf
150+
2. Rechnungen\Auftrag\Zahlungsart\Name **Enthalt** SEPA-Lastschrift
151+
3. Rechnungen\Auftrag\Zahlungsart\Name **Enthalt** Ratenkauf
152+
4. Rechnungen\Auftrag\Zahlungsart\Name **Enthalt** Echtzeitüberweisung
108153
5. Configure action
109154
1. Web-Request POST:
110155
1. URL:

Src/Controllers/Frontend/WebhookController.php

Lines changed: 112 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace Plugin\MonduPayment\Src\Controllers\Frontend;
44

55
use JTL\Shop;
6-
use Plugin\MonduPayment\PaymentMethod\MonduPayment;
76
use Plugin\MonduPayment\Src\Exceptions\DatabaseQueryException;
87
use Plugin\MonduPayment\Src\Helpers\Response;
98
use Plugin\MonduPayment\Src\Models\MonduInvoice;
@@ -13,10 +12,10 @@
1312
class WebhookController
1413
{
1514
public const MONDU_JTL_MAPPING = [
16-
MonduPayment::STATE_CONFIRMED => \BESTELLUNG_STATUS_BEZAHLT,
17-
MonduPayment::STATE_PENDING => \BESTELLUNG_STATUS_IN_BEARBEITUNG,
18-
MonduPayment::STATE_CANCELED => \BESTELLUNG_STATUS_STORNO,
19-
MonduPayment::STATE_DECLINED => \BESTELLUNG_STATUS_STORNO,
15+
'confirmed' => \BESTELLUNG_STATUS_BEZAHLT,
16+
'pending' => \BESTELLUNG_STATUS_IN_BEARBEITUNG,
17+
'canceled' => \BESTELLUNG_STATUS_STORNO,
18+
'declined' => \BESTELLUNG_STATUS_STORNO,
2019
];
2120

2221
private Request $request;
@@ -43,17 +42,26 @@ public function index()
4342
*/
4443
private function handleWebhook()
4544
{
46-
$requestData = $this->request->all();
47-
48-
switch ($requestData['topic']) {
49-
case 'order/confirmed':
50-
case 'order/declined':
51-
case 'order/pending':
52-
return $this->handleOrderStateChanged($requestData);
53-
case 'invoice/canceled':
54-
return $this->handleInvoiceStateChanged($requestData, 'canceled');
55-
default:
56-
return [['message' => 'Unregistered topic'], Response::HTTP_OK];
45+
try {
46+
$requestData = $this->request->all();
47+
48+
// Проверяем наличие topic
49+
if (!isset($requestData['topic'])) {
50+
return [['message' => 'Missing topic parameter', 'received_data' => $requestData], Response::HTTP_BAD_REQUEST];
51+
}
52+
53+
switch ($requestData['topic']) {
54+
case 'order/confirmed':
55+
case 'order/declined':
56+
case 'order/pending':
57+
return $this->handleOrderStateChanged($requestData);
58+
case 'invoice/canceled':
59+
return $this->handleInvoiceStateChanged($requestData, 'canceled');
60+
default:
61+
return [['message' => 'Unregistered topic: ' . $requestData['topic'], 'available_topics' => ['order/confirmed', 'order/declined', 'order/pending', 'invoice/canceled']], Response::HTTP_OK];
62+
}
63+
} catch (\Exception $e) {
64+
return [['message' => 'Error processing webhook', 'error' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR];
5765
}
5866
}
5967

@@ -75,13 +83,50 @@ public function handleOrderStateChanged($requestData)
7583
return [['message' => 'Order not found'], Response::HTTP_NOT_FOUND];
7684
}
7785

78-
$this->monduOrder->update(['state' => $params['order_state']], $monduOrder->id);
86+
// If order found via fallback (id is null), create mondu_orders record
87+
if (empty($monduOrder->id)) {
88+
$monduOrderData = [
89+
'order_id' => $monduOrder->order_id,
90+
'external_reference_id' => $monduOrder->external_reference_id,
91+
'order_uuid' => $requestData['order_uuid'] ?? null,
92+
'state' => $params['order_state']
93+
];
94+
95+
// Use direct SQL insert
96+
$pdo = new \PDO(
97+
'mysql:host=' . DB_HOST . ';dbname=' . DB_NAME,
98+
DB_USER,
99+
DB_PASS
100+
);
101+
102+
$stmt = $pdo->prepare("
103+
INSERT INTO mondu_orders
104+
(order_id, external_reference_id, order_uuid, state, created_at, updated_at)
105+
VALUES (?, ?, ?, ?, NOW(), NOW())
106+
");
107+
108+
$stmt->execute([
109+
$monduOrderData['order_id'],
110+
$monduOrderData['external_reference_id'],
111+
$monduOrderData['order_uuid'],
112+
$monduOrderData['state']
113+
]);
114+
115+
$newId = $pdo->lastInsertId();
116+
117+
// Update monduOrder with new ID
118+
$monduOrder->id = $newId;
119+
$monduOrder->order_uuid = $monduOrderData['order_uuid'];
120+
} else {
121+
// Update existing record
122+
$this->monduOrder->update(['state' => $params['order_state']], $monduOrder->id);
123+
}
79124

80125
if (isset(self::MONDU_JTL_MAPPING[$params['order_state']])) {
81126
$this->updateOrderStatus($monduOrder, self::MONDU_JTL_MAPPING[$params['order_state']]);
82127
}
83128

84-
if ($params['order_state'] === MonduPayment::STATE_CONFIRMED) {
129+
if ($params['order_state'] === 'confirmed') {
85130
$this->unlockOrderForWawiSync($monduOrder);
86131
}
87132

@@ -91,7 +136,6 @@ public function handleOrderStateChanged($requestData)
91136
/**
92137
* @param $requestData
93138
* @param $state
94-
*
95139
* @return array
96140
*/
97141
public function handleInvoiceStateChanged($requestData, $state): array
@@ -122,7 +166,55 @@ public function handleInvoiceStateChanged($requestData, $state): array
122166
*/
123167
private function getOrder($orderUuid)
124168
{
125-
return $this->monduOrder->select('id', 'order_uuid', 'order_id')->where('external_reference_id', $orderUuid)->first()[0];
169+
// Try to find in mondu_orders by external_reference_id
170+
$query = $this->monduOrder->select('id', 'order_uuid', 'order_id')->where('external_reference_id', $orderUuid);
171+
$result = $query->first();
172+
173+
if (is_array($result) && isset($result[0])) {
174+
return $result[0];
175+
}
176+
177+
// Fallback: Search in tbestellung
178+
$jtlOrderId = null;
179+
180+
// Extract JTL order ID from external_reference_id (e.g. "JTL5-10005" -> 10005)
181+
if (preg_match('/^[A-Z0-9]+-(\d+)$/', $orderUuid, $matches)) {
182+
$jtlOrderId = (int)$matches[1];
183+
} elseif (is_numeric($orderUuid)) {
184+
$jtlOrderId = (int)$orderUuid;
185+
}
186+
187+
// Search in tbestellung
188+
try {
189+
$pdo = new \PDO(
190+
'mysql:host=' . DB_HOST . ';dbname=' . DB_NAME . ';charset=utf8mb4',
191+
DB_USER,
192+
DB_PASS,
193+
[\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]
194+
);
195+
196+
// Search by cBestellNr first (most reliable), then by kBestellung
197+
$stmt = $pdo->prepare("SELECT kBestellung FROM tbestellung WHERE cBestellNr = ? OR kBestellung = ?");
198+
$stmt->execute([$orderUuid, $jtlOrderId ?: 0]);
199+
200+
$jtlOrder = $stmt->fetch(\PDO::FETCH_ASSOC);
201+
202+
if ($jtlOrder) {
203+
// Return a compatible format (object with order_id property)
204+
$compatibleResult = new \stdClass();
205+
$compatibleResult->id = null; // No mondu_orders record yet
206+
$compatibleResult->order_id = $jtlOrder['kBestellung'];
207+
$compatibleResult->order_uuid = null;
208+
$compatibleResult->external_reference_id = $orderUuid;
209+
210+
return $compatibleResult;
211+
}
212+
213+
} catch (\Exception $e) {
214+
// Silent fail
215+
}
216+
217+
return null;
126218
}
127219

128220
/**

Src/Services/ConfigService.php

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -72,16 +72,6 @@ public function getPaymentMethodNameVisible()
7272
return $this->config->getValue('payment_method_name_visible');
7373
}
7474

75-
public function getNetTermTitle()
76-
{
77-
return $this->config->getValue('net_term_title');
78-
}
79-
80-
public function getNetTermDescription()
81-
{
82-
return $this->config->getValue('net_term_description');
83-
}
84-
8575
public function getPaymentMethodByKPlugin($kPlugin)
8676
{
8777
return $this->config->getValue($kPlugin . '_payment_method');
@@ -115,8 +105,7 @@ public function getBenefitsText()
115105
return [
116106
'invoice' => $this->getConfigurationDescription('invoice_benefits'),
117107
'direct_debit' => $this->getConfigurationDescription('sepa_benefits'),
118-
'installment' => $this->getConfigurationDescription('installments_benefits'),
119-
'pay_now' => $this->getConfigurationDescription('pay_now_benefits'),
108+
'installment' => $this->getConfigurationDescription('installments_benefits')
120109
];
121110

122111
}

Src/Support/HttpClients/MonduClient.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,14 @@ public function registerWebhooks(array $data = []): ?array
138138
return $this->client->post('webhooks', $data);
139139
} catch (InvalidRequestException $e) {
140140
$this->logEvent($e);
141-
return ['error' => true];
141+
$exceptionData = $e->getExceptionData();
142+
$responseBody = json_decode($exceptionData->response_body, true);
143+
144+
return [
145+
'error' => true,
146+
'message' => $responseBody['detail'] ?? $responseBody['message'] ?? 'Unknown error',
147+
'status_code' => $exceptionData->response_code
148+
];
142149
}
143150
}
144151

0 commit comments

Comments
 (0)