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.
- package/admin/src/components/Initializer/index.jsx +16 -0
- package/admin/src/components/PluginIcon/index.jsx +6 -0
- package/admin/src/index.js +3 -3
- package/admin/src/pages/App/components/AppHeader.jsx +55 -0
- package/admin/src/pages/App/components/AppTabs.jsx +158 -0
- package/admin/src/pages/App/components/ApplePayBtn.jsx +304 -0
- package/admin/src/pages/App/components/ApplePayButton.js +139 -93
- package/admin/src/pages/App/components/ApplePayButton.jsx +908 -0
- package/admin/src/pages/App/components/ApplePayConfig.jsx +298 -0
- package/admin/src/pages/App/components/ApplePayConfigPanel.jsx +81 -0
- package/admin/src/pages/App/components/ConfigurationPanel.jsx +280 -0
- package/admin/src/pages/App/components/DocsPanel.jsx +1057 -0
- package/admin/src/pages/App/components/GooglePayConfig.jsx +217 -0
- package/admin/src/pages/App/components/GooglePayConfigPanel.jsx +82 -0
- package/admin/src/pages/App/components/GooglePaybutton.jsx +300 -0
- package/admin/src/pages/App/components/HistoryPanel.jsx +285 -0
- package/admin/src/pages/App/components/PaymentActionsPanel.jsx +238 -0
- package/admin/src/pages/App/components/StatusBadge.jsx +24 -0
- package/admin/src/pages/App/components/TransactionHistoryItem.jsx +377 -0
- package/admin/src/pages/App/components/icons/BankIcon.jsx +10 -0
- package/admin/src/pages/App/components/icons/ChevronDownIcon.jsx +9 -0
- package/admin/src/pages/App/components/icons/ChevronUpIcon.jsx +9 -0
- package/admin/src/pages/App/components/icons/CreditCardIcon.jsx +9 -0
- package/admin/src/pages/App/components/icons/ErrorIcon.jsx +10 -0
- package/admin/src/pages/App/components/icons/InfoIcon.jsx +9 -0
- package/admin/src/pages/App/components/icons/PaymentIcon.jsx +10 -0
- package/admin/src/pages/App/components/icons/PendingIcon.jsx +9 -0
- package/admin/src/pages/App/components/icons/PersonIcon.jsx +9 -0
- package/admin/src/pages/App/components/icons/SuccessIcon.jsx +9 -0
- package/admin/src/pages/App/components/icons/WalletIcon.jsx +9 -0
- package/admin/src/pages/App/components/icons/index.jsx +11 -0
- package/admin/src/pages/App/components/paymentActions/AuthorizationForm.jsx +205 -0
- package/admin/src/pages/App/components/paymentActions/CaptureForm.jsx +65 -0
- package/admin/src/pages/App/components/paymentActions/CardDetailsInput.jsx +191 -0
- package/admin/src/pages/App/components/paymentActions/PaymentMethodSelector.jsx +236 -0
- package/admin/src/pages/App/components/paymentActions/PaymentResult.jsx +148 -0
- package/admin/src/pages/App/components/paymentActions/PreauthorizationForm.jsx +132 -0
- package/admin/src/pages/App/components/paymentActions/RefundForm.jsx +90 -0
- package/admin/src/pages/App/index.jsx +127 -0
- package/admin/src/pages/constants/paymentConstants.js +1 -2
- package/admin/src/pages/hooks/usePaymentActions.js +96 -0
- package/package.json +2 -2
- package/server/bootstrap.js +65 -0
- package/server/controllers/payone.js +4 -48
- package/server/routes/index.js +1 -1
- package/server/services/applePayService.js +51 -407
- package/server/services/paymentService.js +0 -68
- package/server/services/payone.js +0 -3
- package/server/services/settingsService.js +0 -21
- package/server/services/testConnectionService.js +0 -14
- package/server/services/transactionService.js +14 -0
- package/server/utils/paymentMethodParams.js +60 -27
- 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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
31
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
91
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
70
|
+
if (!validateSettings(settings)) {
|
|
71
|
+
strapi.log.error("Payone settings not configured");
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
191
74
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
|
|
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
|
-
|
|
205
|
-
|
|
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
|
-
|
|
221
|
-
|
|
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
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
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
|
-
|
|
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
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
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,
|