strapi-plugin-payone-provider 1.1.2 → 1.2.4

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 (36) hide show
  1. package/README.md +1045 -330
  2. package/admin/src/index.js +4 -1
  3. package/admin/src/pages/App/components/AppHeader.js +37 -0
  4. package/admin/src/pages/App/components/AppTabs.js +126 -0
  5. package/admin/src/pages/App/components/ConfigurationPanel.js +34 -35
  6. package/admin/src/pages/App/components/GooglePaybutton.js +300 -0
  7. package/admin/src/pages/App/components/HistoryPanel.js +25 -38
  8. package/admin/src/pages/App/components/PaymentActionsPanel.js +95 -280
  9. package/admin/src/pages/App/components/TransactionHistoryItem.js +4 -1
  10. package/admin/src/pages/App/components/paymentActions/AuthorizationForm.js +93 -0
  11. package/admin/src/pages/App/components/paymentActions/CaptureForm.js +64 -0
  12. package/admin/src/pages/App/components/paymentActions/PaymentMethodSelector.js +52 -0
  13. package/admin/src/pages/App/components/paymentActions/PaymentResult.js +85 -0
  14. package/admin/src/pages/App/components/paymentActions/PreauthorizationForm.js +93 -0
  15. package/admin/src/pages/App/components/paymentActions/RefundForm.js +89 -0
  16. package/admin/src/pages/App/index.js +41 -465
  17. package/admin/src/pages/App/styles.css +294 -0
  18. package/admin/src/pages/constants/paymentConstants.js +37 -0
  19. package/admin/src/pages/hooks/usePaymentActions.js +271 -0
  20. package/admin/src/pages/hooks/useSettings.js +111 -0
  21. package/admin/src/pages/hooks/useTransactionHistory.js +87 -0
  22. package/admin/src/pages/utils/api.js +10 -0
  23. package/admin/src/pages/utils/injectGooglePayScript.js +31 -0
  24. package/admin/src/pages/utils/paymentUtils.js +113 -13
  25. package/package.json +1 -1
  26. package/server/controllers/payone.js +71 -64
  27. package/server/routes/index.js +17 -0
  28. package/server/services/paymentService.js +214 -0
  29. package/server/services/payone.js +25 -648
  30. package/server/services/settingsService.js +59 -0
  31. package/server/services/testConnectionService.js +190 -0
  32. package/server/services/transactionService.js +114 -0
  33. package/server/utils/normalize.js +51 -0
  34. package/server/utils/paymentMethodParams.js +126 -0
  35. package/server/utils/requestBuilder.js +110 -0
  36. package/server/utils/responseParser.js +80 -0
@@ -0,0 +1,87 @@
1
+ import { useState, useEffect } from "react";
2
+ import { useNotification } from "@strapi/helper-plugin";
3
+ import payoneRequests from "../utils/api";
4
+
5
+ const PAGE_SIZE = 10;
6
+
7
+ const useTransactionHistory = () => {
8
+ const toggleNotification = useNotification();
9
+ const [transactionHistory, setTransactionHistory] = useState([]);
10
+ const [isLoadingHistory, setIsLoadingHistory] = useState(false);
11
+ const [selectedTransaction, setSelectedTransaction] = useState(null);
12
+ const [currentPage, setCurrentPage] = useState(1);
13
+ const [filters, setFilters] = useState({
14
+ status: "",
15
+ request_type: "",
16
+ txid: "",
17
+ reference: "",
18
+ date_from: "",
19
+ date_to: ""
20
+ });
21
+
22
+ useEffect(() => {
23
+ loadTransactionHistory();
24
+ }, []);
25
+
26
+ const loadTransactionHistory = async () => {
27
+ setIsLoadingHistory(true);
28
+ try {
29
+ const result = await payoneRequests.getTransactionHistory(filters);
30
+ setTransactionHistory(result.data || []);
31
+ setCurrentPage(1);
32
+ } catch (error) {
33
+ toggleNotification({
34
+ type: "warning",
35
+ message: "Failed to load transaction history"
36
+ });
37
+ } finally {
38
+ setIsLoadingHistory(false);
39
+ }
40
+ };
41
+
42
+ const handleFilterChange = (field, value) => {
43
+ setFilters((prev) => ({ ...prev, [field]: value }));
44
+ };
45
+
46
+ const handleFilterApply = () => {
47
+ loadTransactionHistory();
48
+ };
49
+
50
+ const handleTransactionSelect = (transaction) => {
51
+ if (selectedTransaction?.id === transaction?.id) {
52
+ setSelectedTransaction(null);
53
+ } else {
54
+ setSelectedTransaction(transaction);
55
+ }
56
+ };
57
+
58
+ const handlePageChange = (page) => {
59
+ setCurrentPage(page);
60
+ setSelectedTransaction(null);
61
+ };
62
+
63
+ // Pagination calculations
64
+ const totalPages = Math.ceil(transactionHistory.length / PAGE_SIZE);
65
+ const startIndex = (currentPage - 1) * PAGE_SIZE;
66
+ const endIndex = startIndex + PAGE_SIZE;
67
+ const paginatedTransactions = transactionHistory.slice(startIndex, endIndex);
68
+
69
+ return {
70
+ transactionHistory,
71
+ paginatedTransactions,
72
+ isLoadingHistory,
73
+ selectedTransaction,
74
+ filters,
75
+ currentPage,
76
+ totalPages,
77
+ pageSize: PAGE_SIZE,
78
+ handleFilterChange,
79
+ handleFilterApply,
80
+ handleTransactionSelect,
81
+ handlePageChange,
82
+ loadTransactionHistory
83
+ };
84
+ };
85
+
86
+ export default useTransactionHistory;
87
+
@@ -69,6 +69,16 @@ const payoneRequests = {
69
69
  "Content-Type": "application/json"
70
70
  }
71
71
  });
72
+ },
73
+
74
+ handle3DSCallback: (data) => {
75
+ return request(`/${pluginId}/3ds-callback`, {
76
+ method: "POST",
77
+ body: data,
78
+ headers: {
79
+ "Content-Type": "application/json"
80
+ }
81
+ });
72
82
  }
73
83
  };
74
84
 
@@ -0,0 +1,31 @@
1
+ export const injectGooglePayScript = () => {
2
+ const scriptUrl = "https://pay.google.com/gp/p/js/pay.js";
3
+
4
+ if (document.querySelector(`script[src="${scriptUrl}"]`)) {
5
+ if (typeof window !== 'undefined' && typeof window.google !== 'undefined' && window.google.payments?.api?.PaymentsClient) {
6
+ window.dispatchEvent(new CustomEvent("googlePayScriptLoaded"));
7
+ }
8
+ return;
9
+ }
10
+
11
+ const script = document.createElement("script");
12
+ script.src = scriptUrl;
13
+ script.async = true;
14
+
15
+ script.onload = () => {
16
+ setTimeout(() => {
17
+ if (typeof window !== 'undefined' && typeof window.google !== 'undefined' && window.google.payments?.api?.PaymentsClient) {
18
+ window.dispatchEvent(new CustomEvent("googlePayScriptLoaded"));
19
+ } else {
20
+ window.dispatchEvent(new CustomEvent("googlePayScriptLoaded"));
21
+ }
22
+ }, 500);
23
+ };
24
+
25
+ script.onerror = () => {
26
+ window.dispatchEvent(new CustomEvent("googlePayScriptError"));
27
+ };
28
+
29
+ document.head.appendChild(script);
30
+ };
31
+
@@ -11,6 +11,8 @@
11
11
  * Supported Payment Methods:
12
12
  * - Credit Card (cc)
13
13
  * - PayPal (wlt)
14
+ * - Google Pay (gpp)
15
+ * - Apple Pay (apl)
14
16
  * - Sofort Banking (sb)
15
17
  * - SEPA Direct Debit (elv)
16
18
  */
@@ -45,8 +47,18 @@ export const getBaseParams = (options = {}) => {
45
47
  backurl = "https://www.example.com/back"
46
48
  } = options;
47
49
 
48
- // Generate unique customer ID if not provided
49
- const finalCustomerId = customerid || `CUST-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
50
+ const generateCustomerId = () => {
51
+ const timestamp = Date.now().toString().slice(-10);
52
+ const random = Math.random().toString(36).substring(2, 6).toUpperCase();
53
+ const id = `${timestamp}${random}`.slice(0, 17);
54
+ return id;
55
+ };
56
+
57
+ let finalCustomerId = customerid || generateCustomerId();
58
+
59
+ if (finalCustomerId && finalCustomerId.length > 17) {
60
+ finalCustomerId = finalCustomerId.slice(0, 17);
61
+ }
50
62
 
51
63
  return {
52
64
  // Required core parameters (Payone v1)
@@ -95,9 +107,33 @@ export const getPaymentMethodParams = (paymentMethod, options = {}) => {
95
107
  cardcvc2,
96
108
  iban,
97
109
  bic,
98
- bankaccountholder
110
+ bankaccountholder,
111
+ // Shipping address for wallet payments (Google Pay, Apple Pay, PayPal)
112
+ shipping_firstname,
113
+ shipping_lastname,
114
+ shipping_street,
115
+ shipping_zip,
116
+ shipping_city,
117
+ shipping_country,
118
+ // Billing address (used as fallback for shipping)
119
+ firstname,
120
+ lastname,
121
+ street,
122
+ zip,
123
+ city,
124
+ country
99
125
  } = options;
100
126
 
127
+ // Helper to get shipping params for wallet payments
128
+ const getShippingParams = () => ({
129
+ shipping_firstname: shipping_firstname || firstname || "John",
130
+ shipping_lastname: shipping_lastname || lastname || "Doe",
131
+ shipping_street: shipping_street || street || "Test Street 123",
132
+ shipping_zip: shipping_zip || zip || "12345",
133
+ shipping_city: shipping_city || city || "Test City",
134
+ shipping_country: (shipping_country || country || "DE").toUpperCase()
135
+ });
136
+
101
137
  switch (paymentMethod) {
102
138
  case "cc": // Credit Card (Visa, Mastercard, Amex)
103
139
  return {
@@ -112,12 +148,34 @@ export const getPaymentMethodParams = (paymentMethod, options = {}) => {
112
148
  return {
113
149
  clearingtype: "wlt",
114
150
  wallettype: "PPE", // PayPal Express
115
- shipping_firstname: "John",
116
- shipping_lastname: "Doe",
117
- shipping_street: "Test Street 123",
118
- shipping_zip: "12345",
119
- shipping_city: "Test City",
120
- shipping_country: "DE"
151
+ ...getShippingParams()
152
+ };
153
+
154
+ case "gpp":
155
+ const googlePayParams = {
156
+ clearingtype: "wlt",
157
+ wallettype: "GGP",
158
+ ...getShippingParams()
159
+ };
160
+
161
+ if (options.googlePayToken) {
162
+ const gatewayMerchantId = options.settings?.mid || options.settings?.portalid || '';
163
+ googlePayParams["add_paydata[paymentmethod_token_data]"] = options.googlePayToken;
164
+ googlePayParams["add_paydata[paymentmethod]"] = "GGP";
165
+ googlePayParams["add_paydata[paymentmethod_type]"] = "GOOGLEPAY";
166
+ googlePayParams["add_paydata[gatewayid]"] = "payonegmbh";
167
+ if (gatewayMerchantId) {
168
+ googlePayParams["add_paydata[gateway_merchantid]"] = gatewayMerchantId;
169
+ }
170
+ }
171
+
172
+ return googlePayParams;
173
+
174
+ case "apl": // Apple Pay
175
+ return {
176
+ clearingtype: "wlt",
177
+ wallettype: "APL", // Apple Pay
178
+ ...getShippingParams()
121
179
  };
122
180
 
123
181
  case "sb": // Sofort Banking
@@ -159,11 +217,19 @@ export const getPreauthorizationParams = (paymentMethod, options = {}) => {
159
217
  const baseParams = getBaseParams(options);
160
218
  const methodParams = getPaymentMethodParams(paymentMethod, options);
161
219
 
162
- return {
220
+ const params = {
163
221
  ...baseParams,
164
222
  ...methodParams,
165
223
  request: "preauthorization" // Required for Payone API
166
224
  };
225
+
226
+ // Add 3D Secure parameters for credit card payments if enabled
227
+ if (paymentMethod === "cc" && options.enable3DSecure !== false) {
228
+ params["3dsecure"] = "yes";
229
+ params.ecommercemode = options.ecommercemode || "internet";
230
+ }
231
+
232
+ return params;
167
233
  };
168
234
 
169
235
  /**
@@ -177,11 +243,19 @@ export const getAuthorizationParams = (paymentMethod, options = {}) => {
177
243
  const baseParams = getBaseParams(options);
178
244
  const methodParams = getPaymentMethodParams(paymentMethod, options);
179
245
 
180
- return {
246
+ const params = {
181
247
  ...baseParams,
182
248
  ...methodParams,
183
249
  request: "authorization" // Required for Payone API
184
250
  };
251
+
252
+ // Add 3D Secure parameters for credit card payments if enabled
253
+ if (paymentMethod === "cc" && options.enable3DSecure !== false) {
254
+ params["3dsecure"] = "yes";
255
+ params.ecommercemode = options.ecommercemode || "internet";
256
+ }
257
+
258
+ return params;
185
259
  };
186
260
 
187
261
  /**
@@ -220,6 +294,8 @@ export const getCaptureParams = (paymentMethod, options = {}) => {
220
294
  break;
221
295
 
222
296
  case "wlt": // PayPal
297
+ case "gpp": // Google Pay
298
+ case "apl": // Apple Pay
223
299
  methodParams = {
224
300
  capturemode: captureMode // full or partial
225
301
  };
@@ -279,7 +355,9 @@ export const getRefundParams = (paymentMethod, options = {}) => {
279
355
  break;
280
356
 
281
357
  case "wlt": // PayPal
282
- // PayPal specific refund parameters (if needed)
358
+ case "gpp": // Google Pay
359
+ case "apl": // Apple Pay
360
+ // Wallet payment specific refund parameters (if needed)
283
361
  break;
284
362
 
285
363
  case "sb": // Sofort Banking
@@ -310,6 +388,8 @@ export const getPaymentMethodDisplayName = (paymentMethod) => {
310
388
  const displayNames = {
311
389
  cc: "Credit Card (Visa, Mastercard)",
312
390
  wlt: "PayPal",
391
+ gpp: "Google Pay",
392
+ apl: "Apple Pay",
313
393
  sb: "Sofort Banking",
314
394
  elv: "SEPA Direct Debit"
315
395
  };
@@ -325,6 +405,8 @@ export const getPaymentMethodOptions = () => {
325
405
  return [
326
406
  { value: "cc", label: "Credit Card (Visa, Mastercard)" },
327
407
  { value: "wlt", label: "PayPal" },
408
+ { value: "gpp", label: "Google Pay" },
409
+ { value: "apl", label: "Apple Pay" },
328
410
  { value: "sb", label: "Sofort Banking" },
329
411
  { value: "elv", label: "SEPA Direct Debit" }
330
412
  ];
@@ -336,7 +418,7 @@ export const getPaymentMethodOptions = () => {
336
418
  * @returns {boolean} True if supports capture mode
337
419
  */
338
420
  export const supportsCaptureMode = (paymentMethod) => {
339
- return paymentMethod === "wlt"; // Only PayPal supports capture mode
421
+ return paymentMethod === "wlt" || paymentMethod === "gpp" || paymentMethod === "apl"; // PayPal, Google Pay, and Apple Pay support capture mode
340
422
  };
341
423
 
342
424
  /**
@@ -487,6 +569,24 @@ export const validatePaymentParams = (operation, paymentMethod, params) => {
487
569
  }
488
570
  break;
489
571
 
572
+ case "gpp":
573
+ if (!params.wallettype) {
574
+ errors.push("Wallet type is required for Google Pay payments");
575
+ }
576
+ if (params.wallettype && params.wallettype !== "GGP") {
577
+ errors.push("Wallet type must be GGP for Google Pay payments");
578
+ }
579
+ break;
580
+
581
+ case "apl":
582
+ if (!params.wallettype) {
583
+ errors.push("Wallet type is required for Apple Pay payments");
584
+ }
585
+ if (params.wallettype && params.wallettype !== "APL") {
586
+ errors.push("Wallet type must be APL for Apple Pay payments");
587
+ }
588
+ break;
589
+
490
590
  case "sb":
491
591
  if (!params.bankcountry) {
492
592
  errors.push("Bank country is required for Sofort payments");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-plugin-payone-provider",
3
- "version": "1.1.2",
3
+ "version": "1.2.4",
4
4
  "description": "Strapi plugin for Payone payment gateway integration",
5
5
  "license": "MIT",
6
6
  "maintainers": [
@@ -1,62 +1,72 @@
1
1
  "use strict";
2
2
 
3
+ const PLUGIN_NAME = "strapi-plugin-payone-provider";
4
+
5
+ /**
6
+ * Get Payone service
7
+ * @param {Object} strapi - Strapi instance
8
+ * @returns {Object} Payone service
9
+ */
10
+ const getPayoneService = (strapi) => {
11
+ return strapi.plugin(PLUGIN_NAME).service("payone");
12
+ };
13
+
14
+ /**
15
+ * Handle error response
16
+ * @param {Object} ctx - Koa context
17
+ * @param {Error} error - Error object
18
+ */
19
+ const handleError = (ctx, error) => {
20
+ strapi.log.error("Payone controller error:", error);
21
+ ctx.throw(500, error);
22
+ };
23
+
24
+ /**
25
+ * Hide sensitive key in settings
26
+ * @param {Object} settings - Settings object
27
+ * @returns {Object} Settings with hidden key
28
+ */
29
+ const hideKey = (settings) => {
30
+ if (settings && settings.key) {
31
+ settings.key = "***HIDDEN***";
32
+ }
33
+ return settings;
34
+ };
35
+
3
36
  module.exports = ({ strapi }) => ({
4
37
  async getSettings(ctx) {
5
38
  try {
6
- const settings = await strapi
7
- .plugin("strapi-plugin-payone-provider")
8
- .service("payone")
9
- .getSettings();
10
-
11
- if (settings && settings.key) {
12
- settings.key = "***HIDDEN***";
13
- }
14
-
15
- ctx.body = { data: settings };
39
+ const settings = await getPayoneService(strapi).getSettings();
40
+ ctx.body = { data: hideKey(settings) };
16
41
  } catch (error) {
17
- ctx.throw(500, error);
42
+ handleError(ctx, error);
18
43
  }
19
44
  },
20
45
 
21
46
  async updateSettings(ctx) {
22
47
  try {
23
48
  const { body } = ctx.request;
49
+ const currentSettings = await getPayoneService(strapi).getSettings();
24
50
 
25
- const currentSettings = await strapi
26
- .plugin("strapi-plugin-payone-provider")
27
- .service("payone")
28
- .getSettings();
29
-
51
+ // Preserve existing key if hidden or not provided
30
52
  if (body.key === "***HIDDEN***" || !body.key) {
31
- body.key = currentSettings.key;
53
+ body.key = currentSettings?.key;
32
54
  }
33
55
 
34
- const settings = await strapi
35
- .plugin("strapi-plugin-payone-provider")
36
- .service("payone")
37
- .updateSettings(body);
38
-
39
- if (settings && settings.key) {
40
- settings.key = "***HIDDEN***";
41
- }
42
-
43
- ctx.body = { data: settings };
56
+ const settings = await getPayoneService(strapi).updateSettings(body);
57
+ ctx.body = { data: hideKey(settings) };
44
58
  } catch (error) {
45
- ctx.throw(500, error);
59
+ handleError(ctx, error);
46
60
  }
47
61
  },
48
62
 
49
63
  async preauthorization(ctx) {
50
64
  try {
51
65
  const params = ctx.request.body;
52
- const result = await strapi
53
- .plugin("strapi-plugin-payone-provider")
54
- .service("payone")
55
- .preauthorization(params);
56
-
66
+ const result = await getPayoneService(strapi).preauthorization(params);
57
67
  ctx.body = { data: result };
58
68
  } catch (error) {
59
- ctx.throw(500, error);
69
+ handleError(ctx, error);
60
70
  }
61
71
  },
62
72
 
@@ -64,71 +74,68 @@ module.exports = ({ strapi }) => ({
64
74
  try {
65
75
  const params = ctx.request.body;
66
76
  strapi.log.info("Payone authorization controller called with:", params);
67
-
68
- const result = await strapi
69
- .plugin("strapi-plugin-payone-provider")
70
- .service("payone")
71
- .authorization(params);
72
-
77
+ const result = await getPayoneService(strapi).authorization(params);
73
78
  ctx.body = { data: result };
74
79
  } catch (error) {
75
80
  strapi.log.error("Payone authorization error:", error);
76
- ctx.throw(500, error);
81
+ handleError(ctx, error);
77
82
  }
78
83
  },
79
84
 
80
85
  async capture(ctx) {
81
86
  try {
82
87
  const params = ctx.request.body;
83
- const result = await strapi
84
- .plugin("strapi-plugin-payone-provider")
85
- .service("payone")
86
- .capture(params);
87
-
88
+ const result = await getPayoneService(strapi).capture(params);
88
89
  ctx.body = { data: result };
89
90
  } catch (error) {
90
- ctx.throw(500, error);
91
+ handleError(ctx, error);
91
92
  }
92
93
  },
93
94
 
94
95
  async refund(ctx) {
95
96
  try {
96
97
  const params = ctx.request.body;
97
- const result = await strapi
98
- .plugin("strapi-plugin-payone-provider")
99
- .service("payone")
100
- .refund(params);
101
-
98
+ const result = await getPayoneService(strapi).refund(params);
102
99
  ctx.body = { data: result };
103
100
  } catch (error) {
104
- ctx.throw(500, error);
101
+ handleError(ctx, error);
105
102
  }
106
103
  },
107
104
 
108
105
  async getTransactionHistory(ctx) {
109
106
  try {
110
107
  const filters = ctx.query || {};
111
- const history = await strapi
112
- .plugin("strapi-plugin-payone-provider")
113
- .service("payone")
114
- .getTransactionHistory(filters);
115
-
108
+ const history = await getPayoneService(strapi).getTransactionHistory(filters);
116
109
  ctx.body = { data: history };
117
110
  } catch (error) {
118
- ctx.throw(500, error);
111
+ handleError(ctx, error);
119
112
  }
120
113
  },
121
114
 
122
115
  async testConnection(ctx) {
123
116
  try {
124
- const result = await strapi
125
- .plugin("strapi-plugin-payone-provider")
126
- .service("payone")
127
- .testConnection();
117
+ const result = await getPayoneService(strapi).testConnection();
118
+ ctx.body = { data: result };
119
+ } catch (error) {
120
+ handleError(ctx, error);
121
+ }
122
+ },
123
+
124
+ /**
125
+ * Handle 3D Secure callback from Payone
126
+ * This endpoint receives the callback after customer completes 3DS authentication
127
+ */
128
+ async handle3DSCallback(ctx) {
129
+ try {
130
+ strapi.log.info("3DS callback received:", ctx.request.body);
131
+
132
+ const callbackData = ctx.request.body;
133
+ const result = await getPayoneService(strapi).handle3DSCallback(callbackData);
128
134
 
129
135
  ctx.body = { data: result };
130
136
  } catch (error) {
131
- ctx.throw(500, error);
137
+ strapi.log.error("3DS callback error:", error);
138
+ handleError(ctx, error);
132
139
  }
133
140
  }
134
141
  });
@@ -67,6 +67,14 @@ module.exports = {
67
67
  config: {
68
68
  policies: ["admin::isAuthenticatedAdmin"]
69
69
  }
70
+ },
71
+ {
72
+ method: "POST",
73
+ path: "/3ds-callback",
74
+ handler: "payone.handle3DSCallback",
75
+ config: {
76
+ policies: ["admin::isAuthenticatedAdmin"]
77
+ }
70
78
  }
71
79
  ]
72
80
  },
@@ -118,6 +126,15 @@ module.exports = {
118
126
  policies: ["plugin::strapi-plugin-payone-provider.is-auth"],
119
127
  auth: false
120
128
  }
129
+ },
130
+ {
131
+ method: "POST",
132
+ path: "/3ds-callback",
133
+ handler: "payone.handle3DSCallback",
134
+ config: {
135
+ policies: ["plugin::strapi-plugin-payone-provider.is-auth"],
136
+ auth: false
137
+ }
121
138
  }
122
139
  ]
123
140
  }