strapi-plugin-payone-provider 1.4.2 → 1.5.0

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.
@@ -0,0 +1,305 @@
1
+ import React from "react";
2
+ import { Box, Flex, Typography, Select, Option, Checkbox, TextInput } 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 padding={4}>
90
+ <Flex direction="column" gap={4}>
91
+ <Typography variant="omega" fontWeight="semiBold">
92
+ Apple Pay Configuration
93
+ </Typography>
94
+
95
+ {/* Country Code */}
96
+ <Box>
97
+ <Select
98
+ label="Country Code *"
99
+ name="countryCode"
100
+ value={countryCode}
101
+ onChange={handleCountryChange}
102
+ hint="Select the country where your business operates"
103
+ required
104
+ >
105
+ {APPLE_PAY_SUPPORTED_COUNTRIES.map(country => (
106
+ <Option key={country.code} value={country.code}>
107
+ {country.name} ({country.code})
108
+ </Option>
109
+ ))}
110
+ </Select>
111
+ </Box>
112
+
113
+ {/* Currency Code */}
114
+ <Box>
115
+ <Select
116
+ label="Currency Code *"
117
+ name="currencyCode"
118
+ value={currencyCode}
119
+ onChange={handleCurrencyChange}
120
+ hint={`Supported currencies for ${countryCode}. Some currencies may be restricted.`}
121
+ required
122
+ >
123
+ {supportedCurrencies.map(currency => (
124
+ <Option key={currency.code} value={currency.code}>
125
+ {currency.name} ({currency.code}) {currency.symbol}
126
+ </Option>
127
+ ))}
128
+ </Select>
129
+ {supportedCurrencies.length === 0 && (
130
+ <Typography variant="pi" textColor="danger600" style={{ marginTop: "4px" }}>
131
+ No supported currencies for this country. Please select a different country.
132
+ </Typography>
133
+ )}
134
+ </Box>
135
+
136
+ {/* Supported Networks */}
137
+ <Box>
138
+ <Typography variant="pi" fontWeight="semiBold" style={{ marginBottom: "8px" }}>
139
+ Supported Networks *
140
+ </Typography>
141
+ <Typography variant="sigma" textColor="neutral600" style={{ marginBottom: "12px" }}>
142
+ Select payment networks supported in {countryCode}
143
+ </Typography>
144
+ <Flex direction="column" gap={2}>
145
+ {APPLE_PAY_SUPPORTED_NETWORKS.map(network => {
146
+ const isSupported = supportedNetworksForCountry.includes(network.code);
147
+ const isSelected = supportedNetworks?.includes(network.code);
148
+
149
+ return (
150
+ <Checkbox
151
+ key={network.code}
152
+ name={`network-${network.code}`}
153
+ checked={isSelected}
154
+ onChange={() => handleNetworkToggle(network.code)}
155
+ disabled={!isSupported}
156
+ hint={!isSupported ? `Not supported in ${countryCode}` : undefined}
157
+ >
158
+ {network.name} ({network.code})
159
+ {!isSupported && (
160
+ <Typography variant="sigma" textColor="neutral500" style={{ marginLeft: "8px" }}>
161
+ (Not available in {countryCode})
162
+ </Typography>
163
+ )}
164
+ </Checkbox>
165
+ );
166
+ })}
167
+ </Flex>
168
+ {supportedNetworks?.length === 0 && (
169
+ <Typography variant="pi" textColor="danger600" style={{ marginTop: "8px" }}>
170
+ At least one network must be selected
171
+ </Typography>
172
+ )}
173
+ </Box>
174
+
175
+ {/* Merchant Capabilities */}
176
+ <Box>
177
+ <Typography variant="pi" fontWeight="semiBold" style={{ marginBottom: "8px" }}>
178
+ Merchant Capabilities *
179
+ </Typography>
180
+ <Typography variant="sigma" textColor="neutral600" style={{ marginBottom: "12px" }}>
181
+ Select payment capabilities. "3D Secure" is required for most payment methods.
182
+ </Typography>
183
+ <Flex direction="column" gap={2}>
184
+ {APPLE_PAY_MERCHANT_CAPABILITIES.map(capability => {
185
+ const isSelected = merchantCapabilities?.includes(capability.code);
186
+
187
+ return (
188
+ <Checkbox
189
+ key={capability.code}
190
+ name={`capability-${capability.code}`}
191
+ checked={isSelected}
192
+ onChange={() => handleCapabilityToggle(capability.code)}
193
+ >
194
+ {capability.name} - {capability.description}
195
+ </Checkbox>
196
+ );
197
+ })}
198
+ </Flex>
199
+ {merchantCapabilities?.length === 0 && (
200
+ <Typography variant="pi" textColor="danger600" style={{ marginTop: "8px" }}>
201
+ At least one capability must be selected. "supports3DS" is recommended.
202
+ </Typography>
203
+ )}
204
+ </Box>
205
+
206
+ {/* Button Style */}
207
+ <Box>
208
+ <Select
209
+ label="Button Style"
210
+ name="buttonStyle"
211
+ value={buttonStyle}
212
+ onChange={(value) => onConfigChange({ ...config, buttonStyle: value })}
213
+ hint="Visual style of the Apple Pay button"
214
+ >
215
+ {APPLE_PAY_BUTTON_STYLES.map(style => (
216
+ <Option key={style.code} value={style.code}>
217
+ {style.name}
218
+ </Option>
219
+ ))}
220
+ </Select>
221
+ </Box>
222
+
223
+ {/* Button Type */}
224
+ <Box>
225
+ <Select
226
+ label="Button Type"
227
+ name="buttonType"
228
+ value={buttonType}
229
+ onChange={(value) => onConfigChange({ ...config, buttonType: value })}
230
+ hint="Type of action the button represents"
231
+ >
232
+ {APPLE_PAY_BUTTON_TYPES.map(type => (
233
+ <Option key={type.code} value={type.code}>
234
+ {type.name}
235
+ </Option>
236
+ ))}
237
+ </Select>
238
+ </Box>
239
+
240
+ {/* Payment Options */}
241
+ <Box>
242
+ <Typography variant="pi" fontWeight="semiBold" style={{ marginBottom: "8px" }}>
243
+ Payment Options
244
+ </Typography>
245
+ <Flex direction="column" gap={2}>
246
+ <Checkbox
247
+ name="requestPayerName"
248
+ checked={requestPayerName}
249
+ onChange={(checked) => onConfigChange({ ...config, requestPayerName: checked })}
250
+ >
251
+ Request Payer Name
252
+ </Checkbox>
253
+ <Checkbox
254
+ name="requestBillingAddress"
255
+ checked={requestBillingAddress}
256
+ onChange={(checked) => onConfigChange({ ...config, requestBillingAddress: checked })}
257
+ >
258
+ Request Billing Address
259
+ </Checkbox>
260
+ <Checkbox
261
+ name="requestPayerEmail"
262
+ checked={requestPayerEmail}
263
+ onChange={(checked) => onConfigChange({ ...config, requestPayerEmail: checked })}
264
+ >
265
+ Request Payer Email
266
+ </Checkbox>
267
+ <Checkbox
268
+ name="requestPayerPhone"
269
+ checked={requestPayerPhone}
270
+ onChange={(checked) => onConfigChange({ ...config, requestPayerPhone: checked })}
271
+ >
272
+ Request Payer Phone
273
+ </Checkbox>
274
+ <Checkbox
275
+ name="requestShipping"
276
+ checked={requestShipping}
277
+ onChange={(checked) => onConfigChange({ ...config, requestShipping: checked })}
278
+ >
279
+ Request Shipping Address
280
+ </Checkbox>
281
+ </Flex>
282
+ </Box>
283
+
284
+ {/* Merchant Identifier Info */}
285
+ <Box padding={3} background="neutral100" borderRadius="4px">
286
+ <Typography variant="pi" fontWeight="semiBold" style={{ marginBottom: "4px" }}>
287
+ Merchant Identifier
288
+ </Typography>
289
+ <Typography variant="sigma" textColor="neutral600">
290
+ {settings?.mid || settings?.portalid
291
+ ? `Using: ${settings.mid || settings.portalid}`
292
+ : "Merchant identifier will be obtained from Payone after domain verification. See documentation for setup instructions."
293
+ }
294
+ </Typography>
295
+ </Box>
296
+ </Flex>
297
+ </Box>
298
+ );
299
+ };
300
+
301
+ export default ApplePayConfig;
302
+
303
+
304
+
305
+
@@ -36,6 +36,8 @@ const PaymentActionsPanel = ({
36
36
  settings,
37
37
  googlePayToken,
38
38
  setGooglePayToken,
39
+ applePayToken,
40
+ setApplePayToken,
39
41
  cardtype,
40
42
  setCardtype,
41
43
  cardpan,
@@ -84,6 +86,8 @@ const PaymentActionsPanel = ({
84
86
  settings={settings}
85
87
  googlePayToken={googlePayToken}
86
88
  setGooglePayToken={setGooglePayToken}
89
+ applePayToken={applePayToken}
90
+ setApplePayToken={setApplePayToken}
87
91
  cardtype={cardtype}
88
92
  setCardtype={setCardtype}
89
93
  cardpan={cardpan}
@@ -109,6 +113,8 @@ const PaymentActionsPanel = ({
109
113
  settings={settings}
110
114
  googlePayToken={googlePayToken}
111
115
  setGooglePayToken={setGooglePayToken}
116
+ applePayToken={applePayToken}
117
+ setApplePayToken={setApplePayToken}
112
118
  cardtype={cardtype}
113
119
  setCardtype={setCardtype}
114
120
  cardpan={cardpan}
@@ -2,6 +2,7 @@ import React from "react";
2
2
  import { Box, Flex, Typography, TextInput, Button } from "@strapi/design-system";
3
3
  import { Play } from "@strapi/icons";
4
4
  import GooglePayButton from "../GooglePaybutton";
5
+ import ApplePayButton from "../ApplePayButton";
5
6
  import CardDetailsInput from "./CardDetailsInput";
6
7
 
7
8
  const AuthorizationForm = ({
@@ -15,6 +16,8 @@ const AuthorizationForm = ({
15
16
  settings,
16
17
  googlePayToken,
17
18
  setGooglePayToken,
19
+ applePayToken,
20
+ setApplePayToken,
18
21
  cardtype,
19
22
  setCardtype,
20
23
  cardpan,
@@ -37,6 +40,22 @@ const AuthorizationForm = ({
37
40
  onError(error);
38
41
  }
39
42
  };
43
+
44
+ const handleApplePayToken = (token, paymentData) => {
45
+ if (!token) {
46
+ return;
47
+ }
48
+ setApplePayToken(token);
49
+ onAuthorization(token);
50
+ };
51
+
52
+ const handleApplePayError = (error) => {
53
+ if (onError) {
54
+ onError(error);
55
+ }
56
+ };
57
+
58
+
40
59
  return (
41
60
  <Flex direction="column" alignItems="stretch" gap={4}>
42
61
  <Flex direction="row" gap={2}>
@@ -73,8 +92,7 @@ const AuthorizationForm = ({
73
92
  />
74
93
  </Flex>
75
94
 
76
- {/* Show card details input if 3DS is enabled and payment method is credit card */}
77
- {paymentMethod === "cc" && settings?.enable3DSecure !== false && (
95
+ {paymentMethod === "cc" && settings?.enable3DSecure && (
78
96
  <Box marginTop={4}>
79
97
  <CardDetailsInput
80
98
  cardtype={cardtype}
@@ -97,12 +115,21 @@ const AuthorizationForm = ({
97
115
  onError={handleGooglePayError}
98
116
  settings={settings}
99
117
  />
118
+ ) : paymentMethod === "apl" ? (
119
+ <ApplePayButton
120
+ amount={paymentAmount}
121
+ currency="EUR"
122
+ onTokenReceived={handleApplePayToken}
123
+ onError={handleApplePayError}
124
+ settings={settings}
125
+ />
100
126
  ) : (
101
127
  <Button
102
128
  variant="default"
103
129
  onClick={onAuthorization}
104
130
  loading={isProcessingPayment}
105
131
  startIcon={<Play />}
132
+ style={{ maxWidth: '200px' }}
106
133
  className="payment-button payment-button-primary"
107
134
  disabled={
108
135
  !paymentAmount.trim() ||
@@ -51,6 +51,7 @@ const CaptureForm = ({
51
51
  onClick={onCapture}
52
52
  loading={isProcessingPayment}
53
53
  startIcon={<Play />}
54
+ style={{ maxWidth: '200px' }}
54
55
  className="payment-button payment-button-primary"
55
56
  disabled={!captureTxid.trim() || !paymentAmount.trim()}
56
57
  >
@@ -94,22 +94,24 @@ const CardDetailsInput = ({
94
94
  return (
95
95
  <Box>
96
96
  <Flex direction="column" alignItems="stretch" gap={4}>
97
- <Select
98
- label="3D Secure Test Cards (Requires Redirect)"
99
- name="testCard"
100
- value={selectedTestCard}
101
- placeholder="Select a 3DS test card to auto-fill"
102
- hint="These cards will trigger 3DS authentication redirect. Password: 12345"
103
- onChange={handleTestCardSelect}
104
- className="payment-input"
105
- >
106
- <Option value="">-- Select a test card --</Option>
107
- {TEST_3DS_CARDS.map((card, index) => (
108
- <Option key={index} value={`${card.cardtype}-${card.cardpan}`}>
109
- {card.name} - {card.description}
110
- </Option>
111
- ))}
112
- </Select>
97
+ <Flex direction="row" gap={2} alignItems="flex-start">
98
+ <Select
99
+ label="3D Secure Test Cards"
100
+ name="testCard"
101
+ value={selectedTestCard}
102
+ placeholder="Select a 3DS test card to auto-fill"
103
+ hint="These cards will trigger 3DS authentication redirect. Password: 12345"
104
+ onChange={handleTestCardSelect}
105
+ className="payment-input"
106
+ >
107
+ <Option value="">-- Select a test card --</Option>
108
+ {TEST_3DS_CARDS.map((card, index) => (
109
+ <Option key={index} value={`${card.cardtype}-${card.cardpan}`}>
110
+ {card.name} - {card.description}
111
+ </Option>
112
+ ))}
113
+ </Select>
114
+ </Flex>
113
115
 
114
116
  <Flex gap={4} wrap="wrap" alignItems="flex-start">
115
117
  <Select
@@ -2,6 +2,7 @@ import React from "react";
2
2
  import { Box, Flex, Typography, TextInput, Button } from "@strapi/design-system";
3
3
  import { Play } from "@strapi/icons";
4
4
  import GooglePayButton from "../GooglePaybutton";
5
+ import ApplePayButton from "../ApplePayButton";
5
6
  import CardDetailsInput from "./CardDetailsInput";
6
7
 
7
8
  const PreauthorizationForm = ({
@@ -15,6 +16,8 @@ const PreauthorizationForm = ({
15
16
  settings,
16
17
  googlePayToken,
17
18
  setGooglePayToken,
19
+ applePayToken,
20
+ setApplePayToken,
18
21
  cardtype,
19
22
  setCardtype,
20
23
  cardpan,
@@ -37,6 +40,20 @@ const PreauthorizationForm = ({
37
40
  onError(error);
38
41
  }
39
42
  };
43
+
44
+ const handleApplePayToken = (token, paymentData) => {
45
+ if (!token) {
46
+ return;
47
+ }
48
+ setApplePayToken(token);
49
+ onPreauthorization(token);
50
+ };
51
+
52
+ const handleApplePayError = (error) => {
53
+ if (onError) {
54
+ onError(error);
55
+ }
56
+ };
40
57
  return (
41
58
  <Flex direction="column" alignItems="stretch" gap={4}>
42
59
  <Flex direction="row" gap={2}>
@@ -73,8 +90,7 @@ const PreauthorizationForm = ({
73
90
  />
74
91
  </Flex>
75
92
 
76
- {/* Show card details input if 3DS is enabled and payment method is credit card */}
77
- {paymentMethod === "cc" && settings?.enable3DSecure !== false && (
93
+ {paymentMethod === "cc" && settings?.enable3DSecure && (
78
94
  <Box marginTop={4}>
79
95
  <CardDetailsInput
80
96
  cardtype={cardtype}
@@ -97,12 +113,38 @@ const PreauthorizationForm = ({
97
113
  onError={handleGooglePayError}
98
114
  settings={settings}
99
115
  />
116
+ ) : paymentMethod === "apl" ? (
117
+ <Box>
118
+ <ApplePayButton
119
+ amount={paymentAmount}
120
+ currency="EUR"
121
+ onTokenReceived={handleApplePayToken}
122
+ onError={handleApplePayError}
123
+ settings={settings}
124
+ />
125
+ <Box marginTop={3} style={{ width: "100%", display: "flex", flexDirection: "column", alignItems: "flex-start", gap: "8px" }}>
126
+ <Typography variant="pi" textColor="neutral600" style={{ marginBottom: "8px" }}>
127
+ Apple Pay is not available on localhost. You can test the payment flow without Apple Pay token:
128
+ </Typography>
129
+ <Button
130
+ variant="secondary"
131
+ onClick={() => onPreauthorization(null)}
132
+ loading={isProcessingPayment}
133
+ startIcon={<Play />}
134
+ style={{ maxWidth: '200px' }}
135
+ disabled={!paymentAmount.trim() || !preauthReference.trim()}
136
+ >
137
+ Process Preauthorization
138
+ </Button>
139
+ </Box>
140
+ </Box>
100
141
  ) : (
101
142
  <Button
102
143
  variant="default"
103
144
  onClick={onPreauthorization}
104
145
  loading={isProcessingPayment}
105
146
  startIcon={<Play />}
147
+ style={{ maxWidth: '200px' }}
106
148
  className="payment-button payment-button-primary"
107
149
  disabled={
108
150
  !paymentAmount.trim() ||
@@ -76,6 +76,7 @@ const RefundForm = ({
76
76
  onClick={onRefund}
77
77
  loading={isProcessingPayment}
78
78
  startIcon={<Play />}
79
+ style={{ maxWidth: '200px' }}
79
80
  className="payment-button payment-button-primary"
80
81
  disabled={!refundTxid.trim() || !paymentAmount.trim()}
81
82
  >
@@ -50,6 +50,7 @@ const usePaymentActions = () => {
50
50
  const [paymentMethod, setPaymentMethod] = useState("cc");
51
51
  const [captureMode, setCaptureMode] = useState("full");
52
52
  const [googlePayToken, setGooglePayToken] = useState(null);
53
+ const [applePayToken, setApplePayToken] = useState(null);
53
54
 
54
55
  // Card details for 3DS testing
55
56
  const [cardtype, setCardtype] = useState("");
@@ -129,10 +130,13 @@ const usePaymentActions = () => {
129
130
  baseParams.backurl = `${baseUrl}${basePath}${pluginPath}/back`;
130
131
  }
131
132
 
132
- const tokenToUse = tokenParam || googlePayToken;
133
+ const tokenToUse = tokenParam || googlePayToken || applePayToken;
133
134
  if (paymentMethod === "gpp" && tokenToUse) {
134
135
  baseParams.googlePayToken = tokenToUse;
135
136
  baseParams.settings = settings;
137
+ } else if (paymentMethod === "apl" && tokenToUse) {
138
+ baseParams.applePayToken = tokenToUse;
139
+ baseParams.settings = settings;
136
140
  }
137
141
 
138
142
  const params = getPreauthorizationParams(paymentMethod, baseParams);
@@ -271,10 +275,13 @@ const usePaymentActions = () => {
271
275
  baseParams.backurl = `${baseUrl}${basePath}${pluginPath}/back`;
272
276
  }
273
277
 
274
- const tokenToUse = tokenParam || googlePayToken;
278
+ const tokenToUse = tokenParam || googlePayToken || applePayToken;
275
279
  if (paymentMethod === "gpp" && tokenToUse) {
276
280
  baseParams.googlePayToken = tokenToUse;
277
281
  baseParams.settings = settings;
282
+ } else if (paymentMethod === "apl" && tokenToUse) {
283
+ baseParams.applePayToken = tokenToUse;
284
+ baseParams.settings = settings;
278
285
  }
279
286
 
280
287
  const params = getAuthorizationParams(paymentMethod, baseParams);
@@ -454,6 +461,10 @@ const usePaymentActions = () => {
454
461
  googlePayToken,
455
462
  setGooglePayToken,
456
463
 
464
+ // Apple Pay
465
+ applePayToken,
466
+ setApplePayToken,
467
+
457
468
  // Card details for 3DS
458
469
  cardtype,
459
470
  setCardtype,