strapi-plugin-payone-provider 1.5.8 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/admin/src/components/Initializer/index.jsx +16 -0
  2. package/admin/src/components/PluginIcon/index.jsx +6 -0
  3. package/admin/src/index.js +3 -3
  4. package/admin/src/pages/App/components/AppHeader.jsx +55 -0
  5. package/admin/src/pages/App/components/AppTabs.jsx +158 -0
  6. package/admin/src/pages/App/components/ApplePayBtn.jsx +304 -0
  7. package/admin/src/pages/App/components/ApplePayButton.js +139 -93
  8. package/admin/src/pages/App/components/ApplePayButton.jsx +908 -0
  9. package/admin/src/pages/App/components/ApplePayConfig.jsx +298 -0
  10. package/admin/src/pages/App/components/ApplePayConfigPanel.jsx +81 -0
  11. package/admin/src/pages/App/components/ConfigurationPanel.jsx +280 -0
  12. package/admin/src/pages/App/components/DocsPanel.jsx +1057 -0
  13. package/admin/src/pages/App/components/GooglePayConfig.jsx +217 -0
  14. package/admin/src/pages/App/components/GooglePayConfigPanel.jsx +82 -0
  15. package/admin/src/pages/App/components/GooglePaybutton.jsx +300 -0
  16. package/admin/src/pages/App/components/HistoryPanel.jsx +285 -0
  17. package/admin/src/pages/App/components/PaymentActionsPanel.jsx +238 -0
  18. package/admin/src/pages/App/components/StatusBadge.jsx +24 -0
  19. package/admin/src/pages/App/components/TransactionHistoryItem.jsx +377 -0
  20. package/admin/src/pages/App/components/icons/BankIcon.jsx +10 -0
  21. package/admin/src/pages/App/components/icons/ChevronDownIcon.jsx +9 -0
  22. package/admin/src/pages/App/components/icons/ChevronUpIcon.jsx +9 -0
  23. package/admin/src/pages/App/components/icons/CreditCardIcon.jsx +9 -0
  24. package/admin/src/pages/App/components/icons/ErrorIcon.jsx +10 -0
  25. package/admin/src/pages/App/components/icons/InfoIcon.jsx +9 -0
  26. package/admin/src/pages/App/components/icons/PaymentIcon.jsx +10 -0
  27. package/admin/src/pages/App/components/icons/PendingIcon.jsx +9 -0
  28. package/admin/src/pages/App/components/icons/PersonIcon.jsx +9 -0
  29. package/admin/src/pages/App/components/icons/SuccessIcon.jsx +9 -0
  30. package/admin/src/pages/App/components/icons/WalletIcon.jsx +9 -0
  31. package/admin/src/pages/App/components/icons/index.jsx +11 -0
  32. package/admin/src/pages/App/components/paymentActions/AuthorizationForm.jsx +205 -0
  33. package/admin/src/pages/App/components/paymentActions/CaptureForm.jsx +65 -0
  34. package/admin/src/pages/App/components/paymentActions/CardDetailsInput.jsx +191 -0
  35. package/admin/src/pages/App/components/paymentActions/PaymentMethodSelector.jsx +236 -0
  36. package/admin/src/pages/App/components/paymentActions/PaymentResult.jsx +148 -0
  37. package/admin/src/pages/App/components/paymentActions/PreauthorizationForm.jsx +132 -0
  38. package/admin/src/pages/App/components/paymentActions/RefundForm.jsx +90 -0
  39. package/admin/src/pages/App/index.jsx +127 -0
  40. package/admin/src/pages/constants/paymentConstants.js +1 -2
  41. package/admin/src/pages/hooks/usePaymentActions.js +96 -0
  42. package/package.json +2 -2
  43. package/server/bootstrap.js +65 -0
  44. package/server/controllers/payone.js +4 -48
  45. package/server/routes/index.js +1 -1
  46. package/server/services/applePayService.js +51 -407
  47. package/server/services/paymentService.js +0 -68
  48. package/server/services/payone.js +0 -3
  49. package/server/services/settingsService.js +0 -21
  50. package/server/services/testConnectionService.js +0 -14
  51. package/server/services/transactionService.js +14 -0
  52. package/server/utils/paymentMethodParams.js +60 -27
  53. package/server/utils/requestBuilder.js +0 -22
@@ -6,133 +6,48 @@ const { getSettings, validateSettings } = require("./settingsService");
6
6
 
7
7
  const POST_GATEWAY_URL = "https://api.pay1.de/post-gateway/";
8
8
 
9
- /**
10
- * Initialize Apple Pay session with Payone
11
- * According to Payone documentation:
12
- * https://docs.payone.com/payment-methods/apple-pay/apple-pay-without-dev
13
- *
14
- * Request: genericpayment
15
- * Required parameters:
16
- * - request="genericpayment"
17
- * - clearingtype="wlt"
18
- * - wallettype="APL"
19
- * - add_paydata[action]="init_applepay_session"
20
- * - add_paydata[display_name]="Store Name"
21
- * - add_paydata[domain_name]="yourdomain.com"
22
- */
9
+ const parseResponse = (responseData) => {
10
+ if (typeof responseData === 'string') {
11
+ const params = new URLSearchParams(responseData);
12
+ const parsed = {};
13
+ for (const [key, value] of params.entries()) {
14
+ parsed[key] = value;
15
+ }
16
+ return parsed;
17
+ }
18
+ return responseData;
19
+ };
20
+
23
21
  const initializeApplePaySession = async (strapi, params) => {
24
22
  try {
25
- strapi.log.info("[Apple Pay] Initializing Apple Pay session with Payone");
26
- strapi.log.info("[Apple Pay] Request params:", JSON.stringify(params, null, 2));
27
-
28
23
  const settings = await getSettings(strapi);
24
+ const { displayName, domainName } = params;
29
25
 
30
- if (!validateSettings(settings)) {
31
- strapi.log.error("[Apple Pay] Payone settings not configured");
32
- throw new Error("Payone settings not configured");
33
- }
34
-
35
- strapi.log.info("[Apple Pay] Settings loaded:", {
36
- mode: settings.mode,
37
- mid: settings.mid,
38
- portalid: settings.portalid,
39
- hasKey: !!settings.key
40
- });
41
-
42
- const {
43
- displayName,
44
- domainName,
45
- mid,
46
- portalid
47
- } = params;
26
+ const merchantName = displayName || "Store";
27
+ const domain = domainName;
48
28
 
49
- // Get merchant data from settings (test or live mode)
50
- const merchantName = displayName || settings.merchantName || settings.displayName || "Test Store";
51
- const merchantId = mid || settings.mid || settings.merchantIdentifier;
52
- const portalId = portalid || settings.portalid;
53
- const accountId = settings.aid;
54
- const apiKey = settings.key;
55
- const mode = settings.mode || "test"; // test or live
56
-
57
- // Get domain from params or settings or server config
58
- const domain = domainName || settings.domainName ||
59
- (strapi.config.get("server.url") ? new URL(strapi.config.get("server.url")).hostname : null) ||
60
- "localhost";
61
-
62
- // Build request parameters for Apple Pay session initialization
63
- // According to Payone documentation: request="genericpayment"
64
- const requestParams = {
29
+ const baseParams = {
65
30
  request: "genericpayment",
66
- mid: merchantId,
67
- aid: accountId,
68
- portalid: portalId,
69
- key: apiKey,
70
- mode: mode, // Use test or live mode from settings
71
31
  clearingtype: "wlt",
72
32
  wallettype: "APL",
73
- currency: "EUR", // Default, can be overridden
33
+ currency: params.currency,
74
34
  "add_paydata[action]": "init_applepay_session",
75
35
  "add_paydata[display_name]": merchantName,
76
36
  "add_paydata[domain_name]": domain
77
37
  };
78
38
 
79
- strapi.log.info("[Apple Pay] Sending request to Payone:", {
80
- url: POST_GATEWAY_URL,
81
- mode: mode,
82
- merchantName: merchantName,
83
- domain: domain,
84
- merchantId: merchantId,
85
- portalId: portalId
86
- });
39
+ const requestParams = buildClientRequestParams(settings, baseParams, strapi.log);
87
40
 
88
41
  const formData = toFormData(requestParams);
89
-
90
- strapi.log.info("[Apple Pay] Sending request to Payone API:", {
91
- url: POST_GATEWAY_URL,
92
- params: {
93
- ...requestParams,
94
- key: "***HIDDEN***" // Hide API key in logs
95
- }
42
+ const response = await axios.post(`${POST_GATEWAY_URL}Genericpayment`, formData, {
43
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
44
+ timeout: 30000
96
45
  });
97
46
 
98
- let response;
99
- try {
100
- response = await axios.post(POST_GATEWAY_URL, formData, {
101
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
102
- timeout: 30000
103
- });
104
- } catch (axiosError) {
105
- strapi.log.error("[Apple Pay] Payone API request failed:", {
106
- message: axiosError.message,
107
- status: axiosError.response?.status,
108
- statusText: axiosError.response?.statusText,
109
- data: axiosError.response?.data,
110
- config: {
111
- url: axiosError.config?.url,
112
- method: axiosError.config?.method
113
- }
114
- });
115
- throw axiosError;
116
- }
117
-
118
- strapi.log.info("[Apple Pay] Payone response received:", {
119
- status: response.status,
120
- statusText: response.statusText,
121
- headers: response.headers
122
- });
123
-
124
- // Parse response
125
- const responseData = parseResponse(response.data, strapi.log);
126
-
127
- strapi.log.info("[Apple Pay] Session initialization response:", JSON.stringify(responseData, null, 2));
128
- strapi.log.info("[Apple Pay] Response status:", responseData.status || responseData.Status || "NOT_SET");
129
- strapi.log.info("[Apple Pay] Response errorcode:", responseData.errorcode || responseData.ErrorCode || responseData.error_code || "none");
130
- strapi.log.info("[Apple Pay] Response errormessage:", responseData.errormessage || responseData.ErrorMessage || responseData.errortxt || responseData.ErrorTxt || responseData.error_message || "none");
131
- strapi.log.info("[Apple Pay] All response keys:", Object.keys(responseData));
132
- strapi.log.info("[Apple Pay] Full response for debugging:", JSON.stringify(responseData));
47
+ const responseData = parseResponse(response.data);
133
48
 
134
49
  if (responseData.errorcode || responseData.ErrorCode) {
135
- strapi.log.warn("[Apple Pay] Response contains error:", {
50
+ strapi.log.error("[Apple Pay] Payone error:", {
136
51
  errorcode: responseData.errorcode || responseData.ErrorCode,
137
52
  errormessage: responseData.errormessage || responseData.ErrorMessage
138
53
  });
@@ -149,322 +64,51 @@ const initializeApplePaySession = async (strapi, params) => {
149
64
  }
150
65
  };
151
66
 
152
- /**
153
- * Validate Apple Pay merchant with Payone
154
- * This is called when Apple Pay requests merchant validation
155
- */
156
67
  const validateApplePayMerchant = async (strapi, params) => {
157
- try {
158
- strapi.log.info("[Apple Pay] Validating merchant with Payone");
159
- strapi.log.info("[Apple Pay] Validation params:", JSON.stringify({
160
- validationURL: params.validationURL,
161
- domain: params.domain,
162
- displayName: params.displayName,
163
- mid: params.mid,
164
- portalid: params.portalid
165
- }, null, 2));
166
-
167
- const settings = await getSettings(strapi);
168
-
169
- if (!validateSettings(settings)) {
170
- strapi.log.error("[Apple Pay] Payone settings not configured for merchant validation");
171
- throw new Error("Payone settings not configured");
172
- }
173
-
174
- const {
175
- validationURL,
176
- mid,
177
- portalid,
178
- domain,
179
- displayName
180
- } = params;
181
-
182
- // Get merchant data from settings (test or live mode)
183
- const merchantName = displayName || settings.merchantName || settings.displayName || "Test Store";
184
- const merchantId = mid || settings.mid || settings.merchantIdentifier;
185
- const portalId = portalid || settings.portalid;
68
+ const settings = await getSettings(strapi);
186
69
 
187
- // Get domain from params or settings or server config
188
- const domainName = domain || settings.domainName ||
189
- (strapi.config.get("server.url") ? new URL(strapi.config.get("server.url")).hostname : null) ||
190
- "localhost";
70
+ if (!validateSettings(settings)) {
71
+ strapi.log.error("Payone settings not configured");
72
+ return null;
73
+ }
191
74
 
192
- // For Payone integration without developer account,
193
- // Payone handles merchant validation
194
- // We need to initialize the session first
195
- const sessionParams = {
196
- displayName: merchantName,
197
- domainName: domainName,
198
- mid: merchantId,
199
- portalid: portalId
200
- };
75
+ const sessionResponse = await initializeApplePaySession(strapi, params);
76
+ const applePaySessionBase64 = sessionResponse["add_paydata[applepay_payment_session]"] ||
77
+ sessionResponse.add_paydata?.applepay_payment_session;
201
78
 
202
- strapi.log.info("[Apple Pay] Initializing session with params:", JSON.stringify(sessionParams, null, 2));
79
+ if (sessionResponse.status === "OK" && applePaySessionBase64 && applePaySessionBase64.length > 0) {
80
+ const merchantSessionJson = Buffer.from(applePaySessionBase64, 'base64').toString('utf-8');
81
+ const merchantSession = JSON.parse(merchantSessionJson);
203
82
 
204
- // Initialize Apple Pay session with Payone
205
- let sessionResponse;
206
- try {
207
- sessionResponse = await initializeApplePaySession(strapi, sessionParams);
208
- } catch (error) {
209
- strapi.log.error("[Apple Pay] Failed to initialize session with Payone:", {
210
- message: error.message,
211
- status: error.response?.status,
212
- data: error.response?.data,
213
- stack: error.stack
214
- });
215
- // DO NOT return empty object - throw error instead
216
- // Empty object causes Apple Pay to close dialog without proper error message
217
- throw new Error(`Failed to initialize Apple Pay session with Payone: ${error.message}. Please check your Payone configuration and ensure Apple Pay is properly set up in PMI.`);
83
+ if (merchantSession.epochTimestamp && merchantSession.epochTimestamp > 1000000000000) {
84
+ merchantSession.epochTimestamp = Math.floor(merchantSession.epochTimestamp / 1000);
218
85
  }
219
86
 
220
- strapi.log.info("[Apple Pay] Session initialization result:", {
221
- status: sessionResponse.status || sessionResponse.Status,
222
- hasMerchantIdentifier: !!(sessionResponse.merchantIdentifier || sessionResponse.merchantSessionIdentifier),
223
- hasApplePaySession: !!(sessionResponse["add_paydata[applepay_payment_session]"] ||
224
- sessionResponse["add_paydata[applepay_payment_session]"] ||
225
- sessionResponse.add_paydata?.applepay_payment_session),
226
- fullResponse: JSON.stringify(sessionResponse, null, 2)
227
- });
228
-
229
- // If session initialization is successful, extract merchant session from Payone response
230
- // Payone returns: add_paydata[applepay_payment_session] = BASE64 encoded merchant session
231
- // Check for both uppercase and lowercase status
232
- const responseStatus = sessionResponse.status || sessionResponse.Status;
233
- if (responseStatus === "APPROVED" || responseStatus === "OK" ||
234
- responseStatus === "approved" || responseStatus === "ok") {
235
-
236
- // Extract BASE64 encoded merchant session from Payone response
237
- // Payone returns it in: add_paydata[applepay_payment_session]
238
- // Try all possible variations of the field name
239
- const applePaySessionBase64 =
240
- sessionResponse["add_paydata[applepay_payment_session]"] ||
241
- sessionResponse["add_paydata[applepay_payment_session]"] ||
242
- sessionResponse["add_paydata_applepay_payment_session"] ||
243
- sessionResponse.add_paydata?.applepay_payment_session ||
244
- sessionResponse.add_paydata?.["applepay_payment_session"] ||
245
- sessionResponse["addPaydata[applepay_payment_session]"] ||
246
- sessionResponse["addPaydata_applepay_payment_session"] ||
247
- null;
248
-
249
- strapi.log.info("[Apple Pay] Checking for merchant session in response:", {
250
- hasWorkorderid: !!sessionResponse.workorderid,
251
- workorderid: sessionResponse.workorderid,
252
- allKeys: Object.keys(sessionResponse).filter(k => k.includes('applepay') || k.includes('session') || k.includes('paydata')),
253
- responseKeys: Object.keys(sessionResponse)
254
- });
255
-
256
- strapi.log.info("[Apple Pay] Extracted Apple Pay session data:", {
257
- hasBase64Session: !!applePaySessionBase64,
258
- sessionLength: applePaySessionBase64?.length,
259
- workorderid: sessionResponse.workorderid,
260
- allResponseKeys: Object.keys(sessionResponse),
261
- responseSample: JSON.stringify(sessionResponse).substring(0, 500)
262
- });
263
-
264
- if (applePaySessionBase64 && applePaySessionBase64.length > 0) {
265
- try {
266
- // Decode BASE64 merchant session
267
- const merchantSessionJson = Buffer.from(applePaySessionBase64, 'base64').toString('utf-8');
268
- const merchantSession = JSON.parse(merchantSessionJson);
269
-
270
- strapi.log.info("[Apple Pay] Decoded merchant session:", {
271
- merchantIdentifier: merchantSession.merchantIdentifier,
272
- domainName: merchantSession.domainName,
273
- displayName: merchantSession.displayName,
274
- hasEpochTimestamp: !!merchantSession.epochTimestamp,
275
- hasExpiresAt: !!merchantSession.expiresAt,
276
- fullSession: merchantSession
277
- });
278
-
279
- // Validate decoded merchant session
280
- if (!merchantSession.merchantIdentifier) {
281
- strapi.log.warn("[Apple Pay] Decoded merchant session missing merchantIdentifier, using fallback");
282
- // Use fallback merchant identifier
283
- merchantSession.merchantIdentifier = settings.merchantIdentifier ||
284
- settings.mid ||
285
- settings.portalid ||
286
- `merchant.${domainName}`;
287
- }
288
-
289
- // Ensure epochTimestamp and expiresAt are in seconds (not milliseconds)
290
- if (merchantSession.epochTimestamp && merchantSession.epochTimestamp > 1000000000000) {
291
- // If timestamp is in milliseconds, convert to seconds
292
- merchantSession.epochTimestamp = Math.floor(merchantSession.epochTimestamp / 1000);
293
- }
294
- if (merchantSession.expiresAt && merchantSession.expiresAt > 1000000000000) {
295
- // If timestamp is in milliseconds, convert to seconds
296
- merchantSession.expiresAt = Math.floor(merchantSession.expiresAt / 1000);
297
- }
298
-
299
- // Validate final merchant session
300
- if (!merchantSession.merchantIdentifier || merchantSession.merchantIdentifier === 'undefined' || merchantSession.merchantIdentifier === 'null') {
301
- throw new Error("Decoded merchant session has invalid merchantIdentifier");
302
- }
303
-
304
- strapi.log.info("[Apple Pay] Validated merchant session:", {
305
- merchantIdentifier: merchantSession.merchantIdentifier,
306
- domainName: merchantSession.domainName,
307
- epochTimestamp: merchantSession.epochTimestamp,
308
- expiresAt: merchantSession.expiresAt
309
- });
310
-
311
- return merchantSession;
312
- } catch (decodeError) {
313
- strapi.log.error("[Apple Pay] Failed to decode merchant session:", {
314
- error: decodeError.message,
315
- base64Length: applePaySessionBase64?.length,
316
- base64Preview: applePaySessionBase64?.substring(0, 100)
317
- });
318
-
319
- // If decoding fails, we cannot proceed - merchant session is invalid
320
- throw new Error(`Failed to decode Apple Pay merchant session: ${decodeError.message}`);
321
- }
322
- } else {
323
- // CRITICAL: If Payone doesn't return merchant session, we cannot proceed
324
- // Apple Pay requires the merchant session to be validated by Apple's servers
325
- // Payone should return add_paydata[applepay_payment_session] after successful initialization
326
- strapi.log.error("[Apple Pay] CRITICAL: No Apple Pay session data in Payone response!");
327
- strapi.log.error("[Apple Pay] This means merchant validation will fail. Possible causes:");
328
- strapi.log.error("[Apple Pay] 1. Apple Pay not properly configured in Payone PMI");
329
- strapi.log.error("[Apple Pay] 2. Domain not verified in Payone PMI");
330
- strapi.log.error("[Apple Pay] 3. Merchant identifier not configured in Payone PMI");
331
- strapi.log.error("[Apple Pay] 4. Apple Pay onboarding not completed in Payone PMI");
332
- strapi.log.error("[Apple Pay] Response status:", responseStatus);
333
- strapi.log.error("[Apple Pay] Full response keys:", Object.keys(sessionResponse));
334
- strapi.log.error("[Apple Pay] Response sample:", JSON.stringify(sessionResponse).substring(0, 1000));
335
-
336
- // DO NOT create a fallback session - it will fail validation
337
- // Instead, throw an error so the frontend knows validation failed
338
- throw new Error("Payone did not return Apple Pay merchant session. Please ensure Apple Pay is properly configured in Payone Merchant Interface (PMI): CONFIGURATION → PAYMENT PORTALS → [Your Portal] → Apple Pay configuration. The merchant session must come from Payone after successful Apple Pay onboarding.");
339
-
340
- // Get merchant identifier from settings
341
- // According to Payone docs, merchant identifier should be visible in PMI after onboarding
342
- // Path: CONFIGURATION/PAYMENT PORTALS - choose an onboarded Portal - Payment type configuration tab
343
- let merchantIdentifier = settings.merchantIdentifier ||
344
- settings.mid ||
345
- settings.portalid;
346
-
347
- // If still no merchant identifier, try to construct one from domain
348
- // But this is not ideal - merchant identifier should come from Payone PMI
349
- if (!merchantIdentifier) {
350
- strapi.log.warn("[Apple Pay] No merchant identifier found in settings, using domain-based fallback");
351
- merchantIdentifier = `merchant.${domainName}`;
352
- }
353
-
354
- // Ensure merchant identifier is a string and not empty
355
- merchantIdentifier = merchantIdentifier.toString().trim();
356
- if (!merchantIdentifier || merchantIdentifier === 'undefined' || merchantIdentifier === 'null') {
357
- strapi.log.error("[Apple Pay] Invalid merchant identifier:", merchantIdentifier);
358
- throw new Error("Merchant identifier is invalid. Please configure a valid merchant identifier in Payone Merchant Interface (PMI) after Apple Pay onboarding. Path: CONFIGURATION → PAYMENT PORTALS → [Your Portal] → Payment type configuration tab");
359
- }
360
-
361
- // Create a valid merchant session object
362
- // This format is required by Apple Pay Payment Request API
363
- // IMPORTANT: epochTimestamp and expiresAt must be in seconds (Unix timestamp), not milliseconds
364
- const merchantSession = {
365
- epochTimestamp: Math.floor(Date.now() / 1000), // Unix timestamp in seconds
366
- expiresAt: Math.floor((Date.now() + (5 * 60 * 1000)) / 1000), // 5 minutes from now, in seconds
367
- merchantSessionIdentifier: `merchant.${domainName}`,
368
- nonce: generateNonce(),
369
- merchantIdentifier: merchantIdentifier, // Already validated and converted to string
370
- domainName: domainName,
371
- displayName: merchantName
372
- };
373
-
374
- // Validate merchant session before returning
375
- if (!merchantSession.merchantIdentifier || merchantSession.merchantIdentifier === 'undefined' || merchantSession.merchantIdentifier === 'null') {
376
- strapi.log.error("[Apple Pay] Created merchant session is missing or invalid merchantIdentifier!", {
377
- merchantIdentifier: merchantSession.merchantIdentifier,
378
- settings: {
379
- hasMerchantIdentifier: !!settings.merchantIdentifier,
380
- hasMid: !!settings.mid,
381
- hasPortalid: !!settings.portalid,
382
- mid: settings.mid,
383
- portalid: settings.portalid
384
- }
385
- });
386
- throw new Error("Merchant identifier is required but not found in settings. Please configure merchant identifier in Payone Merchant Interface (PMI) after Apple Pay onboarding.");
387
- }
388
-
389
- strapi.log.info("[Apple Pay] Created merchant session from settings:", {
390
- merchantIdentifier: merchantSession.merchantIdentifier,
391
- domainName: merchantSession.domainName,
392
- displayName: merchantSession.displayName,
393
- epochTimestamp: merchantSession.epochTimestamp,
394
- expiresAt: merchantSession.expiresAt
395
- });
396
-
397
- return merchantSession;
398
- }
87
+ if (merchantSession.expiresAt && merchantSession.expiresAt > 1000000000000) {
88
+ merchantSession.expiresAt = Math.floor(merchantSession.expiresAt / 1000);
399
89
  }
400
90
 
401
- // If initialization failed, we cannot proceed
402
- // Payment Request API requires a valid merchant session
403
- strapi.log.error("[Apple Pay] Session initialization failed - status:", responseStatus);
404
- strapi.log.error("[Apple Pay] Full Payone response:", JSON.stringify(sessionResponse, null, 2));
405
- strapi.log.error("[Apple Pay] This means merchant validation will fail.");
406
- strapi.log.error("[Apple Pay] Possible causes:");
407
- strapi.log.error("[Apple Pay] 1. Payone returned ERROR status - check errorcode and errormessage in response");
408
- strapi.log.error("[Apple Pay] 2. Apple Pay not configured in Payone PMI");
409
- strapi.log.error("[Apple Pay] 3. Domain not verified in Payone PMI");
410
- strapi.log.error("[Apple Pay] 4. Merchant identifier not configured correctly");
411
- strapi.log.error("[Apple Pay] 5. Apple Pay onboarding not completed");
412
-
413
- // Extract error details from Payone response
414
- const errorCode = sessionResponse.errorcode || sessionResponse.ErrorCode;
415
- const errorMessage = sessionResponse.errormessage || sessionResponse.ErrorMessage || sessionResponse.errortxt || sessionResponse.ErrorTxt;
416
-
417
- if (errorCode || errorMessage) {
418
- strapi.log.error("[Apple Pay] Payone error details:", {
419
- errorCode: errorCode,
420
- errorMessage: errorMessage
421
- });
422
- throw new Error(`Payone Apple Pay initialization failed: ${errorCode ? `Error ${errorCode}` : ''} ${errorMessage || 'Unknown error'}. Please check your Payone Apple Pay configuration in PMI (CONFIGURATION → PAYMENT PORTALS → [Your Portal] → Apple Pay).`);
423
- } else {
424
- throw new Error(`Apple Pay session initialization failed with status: ${responseStatus || 'UNKNOWN'}. Please check your Payone Apple Pay configuration in PMI (CONFIGURATION → PAYMENT PORTALS → [Your Portal] → Apple Pay).`);
91
+ if (!merchantSession.merchantIdentifier ||
92
+ merchantSession.merchantIdentifier === 'undefined' ||
93
+ merchantSession.merchantIdentifier === 'null') {
94
+ strapi.log.error("Decoded merchant session has invalid merchantIdentifier");
425
95
  }
426
- } catch (error) {
427
- strapi.log.error("[Apple Pay] Merchant validation error:", {
428
- message: error.message,
429
- stack: error.stack,
430
- response: error.response?.data,
431
- status: error.response?.status
432
- });
433
96
 
434
- // DO NOT return empty object - this causes Apple Pay to close the dialog
435
- // Instead, re-throw the error so the frontend can handle it properly
436
- // The error message will help the user understand what went wrong
437
- throw error;
97
+ return merchantSession;
438
98
  }
439
- };
440
99
 
441
- /**
442
- * Parse Payone response
443
- */
444
- const parseResponse = (responseData, logger) => {
445
- if (typeof responseData === 'string') {
446
- // Parse form-encoded response
447
- const params = new URLSearchParams(responseData);
448
- const parsed = {};
449
- for (const [key, value] of params.entries()) {
450
- parsed[key] = value;
451
- }
452
- return parsed;
453
- }
454
- return responseData;
455
- };
100
+ const errorCode = sessionResponse.errorcode || sessionResponse.ErrorCode;
101
+ const errorMessage = sessionResponse.errormessage || sessionResponse.ErrorMessage ||
102
+ sessionResponse.errortxt || sessionResponse.ErrorTxt;
103
+
104
+ strapi.log.error(
105
+ `Payone Apple Pay initialization failed: ${errorCode ? `Error ${errorCode}` : ''} ${errorMessage || 'Unknown error'}`
106
+ );
456
107
 
457
- /**
458
- * Generate nonce for merchant session
459
- */
460
- const generateNonce = () => {
461
- return Math.random().toString(36).substring(2, 15) +
462
- Math.random().toString(36).substring(2, 15);
108
+ return null;
463
109
  };
464
110
 
465
111
  module.exports = {
466
112
  initializeApplePaySession,
467
113
  validateApplePayMerchant
468
114
  };
469
-
470
-
@@ -9,12 +9,6 @@ const { logTransaction } = require("./transactionService");
9
9
 
10
10
  const POST_GATEWAY_URL = "https://api.pay1.de/post-gateway/";
11
11
 
12
- /**
13
- * Send request to Payone API
14
- * @param {Object} strapi - Strapi instance
15
- * @param {Object} params - Request parameters
16
- * @returns {Promise<Object>} Response data
17
- */
18
12
  const sendRequest = async (strapi, params) => {
19
13
  try {
20
14
  const settings = await getSettings(strapi);
@@ -23,8 +17,6 @@ const sendRequest = async (strapi, params) => {
23
17
  throw new Error("Payone settings not configured");
24
18
  }
25
19
 
26
- // Reference is saved as-is without normalization
27
-
28
20
  const requestParams = buildClientRequestParams(settings, params, strapi.log);
29
21
  const formData = toFormData(requestParams);
30
22
 
@@ -35,23 +27,6 @@ const sendRequest = async (strapi, params) => {
35
27
 
36
28
  const responseData = parseResponse(response.data, strapi.log);
37
29
 
38
- // Log full response for debugging
39
- strapi.log.info("Payone API Response:", JSON.stringify(responseData, null, 2));
40
- strapi.log.info("Response Status:", responseData.status || responseData.Status);
41
- strapi.log.info("Response Error Code:", responseData.errorcode || responseData.ErrorCode || responseData.Error?.ErrorCode);
42
- strapi.log.info("Response Error Message:", responseData.errormessage || responseData.ErrorMessage || responseData.Error?.ErrorMessage);
43
-
44
- // Log all possible redirect URL fields
45
- strapi.log.info("Redirect URL fields:", {
46
- redirecturl: responseData.redirecturl,
47
- RedirectUrl: responseData.RedirectUrl,
48
- redirect_url: responseData.redirect_url,
49
- redirectUrl: responseData.redirectUrl,
50
- url: responseData.url,
51
- Url: responseData.Url
52
- });
53
-
54
- // Extract error information from various possible fields
55
30
  const errorCode =
56
31
  responseData.errorcode ||
57
32
  responseData.ErrorCode ||
@@ -59,7 +34,6 @@ const sendRequest = async (strapi, params) => {
59
34
  responseData.error_code ||
60
35
  null;
61
36
 
62
- // Check for 3DS redirect
63
37
  const requires3DSErrorCodes = ["4219", 4219];
64
38
  const is3DSRequiredError = requires3DSErrorCodes.includes(errorCode);
65
39
 
@@ -69,7 +43,6 @@ const sendRequest = async (strapi, params) => {
69
43
  responseData.redirectUrl = redirectUrl;
70
44
  responseData.is3DSRequired = is3DSRequiredError;
71
45
 
72
- // If 3DS required but no redirect URL, log for debugging
73
46
  if (is3DSRequiredError && !redirectUrl) {
74
47
  strapi.log.warn("3DS authentication required (Error 4219) but no redirect URL found. May need 3dscheck request.");
75
48
  strapi.log.info("Full response data:", JSON.stringify(responseData, null, 2));
@@ -92,7 +65,6 @@ const sendRequest = async (strapi, params) => {
92
65
 
93
66
  const status = (responseData.status || responseData.Status || "unknown").toUpperCase();
94
67
 
95
- // Log transaction
96
68
  await logTransaction(strapi, {
97
69
  txid: extractTxId(responseData) || params.txid || null,
98
70
  reference: params.reference || null,
@@ -107,7 +79,6 @@ const sendRequest = async (strapi, params) => {
107
79
  customer_message: customerMessage
108
80
  });
109
81
 
110
- // Add normalized error fields to response
111
82
  responseData.errorCode = errorCode;
112
83
  responseData.errorMessage = errorMessage;
113
84
  responseData.customerMessage = customerMessage;
@@ -120,12 +91,6 @@ const sendRequest = async (strapi, params) => {
120
91
  }
121
92
  };
122
93
 
123
- /**
124
- * Preauthorization
125
- * @param {Object} strapi - Strapi instance
126
- * @param {Object} params - Request parameters
127
- * @returns {Promise<Object>} Response data
128
- */
129
94
  const preauthorization = async (strapi, params) => {
130
95
  const requiredParams = {
131
96
  request: "preauthorization",
@@ -147,12 +112,6 @@ const preauthorization = async (strapi, params) => {
147
112
  return await sendRequest(strapi, updatedParams);
148
113
  };
149
114
 
150
- /**
151
- * Authorization
152
- * @param {Object} strapi - Strapi instance
153
- * @param {Object} params - Request parameters
154
- * @returns {Promise<Object>} Response data
155
- */
156
115
  const authorization = async (strapi, params) => {
157
116
  const requiredParams = {
158
117
  request: "authorization",
@@ -164,12 +123,6 @@ const authorization = async (strapi, params) => {
164
123
  return await sendRequest(strapi, updatedParams);
165
124
  };
166
125
 
167
- /**
168
- * Capture
169
- * @param {Object} strapi - Strapi instance
170
- * @param {Object} params - Request parameters
171
- * @returns {Promise<Object>} Response data
172
- */
173
126
  const capture = async (strapi, params) => {
174
127
  if (!params.txid) {
175
128
  throw new Error("Transaction ID (txid) is required for capture");
@@ -187,12 +140,6 @@ const capture = async (strapi, params) => {
187
140
  return await sendRequest(strapi, requiredParams);
188
141
  };
189
142
 
190
- /**
191
- * Refund
192
- * @param {Object} strapi - Strapi instance
193
- * @param {Object} params - Request parameters
194
- * @returns {Promise<Object>} Response data
195
- */
196
143
  const refund = async (strapi, params) => {
197
144
  if (!params.txid) {
198
145
  throw new Error("Transaction ID (txid) is required for refund");
@@ -210,28 +157,15 @@ const refund = async (strapi, params) => {
210
157
  return await sendRequest(strapi, requiredParams);
211
158
  };
212
159
 
213
- /**
214
- * Handle 3D Secure callback from Payone
215
- * This processes the callback after customer completes 3DS authentication
216
- * Note: Payone's redirect callback typically doesn't include transaction details -
217
- * the URL path (success/error/back) indicates the result.
218
- * @param {Object} strapi - Strapi instance
219
- * @param {Object} callbackData - Callback data from Payone (may be empty or minimal)
220
- * @param {string} resultType - Result type from URL path: 'success', 'error', 'cancelled', or 'callback'
221
- * @returns {Promise<Object>} Processed callback result
222
- */
223
160
  const handle3DSCallback = async (strapi, callbackData, resultType = 'callback') => {
224
161
  try {
225
- // Parse any data that Payone might have sent
226
162
  const parsedData = callbackData && Object.keys(callbackData).length > 0
227
163
  ? parseResponse(callbackData, strapi.log)
228
164
  : {};
229
165
 
230
- // Extract transaction information if available
231
166
  const txid = extractTxId(parsedData);
232
167
  const reference = parsedData.reference || parsedData.Reference || null;
233
168
 
234
- // Determine status from resultType (URL path) since Payone callback may not include status
235
169
  let status;
236
170
  if (resultType === 'success') {
237
171
  status = 'APPROVED';
@@ -240,11 +174,9 @@ const handle3DSCallback = async (strapi, callbackData, resultType = 'callback')
240
174
  } else if (resultType === 'cancelled') {
241
175
  status = 'CANCELLED';
242
176
  } else {
243
- // Fallback to parsed data if available
244
177
  status = parsedData.status || parsedData.Status || 'PENDING';
245
178
  }
246
179
 
247
- // Log for debugging purposes only (not saved to transaction history)
248
180
  strapi.log.info("3DS callback processed:", {
249
181
  resultType,
250
182
  status,