strapi-plugin-payone-provider 1.4.2 → 1.5.2

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 (32) hide show
  1. package/README.md +179 -22
  2. package/admin/src/pages/App/components/AppHeader.js +22 -4
  3. package/admin/src/pages/App/components/AppTabs.js +25 -1
  4. package/admin/src/pages/App/components/ApplePayButton.js +737 -0
  5. package/admin/src/pages/App/components/ApplePayConfig.js +364 -0
  6. package/admin/src/pages/App/components/ApplePayConfigPanel.js +81 -0
  7. package/admin/src/pages/App/components/ConfigurationPanel.js +19 -3
  8. package/admin/src/pages/App/components/DocsPanel.js +1057 -0
  9. package/admin/src/pages/App/components/GooglePayConfig.js +217 -0
  10. package/admin/src/pages/App/components/GooglePayConfigPanel.js +82 -0
  11. package/admin/src/pages/App/components/GooglePaybutton.js +1 -1
  12. package/admin/src/pages/App/components/PaymentActionsPanel.js +24 -6
  13. package/admin/src/pages/App/components/paymentActions/AuthorizationForm.js +60 -4
  14. package/admin/src/pages/App/components/paymentActions/CaptureForm.js +1 -0
  15. package/admin/src/pages/App/components/paymentActions/CardDetailsInput.js +18 -16
  16. package/admin/src/pages/App/components/paymentActions/PaymentMethodSelector.js +106 -2
  17. package/admin/src/pages/App/components/paymentActions/PreauthorizationForm.js +64 -4
  18. package/admin/src/pages/App/components/paymentActions/RefundForm.js +1 -0
  19. package/admin/src/pages/App/index.js +70 -1
  20. package/admin/src/pages/hooks/usePaymentActions.js +13 -2
  21. package/admin/src/pages/hooks/useSettings.js +2 -0
  22. package/admin/src/pages/utils/applePayConstants.js +222 -0
  23. package/admin/src/pages/utils/googlePayConstants.js +79 -0
  24. package/admin/src/pages/utils/paymentUtils.js +22 -74
  25. package/package.json +1 -1
  26. package/server/bootstrap.js +5 -1
  27. package/server/config/index.js +5 -1
  28. package/server/controllers/payone.js +10 -0
  29. package/server/routes/index.js +17 -0
  30. package/server/services/applePayService.js +261 -0
  31. package/server/services/payone.js +10 -0
  32. package/server/utils/paymentMethodParams.js +19 -2
@@ -0,0 +1,364 @@
1
+ import React from "react";
2
+ import { Box, Flex, Typography, Select, Option, Checkbox, Stack } from "@strapi/design-system";
3
+ import {
4
+ APPLE_PAY_SUPPORTED_COUNTRIES,
5
+ APPLE_PAY_SUPPORTED_CURRENCIES,
6
+ APPLE_PAY_SUPPORTED_NETWORKS,
7
+ APPLE_PAY_MERCHANT_CAPABILITIES,
8
+ getSupportedCurrenciesForCountry,
9
+ getSupportedNetworksForCountry,
10
+ APPLE_PAY_BUTTON_STYLES,
11
+ APPLE_PAY_BUTTON_TYPES,
12
+ DEFAULT_APPLE_PAY_CONFIG
13
+ } from "../../utils/applePayConstants";
14
+
15
+ const ApplePayConfig = ({
16
+ config,
17
+ onConfigChange,
18
+ settings
19
+ }) => {
20
+ const {
21
+ countryCode = DEFAULT_APPLE_PAY_CONFIG.countryCode,
22
+ currencyCode = DEFAULT_APPLE_PAY_CONFIG.currencyCode,
23
+ merchantCapabilities = DEFAULT_APPLE_PAY_CONFIG.merchantCapabilities,
24
+ supportedNetworks = DEFAULT_APPLE_PAY_CONFIG.supportedNetworks,
25
+ buttonStyle = DEFAULT_APPLE_PAY_CONFIG.buttonStyle,
26
+ buttonType = DEFAULT_APPLE_PAY_CONFIG.buttonType,
27
+ requestPayerName = DEFAULT_APPLE_PAY_CONFIG.requestPayerName,
28
+ requestBillingAddress = DEFAULT_APPLE_PAY_CONFIG.requestBillingAddress,
29
+ requestPayerEmail = DEFAULT_APPLE_PAY_CONFIG.requestPayerEmail,
30
+ requestPayerPhone = DEFAULT_APPLE_PAY_CONFIG.requestPayerPhone,
31
+ requestShipping = DEFAULT_APPLE_PAY_CONFIG.requestShipping,
32
+ shippingType = DEFAULT_APPLE_PAY_CONFIG.shippingType
33
+ } = config || {};
34
+
35
+ // Get supported currencies and networks based on selected country
36
+ const supportedCurrencies = getSupportedCurrenciesForCountry(countryCode);
37
+ const supportedNetworksForCountry = getSupportedNetworksForCountry(countryCode);
38
+
39
+ const handleCountryChange = (value) => {
40
+ const newConfig = {
41
+ ...config,
42
+ countryCode: value
43
+ };
44
+
45
+ // Auto-update currency if current currency is not supported in new country
46
+ const newSupportedCurrencies = getSupportedCurrenciesForCountry(value);
47
+ if (!newSupportedCurrencies.find(c => c.code === currencyCode)) {
48
+ newConfig.currencyCode = newSupportedCurrencies[0]?.code || "USD";
49
+ }
50
+
51
+ // Auto-update networks based on country
52
+ newConfig.supportedNetworks = getSupportedNetworksForCountry(value);
53
+
54
+ onConfigChange(newConfig);
55
+ };
56
+
57
+ const handleCurrencyChange = (value) => {
58
+ onConfigChange({
59
+ ...config,
60
+ currencyCode: value
61
+ });
62
+ };
63
+
64
+ const handleNetworkToggle = (networkCode) => {
65
+ const currentNetworks = supportedNetworks || [];
66
+ const newNetworks = currentNetworks.includes(networkCode)
67
+ ? currentNetworks.filter(n => n !== networkCode)
68
+ : [...currentNetworks, networkCode];
69
+
70
+ onConfigChange({
71
+ ...config,
72
+ supportedNetworks: newNetworks
73
+ });
74
+ };
75
+
76
+ const handleCapabilityToggle = (capabilityCode) => {
77
+ const currentCapabilities = merchantCapabilities || [];
78
+ const newCapabilities = currentCapabilities.includes(capabilityCode)
79
+ ? currentCapabilities.filter(c => c !== capabilityCode)
80
+ : [...currentCapabilities, capabilityCode];
81
+
82
+ onConfigChange({
83
+ ...config,
84
+ merchantCapabilities: newCapabilities
85
+ });
86
+ };
87
+
88
+ return (
89
+ <Box>
90
+ <Stack spacing={6}>
91
+ <Box>
92
+ <Typography variant="delta" as="h3" fontWeight="bold" style={{ marginBottom: "6px" }}>
93
+ Apple Pay Configuration
94
+ </Typography>
95
+ <Typography variant="pi" textColor="neutral600">
96
+ Configure Apple Pay settings for your payment gateway
97
+ </Typography>
98
+ </Box>
99
+
100
+ {/* Country and Currency */}
101
+ <Flex gap={4} wrap="wrap">
102
+ <Box style={{ flex: 1, minWidth: "300px" }}>
103
+ <Select
104
+ label="Country Code"
105
+ name="countryCode"
106
+ value={countryCode}
107
+ onChange={handleCountryChange}
108
+ hint="Select the country where your business operates"
109
+ required
110
+ >
111
+ {APPLE_PAY_SUPPORTED_COUNTRIES.map(country => (
112
+ <Option key={country.code} value={country.code}>
113
+ {country.name} ({country.code})
114
+ </Option>
115
+ ))}
116
+ </Select>
117
+ </Box>
118
+
119
+ <Box style={{ flex: 1, minWidth: "300px" }}>
120
+ <Select
121
+ label="Currency Code"
122
+ name="currencyCode"
123
+ value={currencyCode}
124
+ onChange={handleCurrencyChange}
125
+ hint={`Supported currencies for ${countryCode}`}
126
+ required
127
+ >
128
+ {supportedCurrencies.map(currency => (
129
+ <Option key={currency.code} value={currency.code}>
130
+ {currency.name} ({currency.code}) {currency.symbol}
131
+ </Option>
132
+ ))}
133
+ </Select>
134
+ {supportedCurrencies.length === 0 && (
135
+ <Typography variant="pi" textColor="danger600" style={{ marginTop: "4px" }}>
136
+ No supported currencies for this country. Please select a different country.
137
+ </Typography>
138
+ )}
139
+ </Box>
140
+ </Flex>
141
+
142
+ {/* Button Style and Type */}
143
+ <Flex gap={4} wrap="wrap">
144
+ <Box style={{ flex: 1, minWidth: "300px" }}>
145
+ <Select
146
+ label="Button Style"
147
+ name="buttonStyle"
148
+ value={buttonStyle}
149
+ onChange={(value) => onConfigChange({ ...config, buttonStyle: value })}
150
+ hint="Visual style of the Apple Pay button"
151
+ >
152
+ {APPLE_PAY_BUTTON_STYLES.map(style => (
153
+ <Option key={style.code} value={style.code}>
154
+ {style.name}
155
+ </Option>
156
+ ))}
157
+ </Select>
158
+ </Box>
159
+
160
+ <Box style={{ flex: 1, minWidth: "300px" }}>
161
+ <Select
162
+ label="Button Type"
163
+ name="buttonType"
164
+ value={buttonType}
165
+ onChange={(value) => onConfigChange({ ...config, buttonType: value })}
166
+ hint="Type of action the button represents"
167
+ >
168
+ {APPLE_PAY_BUTTON_TYPES.map(type => (
169
+ <Option key={type.code} value={type.code}>
170
+ {type.name}
171
+ </Option>
172
+ ))}
173
+ </Select>
174
+ </Box>
175
+ </Flex>
176
+
177
+ {/* Supported Networks */}
178
+ <Box>
179
+ <Typography variant="pi" fontWeight="semiBold" style={{ marginLeft: "2px" }}>
180
+ Supported Networks
181
+ </Typography>
182
+ <Typography variant="pi" textColor="neutral600" style={{ marginLeft: "2px" }}>
183
+ Select payment networks supported in {countryCode}
184
+ </Typography>
185
+ <Flex wrap="wrap" gap={4} style={{ marginTop: "12px" }}>
186
+ {APPLE_PAY_SUPPORTED_NETWORKS.map(network => {
187
+ const isSupported = supportedNetworksForCountry.includes(network.code);
188
+ const isSelected = supportedNetworks?.includes(network.code);
189
+
190
+ return (
191
+ <Box key={network.code} style={{ flex: "0 0 calc(50% - 8px)", minWidth: "250px" }}>
192
+ <Checkbox
193
+ name={`network-${network.code}`}
194
+ checked={isSelected}
195
+ onChange={() => handleNetworkToggle(network.code)}
196
+ disabled={!isSupported}
197
+ hint={!isSupported ? `Not supported in ${countryCode}` : undefined}
198
+ >
199
+ {network.name} ({network.code})
200
+ {!isSupported && (
201
+ <Typography variant="sigma" textColor="neutral500" style={{ marginLeft: "8px" }}>
202
+ (Not available)
203
+ </Typography>
204
+ )}
205
+ </Checkbox>
206
+ </Box>
207
+ );
208
+ })}
209
+ </Flex>
210
+ {supportedNetworks?.length === 0 && (
211
+ <Typography variant="pi" textColor="danger600" style={{ marginTop: "8px" }}>
212
+ At least one network must be selected
213
+ </Typography>
214
+ )}
215
+ </Box>
216
+
217
+ {/* Merchant Capabilities */}
218
+ <Box>
219
+ <Typography variant="pi" fontWeight="semiBold" style={{ marginLeft: "2px" }}>
220
+ Merchant Capabilities
221
+ </Typography>
222
+ <Typography variant="pi" textColor="neutral600" style={{ marginLeft: "2px" }}>
223
+ Select payment capabilities. "3D Secure" is required for most payment methods.
224
+ </Typography>
225
+ <Flex wrap="wrap" gap={4} style={{ marginTop: "12px" }}>
226
+ {APPLE_PAY_MERCHANT_CAPABILITIES.map(capability => {
227
+ const isSelected = merchantCapabilities?.includes(capability.code);
228
+
229
+ return (
230
+ <Box key={capability.code} style={{ flex: "0 0 calc(50% - 8px)", minWidth: "250px" }}>
231
+ <Checkbox
232
+ name={`capability-${capability.code}`}
233
+ checked={isSelected}
234
+ onChange={() => handleCapabilityToggle(capability.code)}
235
+ >
236
+ {capability.name} - {capability.description}
237
+ </Checkbox>
238
+ </Box>
239
+ );
240
+ })}
241
+ </Flex>
242
+ {merchantCapabilities?.length === 0 && (
243
+ <Typography variant="pi" textColor="danger600" style={{ marginTop: "8px" }}>
244
+ At least one capability must be selected. "supports3DS" is recommended.
245
+ </Typography>
246
+ )}
247
+ </Box>
248
+
249
+ {/* Payment Options */}
250
+ {/* <Box>
251
+ <Typography variant="pi" fontWeight="semiBold" style={{ marginLeft: "2px", marginBottom: "6px" }}>
252
+ Payment Options
253
+ </Typography>
254
+ <Typography variant="pi" textColor="neutral600" style={{ marginLeft: "2px" }}>
255
+ Select the information you want to collect during payment
256
+ </Typography>
257
+ <Flex wrap="wrap" gap={4} style={{ marginTop: "12px" }}>
258
+ <Box style={{ flex: "0 0 calc(33.333% - 11px)", minWidth: "200px" }}>
259
+ <Checkbox
260
+ name="requestPayerName"
261
+ checked={requestPayerName}
262
+ onChange={(checked) => onConfigChange({ ...config, requestPayerName: checked })}
263
+ >
264
+ Request Payer Name
265
+ </Checkbox>
266
+ </Box>
267
+ <Box style={{ flex: "0 0 calc(33.333% - 11px)", minWidth: "200px" }}>
268
+ <Checkbox
269
+ name="requestBillingAddress"
270
+ checked={requestBillingAddress}
271
+ onChange={(checked) => onConfigChange({ ...config, requestBillingAddress: checked })}
272
+ >
273
+ Request Billing Address
274
+ </Checkbox>
275
+ </Box>
276
+ <Box style={{ flex: "0 0 calc(33.333% - 11px)", minWidth: "200px" }}>
277
+ <Checkbox
278
+ name="requestPayerEmail"
279
+ checked={requestPayerEmail}
280
+ onChange={(checked) => onConfigChange({ ...config, requestPayerEmail: checked })}
281
+ >
282
+ Request Payer Email
283
+ </Checkbox>
284
+ </Box>
285
+ <Box style={{ flex: "0 0 calc(33.333% - 11px)", minWidth: "200px" }}>
286
+ <Checkbox
287
+ name="requestPayerPhone"
288
+ checked={requestPayerPhone}
289
+ onChange={(checked) => onConfigChange({ ...config, requestPayerPhone: checked })}
290
+ >
291
+ Request Payer Phone
292
+ </Checkbox>
293
+ </Box>
294
+ <Box style={{ flex: "0 0 calc(33.333% - 11px)", minWidth: "200px" }}>
295
+ <Checkbox
296
+ name="requestShipping"
297
+ checked={requestShipping}
298
+ onChange={(checked) => onConfigChange({ ...config, requestShipping: checked })}
299
+ >
300
+ Request Shipping Address
301
+ </Checkbox>
302
+ </Box>
303
+ </Flex>
304
+ </Box> */}
305
+
306
+ {/* Merchant Identifier Info */}
307
+ <Box>
308
+ <Typography variant="pi" fontWeight="semiBold" style={{ marginLeft: "2px" }}>
309
+ Merchant Identifier
310
+ </Typography>
311
+ <Typography variant="pi" textColor="neutral600">
312
+ {settings?.mid || settings?.portalid
313
+ ? `Using: ${settings.mid || settings.portalid}`
314
+ : "Merchant identifier will be obtained from Payone after domain verification. See documentation for setup instructions."
315
+ }
316
+ </Typography>
317
+ </Box>
318
+
319
+ {/* Domain Verification File Alert */}
320
+ <Box marginTop={4}>
321
+ <Box padding={3} background="warning100" borderRadius="4px" borderColor="warning200" borderWidth="1px" borderStyle="solid">
322
+ <Typography variant="pi" fontWeight="bold" textColor="warning700" marginBottom={2}>
323
+ ⚠️ Domain Verification File Required
324
+ </Typography>
325
+ <Typography variant="pi" textColor="neutral700" marginBottom={2}>
326
+ <strong>Download the Apple Pay domain verification file</strong> from your Payone merchant portal:
327
+ </Typography>
328
+ <Box padding={2} background="neutral0" borderRadius="4px" marginTop={2} marginBottom={2}>
329
+ <Typography variant="pi" style={{ fontSize: "12px" }}>
330
+ <strong>Download URL:</strong> Download the domain verification file from Payone documentation:{" "}
331
+ <a
332
+ href="https://docs.payone.com/payment-methods/apple-pay/apple-pay-without-dev"
333
+ target="_blank"
334
+ rel="noopener noreferrer"
335
+ style={{ color: "#0066ff", textDecoration: "underline" }}
336
+ >
337
+ https://docs.payone.com/payment-methods/apple-pay/apple-pay-without-dev
338
+ </a>
339
+ {" "}or log into your Payone Merchant Interface (PMI) → Configuration → Payment Portals → Apple Pay → Download domain verification file
340
+ </Typography>
341
+ </Box>
342
+ <Typography variant="pi" textColor="neutral700" marginBottom={2}>
343
+ <strong>Place the file at:</strong>
344
+ </Typography>
345
+ <Box padding={2} background="neutral0" borderRadius="4px" marginTop={2} marginBottom={2}>
346
+ <Typography variant="pi" style={{ fontFamily: "monospace", fontSize: "12px" }}>
347
+ <strong>Strapi:</strong> <code>public/.well-known/apple-developer-merchantid-domain-association</code><br/>
348
+ <strong>Frontend:</strong> <code>public/.well-known/apple-developer-merchantid-domain-association</code>
349
+ </Typography>
350
+ </Box>
351
+ <Typography variant="pi" textColor="neutral700" marginTop={2}>
352
+ The file must be accessible at: <code>https://yourdomain.com/.well-known/apple-developer-merchantid-domain-association</code>
353
+ </Typography>
354
+ <Typography variant="pi" fontWeight="bold" textColor="danger600" marginTop={2}>
355
+ ⚠️ Without this file, Apple Pay will NOT work on your domain!
356
+ </Typography>
357
+ </Box>
358
+ </Box>
359
+ </Stack>
360
+ </Box>
361
+ );
362
+ };
363
+
364
+ export default ApplePayConfig;
@@ -0,0 +1,81 @@
1
+ import React, { useState, useEffect } from "react";
2
+ import {
3
+ Box,
4
+ Card,
5
+ CardBody,
6
+ Flex,
7
+ Typography,
8
+ Button
9
+ } from "@strapi/design-system";
10
+ import { Check } from "@strapi/icons";
11
+ import ApplePayConfig from "./ApplePayConfig";
12
+
13
+ const ApplePayConfigPanel = ({
14
+ settings,
15
+ onInputChange,
16
+ isSaving,
17
+ onSave
18
+ }) => {
19
+ const [applePayConfig, setApplePayConfig] = useState(settings?.applePayConfig || {});
20
+
21
+ useEffect(() => {
22
+ setApplePayConfig(settings?.applePayConfig || {});
23
+ }, [settings?.applePayConfig]);
24
+
25
+ return (
26
+ <Box
27
+ className="payment-container"
28
+ paddingTop={8}
29
+ paddingBottom={8}
30
+ paddingLeft={8}
31
+ paddingRight={8}
32
+ >
33
+ <Flex direction="column" alignItems="stretch" gap={8}>
34
+ <Box>
35
+ <Typography variant="beta" as="h2" fontWeight="bold" className="payment-title" style={{ fontSize: '20px', marginBottom: '4px' }}>
36
+ Apple Pay Configuration
37
+ </Typography>
38
+ <Typography variant="pi" textColor="neutral600" marginTop={2} className="payment-subtitle" style={{ fontSize: '14px' }}>
39
+ Configure Apple Pay settings for your payment gateway
40
+ </Typography>
41
+ </Box>
42
+
43
+ <Box>
44
+ <Card className="payment-card">
45
+ <CardBody padding={6}>
46
+ <ApplePayConfig
47
+ config={applePayConfig}
48
+ onConfigChange={(newConfig) => {
49
+ setApplePayConfig(newConfig);
50
+ onInputChange("applePayConfig", newConfig);
51
+ }}
52
+ settings={settings}
53
+ />
54
+ </CardBody>
55
+ </Card>
56
+ </Box>
57
+
58
+ <Box paddingTop={4}>
59
+ <Flex direction="row" gap={4} alignItems="center">
60
+ <Button
61
+ loading={isSaving}
62
+ onClick={onSave}
63
+ startIcon={<Check />}
64
+ size="L"
65
+ variant="default"
66
+ className="payment-button payment-button-success"
67
+ >
68
+ Save Apple Pay Configuration
69
+ </Button>
70
+ <Typography variant="sigma" textColor="neutral600">
71
+ Note: Apple Pay configuration is used for Apple Pay payment requests. Make sure to configure the correct merchant identifier, supported networks, and capabilities for your region.
72
+ </Typography>
73
+ </Flex>
74
+ </Box>
75
+ </Flex>
76
+ </Box>
77
+ );
78
+ };
79
+
80
+ export default ApplePayConfigPanel;
81
+
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React, { useEffect } from "react";
2
2
  import {
3
3
  Box,
4
4
  Button,
@@ -23,6 +23,16 @@ const ConfigurationPanel = ({
23
23
  onTestConnection,
24
24
  onInputChange
25
25
  }) => {
26
+ const mode = (settings?.mode || "test").toLowerCase();
27
+
28
+ useEffect(() => {
29
+ if (settings?.mode) {
30
+ const currentMode = (settings.mode || "test").toLowerCase();
31
+ console.log("[ConfigurationPanel] Mode updated:", currentMode);
32
+ }
33
+ }, [settings?.mode]);
34
+
35
+
26
36
  return (
27
37
  <Box
28
38
  className="payment-container"
@@ -104,8 +114,8 @@ const ConfigurationPanel = ({
104
114
  className="payment-input"
105
115
  style={{ flex: 1, minWidth: "300px" }}
106
116
  >
107
- <Option value="test">Test Environment</Option>
108
- <Option value="live">Live Environment</Option>
117
+ <Option value="test">Test</Option>
118
+ <Option value="live">Live</Option>
109
119
  </Select>
110
120
 
111
121
  <TextInput
@@ -169,9 +179,15 @@ const ConfigurationPanel = ({
169
179
  loading={isTesting}
170
180
  startIcon={<Play />}
171
181
  className="payment-button payment-button-success"
182
+ disabled={mode === 'live'}
172
183
  >
173
184
  {isTesting ? "Testing Connection..." : "Test Connection"}
174
185
  </Button>
186
+ {mode === 'live' && (
187
+ <Typography variant="pi" textColor="neutral600" style={{ marginTop: "8px" }}>
188
+ Test Connection is disabled in live mode for security reasons.
189
+ </Typography>
190
+ )}
175
191
 
176
192
  {testResult && (
177
193
  <Alert