strapi-plugin-payone-provider 1.5.0 → 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.
package/README.md CHANGED
@@ -85,6 +85,159 @@ After installation, you need to configure your Payone credentials:
85
85
  5. Click **"Test Connection"** to verify your credentials
86
86
  6. Click **"Save Configuration"** to store your settings
87
87
 
88
+ ### Apple Pay Configuration
89
+
90
+ To configure Apple Pay settings:
91
+
92
+ 1. Navigate to **Payone Provider** in the sidebar menu
93
+ 2. Go to **Payment Actions** tab
94
+ 3. Select **Apple Pay** as the payment method
95
+ 4. Click on the Apple Pay configuration link: `/plugins/strapi-plugin-payone-provider/apple-pay-config`
96
+ 5. Configure the following settings:
97
+ - **Country Code**: Select the country where your business operates
98
+ - **Currency Code**: Select the currency for transactions
99
+ - **Supported Networks**: Select payment card networks (Visa, Mastercard, Amex, etc.)
100
+ - **Merchant Capabilities**: Select payment capabilities (3D Secure is recommended)
101
+ - **Button Style & Type**: Customize the Apple Pay button appearance
102
+ 6. Click **"Save Apple Pay Configuration"** to store your settings
103
+
104
+ > ⚠️ **Important**: Apple Pay requires a registered domain with HTTPS. It does NOT work on localhost. For testing, use a production domain with HTTPS or test on a device with Safari (iOS/macOS).
105
+
106
+ #### Apple Pay Domain Verification File (.well-known)
107
+
108
+ Apple Pay requires a domain verification file to be placed on your server. This file must be accessible at:
109
+
110
+ ```
111
+ https://yourdomain.com/.well-known/apple-developer-merchantid-domain-association
112
+ ```
113
+
114
+ **Steps to set up the domain verification file:**
115
+
116
+ 1. **Download the file from Payone:**
117
+
118
+ - Download the domain verification file from Payone documentation: [https://docs.payone.com/payment-methods/apple-pay/apple-pay-without-dev](https://docs.payone.com/payment-methods/apple-pay/apple-pay-without-dev)
119
+ - Alternatively, log into your Payone Merchant Interface (PMI)
120
+ - Navigate to **Configuration** → **Payment Portals** → **Apple Pay**
121
+ - Download the `apple-developer-merchantid-domain-association` file
122
+
123
+ 2. **Place the file in Strapi:**
124
+
125
+ - Create the directory: `public/.well-known/` (if it doesn't exist)
126
+ - Place the file at: `public/.well-known/apple-developer-merchantid-domain-association`
127
+
128
+ 3. **Place the file in your Frontend (if separate):**
129
+
130
+ - Create the directory: `public/.well-known/` (if it doesn't exist)
131
+ - Place the file at: `public/.well-known/apple-developer-merchantid-domain-association`
132
+
133
+ 4. **Verify accessibility:**
134
+ - The file must be accessible via HTTPS at: `https://yourdomain.com/.well-known/apple-developer-merchantid-domain-association`
135
+ - Test by visiting the URL in your browser - you should see the file content
136
+
137
+ > ⚠️ **Critical**: Without this file, Apple Pay will NOT work on your domain. The file must be accessible via HTTPS and must match exactly what Payone provides.
138
+
139
+ #### Middleware Configuration for Apple Pay
140
+
141
+ Apple Pay requires Content Security Policy (CSP) configuration in `config/middlewares.js` to allow Apple Pay scripts. Without this configuration, Apple Pay will NOT work.
142
+
143
+ **Required CSP directives:**
144
+
145
+ ```javascript
146
+ module.exports = [
147
+ "strapi::logger",
148
+ "strapi::errors",
149
+ {
150
+ name: "strapi::security",
151
+ config: {
152
+ contentSecurityPolicy: {
153
+ useDefaults: true,
154
+ directives: {
155
+ "script-src": [
156
+ "'self'",
157
+ "'unsafe-inline'",
158
+ "'unsafe-eval'",
159
+ "https://applepay.cdn-apple.com", // Apple Pay SDK
160
+ "https://www.apple.com", // Apple Pay manifest
161
+ ],
162
+ "connect-src": [
163
+ "'self'",
164
+ "https:",
165
+ "https://applepay.cdn-apple.com", // Apple Pay API
166
+ "https://www.apple.com", // Apple Pay manifest
167
+ ],
168
+ "frame-src": [
169
+ "'self'",
170
+ "https://applepay.cdn-apple.com", // Apple Pay iframe
171
+ ],
172
+ },
173
+ },
174
+ },
175
+ },
176
+ // ... other middlewares
177
+ ];
178
+ ```
179
+
180
+ > ⚠️ **Important**: Without this middleware configuration, Apple Pay scripts will be blocked and Apple Pay will NOT work!
181
+
182
+ ### Google Pay Configuration
183
+
184
+ To configure Google Pay settings:
185
+
186
+ 1. Navigate to **Payone Provider** in the sidebar menu
187
+ 2. Go to **Payment Actions** tab
188
+ 3. Select **Google Pay** as the payment method
189
+ 4. Click on the Google Pay configuration link: `/plugins/strapi-plugin-payone-provider/google-pay-config`
190
+ 5. Configure the following settings:
191
+ - **Country Code**: Select the country where your business operates
192
+ - **Currency Code**: Select the currency for transactions
193
+ - **Merchant Name**: Enter your business name as it will appear in Google Pay
194
+ - **Allowed Card Networks**: Select payment card networks (Mastercard, Visa, Amex, etc.)
195
+ - **Allowed Authentication Methods**: Select authentication methods (PAN Only, 3D Secure)
196
+ 6. Click **"Save Google Pay Configuration"** to store your settings
197
+
198
+ > ℹ️ **Note**: The Gateway Merchant ID will be automatically obtained from your Payone Merchant ID (MID) or Portal ID configured in the main Configuration tab.
199
+
200
+ #### Middleware Configuration for Google Pay
201
+
202
+ Google Pay requires Content Security Policy (CSP) configuration in `config/middlewares.js` to allow Google Pay scripts. Without this configuration, Google Pay will NOT work.
203
+
204
+ **Required CSP directives:**
205
+
206
+ ```javascript
207
+ module.exports = [
208
+ "strapi::logger",
209
+ "strapi::errors",
210
+ {
211
+ name: "strapi::security",
212
+ config: {
213
+ contentSecurityPolicy: {
214
+ useDefaults: true,
215
+ directives: {
216
+ "script-src": [
217
+ "'self'",
218
+ "'unsafe-inline'",
219
+ "'unsafe-eval'",
220
+ "https://pay.google.com", // Google Pay SDK
221
+ ],
222
+ "connect-src": [
223
+ "'self'",
224
+ "https:",
225
+ "https://pay.google.com", // Google Pay API
226
+ ],
227
+ "frame-src": [
228
+ "'self'",
229
+ "https://pay.google.com", // Google Pay iframe
230
+ ],
231
+ },
232
+ },
233
+ },
234
+ },
235
+ // ... other middlewares
236
+ ];
237
+ ```
238
+
239
+ > ⚠️ **Important**: Without this middleware configuration, Google Pay scripts will be blocked and Google Pay will NOT work!
240
+
88
241
  ## 🚀 Getting Started
89
242
 
90
243
  ### 1. Test Your Connection
@@ -590,7 +743,7 @@ Google Pay integration requires obtaining an encrypted payment token from Google
590
743
 
591
744
  ```javascript
592
745
  const paymentsClient = new google.payments.api.PaymentsClient({
593
- environment: 'TEST', // or "PRODUCTION" for live
746
+ environment: "TEST", // or "PRODUCTION" for live
594
747
  });
595
748
 
596
749
  const baseRequest = {
@@ -598,19 +751,19 @@ const baseRequest = {
598
751
  apiVersionMinor: 0,
599
752
  };
600
753
 
601
- const allowedCardNetworks = ['MASTERCARD', 'VISA'];
602
- const allowedAuthMethods = ['PAN_ONLY', 'CRYPTOGRAM_3DS'];
754
+ const allowedCardNetworks = ["MASTERCARD", "VISA"];
755
+ const allowedAuthMethods = ["PAN_ONLY", "CRYPTOGRAM_3DS"];
603
756
 
604
757
  const tokenizationSpecification = {
605
- type: 'PAYMENT_GATEWAY',
758
+ type: "PAYMENT_GATEWAY",
606
759
  parameters: {
607
- gateway: 'payonegmbh',
608
- gatewayMerchantId: 'YOUR_PAYONE_MERCHANT_ID', // Use your Payone MID or Portal ID
760
+ gateway: "payonegmbh",
761
+ gatewayMerchantId: "YOUR_PAYONE_MERCHANT_ID", // Use your Payone MID or Portal ID
609
762
  },
610
763
  };
611
764
 
612
765
  const cardPaymentMethod = {
613
- type: 'CARD',
766
+ type: "CARD",
614
767
  parameters: {
615
768
  allowedCardNetworks,
616
769
  allowedAuthMethods,
@@ -634,45 +787,47 @@ paymentsClient.isReadyToPay(isReadyToPayRequest).then(function (response) {
634
787
  const paymentDataRequest = Object.assign({}, baseRequest);
635
788
  paymentDataRequest.allowedPaymentMethods = [cardPaymentMethod];
636
789
  paymentDataRequest.transactionInfo = {
637
- totalPriceStatus: 'FINAL',
638
- totalPrice: '10.00',
639
- currencyCode: 'EUR',
790
+ totalPriceStatus: "FINAL",
791
+ totalPrice: "10.00",
792
+ currencyCode: "EUR",
640
793
  };
641
794
  paymentDataRequest.merchantInfo = {
642
- merchantId: 'YOUR_GOOGLE_MERCHANT_ID', // Optional: from Google Console
643
- merchantName: 'Your Merchant Name',
795
+ merchantId: "YOUR_GOOGLE_MERCHANT_ID", // Optional: from Google Console
796
+ merchantName: "Your Merchant Name",
644
797
  };
645
798
 
646
799
  const button = paymentsClient.createButton({
647
800
  onClick: async () => {
648
801
  try {
649
- const paymentData = await paymentsClient.loadPaymentData(paymentDataRequest);
802
+ const paymentData = await paymentsClient.loadPaymentData(
803
+ paymentDataRequest
804
+ );
650
805
  const token = paymentData.paymentMethodData.tokenizationData.token;
651
806
 
652
807
  // Token is a JSON string, encode it to Base64 for Payone
653
808
  const base64Token = btoa(unescape(encodeURIComponent(token)));
654
809
 
655
810
  // Send to your backend
656
- await fetch('/api/strapi-plugin-payone-provider/preauthorization', {
657
- method: 'POST',
811
+ await fetch("/api/strapi-plugin-payone-provider/preauthorization", {
812
+ method: "POST",
658
813
  headers: {
659
- 'Content-Type': 'application/json',
660
- Authorization: 'Bearer YOUR_TOKEN',
814
+ "Content-Type": "application/json",
815
+ Authorization: "Bearer YOUR_TOKEN",
661
816
  },
662
817
  body: JSON.stringify({
663
818
  amount: 1000,
664
- currency: 'EUR',
665
- reference: 'PAY1234567890ABCDEF',
819
+ currency: "EUR",
820
+ reference: "PAY1234567890ABCDEF",
666
821
  googlePayToken: base64Token,
667
822
  }),
668
823
  });
669
824
  } catch (error) {
670
- console.error('Google Pay error:', error);
825
+ console.error("Google Pay error:", error);
671
826
  }
672
827
  },
673
828
  });
674
829
 
675
- document.getElementById('google-pay-button').appendChild(button);
830
+ document.getElementById("google-pay-button").appendChild(button);
676
831
  ```
677
832
 
678
833
  **Token Format**
@@ -684,7 +839,9 @@ The token from Google Pay is a JSON string with the following structure:
684
839
  "signature": "MEUCIFr4ETGzv0uLZX3sR+i1ScARXnRBrncyYFDX/TI/VSLCAiEAvC/Q4dqXMQhwcSdg/ZvXj8+up0wXsfHja3V/6z48/vk=",
685
840
  "intermediateSigningKey": {
686
841
  "signedKey": "{\"keyValue\":\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7PWUi+e6WPUhNmTSQ2WN006oWlcWy0FtBWizw9sph1wvX9XcXUNRLcfcsmCBfI5IsKQkjAmYxpCSB+L5sIudLw\\u003d\\u003d\",\"keyExpiration\":\"1722393105282\"}",
687
- "signatures": ["MEUCIQCpU30A3g2pP93IBE5NxgO9ZcJlGF9YPzCZS7H4/IR1CQIgF6+I5t8olT8YsRDUcj7w3R1bvX4ZCcyFXE2+YXa+3H0="]
842
+ "signatures": [
843
+ "MEUCIQCpU30A3g2pP93IBE5NxgO9ZcJlGF9YPzCZS7H4/IR1CQIgF6+I5t8olT8YsRDUcj7w3R1bvX4ZCcyFXE2+YXa+3H0="
844
+ ]
688
845
  },
689
846
  "protocolVersion": "ECv2",
690
847
  "signedMessage": "{\"encryptedMessage\":\"...\",\"ephemeralPublicKey\":\"...\",\"tag\":\"...\"}"
@@ -1,22 +1,40 @@
1
1
  import React from "react";
2
2
  import { HeaderLayout, Box, Typography, Button } from "@strapi/design-system";
3
- import { Check } from "@strapi/icons";
3
+ import { Check, ArrowLeft } from "@strapi/icons";
4
+ import { useHistory, useLocation } from "react-router-dom";
5
+ import pluginId from "../../../pluginId";
4
6
 
5
7
  const AppHeader = ({ activeTab, isSaving, onSave }) => {
8
+ const history = useHistory();
9
+ const location = useLocation();
10
+ const isApplePayConfigPage = location.pathname.includes('/apple-pay-config');
11
+
6
12
  return (
7
13
  <HeaderLayout
8
14
  title={
9
15
  <Box>
10
16
  <Typography variant="alpha" as="h1" fontWeight="bold" className="payment-title">
11
- Payone Provider
17
+ {isApplePayConfigPage ? "Apple Pay Configuration" : "Payone Provider"}
12
18
  </Typography>
13
19
  <Typography variant="pi" marginTop={2} className="payment-subtitle">
14
- Configure your Payone integration and manage payment transactions
20
+ {isApplePayConfigPage
21
+ ? "Configure Apple Pay settings for your payment gateway"
22
+ : "Configure your Payone integration and manage payment transactions"
23
+ }
15
24
  </Typography>
16
25
  </Box>
17
26
  }
18
27
  primaryAction={
19
- activeTab === 0 ? (
28
+ isApplePayConfigPage ? (
29
+ <Button
30
+ onClick={() => history.push(`/plugins/${pluginId}`)}
31
+ startIcon={<ArrowLeft />}
32
+ size="L"
33
+ variant="secondary"
34
+ >
35
+ Back to Main
36
+ </Button>
37
+ ) : activeTab === 0 ? (
20
38
  <Button
21
39
  loading={isSaving}
22
40
  onClick={onSave}
@@ -1,8 +1,10 @@
1
1
  import React from "react";
2
2
  import { Tabs, Tab, TabGroup, TabPanels, TabPanel } from "@strapi/design-system";
3
+ import pluginId from "../../../pluginId";
3
4
  import ConfigurationPanel from "./ConfigurationPanel";
4
5
  import HistoryPanel from "./HistoryPanel";
5
6
  import PaymentActionsPanel from "./PaymentActionsPanel";
7
+ import DocsPanel from "./DocsPanel";
6
8
 
7
9
  const AppTabs = ({
8
10
  activeTab,
@@ -30,8 +32,18 @@ const AppTabs = ({
30
32
  selectedTransaction,
31
33
  onTransactionSelect,
32
34
  // Payment actions props
33
- paymentActions
35
+ paymentActions,
36
+ history
34
37
  }) => {
38
+ const handleNavigateToConfig = (configType = "apple-pay") => {
39
+ if (history) {
40
+ if (configType === "google-pay") {
41
+ history.push(`/plugins/${pluginId}/google-pay-config`);
42
+ } else {
43
+ history.push(`/plugins/${pluginId}/apple-pay-config`);
44
+ }
45
+ }
46
+ };
35
47
  return (
36
48
  <TabGroup
37
49
  label="Payone Provider Tabs"
@@ -53,6 +65,11 @@ const AppTabs = ({
53
65
  >
54
66
  Payment Actions
55
67
  </Tab>
68
+ <Tab
69
+ className={`payment-tab ${activeTab === 3 ? 'payment-tab-active' : ''}`}
70
+ >
71
+ Documentation
72
+ </Tab>
56
73
  </Tabs>
57
74
  <TabPanels>
58
75
  <TabPanel>
@@ -125,8 +142,13 @@ const AppTabs = ({
125
142
  setCardexpiredate={paymentActions.setCardexpiredate}
126
143
  cardcvc2={paymentActions.cardcvc2}
127
144
  setCardcvc2={paymentActions.setCardcvc2}
145
+ onNavigateToConfig={handleNavigateToConfig}
128
146
  />
129
147
  </TabPanel>
148
+
149
+ <TabPanel>
150
+ <DocsPanel />
151
+ </TabPanel>
130
152
  </TabPanels>
131
153
  </TabGroup>
132
154
  );
@@ -499,7 +499,16 @@ const ApplePayButton = ({
499
499
 
500
500
  // Show payment sheet and get response
501
501
  console.log("[Apple Pay] Showing payment sheet...");
502
- const response = await request.show();
502
+ let response;
503
+ try {
504
+ response = await request.show();
505
+ } catch (error) {
506
+ console.error("[Apple Pay] Error showing payment sheet:", error);
507
+ if (onError) {
508
+ onError(error);
509
+ }
510
+ return;
511
+ }
503
512
 
504
513
  console.log("[Apple Pay] Payment response received:", {
505
514
  hasDetails: !!response.details,
@@ -512,7 +521,11 @@ const ApplePayButton = ({
512
521
 
513
522
  if (!paymentToken) {
514
523
  console.error("[Apple Pay] Payment token is missing from response");
515
- await response.complete("fail");
524
+ try {
525
+ await response.complete("fail");
526
+ } catch (completeError) {
527
+ console.error("[Apple Pay] Error completing payment with fail:", completeError);
528
+ }
516
529
  if (onError) {
517
530
  onError(new Error("Apple Pay token is missing"));
518
531
  }
@@ -539,21 +552,41 @@ const ApplePayButton = ({
539
552
  tokenString = btoa(unescape(encodeURIComponent(JSON.stringify(paymentToken))));
540
553
  }
541
554
 
542
- // Call the callback with the token
555
+ // Call the callback with the token BEFORE completing payment
556
+ // This ensures the token is processed before the dialog closes
543
557
  console.log("[Apple Pay] Sending token to callback");
558
+ let callbackSuccess = true;
544
559
  if (onTokenReceived) {
545
- onTokenReceived(tokenString, {
546
- paymentToken: paymentToken,
547
- billingContact: response.payerName || response.details?.billingContact,
548
- shippingContact: response.shippingAddress || response.details?.shippingAddress,
549
- shippingOption: response.shippingOption || response.details?.shippingOption
550
- });
560
+ try {
561
+ // If callback is async, wait for it
562
+ const callbackResult = onTokenReceived(tokenString, {
563
+ paymentToken: paymentToken,
564
+ billingContact: response.payerName || response.details?.billingContact,
565
+ shippingContact: response.shippingAddress || response.details?.shippingAddress,
566
+ shippingOption: response.shippingOption || response.details?.shippingOption
567
+ });
568
+
569
+ // If callback returns a promise, wait for it
570
+ if (callbackResult && typeof callbackResult.then === 'function') {
571
+ await callbackResult;
572
+ }
573
+ } catch (callbackError) {
574
+ console.error("[Apple Pay] Error in token callback:", callbackError);
575
+ callbackSuccess = false;
576
+ }
551
577
  }
552
578
 
553
- // Complete payment with success
554
- console.log("[Apple Pay] Completing payment with success status");
555
- await response.complete("success");
556
- console.log("[Apple Pay] Payment completed successfully");
579
+ // Complete payment with success or fail based on callback result
580
+ console.log("[Apple Pay] Completing payment with status:", callbackSuccess ? "success" : "fail");
581
+ try {
582
+ await response.complete(callbackSuccess ? "success" : "fail");
583
+ console.log("[Apple Pay] Payment completed successfully");
584
+ } catch (completeError) {
585
+ console.error("[Apple Pay] Error completing payment:", completeError);
586
+ if (onError) {
587
+ onError(completeError);
588
+ }
589
+ }
557
590
 
558
591
  } catch (error) {
559
592
  console.error("[Apple Pay] Payment error:", {