@sikka/aps 0.0.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/LICENSE +21 -0
- package/README.md +892 -0
- package/dist/chunk-FLFFXAST.mjs +408 -0
- package/dist/chunk-TGVOU6OE.mjs +409 -0
- package/dist/index.d.mts +2007 -0
- package/dist/index.d.ts +2007 -0
- package/dist/index.js +2822 -0
- package/dist/index.mjs +2741 -0
- package/dist/react/index.d.mts +932 -0
- package/dist/react/index.d.ts +932 -0
- package/dist/react/index.js +1863 -0
- package/dist/react/index.mjs +1829 -0
- package/package.json +77 -0
|
@@ -0,0 +1,1829 @@
|
|
|
1
|
+
// src/react/APSProvider.tsx
|
|
2
|
+
import { createContext, useContext } from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
var APSContext = createContext(void 0);
|
|
5
|
+
var APSProvider = ({ client, children }) => {
|
|
6
|
+
return /* @__PURE__ */ jsx(APSContext.Provider, { value: { client }, children });
|
|
7
|
+
};
|
|
8
|
+
function useAPSContext() {
|
|
9
|
+
const context = useContext(APSContext);
|
|
10
|
+
if (context === void 0) {
|
|
11
|
+
throw new Error(
|
|
12
|
+
"useAPS must be used within an APSProvider. Make sure to wrap your app with <APSProvider client={aps}>...</APSProvider>"
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
return context;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// src/react/useAPS.ts
|
|
19
|
+
function useAPS() {
|
|
20
|
+
const { client } = useAPSContext();
|
|
21
|
+
return client;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// src/react/useCheckout.ts
|
|
25
|
+
import { useState, useCallback, useRef } from "react";
|
|
26
|
+
function useCheckout() {
|
|
27
|
+
const aps = useAPS();
|
|
28
|
+
const [state, setState] = useState({
|
|
29
|
+
checkout: null,
|
|
30
|
+
isLoading: false,
|
|
31
|
+
error: null
|
|
32
|
+
});
|
|
33
|
+
const isMountedRef = useRef(true);
|
|
34
|
+
const createCheckout = useCallback(async (options) => {
|
|
35
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
36
|
+
try {
|
|
37
|
+
const checkout = await aps.hostedCheckout.create(options);
|
|
38
|
+
if (isMountedRef.current) {
|
|
39
|
+
setState({
|
|
40
|
+
checkout,
|
|
41
|
+
isLoading: false,
|
|
42
|
+
error: null
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return checkout;
|
|
46
|
+
} catch (err) {
|
|
47
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
48
|
+
if (isMountedRef.current) {
|
|
49
|
+
setState((prev) => ({
|
|
50
|
+
...prev,
|
|
51
|
+
isLoading: false,
|
|
52
|
+
error
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
}, [aps]);
|
|
58
|
+
const redirectToCheckout = useCallback(() => {
|
|
59
|
+
const { checkout } = state;
|
|
60
|
+
if (!checkout?.redirectForm) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
"No checkout session available. Call createCheckout() first."
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
const { url, method, params } = checkout.redirectForm;
|
|
66
|
+
const form = document.createElement("form");
|
|
67
|
+
form.method = method;
|
|
68
|
+
form.action = url;
|
|
69
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
70
|
+
const input = document.createElement("input");
|
|
71
|
+
input.type = "hidden";
|
|
72
|
+
input.name = key;
|
|
73
|
+
input.value = value;
|
|
74
|
+
form.appendChild(input);
|
|
75
|
+
});
|
|
76
|
+
document.body.appendChild(form);
|
|
77
|
+
form.submit();
|
|
78
|
+
}, [state]);
|
|
79
|
+
const reset = useCallback(() => {
|
|
80
|
+
setState({
|
|
81
|
+
checkout: null,
|
|
82
|
+
isLoading: false,
|
|
83
|
+
error: null
|
|
84
|
+
});
|
|
85
|
+
}, []);
|
|
86
|
+
return {
|
|
87
|
+
...state,
|
|
88
|
+
createCheckout,
|
|
89
|
+
redirectToCheckout,
|
|
90
|
+
reset
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/react/usePayment.ts
|
|
95
|
+
import { useState as useState2, useCallback as useCallback2, useRef as useRef2 } from "react";
|
|
96
|
+
function usePayment() {
|
|
97
|
+
const aps = useAPS();
|
|
98
|
+
const [state, setState] = useState2({
|
|
99
|
+
payment: null,
|
|
100
|
+
status: null,
|
|
101
|
+
isLoading: false,
|
|
102
|
+
error: null
|
|
103
|
+
});
|
|
104
|
+
const isMountedRef = useRef2(true);
|
|
105
|
+
const createPaymentLink = useCallback2(async (options) => {
|
|
106
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
107
|
+
try {
|
|
108
|
+
const payment = await aps.paymentLinks.create(options);
|
|
109
|
+
if (isMountedRef.current) {
|
|
110
|
+
setState({
|
|
111
|
+
payment,
|
|
112
|
+
status: "pending",
|
|
113
|
+
isLoading: false,
|
|
114
|
+
error: null
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
return payment;
|
|
118
|
+
} catch (err) {
|
|
119
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
120
|
+
if (isMountedRef.current) {
|
|
121
|
+
setState((prev) => ({
|
|
122
|
+
...prev,
|
|
123
|
+
isLoading: false,
|
|
124
|
+
error
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
}, [aps]);
|
|
130
|
+
const createCustomPayment = useCallback2(async (options) => {
|
|
131
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
132
|
+
try {
|
|
133
|
+
const payment = await aps.customPaymentPage.charge(options);
|
|
134
|
+
if (isMountedRef.current) {
|
|
135
|
+
setState({
|
|
136
|
+
payment,
|
|
137
|
+
status: payment.status,
|
|
138
|
+
isLoading: false,
|
|
139
|
+
error: null
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
return payment;
|
|
143
|
+
} catch (err) {
|
|
144
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
145
|
+
if (isMountedRef.current) {
|
|
146
|
+
setState((prev) => ({
|
|
147
|
+
...prev,
|
|
148
|
+
isLoading: false,
|
|
149
|
+
error
|
|
150
|
+
}));
|
|
151
|
+
}
|
|
152
|
+
throw error;
|
|
153
|
+
}
|
|
154
|
+
}, [aps]);
|
|
155
|
+
const checkStatus = useCallback2(async (options) => {
|
|
156
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
157
|
+
try {
|
|
158
|
+
const result = await aps.payments.query(options);
|
|
159
|
+
const status = result.status;
|
|
160
|
+
if (isMountedRef.current) {
|
|
161
|
+
setState((prev) => ({
|
|
162
|
+
...prev,
|
|
163
|
+
status,
|
|
164
|
+
isLoading: false,
|
|
165
|
+
error: null
|
|
166
|
+
}));
|
|
167
|
+
}
|
|
168
|
+
return status;
|
|
169
|
+
} catch (err) {
|
|
170
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
171
|
+
if (isMountedRef.current) {
|
|
172
|
+
setState((prev) => ({
|
|
173
|
+
...prev,
|
|
174
|
+
isLoading: false,
|
|
175
|
+
error
|
|
176
|
+
}));
|
|
177
|
+
}
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
}, [aps]);
|
|
181
|
+
const confirmPayment = useCallback2(async (options) => {
|
|
182
|
+
const {
|
|
183
|
+
transactionId,
|
|
184
|
+
pollInterval = 2e3,
|
|
185
|
+
maxAttempts = 30
|
|
186
|
+
} = options;
|
|
187
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
188
|
+
try {
|
|
189
|
+
let attempts = 0;
|
|
190
|
+
let finalStatus = "pending";
|
|
191
|
+
while (attempts < maxAttempts) {
|
|
192
|
+
const status = await checkStatus({ transactionId });
|
|
193
|
+
finalStatus = status;
|
|
194
|
+
if (["captured", "failed", "voided", "refunded", "cancelled"].includes(status)) {
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
198
|
+
attempts++;
|
|
199
|
+
}
|
|
200
|
+
if (isMountedRef.current) {
|
|
201
|
+
setState((prev) => ({
|
|
202
|
+
...prev,
|
|
203
|
+
status: finalStatus,
|
|
204
|
+
isLoading: false,
|
|
205
|
+
error: null
|
|
206
|
+
}));
|
|
207
|
+
}
|
|
208
|
+
return finalStatus;
|
|
209
|
+
} catch (err) {
|
|
210
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
211
|
+
if (isMountedRef.current) {
|
|
212
|
+
setState((prev) => ({
|
|
213
|
+
...prev,
|
|
214
|
+
isLoading: false,
|
|
215
|
+
error
|
|
216
|
+
}));
|
|
217
|
+
}
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
220
|
+
}, [checkStatus]);
|
|
221
|
+
const capture = useCallback2(async (transactionId, amount) => {
|
|
222
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
223
|
+
try {
|
|
224
|
+
await aps.payments.capture({ transactionId, amount });
|
|
225
|
+
if (isMountedRef.current) {
|
|
226
|
+
setState((prev) => ({
|
|
227
|
+
...prev,
|
|
228
|
+
status: "captured",
|
|
229
|
+
isLoading: false,
|
|
230
|
+
error: null
|
|
231
|
+
}));
|
|
232
|
+
}
|
|
233
|
+
} catch (err) {
|
|
234
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
235
|
+
if (isMountedRef.current) {
|
|
236
|
+
setState((prev) => ({
|
|
237
|
+
...prev,
|
|
238
|
+
isLoading: false,
|
|
239
|
+
error
|
|
240
|
+
}));
|
|
241
|
+
}
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
244
|
+
}, [aps]);
|
|
245
|
+
const refund = useCallback2(async (transactionId, amount, reason) => {
|
|
246
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
247
|
+
try {
|
|
248
|
+
await aps.payments.refund({ transactionId, amount, reason });
|
|
249
|
+
if (isMountedRef.current) {
|
|
250
|
+
setState((prev) => ({
|
|
251
|
+
...prev,
|
|
252
|
+
status: "refunded",
|
|
253
|
+
isLoading: false,
|
|
254
|
+
error: null
|
|
255
|
+
}));
|
|
256
|
+
}
|
|
257
|
+
} catch (err) {
|
|
258
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
259
|
+
if (isMountedRef.current) {
|
|
260
|
+
setState((prev) => ({
|
|
261
|
+
...prev,
|
|
262
|
+
isLoading: false,
|
|
263
|
+
error
|
|
264
|
+
}));
|
|
265
|
+
}
|
|
266
|
+
throw error;
|
|
267
|
+
}
|
|
268
|
+
}, [aps]);
|
|
269
|
+
const voidPayment = useCallback2(async (transactionId, reason) => {
|
|
270
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
271
|
+
try {
|
|
272
|
+
await aps.payments.void({ transactionId, reason });
|
|
273
|
+
if (isMountedRef.current) {
|
|
274
|
+
setState((prev) => ({
|
|
275
|
+
...prev,
|
|
276
|
+
status: "voided",
|
|
277
|
+
isLoading: false,
|
|
278
|
+
error: null
|
|
279
|
+
}));
|
|
280
|
+
}
|
|
281
|
+
} catch (err) {
|
|
282
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
283
|
+
if (isMountedRef.current) {
|
|
284
|
+
setState((prev) => ({
|
|
285
|
+
...prev,
|
|
286
|
+
isLoading: false,
|
|
287
|
+
error
|
|
288
|
+
}));
|
|
289
|
+
}
|
|
290
|
+
throw error;
|
|
291
|
+
}
|
|
292
|
+
}, [aps]);
|
|
293
|
+
const reset = useCallback2(() => {
|
|
294
|
+
setState({
|
|
295
|
+
payment: null,
|
|
296
|
+
status: null,
|
|
297
|
+
isLoading: false,
|
|
298
|
+
error: null
|
|
299
|
+
});
|
|
300
|
+
}, []);
|
|
301
|
+
return {
|
|
302
|
+
...state,
|
|
303
|
+
createPaymentLink,
|
|
304
|
+
createCustomPayment,
|
|
305
|
+
confirmPayment,
|
|
306
|
+
checkStatus,
|
|
307
|
+
capture,
|
|
308
|
+
refund,
|
|
309
|
+
void: voidPayment,
|
|
310
|
+
reset
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// src/react/TokenizationForm.tsx
|
|
315
|
+
import { useState as useState3 } from "react";
|
|
316
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
317
|
+
function detectCardBrand(cardNumber) {
|
|
318
|
+
const number = cardNumber.replace(/\D/g, "");
|
|
319
|
+
if (/^4/.test(number)) return "visa";
|
|
320
|
+
if (/^5[1-5]/.test(number)) return "mastercard";
|
|
321
|
+
if (/^3[47]/.test(number)) return "amex";
|
|
322
|
+
if (/^6(?:011|5)/.test(number)) return "discover";
|
|
323
|
+
if (/^(?:2131|1800|35)/.test(number)) return "jcb";
|
|
324
|
+
if (/^9792/.test(number)) return "troy";
|
|
325
|
+
if (/^50/.test(number) || /^4571/.test(number)) return "mada";
|
|
326
|
+
return "unknown";
|
|
327
|
+
}
|
|
328
|
+
var DefaultIcons = {
|
|
329
|
+
visa: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 48 48", className: "w-8 h-8", children: [
|
|
330
|
+
/* @__PURE__ */ jsx2("rect", { fill: "#1A1F71", rx: "4", width: "48", height: "48" }),
|
|
331
|
+
/* @__PURE__ */ jsx2(
|
|
332
|
+
"path",
|
|
333
|
+
{
|
|
334
|
+
fill: "#fff",
|
|
335
|
+
d: "M19.5 30h-3l1.9-11.5h3L19.5 30zm11.4 0h-2.7l-1.7-8.2c-.2-.9-.4-1.7-.5-2.6-.1 0-.2.1-.3.2-.6.8-1.3 1.6-1.9 2.4L22.5 30h-3l3.1-11.5h2.8l.4 2.1c.7-1 1.5-2 2.3-2.9.9-1.2 2-1.7 3.4-1.7.5 0 1 0 1.5.1l-2.1 13.9zm6.6-11.3c-.8-.3-2.6-.7-4.6-.7-5.1 0-8.7 2.7-8.7 6.5 0 2.9 2.6 4.5 4.6 5.5 2 .9 2.7 1.6 2.7 2.4 0 1.3-1.6 1.9-3 1.9-2 0-3.1-.3-4.8-1l-.7-.3-.7 4.3c1.2.5 3.4 1 5.7 1 5.4 0 8.9-2.7 8.9-6.8 0-2.3-1.4-4-4.4-5.5-1.8-.9-2.9-1.6-2.9-2.5 0-.8.9-1.7 2.8-1.7 1.6 0 2.8.3 3.7.7l.4.2.7-4.2z"
|
|
336
|
+
}
|
|
337
|
+
)
|
|
338
|
+
] }),
|
|
339
|
+
mastercard: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 48 48", className: "w-8 h-8", children: [
|
|
340
|
+
/* @__PURE__ */ jsx2("rect", { fill: "#000", rx: "4", width: "48", height: "48" }),
|
|
341
|
+
/* @__PURE__ */ jsx2("circle", { fill: "#EB001B", cx: "18", cy: "24", r: "10" }),
|
|
342
|
+
/* @__PURE__ */ jsx2("circle", { fill: "#F79E1B", cx: "30", cy: "24", r: "10", opacity: "0.8" })
|
|
343
|
+
] }),
|
|
344
|
+
amex: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 48 48", className: "w-8 h-8", children: [
|
|
345
|
+
/* @__PURE__ */ jsx2("rect", { fill: "#006FCF", rx: "4", width: "48", height: "48" }),
|
|
346
|
+
/* @__PURE__ */ jsx2(
|
|
347
|
+
"path",
|
|
348
|
+
{
|
|
349
|
+
fill: "#fff",
|
|
350
|
+
d: "M10 18h28v12H10V18zm2 2v8h24v-8H12z"
|
|
351
|
+
}
|
|
352
|
+
)
|
|
353
|
+
] }),
|
|
354
|
+
mada: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 48 48", className: "w-8 h-8", children: [
|
|
355
|
+
/* @__PURE__ */ jsx2("rect", { fill: "#006C35", rx: "4", width: "48", height: "48" }),
|
|
356
|
+
/* @__PURE__ */ jsx2(
|
|
357
|
+
"path",
|
|
358
|
+
{
|
|
359
|
+
fill: "#fff",
|
|
360
|
+
d: "M12 20h24v8H12v-8zm2 2v4h20v-4H14z"
|
|
361
|
+
}
|
|
362
|
+
)
|
|
363
|
+
] }),
|
|
364
|
+
unknown: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 48 48", className: "w-8 h-8", children: [
|
|
365
|
+
/* @__PURE__ */ jsx2("rect", { fill: "#666", rx: "4", width: "48", height: "48" }),
|
|
366
|
+
/* @__PURE__ */ jsx2("rect", { fill: "#999", x: "10", y: "18", width: "28", height: "12", rx: "2" })
|
|
367
|
+
] })
|
|
368
|
+
};
|
|
369
|
+
var defaultStyles = {
|
|
370
|
+
container: {
|
|
371
|
+
maxWidth: "480px",
|
|
372
|
+
margin: "0 auto"
|
|
373
|
+
},
|
|
374
|
+
form: {
|
|
375
|
+
display: "flex",
|
|
376
|
+
flexDirection: "column",
|
|
377
|
+
gap: "16px"
|
|
378
|
+
},
|
|
379
|
+
inputGroup: {
|
|
380
|
+
display: "flex",
|
|
381
|
+
flexDirection: "column",
|
|
382
|
+
gap: "4px"
|
|
383
|
+
},
|
|
384
|
+
label: {
|
|
385
|
+
fontSize: "14px",
|
|
386
|
+
fontWeight: "500",
|
|
387
|
+
color: "#374151"
|
|
388
|
+
},
|
|
389
|
+
input: {
|
|
390
|
+
padding: "12px 16px",
|
|
391
|
+
fontSize: "16px",
|
|
392
|
+
border: "1px solid #D1D5DB",
|
|
393
|
+
borderRadius: "8px",
|
|
394
|
+
outline: "none",
|
|
395
|
+
transition: "border-color 0.2s"
|
|
396
|
+
},
|
|
397
|
+
inputError: {
|
|
398
|
+
borderColor: "#EF4444"
|
|
399
|
+
},
|
|
400
|
+
row: {
|
|
401
|
+
display: "grid",
|
|
402
|
+
gridTemplateColumns: "1fr 1fr",
|
|
403
|
+
gap: "12px"
|
|
404
|
+
},
|
|
405
|
+
button: {
|
|
406
|
+
padding: "14px 24px",
|
|
407
|
+
fontSize: "16px",
|
|
408
|
+
fontWeight: "600",
|
|
409
|
+
color: "#fff",
|
|
410
|
+
backgroundColor: "#7C3AED",
|
|
411
|
+
border: "none",
|
|
412
|
+
borderRadius: "8px",
|
|
413
|
+
cursor: "pointer",
|
|
414
|
+
transition: "background-color 0.2s"
|
|
415
|
+
},
|
|
416
|
+
buttonDisabled: {
|
|
417
|
+
backgroundColor: "#A78BFA",
|
|
418
|
+
cursor: "not-allowed"
|
|
419
|
+
},
|
|
420
|
+
securityNotice: {
|
|
421
|
+
padding: "12px",
|
|
422
|
+
backgroundColor: "#D1FAE5",
|
|
423
|
+
borderRadius: "8px",
|
|
424
|
+
fontSize: "13px",
|
|
425
|
+
color: "#065F46"
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
var TokenizationForm = ({
|
|
429
|
+
actionUrl,
|
|
430
|
+
formParams,
|
|
431
|
+
customerEmail = "",
|
|
432
|
+
onSuccess: _onSuccess,
|
|
433
|
+
// Success is handled via returnUrl redirect
|
|
434
|
+
onError,
|
|
435
|
+
styles = {},
|
|
436
|
+
icons = {},
|
|
437
|
+
labels = {},
|
|
438
|
+
placeholders = {},
|
|
439
|
+
disableFormatting = false,
|
|
440
|
+
showCardIcons = true,
|
|
441
|
+
showSecurityNotice = true,
|
|
442
|
+
className = {}
|
|
443
|
+
}) => {
|
|
444
|
+
const [cardNumber, setCardNumber] = useState3("");
|
|
445
|
+
const [expiryDate, setExpiryDate] = useState3("");
|
|
446
|
+
const [cvv, setCvv] = useState3("");
|
|
447
|
+
const [cardHolderName, setCardHolderName] = useState3("");
|
|
448
|
+
const [email, setEmail] = useState3(customerEmail);
|
|
449
|
+
const [loading, setLoading] = useState3(false);
|
|
450
|
+
const [errors, setErrors] = useState3({});
|
|
451
|
+
const [cardBrand, setCardBrand] = useState3("unknown");
|
|
452
|
+
const mergedStyles = {
|
|
453
|
+
...defaultStyles,
|
|
454
|
+
...styles
|
|
455
|
+
};
|
|
456
|
+
const mergedIcons = {
|
|
457
|
+
...DefaultIcons,
|
|
458
|
+
...icons
|
|
459
|
+
};
|
|
460
|
+
const mergedLabels = {
|
|
461
|
+
cardNumber: "Card Number",
|
|
462
|
+
cardHolder: "Card Holder Name",
|
|
463
|
+
customerEmail: "Customer Email",
|
|
464
|
+
expiryDate: "Expiry Date",
|
|
465
|
+
cvv: "CVV",
|
|
466
|
+
submitButton: "Secure Payment",
|
|
467
|
+
processing: "Processing...",
|
|
468
|
+
...labels
|
|
469
|
+
};
|
|
470
|
+
const mergedPlaceholders = {
|
|
471
|
+
cardNumber: "1234 5678 9012 3456",
|
|
472
|
+
cardHolder: "John Doe",
|
|
473
|
+
customerEmail: "customer@example.com",
|
|
474
|
+
expiryDate: "MM/YY",
|
|
475
|
+
cvv: "123",
|
|
476
|
+
...placeholders
|
|
477
|
+
};
|
|
478
|
+
const formatCardNumber = (value) => {
|
|
479
|
+
if (disableFormatting) return value;
|
|
480
|
+
const v = value.replace(/\s+/g, "").replace(/[^0-9]/gi, "");
|
|
481
|
+
const matches = v.match(/\d{4,16}/g);
|
|
482
|
+
const match = matches && matches[0] || "";
|
|
483
|
+
const parts = [];
|
|
484
|
+
for (let i = 0, len = match.length; i < len; i += 4) {
|
|
485
|
+
parts.push(match.substring(i, i + 4));
|
|
486
|
+
}
|
|
487
|
+
if (parts.length) {
|
|
488
|
+
return parts.join(" ");
|
|
489
|
+
}
|
|
490
|
+
return value;
|
|
491
|
+
};
|
|
492
|
+
const formatExpiryDate = (value) => {
|
|
493
|
+
if (disableFormatting) return value;
|
|
494
|
+
const v = value.replace(/\s+/g, "").replace(/[^0-9]/gi, "");
|
|
495
|
+
if (v.length >= 2) {
|
|
496
|
+
return v.substring(0, 2) + "/" + v.substring(2, 4);
|
|
497
|
+
}
|
|
498
|
+
return v;
|
|
499
|
+
};
|
|
500
|
+
const validateForm = () => {
|
|
501
|
+
const newErrors = {};
|
|
502
|
+
if (!cardNumber || cardNumber.replace(/\s/g, "").length < 13) {
|
|
503
|
+
newErrors.cardNumber = "Invalid card number";
|
|
504
|
+
}
|
|
505
|
+
if (!expiryDate || expiryDate.length !== 5) {
|
|
506
|
+
newErrors.expiryDate = "Invalid expiry date";
|
|
507
|
+
}
|
|
508
|
+
if (!cvv || cvv.length < 3) {
|
|
509
|
+
newErrors.cvv = "Invalid CVV";
|
|
510
|
+
}
|
|
511
|
+
if (!cardHolderName || cardHolderName.length < 2) {
|
|
512
|
+
newErrors.cardHolder = "Card holder name is required";
|
|
513
|
+
}
|
|
514
|
+
if (email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
515
|
+
newErrors.email = "Invalid email address";
|
|
516
|
+
}
|
|
517
|
+
setErrors(newErrors);
|
|
518
|
+
return Object.keys(newErrors).length === 0;
|
|
519
|
+
};
|
|
520
|
+
const handleSubmit = (e) => {
|
|
521
|
+
e.preventDefault();
|
|
522
|
+
if (!validateForm()) {
|
|
523
|
+
onError?.("Please fill in all required fields correctly");
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
setLoading(true);
|
|
527
|
+
const form = document.createElement("form");
|
|
528
|
+
form.method = "POST";
|
|
529
|
+
form.action = actionUrl;
|
|
530
|
+
form.style.display = "none";
|
|
531
|
+
Object.entries(formParams).forEach(([key, value]) => {
|
|
532
|
+
const input = document.createElement("input");
|
|
533
|
+
input.type = "hidden";
|
|
534
|
+
input.name = key;
|
|
535
|
+
input.value = value;
|
|
536
|
+
form.appendChild(input);
|
|
537
|
+
});
|
|
538
|
+
const addHiddenInput = (name, value) => {
|
|
539
|
+
const input = document.createElement("input");
|
|
540
|
+
input.type = "hidden";
|
|
541
|
+
input.name = name;
|
|
542
|
+
input.value = value;
|
|
543
|
+
form.appendChild(input);
|
|
544
|
+
};
|
|
545
|
+
const cleanCardNumber = cardNumber.replace(/\s/g, "");
|
|
546
|
+
const cleanExpiryDate = expiryDate.replace("/", "");
|
|
547
|
+
addHiddenInput("card_number", cleanCardNumber);
|
|
548
|
+
addHiddenInput("expiry_date", cleanExpiryDate);
|
|
549
|
+
addHiddenInput("card_security_code", cvv);
|
|
550
|
+
addHiddenInput("card_holder_name", cardHolderName);
|
|
551
|
+
document.body.appendChild(form);
|
|
552
|
+
form.submit();
|
|
553
|
+
setLoading(false);
|
|
554
|
+
};
|
|
555
|
+
const handleCardNumberChange = (e) => {
|
|
556
|
+
const formatted = formatCardNumber(e.target.value);
|
|
557
|
+
setCardNumber(formatted);
|
|
558
|
+
setCardBrand(detectCardBrand(formatted));
|
|
559
|
+
if (errors.cardNumber) {
|
|
560
|
+
setErrors({ ...errors, cardNumber: "" });
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
const handleExpiryDateChange = (e) => {
|
|
564
|
+
const formatted = formatExpiryDate(e.target.value);
|
|
565
|
+
setExpiryDate(formatted);
|
|
566
|
+
if (errors.expiryDate) {
|
|
567
|
+
setErrors({ ...errors, expiryDate: "" });
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
const handleCvvChange = (e) => {
|
|
571
|
+
const value = e.target.value.replace(/\D/g, "").substring(0, 4);
|
|
572
|
+
setCvv(value);
|
|
573
|
+
if (errors.cvv) {
|
|
574
|
+
setErrors({ ...errors, cvv: "" });
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
return /* @__PURE__ */ jsx2("div", { style: mergedStyles.container, className: className.container || "", children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, style: mergedStyles.form, className: className.form || "", children: [
|
|
578
|
+
/* @__PURE__ */ jsxs("div", { style: mergedStyles.inputGroup, className: className.inputGroup || "", children: [
|
|
579
|
+
/* @__PURE__ */ jsx2("label", { style: mergedStyles.label, className: className.label || "", children: mergedLabels.cardNumber }),
|
|
580
|
+
/* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
|
|
581
|
+
/* @__PURE__ */ jsx2(
|
|
582
|
+
"input",
|
|
583
|
+
{
|
|
584
|
+
type: "text",
|
|
585
|
+
value: cardNumber,
|
|
586
|
+
onChange: handleCardNumberChange,
|
|
587
|
+
placeholder: mergedPlaceholders.cardNumber,
|
|
588
|
+
maxLength: 19,
|
|
589
|
+
required: true,
|
|
590
|
+
style: {
|
|
591
|
+
...mergedStyles.input,
|
|
592
|
+
...errors.cardNumber ? mergedStyles.inputError : {},
|
|
593
|
+
paddingRight: showCardIcons ? "60px" : "16px"
|
|
594
|
+
},
|
|
595
|
+
className: className.input || ""
|
|
596
|
+
}
|
|
597
|
+
),
|
|
598
|
+
showCardIcons && cardBrand !== "unknown" && /* @__PURE__ */ jsx2(
|
|
599
|
+
"div",
|
|
600
|
+
{
|
|
601
|
+
style: {
|
|
602
|
+
position: "absolute",
|
|
603
|
+
right: "12px",
|
|
604
|
+
top: "50%",
|
|
605
|
+
transform: "translateY(-50%)"
|
|
606
|
+
},
|
|
607
|
+
children: mergedIcons[cardBrand]
|
|
608
|
+
}
|
|
609
|
+
)
|
|
610
|
+
] }),
|
|
611
|
+
errors.cardNumber && /* @__PURE__ */ jsx2("span", { style: { color: "#EF4444", fontSize: "12px" }, children: errors.cardNumber })
|
|
612
|
+
] }),
|
|
613
|
+
/* @__PURE__ */ jsxs("div", { style: mergedStyles.inputGroup, className: className.inputGroup || "", children: [
|
|
614
|
+
/* @__PURE__ */ jsx2("label", { style: mergedStyles.label, className: className.label || "", children: mergedLabels.cardHolder }),
|
|
615
|
+
/* @__PURE__ */ jsx2(
|
|
616
|
+
"input",
|
|
617
|
+
{
|
|
618
|
+
type: "text",
|
|
619
|
+
value: cardHolderName,
|
|
620
|
+
onChange: (e) => {
|
|
621
|
+
setCardHolderName(e.target.value);
|
|
622
|
+
if (errors.cardHolder) {
|
|
623
|
+
setErrors({ ...errors, cardHolder: "" });
|
|
624
|
+
}
|
|
625
|
+
},
|
|
626
|
+
placeholder: mergedPlaceholders.cardHolder,
|
|
627
|
+
required: true,
|
|
628
|
+
style: {
|
|
629
|
+
...mergedStyles.input,
|
|
630
|
+
...errors.cardHolder ? mergedStyles.inputError : {}
|
|
631
|
+
},
|
|
632
|
+
className: className.input || ""
|
|
633
|
+
}
|
|
634
|
+
),
|
|
635
|
+
errors.cardHolder && /* @__PURE__ */ jsx2("span", { style: { color: "#EF4444", fontSize: "12px" }, children: errors.cardHolder })
|
|
636
|
+
] }),
|
|
637
|
+
/* @__PURE__ */ jsxs("div", { style: mergedStyles.inputGroup, className: className.inputGroup || "", children: [
|
|
638
|
+
/* @__PURE__ */ jsx2("label", { style: mergedStyles.label, className: className.label || "", children: mergedLabels.customerEmail }),
|
|
639
|
+
/* @__PURE__ */ jsx2(
|
|
640
|
+
"input",
|
|
641
|
+
{
|
|
642
|
+
type: "email",
|
|
643
|
+
value: email,
|
|
644
|
+
onChange: (e) => {
|
|
645
|
+
setEmail(e.target.value);
|
|
646
|
+
if (errors.email) {
|
|
647
|
+
setErrors({ ...errors, email: "" });
|
|
648
|
+
}
|
|
649
|
+
},
|
|
650
|
+
placeholder: mergedPlaceholders.customerEmail,
|
|
651
|
+
style: {
|
|
652
|
+
...mergedStyles.input,
|
|
653
|
+
...errors.email ? mergedStyles.inputError : {}
|
|
654
|
+
},
|
|
655
|
+
className: className.input || ""
|
|
656
|
+
}
|
|
657
|
+
),
|
|
658
|
+
errors.email && /* @__PURE__ */ jsx2("span", { style: { color: "#EF4444", fontSize: "12px" }, children: errors.email }),
|
|
659
|
+
/* @__PURE__ */ jsx2("span", { style: { fontSize: "12px", color: "#6B7280" }, children: "Used to associate the saved card with the customer" })
|
|
660
|
+
] }),
|
|
661
|
+
/* @__PURE__ */ jsxs("div", { style: mergedStyles.row, children: [
|
|
662
|
+
/* @__PURE__ */ jsxs("div", { style: mergedStyles.inputGroup, className: className.inputGroup || "", children: [
|
|
663
|
+
/* @__PURE__ */ jsx2("label", { style: mergedStyles.label, className: className.label || "", children: mergedLabels.expiryDate }),
|
|
664
|
+
/* @__PURE__ */ jsx2(
|
|
665
|
+
"input",
|
|
666
|
+
{
|
|
667
|
+
type: "text",
|
|
668
|
+
value: expiryDate,
|
|
669
|
+
onChange: handleExpiryDateChange,
|
|
670
|
+
placeholder: mergedPlaceholders.expiryDate,
|
|
671
|
+
maxLength: 5,
|
|
672
|
+
required: true,
|
|
673
|
+
style: {
|
|
674
|
+
...mergedStyles.input,
|
|
675
|
+
...errors.expiryDate ? mergedStyles.inputError : {}
|
|
676
|
+
},
|
|
677
|
+
className: className.input || ""
|
|
678
|
+
}
|
|
679
|
+
),
|
|
680
|
+
errors.expiryDate && /* @__PURE__ */ jsx2("span", { style: { color: "#EF4444", fontSize: "12px" }, children: errors.expiryDate })
|
|
681
|
+
] }),
|
|
682
|
+
/* @__PURE__ */ jsxs("div", { style: mergedStyles.inputGroup, className: className.inputGroup || "", children: [
|
|
683
|
+
/* @__PURE__ */ jsx2("label", { style: mergedStyles.label, className: className.label || "", children: mergedLabels.cvv }),
|
|
684
|
+
/* @__PURE__ */ jsx2(
|
|
685
|
+
"input",
|
|
686
|
+
{
|
|
687
|
+
type: "text",
|
|
688
|
+
value: cvv,
|
|
689
|
+
onChange: handleCvvChange,
|
|
690
|
+
placeholder: mergedPlaceholders.cvv,
|
|
691
|
+
maxLength: 4,
|
|
692
|
+
required: true,
|
|
693
|
+
style: {
|
|
694
|
+
...mergedStyles.input,
|
|
695
|
+
...errors.cvv ? mergedStyles.inputError : {}
|
|
696
|
+
},
|
|
697
|
+
className: className.input || ""
|
|
698
|
+
}
|
|
699
|
+
),
|
|
700
|
+
errors.cvv && /* @__PURE__ */ jsx2("span", { style: { color: "#EF4444", fontSize: "12px" }, children: errors.cvv })
|
|
701
|
+
] })
|
|
702
|
+
] }),
|
|
703
|
+
showSecurityNotice && /* @__PURE__ */ jsx2("div", { style: mergedStyles.securityNotice, children: "\u{1F512} Your card details are securely processed by Amazon Payment Services. We never store your card information." }),
|
|
704
|
+
/* @__PURE__ */ jsx2(
|
|
705
|
+
"button",
|
|
706
|
+
{
|
|
707
|
+
type: "submit",
|
|
708
|
+
disabled: loading,
|
|
709
|
+
style: {
|
|
710
|
+
...mergedStyles.button,
|
|
711
|
+
...loading ? mergedStyles.buttonDisabled : {}
|
|
712
|
+
},
|
|
713
|
+
className: className.button || "",
|
|
714
|
+
children: loading ? mergedLabels.processing : mergedLabels.submitButton
|
|
715
|
+
}
|
|
716
|
+
)
|
|
717
|
+
] }) });
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
// src/constants.ts
|
|
721
|
+
var ResponseCodes = {
|
|
722
|
+
// Success Codes
|
|
723
|
+
/** Transaction successful */
|
|
724
|
+
SUCCESS: "00000",
|
|
725
|
+
/** Payment link created successfully */
|
|
726
|
+
PAYMENT_LINK_SUCCESS: "48000",
|
|
727
|
+
/** Recurring payment successful */
|
|
728
|
+
RECURRING_SUCCESS: "14000",
|
|
729
|
+
// Authentication & Security
|
|
730
|
+
/** 3D Secure authentication failed */
|
|
731
|
+
AUTHENTICATION_FAILED: "10030",
|
|
732
|
+
/** 3D Secure not enrolled */
|
|
733
|
+
NOT_ENROLLED_3DS: "10031",
|
|
734
|
+
/** Signature mismatch */
|
|
735
|
+
SIGNATURE_MISMATCH: "00008",
|
|
736
|
+
/** Invalid access code */
|
|
737
|
+
INVALID_ACCESS_CODE: "00009",
|
|
738
|
+
/** Invalid merchant identifier */
|
|
739
|
+
INVALID_MERCHANT_ID: "00010",
|
|
740
|
+
// Card Errors
|
|
741
|
+
/** Card expired */
|
|
742
|
+
CARD_EXPIRED: "10035",
|
|
743
|
+
/** Invalid card number */
|
|
744
|
+
INVALID_CARD_NUMBER: "10036",
|
|
745
|
+
/** Invalid CVV */
|
|
746
|
+
INVALID_CVV: "10037",
|
|
747
|
+
/** Card not supported */
|
|
748
|
+
CARD_NOT_SUPPORTED: "10038",
|
|
749
|
+
/** Lost card */
|
|
750
|
+
LOST_CARD: "10039",
|
|
751
|
+
/** Stolen card */
|
|
752
|
+
STOLEN_CARD: "10040",
|
|
753
|
+
/** Restricted card */
|
|
754
|
+
RESTRICTED_CARD: "10041",
|
|
755
|
+
/** Card blocked */
|
|
756
|
+
CARD_BLOCKED: "10042",
|
|
757
|
+
/** Invalid expiry date */
|
|
758
|
+
INVALID_EXPIRY: "10043",
|
|
759
|
+
// Transaction Errors
|
|
760
|
+
/** Transaction declined */
|
|
761
|
+
DECLINED: "14001",
|
|
762
|
+
/** Insufficient funds */
|
|
763
|
+
INSUFFICIENT_FUNDS: "14002",
|
|
764
|
+
/** Transaction limit exceeded */
|
|
765
|
+
LIMIT_EXCEEDED: "14003",
|
|
766
|
+
/** Invalid transaction */
|
|
767
|
+
INVALID_TRANSACTION: "14004",
|
|
768
|
+
/** Duplicate transaction */
|
|
769
|
+
DUPLICATE_TRANSACTION: "14005",
|
|
770
|
+
/** Transaction not allowed */
|
|
771
|
+
TRANSACTION_NOT_ALLOWED: "14006",
|
|
772
|
+
/** Transaction expired */
|
|
773
|
+
TRANSACTION_EXPIRED: "14007",
|
|
774
|
+
/** Transaction already captured */
|
|
775
|
+
ALREADY_CAPTURED: "14008",
|
|
776
|
+
/** Transaction already voided */
|
|
777
|
+
ALREADY_VOIDED: "14009",
|
|
778
|
+
/** Transaction already refunded */
|
|
779
|
+
ALREADY_REFUNDED: "14010",
|
|
780
|
+
// Amount Errors
|
|
781
|
+
/** Invalid amount */
|
|
782
|
+
INVALID_AMOUNT: "15001",
|
|
783
|
+
/** Amount too small */
|
|
784
|
+
AMOUNT_TOO_SMALL: "15002",
|
|
785
|
+
/** Amount too large */
|
|
786
|
+
AMOUNT_TOO_LARGE: "15003",
|
|
787
|
+
/** Currency not supported */
|
|
788
|
+
CURRENCY_NOT_SUPPORTED: "15004",
|
|
789
|
+
// Parameter Errors
|
|
790
|
+
/** Missing parameter */
|
|
791
|
+
MISSING_PARAMETER: "00001",
|
|
792
|
+
/** Invalid parameter format */
|
|
793
|
+
INVALID_PARAMETER: "00002",
|
|
794
|
+
/** Invalid command */
|
|
795
|
+
INVALID_COMMAND: "00003",
|
|
796
|
+
/** Invalid language */
|
|
797
|
+
INVALID_LANGUAGE: "00004",
|
|
798
|
+
/** Invalid currency */
|
|
799
|
+
INVALID_CURRENCY: "00005",
|
|
800
|
+
/** Invalid return URL */
|
|
801
|
+
INVALID_RETURN_URL: "00006",
|
|
802
|
+
/** Invalid customer email */
|
|
803
|
+
INVALID_EMAIL: "00007",
|
|
804
|
+
/** Parameter value not allowed */
|
|
805
|
+
PARAMETER_VALUE_NOT_ALLOWED: "00033",
|
|
806
|
+
// Tokenization Errors
|
|
807
|
+
/** Token not found */
|
|
808
|
+
TOKEN_NOT_FOUND: "16001",
|
|
809
|
+
/** Token expired */
|
|
810
|
+
TOKEN_EXPIRED: "16002",
|
|
811
|
+
/** Token already used */
|
|
812
|
+
TOKEN_ALREADY_USED: "16003",
|
|
813
|
+
/** Invalid token */
|
|
814
|
+
INVALID_TOKEN: "16004",
|
|
815
|
+
// System Errors
|
|
816
|
+
/** System error */
|
|
817
|
+
SYSTEM_ERROR: "90001",
|
|
818
|
+
/** Service unavailable */
|
|
819
|
+
SERVICE_UNAVAILABLE: "90002",
|
|
820
|
+
/** Timeout */
|
|
821
|
+
TIMEOUT: "90003",
|
|
822
|
+
/** Database error */
|
|
823
|
+
DATABASE_ERROR: "90004",
|
|
824
|
+
/** Internal error */
|
|
825
|
+
INTERNAL_ERROR: "90005"
|
|
826
|
+
};
|
|
827
|
+
var ErrorCategories = {
|
|
828
|
+
/** Success - no error */
|
|
829
|
+
SUCCESS: "success",
|
|
830
|
+
/** Card-related errors (expired, invalid, blocked) */
|
|
831
|
+
CARD_ERROR: "card_error",
|
|
832
|
+
/** Authentication errors (3DS, signature) */
|
|
833
|
+
AUTHENTICATION_ERROR: "authentication_error",
|
|
834
|
+
/** Transaction errors (declined, insufficient funds) */
|
|
835
|
+
TRANSACTION_ERROR: "transaction_error",
|
|
836
|
+
/** Amount/currency errors */
|
|
837
|
+
AMOUNT_ERROR: "amount_error",
|
|
838
|
+
/** Parameter validation errors */
|
|
839
|
+
VALIDATION_ERROR: "validation_error",
|
|
840
|
+
/** Tokenization errors */
|
|
841
|
+
TOKEN_ERROR: "token_error",
|
|
842
|
+
/** System/technical errors */
|
|
843
|
+
SYSTEM_ERROR: "system_error",
|
|
844
|
+
/** Errors that can be retried */
|
|
845
|
+
RETRYABLE: "retryable",
|
|
846
|
+
/** Errors that should not be retried */
|
|
847
|
+
NON_RETRYABLE: "non_retryable"
|
|
848
|
+
};
|
|
849
|
+
var ERROR_CODE_TO_CATEGORY = {
|
|
850
|
+
[ResponseCodes.SUCCESS]: ErrorCategories.SUCCESS,
|
|
851
|
+
[ResponseCodes.PAYMENT_LINK_SUCCESS]: ErrorCategories.SUCCESS,
|
|
852
|
+
[ResponseCodes.RECURRING_SUCCESS]: ErrorCategories.SUCCESS,
|
|
853
|
+
// Card errors
|
|
854
|
+
[ResponseCodes.CARD_EXPIRED]: ErrorCategories.CARD_ERROR,
|
|
855
|
+
[ResponseCodes.INVALID_CARD_NUMBER]: ErrorCategories.CARD_ERROR,
|
|
856
|
+
[ResponseCodes.INVALID_CVV]: ErrorCategories.CARD_ERROR,
|
|
857
|
+
[ResponseCodes.CARD_NOT_SUPPORTED]: ErrorCategories.CARD_ERROR,
|
|
858
|
+
[ResponseCodes.LOST_CARD]: ErrorCategories.CARD_ERROR,
|
|
859
|
+
[ResponseCodes.STOLEN_CARD]: ErrorCategories.CARD_ERROR,
|
|
860
|
+
[ResponseCodes.RESTRICTED_CARD]: ErrorCategories.CARD_ERROR,
|
|
861
|
+
[ResponseCodes.CARD_BLOCKED]: ErrorCategories.CARD_ERROR,
|
|
862
|
+
[ResponseCodes.INVALID_EXPIRY]: ErrorCategories.CARD_ERROR,
|
|
863
|
+
// Authentication errors
|
|
864
|
+
[ResponseCodes.AUTHENTICATION_FAILED]: ErrorCategories.AUTHENTICATION_ERROR,
|
|
865
|
+
[ResponseCodes.NOT_ENROLLED_3DS]: ErrorCategories.AUTHENTICATION_ERROR,
|
|
866
|
+
[ResponseCodes.SIGNATURE_MISMATCH]: ErrorCategories.AUTHENTICATION_ERROR,
|
|
867
|
+
[ResponseCodes.INVALID_ACCESS_CODE]: ErrorCategories.AUTHENTICATION_ERROR,
|
|
868
|
+
[ResponseCodes.INVALID_MERCHANT_ID]: ErrorCategories.AUTHENTICATION_ERROR,
|
|
869
|
+
// Transaction errors
|
|
870
|
+
[ResponseCodes.DECLINED]: ErrorCategories.TRANSACTION_ERROR,
|
|
871
|
+
[ResponseCodes.INSUFFICIENT_FUNDS]: ErrorCategories.TRANSACTION_ERROR,
|
|
872
|
+
[ResponseCodes.LIMIT_EXCEEDED]: ErrorCategories.TRANSACTION_ERROR,
|
|
873
|
+
[ResponseCodes.INVALID_TRANSACTION]: ErrorCategories.TRANSACTION_ERROR,
|
|
874
|
+
[ResponseCodes.DUPLICATE_TRANSACTION]: ErrorCategories.TRANSACTION_ERROR,
|
|
875
|
+
[ResponseCodes.TRANSACTION_NOT_ALLOWED]: ErrorCategories.TRANSACTION_ERROR,
|
|
876
|
+
[ResponseCodes.TRANSACTION_EXPIRED]: ErrorCategories.TRANSACTION_ERROR,
|
|
877
|
+
[ResponseCodes.ALREADY_CAPTURED]: ErrorCategories.TRANSACTION_ERROR,
|
|
878
|
+
[ResponseCodes.ALREADY_VOIDED]: ErrorCategories.TRANSACTION_ERROR,
|
|
879
|
+
[ResponseCodes.ALREADY_REFUNDED]: ErrorCategories.TRANSACTION_ERROR,
|
|
880
|
+
// Amount errors
|
|
881
|
+
[ResponseCodes.INVALID_AMOUNT]: ErrorCategories.AMOUNT_ERROR,
|
|
882
|
+
[ResponseCodes.AMOUNT_TOO_SMALL]: ErrorCategories.AMOUNT_ERROR,
|
|
883
|
+
[ResponseCodes.AMOUNT_TOO_LARGE]: ErrorCategories.AMOUNT_ERROR,
|
|
884
|
+
[ResponseCodes.CURRENCY_NOT_SUPPORTED]: ErrorCategories.AMOUNT_ERROR,
|
|
885
|
+
// Validation errors
|
|
886
|
+
[ResponseCodes.MISSING_PARAMETER]: ErrorCategories.VALIDATION_ERROR,
|
|
887
|
+
[ResponseCodes.INVALID_PARAMETER]: ErrorCategories.VALIDATION_ERROR,
|
|
888
|
+
[ResponseCodes.INVALID_COMMAND]: ErrorCategories.VALIDATION_ERROR,
|
|
889
|
+
[ResponseCodes.INVALID_LANGUAGE]: ErrorCategories.VALIDATION_ERROR,
|
|
890
|
+
[ResponseCodes.INVALID_CURRENCY]: ErrorCategories.VALIDATION_ERROR,
|
|
891
|
+
[ResponseCodes.INVALID_RETURN_URL]: ErrorCategories.VALIDATION_ERROR,
|
|
892
|
+
[ResponseCodes.INVALID_EMAIL]: ErrorCategories.VALIDATION_ERROR,
|
|
893
|
+
[ResponseCodes.PARAMETER_VALUE_NOT_ALLOWED]: ErrorCategories.VALIDATION_ERROR,
|
|
894
|
+
// Token errors
|
|
895
|
+
[ResponseCodes.TOKEN_NOT_FOUND]: ErrorCategories.TOKEN_ERROR,
|
|
896
|
+
[ResponseCodes.TOKEN_EXPIRED]: ErrorCategories.TOKEN_ERROR,
|
|
897
|
+
[ResponseCodes.TOKEN_ALREADY_USED]: ErrorCategories.TOKEN_ERROR,
|
|
898
|
+
[ResponseCodes.INVALID_TOKEN]: ErrorCategories.TOKEN_ERROR,
|
|
899
|
+
// System errors
|
|
900
|
+
[ResponseCodes.SYSTEM_ERROR]: ErrorCategories.SYSTEM_ERROR,
|
|
901
|
+
[ResponseCodes.SERVICE_UNAVAILABLE]: ErrorCategories.SYSTEM_ERROR,
|
|
902
|
+
[ResponseCodes.TIMEOUT]: ErrorCategories.SYSTEM_ERROR,
|
|
903
|
+
[ResponseCodes.DATABASE_ERROR]: ErrorCategories.SYSTEM_ERROR,
|
|
904
|
+
[ResponseCodes.INTERNAL_ERROR]: ErrorCategories.SYSTEM_ERROR
|
|
905
|
+
};
|
|
906
|
+
var RETRYABLE_CODES = [
|
|
907
|
+
ResponseCodes.TIMEOUT,
|
|
908
|
+
ResponseCodes.SERVICE_UNAVAILABLE,
|
|
909
|
+
ResponseCodes.SYSTEM_ERROR,
|
|
910
|
+
ResponseCodes.INTERNAL_ERROR,
|
|
911
|
+
ResponseCodes.DATABASE_ERROR
|
|
912
|
+
];
|
|
913
|
+
function categorizeError(code) {
|
|
914
|
+
return ERROR_CODE_TO_CATEGORY[code] || ErrorCategories.SYSTEM_ERROR;
|
|
915
|
+
}
|
|
916
|
+
function isRetryableError(code) {
|
|
917
|
+
return RETRYABLE_CODES.includes(code);
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// src/errors.ts
|
|
921
|
+
var ERROR_DETAILS_MAP = {
|
|
922
|
+
// Success codes
|
|
923
|
+
[ResponseCodes.SUCCESS]: {
|
|
924
|
+
message: "Transaction completed successfully.",
|
|
925
|
+
action: "No action needed. Proceed with order fulfillment.",
|
|
926
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html"
|
|
927
|
+
},
|
|
928
|
+
[ResponseCodes.PAYMENT_LINK_SUCCESS]: {
|
|
929
|
+
message: "Payment link created successfully.",
|
|
930
|
+
action: "Share the payment link with your customer via email, SMS, or any channel.",
|
|
931
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html"
|
|
932
|
+
},
|
|
933
|
+
[ResponseCodes.RECURRING_SUCCESS]: {
|
|
934
|
+
message: "Recurring payment processed successfully.",
|
|
935
|
+
action: "Payment has been charged. Update subscription status in your system.",
|
|
936
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html"
|
|
937
|
+
},
|
|
938
|
+
// Authentication & Security
|
|
939
|
+
[ResponseCodes.AUTHENTICATION_FAILED]: {
|
|
940
|
+
message: "3D Secure authentication failed. The customer could not verify their identity.",
|
|
941
|
+
action: "Ask the customer to check their 3D Secure password or use a different card. Some banks require SMS verification - ensure customer has network coverage.",
|
|
942
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#_3d_secure",
|
|
943
|
+
httpStatus: 402
|
|
944
|
+
},
|
|
945
|
+
[ResponseCodes.NOT_ENROLLED_3DS]: {
|
|
946
|
+
message: "Card is not enrolled in 3D Secure.",
|
|
947
|
+
action: "The card does not support 3D Secure. You can proceed without 3DS or ask customer to use a different card.",
|
|
948
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#_3d_secure"
|
|
949
|
+
},
|
|
950
|
+
[ResponseCodes.SIGNATURE_MISMATCH]: {
|
|
951
|
+
message: "Request signature is invalid.",
|
|
952
|
+
action: "Check that your request_secret is correct and that parameters are being signed in the correct order. Use the SDK's built-in signing methods.",
|
|
953
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#signature_calculation",
|
|
954
|
+
httpStatus: 401
|
|
955
|
+
},
|
|
956
|
+
[ResponseCodes.INVALID_ACCESS_CODE]: {
|
|
957
|
+
message: "Invalid access code.",
|
|
958
|
+
action: "Verify your APS_ACCESS_CODE environment variable matches the value in your APS dashboard under Integration Settings.",
|
|
959
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#credentials",
|
|
960
|
+
httpStatus: 401
|
|
961
|
+
},
|
|
962
|
+
[ResponseCodes.INVALID_MERCHANT_ID]: {
|
|
963
|
+
message: "Invalid merchant identifier.",
|
|
964
|
+
action: "Verify your APS_MERCHANT_ID environment variable matches your APS dashboard. Ensure you're using the correct environment (sandbox vs production).",
|
|
965
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#credentials",
|
|
966
|
+
httpStatus: 401
|
|
967
|
+
},
|
|
968
|
+
// Card Errors
|
|
969
|
+
[ResponseCodes.CARD_EXPIRED]: {
|
|
970
|
+
message: "The card has expired.",
|
|
971
|
+
action: "Ask the customer to check the expiry date on their card and enter it correctly, or use a different card.",
|
|
972
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#card_errors",
|
|
973
|
+
httpStatus: 402
|
|
974
|
+
},
|
|
975
|
+
[ResponseCodes.INVALID_CARD_NUMBER]: {
|
|
976
|
+
message: "The card number is invalid.",
|
|
977
|
+
action: "Ask the customer to check their card number and try again. Ensure no spaces or dashes are included.",
|
|
978
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#card_errors",
|
|
979
|
+
httpStatus: 402
|
|
980
|
+
},
|
|
981
|
+
[ResponseCodes.INVALID_CVV]: {
|
|
982
|
+
message: "The CVV code is invalid.",
|
|
983
|
+
action: "Ask the customer to check the CVV on the back of their card (3 digits for Visa/MC, 4 for Amex) and enter it correctly.",
|
|
984
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#card_errors",
|
|
985
|
+
httpStatus: 402
|
|
986
|
+
},
|
|
987
|
+
[ResponseCodes.CARD_NOT_SUPPORTED]: {
|
|
988
|
+
message: "This card type is not supported.",
|
|
989
|
+
action: "The customer is using a card type not accepted by your merchant account. Ask them to use Visa, Mastercard, or MADA.",
|
|
990
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#payment_methods",
|
|
991
|
+
httpStatus: 402
|
|
992
|
+
},
|
|
993
|
+
[ResponseCodes.LOST_CARD]: {
|
|
994
|
+
message: "The card has been reported lost.",
|
|
995
|
+
action: "Do not retry. Ask the customer to use a different card and contact their bank.",
|
|
996
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#card_errors",
|
|
997
|
+
httpStatus: 402
|
|
998
|
+
},
|
|
999
|
+
[ResponseCodes.STOLEN_CARD]: {
|
|
1000
|
+
message: "The card has been reported stolen.",
|
|
1001
|
+
action: "Do not retry. Ask the customer to use a different card and contact their bank immediately.",
|
|
1002
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#card_errors",
|
|
1003
|
+
httpStatus: 402
|
|
1004
|
+
},
|
|
1005
|
+
[ResponseCodes.RESTRICTED_CARD]: {
|
|
1006
|
+
message: "The card has restrictions that prevent this transaction.",
|
|
1007
|
+
action: "The card may be restricted for online or international use. Ask the customer to contact their bank or use a different card.",
|
|
1008
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#card_errors",
|
|
1009
|
+
httpStatus: 402
|
|
1010
|
+
},
|
|
1011
|
+
[ResponseCodes.CARD_BLOCKED]: {
|
|
1012
|
+
message: "The card has been blocked by the issuing bank.",
|
|
1013
|
+
action: "Ask the customer to contact their bank to unblock the card or use a different card.",
|
|
1014
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#card_errors",
|
|
1015
|
+
httpStatus: 402
|
|
1016
|
+
},
|
|
1017
|
+
[ResponseCodes.INVALID_EXPIRY]: {
|
|
1018
|
+
message: "The expiry date is invalid.",
|
|
1019
|
+
action: "Ask the customer to check the expiry date format (MM/YY) and ensure the card has not expired.",
|
|
1020
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#card_errors",
|
|
1021
|
+
httpStatus: 402
|
|
1022
|
+
},
|
|
1023
|
+
// Transaction Errors
|
|
1024
|
+
[ResponseCodes.DECLINED]: {
|
|
1025
|
+
message: "The transaction was declined by the issuing bank.",
|
|
1026
|
+
action: "Ask the customer to contact their bank or use a different card. The bank does not provide specific reasons for declines.",
|
|
1027
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#declined_transactions",
|
|
1028
|
+
httpStatus: 402
|
|
1029
|
+
},
|
|
1030
|
+
[ResponseCodes.INSUFFICIENT_FUNDS]: {
|
|
1031
|
+
message: "The card has insufficient funds.",
|
|
1032
|
+
action: "Ask the customer to use a different card or payment method, or reduce the transaction amount.",
|
|
1033
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#transaction_errors",
|
|
1034
|
+
httpStatus: 402
|
|
1035
|
+
},
|
|
1036
|
+
[ResponseCodes.LIMIT_EXCEEDED]: {
|
|
1037
|
+
message: "The transaction amount exceeds the card limit.",
|
|
1038
|
+
action: "The transaction may exceed the customer's daily limit or card limit. Ask them to contact their bank or use a different card.",
|
|
1039
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#transaction_errors",
|
|
1040
|
+
httpStatus: 402
|
|
1041
|
+
},
|
|
1042
|
+
[ResponseCodes.INVALID_TRANSACTION]: {
|
|
1043
|
+
message: "The transaction is invalid.",
|
|
1044
|
+
action: "Check that all required parameters are provided correctly. Ensure the amount is valid and currency is supported.",
|
|
1045
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#transaction_errors",
|
|
1046
|
+
httpStatus: 400
|
|
1047
|
+
},
|
|
1048
|
+
[ResponseCodes.DUPLICATE_TRANSACTION]: {
|
|
1049
|
+
message: "A duplicate transaction was detected.",
|
|
1050
|
+
action: "This transaction appears to be a duplicate. Use a unique merchant_reference for each transaction.",
|
|
1051
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#idempotency",
|
|
1052
|
+
httpStatus: 409
|
|
1053
|
+
},
|
|
1054
|
+
[ResponseCodes.TRANSACTION_NOT_ALLOWED]: {
|
|
1055
|
+
message: "This transaction type is not allowed for this merchant.",
|
|
1056
|
+
action: "Your merchant account may not be configured for this transaction type. Contact APS support to enable the required service.",
|
|
1057
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#merchant_setup",
|
|
1058
|
+
httpStatus: 403
|
|
1059
|
+
},
|
|
1060
|
+
[ResponseCodes.TRANSACTION_EXPIRED]: {
|
|
1061
|
+
message: "The transaction has expired.",
|
|
1062
|
+
action: "The authorization window has expired. Create a new transaction.",
|
|
1063
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#transaction_lifecycle",
|
|
1064
|
+
httpStatus: 410
|
|
1065
|
+
},
|
|
1066
|
+
[ResponseCodes.ALREADY_CAPTURED]: {
|
|
1067
|
+
message: "The transaction has already been captured.",
|
|
1068
|
+
action: "This transaction was already captured. Check your records or query the transaction status.",
|
|
1069
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#capture",
|
|
1070
|
+
httpStatus: 409
|
|
1071
|
+
},
|
|
1072
|
+
[ResponseCodes.ALREADY_VOIDED]: {
|
|
1073
|
+
message: "The transaction has already been voided.",
|
|
1074
|
+
action: "This transaction was already voided. No further action can be taken on it.",
|
|
1075
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#void",
|
|
1076
|
+
httpStatus: 409
|
|
1077
|
+
},
|
|
1078
|
+
[ResponseCodes.ALREADY_REFUNDED]: {
|
|
1079
|
+
message: "The transaction has already been refunded.",
|
|
1080
|
+
action: "This transaction was already refunded. Check the refund status for details.",
|
|
1081
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#refund",
|
|
1082
|
+
httpStatus: 409
|
|
1083
|
+
},
|
|
1084
|
+
// Amount Errors
|
|
1085
|
+
[ResponseCodes.INVALID_AMOUNT]: {
|
|
1086
|
+
message: "The transaction amount is invalid.",
|
|
1087
|
+
action: "Ensure the amount is a positive number in the smallest currency unit (e.g., cents, fils). Amount should not contain decimals.",
|
|
1088
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#amount_format",
|
|
1089
|
+
httpStatus: 400
|
|
1090
|
+
},
|
|
1091
|
+
[ResponseCodes.AMOUNT_TOO_SMALL]: {
|
|
1092
|
+
message: "The transaction amount is below the minimum allowed.",
|
|
1093
|
+
action: "The amount is below the minimum transaction limit. Increase the amount or contact APS for limit adjustments.",
|
|
1094
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#amount_limits",
|
|
1095
|
+
httpStatus: 400
|
|
1096
|
+
},
|
|
1097
|
+
[ResponseCodes.AMOUNT_TOO_LARGE]: {
|
|
1098
|
+
message: "The transaction amount exceeds the maximum allowed.",
|
|
1099
|
+
action: "The amount exceeds your merchant transaction limit. Contact APS support to increase your limit.",
|
|
1100
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#amount_limits",
|
|
1101
|
+
httpStatus: 400
|
|
1102
|
+
},
|
|
1103
|
+
[ResponseCodes.CURRENCY_NOT_SUPPORTED]: {
|
|
1104
|
+
message: "The currency is not supported.",
|
|
1105
|
+
action: "Use a supported currency (SAR, AED, USD, EUR, etc.). Check APS documentation for the full list.",
|
|
1106
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#currencies",
|
|
1107
|
+
httpStatus: 400
|
|
1108
|
+
},
|
|
1109
|
+
// Parameter Errors
|
|
1110
|
+
[ResponseCodes.MISSING_PARAMETER]: {
|
|
1111
|
+
message: "A required parameter is missing.",
|
|
1112
|
+
action: "Check that all required parameters are provided. Common missing fields: merchant_reference, amount, currency, customer_email.",
|
|
1113
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#required_parameters",
|
|
1114
|
+
httpStatus: 400
|
|
1115
|
+
},
|
|
1116
|
+
[ResponseCodes.INVALID_PARAMETER]: {
|
|
1117
|
+
message: "A parameter has an invalid format or value.",
|
|
1118
|
+
action: "Check parameter formats: emails must be valid, amounts must be numeric, dates must be in correct format.",
|
|
1119
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#parameter_formats",
|
|
1120
|
+
httpStatus: 400
|
|
1121
|
+
},
|
|
1122
|
+
[ResponseCodes.INVALID_COMMAND]: {
|
|
1123
|
+
message: "The command is invalid or not recognized.",
|
|
1124
|
+
action: "Use a valid command: PURCHASE, AUTHORIZATION, CAPTURE, REFUND, VOID, TOKENIZATION, etc.",
|
|
1125
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#commands",
|
|
1126
|
+
httpStatus: 400
|
|
1127
|
+
},
|
|
1128
|
+
[ResponseCodes.INVALID_LANGUAGE]: {
|
|
1129
|
+
message: "The language code is invalid.",
|
|
1130
|
+
action: 'Use "en" for English or "ar" for Arabic.',
|
|
1131
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#language_codes",
|
|
1132
|
+
httpStatus: 400
|
|
1133
|
+
},
|
|
1134
|
+
[ResponseCodes.INVALID_CURRENCY]: {
|
|
1135
|
+
message: "The currency code is invalid.",
|
|
1136
|
+
action: "Use a valid 3-letter ISO currency code (SAR, AED, USD, EUR, etc.).",
|
|
1137
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#currencies",
|
|
1138
|
+
httpStatus: 400
|
|
1139
|
+
},
|
|
1140
|
+
[ResponseCodes.INVALID_RETURN_URL]: {
|
|
1141
|
+
message: "The return URL is invalid.",
|
|
1142
|
+
action: "Ensure the return_url is a valid HTTPS URL and properly URL-encoded if needed.",
|
|
1143
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#return_url",
|
|
1144
|
+
httpStatus: 400
|
|
1145
|
+
},
|
|
1146
|
+
[ResponseCodes.INVALID_EMAIL]: {
|
|
1147
|
+
message: "The customer email is invalid.",
|
|
1148
|
+
action: "Ensure the customer_email is a valid email format (e.g., customer@example.com).",
|
|
1149
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#customer_email",
|
|
1150
|
+
httpStatus: 400
|
|
1151
|
+
},
|
|
1152
|
+
[ResponseCodes.PARAMETER_VALUE_NOT_ALLOWED]: {
|
|
1153
|
+
message: "A parameter value is not allowed for your merchant account.",
|
|
1154
|
+
action: "This error often occurs when trying to use tokenization (remember_me) but your merchant account does not have tokenization enabled. Contact APS support to enable tokenization, or set tokenize: false. Other causes: using a payment method not enabled for your account, or an invalid parameter value.",
|
|
1155
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#merchant_setup",
|
|
1156
|
+
httpStatus: 400
|
|
1157
|
+
},
|
|
1158
|
+
// Tokenization Errors
|
|
1159
|
+
[ResponseCodes.TOKEN_NOT_FOUND]: {
|
|
1160
|
+
message: "The card token was not found.",
|
|
1161
|
+
action: "The token may have been deleted or never created. Ask the customer to re-enter their card details.",
|
|
1162
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#tokenization",
|
|
1163
|
+
httpStatus: 404
|
|
1164
|
+
},
|
|
1165
|
+
[ResponseCodes.TOKEN_EXPIRED]: {
|
|
1166
|
+
message: "The card token has expired.",
|
|
1167
|
+
action: "Tokens expire after a period of inactivity. Ask the customer to re-enter their card details.",
|
|
1168
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#token_expiry",
|
|
1169
|
+
httpStatus: 410
|
|
1170
|
+
},
|
|
1171
|
+
[ResponseCodes.TOKEN_ALREADY_USED]: {
|
|
1172
|
+
message: "The token has already been used.",
|
|
1173
|
+
action: "Each token can only be used once for security. Create a new token for this transaction.",
|
|
1174
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#token_usage",
|
|
1175
|
+
httpStatus: 409
|
|
1176
|
+
},
|
|
1177
|
+
[ResponseCodes.INVALID_TOKEN]: {
|
|
1178
|
+
message: "The token is invalid.",
|
|
1179
|
+
action: "Check that the token was properly generated and is being passed correctly.",
|
|
1180
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#tokenization",
|
|
1181
|
+
httpStatus: 400
|
|
1182
|
+
},
|
|
1183
|
+
// System Errors
|
|
1184
|
+
[ResponseCodes.SYSTEM_ERROR]: {
|
|
1185
|
+
message: "A system error occurred.",
|
|
1186
|
+
action: "This is a temporary issue on APS side. Wait a moment and retry the transaction.",
|
|
1187
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#system_errors",
|
|
1188
|
+
httpStatus: 500
|
|
1189
|
+
},
|
|
1190
|
+
[ResponseCodes.SERVICE_UNAVAILABLE]: {
|
|
1191
|
+
message: "The service is temporarily unavailable.",
|
|
1192
|
+
action: "APS is experiencing high load or maintenance. Wait a moment and retry.",
|
|
1193
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#system_errors",
|
|
1194
|
+
httpStatus: 503
|
|
1195
|
+
},
|
|
1196
|
+
[ResponseCodes.TIMEOUT]: {
|
|
1197
|
+
message: "The request timed out.",
|
|
1198
|
+
action: "The request took too long to complete. Check your network connection and retry.",
|
|
1199
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#timeouts",
|
|
1200
|
+
httpStatus: 504
|
|
1201
|
+
},
|
|
1202
|
+
[ResponseCodes.DATABASE_ERROR]: {
|
|
1203
|
+
message: "A database error occurred.",
|
|
1204
|
+
action: "Temporary database issue. Wait a moment and retry the transaction.",
|
|
1205
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#system_errors",
|
|
1206
|
+
httpStatus: 500
|
|
1207
|
+
},
|
|
1208
|
+
[ResponseCodes.INTERNAL_ERROR]: {
|
|
1209
|
+
message: "An internal error occurred.",
|
|
1210
|
+
action: "Unexpected error on APS side. Contact APS support if this persists.",
|
|
1211
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#support",
|
|
1212
|
+
httpStatus: 500
|
|
1213
|
+
}
|
|
1214
|
+
};
|
|
1215
|
+
function getErrorDetails(code) {
|
|
1216
|
+
const details = ERROR_DETAILS_MAP[code];
|
|
1217
|
+
const category = categorizeError(code);
|
|
1218
|
+
const retryable = isRetryableError(code);
|
|
1219
|
+
if (details) {
|
|
1220
|
+
return {
|
|
1221
|
+
code,
|
|
1222
|
+
message: details.message,
|
|
1223
|
+
action: details.action,
|
|
1224
|
+
category,
|
|
1225
|
+
retryable,
|
|
1226
|
+
documentation: details.documentation,
|
|
1227
|
+
httpStatus: details.httpStatus
|
|
1228
|
+
};
|
|
1229
|
+
}
|
|
1230
|
+
return {
|
|
1231
|
+
code,
|
|
1232
|
+
message: `An unknown error occurred (code: ${code}).`,
|
|
1233
|
+
action: "Contact APS support with this error code for assistance.",
|
|
1234
|
+
category: ErrorCategories.SYSTEM_ERROR,
|
|
1235
|
+
retryable: false,
|
|
1236
|
+
documentation: "https://paymentservices.amazon.com/docs/EN/index.html#support",
|
|
1237
|
+
httpStatus: 500
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
function isRetryableError2(code) {
|
|
1241
|
+
return isRetryableError(code);
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
// src/react/ErrorDisplay.tsx
|
|
1245
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1246
|
+
var defaultStyles2 = {
|
|
1247
|
+
container: {
|
|
1248
|
+
padding: "16px",
|
|
1249
|
+
borderRadius: "8px",
|
|
1250
|
+
border: "1px solid #EF4444",
|
|
1251
|
+
backgroundColor: "#FEF2F2",
|
|
1252
|
+
color: "#991B1B"
|
|
1253
|
+
},
|
|
1254
|
+
header: {
|
|
1255
|
+
display: "flex",
|
|
1256
|
+
alignItems: "center",
|
|
1257
|
+
gap: "8px",
|
|
1258
|
+
marginBottom: "12px"
|
|
1259
|
+
},
|
|
1260
|
+
title: {
|
|
1261
|
+
fontWeight: "600",
|
|
1262
|
+
fontSize: "16px",
|
|
1263
|
+
color: "#DC2626"
|
|
1264
|
+
},
|
|
1265
|
+
code: {
|
|
1266
|
+
fontSize: "12px",
|
|
1267
|
+
padding: "2px 8px",
|
|
1268
|
+
backgroundColor: "#FEE2E2",
|
|
1269
|
+
borderRadius: "4px",
|
|
1270
|
+
fontFamily: "monospace"
|
|
1271
|
+
},
|
|
1272
|
+
message: {
|
|
1273
|
+
fontSize: "14px",
|
|
1274
|
+
marginBottom: "8px",
|
|
1275
|
+
lineHeight: "1.5"
|
|
1276
|
+
},
|
|
1277
|
+
action: {
|
|
1278
|
+
fontSize: "13px",
|
|
1279
|
+
color: "#7F1D1D",
|
|
1280
|
+
marginBottom: "12px",
|
|
1281
|
+
padding: "8px",
|
|
1282
|
+
backgroundColor: "#FEE2E2",
|
|
1283
|
+
borderRadius: "4px"
|
|
1284
|
+
},
|
|
1285
|
+
actions: {
|
|
1286
|
+
display: "flex",
|
|
1287
|
+
gap: "8px",
|
|
1288
|
+
marginTop: "12px"
|
|
1289
|
+
},
|
|
1290
|
+
button: {
|
|
1291
|
+
padding: "8px 16px",
|
|
1292
|
+
borderRadius: "6px",
|
|
1293
|
+
fontSize: "14px",
|
|
1294
|
+
fontWeight: "500",
|
|
1295
|
+
cursor: "pointer",
|
|
1296
|
+
border: "none",
|
|
1297
|
+
transition: "opacity 0.2s"
|
|
1298
|
+
},
|
|
1299
|
+
buttonRetry: {
|
|
1300
|
+
backgroundColor: "#DC2626",
|
|
1301
|
+
color: "#fff"
|
|
1302
|
+
},
|
|
1303
|
+
buttonDismiss: {
|
|
1304
|
+
backgroundColor: "transparent",
|
|
1305
|
+
color: "#7F1D1D",
|
|
1306
|
+
border: "1px solid #EF4444"
|
|
1307
|
+
},
|
|
1308
|
+
documentation: {
|
|
1309
|
+
fontSize: "12px",
|
|
1310
|
+
marginTop: "12px",
|
|
1311
|
+
color: "#991B1B"
|
|
1312
|
+
}
|
|
1313
|
+
};
|
|
1314
|
+
var ErrorDisplay = ({
|
|
1315
|
+
error,
|
|
1316
|
+
onRetry,
|
|
1317
|
+
onDismiss,
|
|
1318
|
+
title: customTitle,
|
|
1319
|
+
showDocumentation = true,
|
|
1320
|
+
styles: customStyles = {},
|
|
1321
|
+
className = ""
|
|
1322
|
+
}) => {
|
|
1323
|
+
const styles = {
|
|
1324
|
+
container: { ...defaultStyles2.container, ...customStyles.container },
|
|
1325
|
+
header: { ...defaultStyles2.header, ...customStyles.header },
|
|
1326
|
+
title: { ...defaultStyles2.title, ...customStyles.title },
|
|
1327
|
+
code: { ...defaultStyles2.code, ...customStyles.code },
|
|
1328
|
+
message: { ...defaultStyles2.message, ...customStyles.message },
|
|
1329
|
+
action: { ...defaultStyles2.action, ...customStyles.action },
|
|
1330
|
+
actions: { ...defaultStyles2.actions, ...customStyles.actions },
|
|
1331
|
+
button: { ...defaultStyles2.button, ...customStyles.button },
|
|
1332
|
+
buttonRetry: { ...defaultStyles2.buttonRetry, ...customStyles.buttonRetry },
|
|
1333
|
+
buttonDismiss: { ...defaultStyles2.buttonDismiss, ...customStyles.buttonDismiss },
|
|
1334
|
+
documentation: { ...defaultStyles2.documentation, ...customStyles.documentation }
|
|
1335
|
+
};
|
|
1336
|
+
let errorCode;
|
|
1337
|
+
let errorMessage;
|
|
1338
|
+
let errorDetails = null;
|
|
1339
|
+
let canRetry = false;
|
|
1340
|
+
if (typeof error === "string") {
|
|
1341
|
+
errorMessage = error;
|
|
1342
|
+
} else if (error instanceof Error) {
|
|
1343
|
+
errorMessage = error.message;
|
|
1344
|
+
if ("code" in error) {
|
|
1345
|
+
errorCode = error.code;
|
|
1346
|
+
errorDetails = getErrorDetails(errorCode);
|
|
1347
|
+
canRetry = isRetryableError2(errorCode);
|
|
1348
|
+
}
|
|
1349
|
+
} else {
|
|
1350
|
+
errorMessage = "An unknown error occurred";
|
|
1351
|
+
}
|
|
1352
|
+
const title = customTitle || errorDetails?.category.toUpperCase() || "Error";
|
|
1353
|
+
return /* @__PURE__ */ jsxs2("div", { style: styles.container, className, role: "alert", children: [
|
|
1354
|
+
/* @__PURE__ */ jsxs2("div", { style: styles.header, children: [
|
|
1355
|
+
/* @__PURE__ */ jsx3(
|
|
1356
|
+
"svg",
|
|
1357
|
+
{
|
|
1358
|
+
width: "20",
|
|
1359
|
+
height: "20",
|
|
1360
|
+
viewBox: "0 0 20 20",
|
|
1361
|
+
fill: "none",
|
|
1362
|
+
style: { flexShrink: 0 },
|
|
1363
|
+
children: /* @__PURE__ */ jsx3(
|
|
1364
|
+
"path",
|
|
1365
|
+
{
|
|
1366
|
+
d: "M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z",
|
|
1367
|
+
fill: "#DC2626"
|
|
1368
|
+
}
|
|
1369
|
+
)
|
|
1370
|
+
}
|
|
1371
|
+
),
|
|
1372
|
+
/* @__PURE__ */ jsx3("span", { style: styles.title, children: title }),
|
|
1373
|
+
errorCode && /* @__PURE__ */ jsx3("span", { style: styles.code, children: errorCode })
|
|
1374
|
+
] }),
|
|
1375
|
+
/* @__PURE__ */ jsx3("div", { style: styles.message, children: errorDetails?.message || errorMessage }),
|
|
1376
|
+
errorDetails?.action && /* @__PURE__ */ jsxs2("div", { style: styles.action, children: [
|
|
1377
|
+
/* @__PURE__ */ jsx3("strong", { children: "Suggested action:" }),
|
|
1378
|
+
" ",
|
|
1379
|
+
errorDetails.action
|
|
1380
|
+
] }),
|
|
1381
|
+
(onRetry || onDismiss) && /* @__PURE__ */ jsxs2("div", { style: styles.actions, children: [
|
|
1382
|
+
onRetry && canRetry && /* @__PURE__ */ jsx3(
|
|
1383
|
+
"button",
|
|
1384
|
+
{
|
|
1385
|
+
onClick: onRetry,
|
|
1386
|
+
style: { ...styles.button, ...styles.buttonRetry },
|
|
1387
|
+
type: "button",
|
|
1388
|
+
children: "Try Again"
|
|
1389
|
+
}
|
|
1390
|
+
),
|
|
1391
|
+
onDismiss && /* @__PURE__ */ jsx3(
|
|
1392
|
+
"button",
|
|
1393
|
+
{
|
|
1394
|
+
onClick: onDismiss,
|
|
1395
|
+
style: { ...styles.button, ...styles.buttonDismiss },
|
|
1396
|
+
type: "button",
|
|
1397
|
+
children: "Dismiss"
|
|
1398
|
+
}
|
|
1399
|
+
)
|
|
1400
|
+
] }),
|
|
1401
|
+
showDocumentation && errorDetails?.documentation && /* @__PURE__ */ jsx3("div", { style: styles.documentation, children: /* @__PURE__ */ jsx3(
|
|
1402
|
+
"a",
|
|
1403
|
+
{
|
|
1404
|
+
href: errorDetails.documentation,
|
|
1405
|
+
target: "_blank",
|
|
1406
|
+
rel: "noopener noreferrer",
|
|
1407
|
+
style: { color: "#991B1B", textDecoration: "underline" },
|
|
1408
|
+
children: "Learn more about this error \u2192"
|
|
1409
|
+
}
|
|
1410
|
+
) })
|
|
1411
|
+
] });
|
|
1412
|
+
};
|
|
1413
|
+
|
|
1414
|
+
// src/react/PaymentStatus.tsx
|
|
1415
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1416
|
+
function getStatusConfig(status) {
|
|
1417
|
+
switch (status.toLowerCase()) {
|
|
1418
|
+
case "captured":
|
|
1419
|
+
case "success":
|
|
1420
|
+
return {
|
|
1421
|
+
icon: /* @__PURE__ */ jsx4("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx4(
|
|
1422
|
+
"path",
|
|
1423
|
+
{
|
|
1424
|
+
d: "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z",
|
|
1425
|
+
stroke: "currentColor",
|
|
1426
|
+
strokeWidth: "2",
|
|
1427
|
+
strokeLinecap: "round",
|
|
1428
|
+
strokeLinejoin: "round"
|
|
1429
|
+
}
|
|
1430
|
+
) }),
|
|
1431
|
+
title: "Payment Successful",
|
|
1432
|
+
color: "#059669",
|
|
1433
|
+
bgColor: "#D1FAE5",
|
|
1434
|
+
borderColor: "#10B981"
|
|
1435
|
+
};
|
|
1436
|
+
case "authorized":
|
|
1437
|
+
return {
|
|
1438
|
+
icon: /* @__PURE__ */ jsx4("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx4(
|
|
1439
|
+
"path",
|
|
1440
|
+
{
|
|
1441
|
+
d: "M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z",
|
|
1442
|
+
stroke: "currentColor",
|
|
1443
|
+
strokeWidth: "2",
|
|
1444
|
+
strokeLinecap: "round",
|
|
1445
|
+
strokeLinejoin: "round"
|
|
1446
|
+
}
|
|
1447
|
+
) }),
|
|
1448
|
+
title: "Payment Authorized",
|
|
1449
|
+
color: "#0891B2",
|
|
1450
|
+
bgColor: "#CFFAFE",
|
|
1451
|
+
borderColor: "#06B6D4"
|
|
1452
|
+
};
|
|
1453
|
+
case "pending":
|
|
1454
|
+
case "3ds":
|
|
1455
|
+
return {
|
|
1456
|
+
icon: /* @__PURE__ */ jsx4("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx4(
|
|
1457
|
+
"path",
|
|
1458
|
+
{
|
|
1459
|
+
d: "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z",
|
|
1460
|
+
stroke: "currentColor",
|
|
1461
|
+
strokeWidth: "2",
|
|
1462
|
+
strokeLinecap: "round",
|
|
1463
|
+
strokeLinejoin: "round"
|
|
1464
|
+
}
|
|
1465
|
+
) }),
|
|
1466
|
+
title: "Payment Pending",
|
|
1467
|
+
color: "#D97706",
|
|
1468
|
+
bgColor: "#FEF3C7",
|
|
1469
|
+
borderColor: "#F59E0B"
|
|
1470
|
+
};
|
|
1471
|
+
case "failed":
|
|
1472
|
+
case "declined":
|
|
1473
|
+
return {
|
|
1474
|
+
icon: /* @__PURE__ */ jsx4("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx4(
|
|
1475
|
+
"path",
|
|
1476
|
+
{
|
|
1477
|
+
d: "M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z",
|
|
1478
|
+
stroke: "currentColor",
|
|
1479
|
+
strokeWidth: "2",
|
|
1480
|
+
strokeLinecap: "round",
|
|
1481
|
+
strokeLinejoin: "round"
|
|
1482
|
+
}
|
|
1483
|
+
) }),
|
|
1484
|
+
title: "Payment Failed",
|
|
1485
|
+
color: "#DC2626",
|
|
1486
|
+
bgColor: "#FEE2E2",
|
|
1487
|
+
borderColor: "#EF4444"
|
|
1488
|
+
};
|
|
1489
|
+
case "voided":
|
|
1490
|
+
return {
|
|
1491
|
+
icon: /* @__PURE__ */ jsx4("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx4(
|
|
1492
|
+
"path",
|
|
1493
|
+
{
|
|
1494
|
+
d: "M6 18L18 6M6 6l12 12",
|
|
1495
|
+
stroke: "currentColor",
|
|
1496
|
+
strokeWidth: "2",
|
|
1497
|
+
strokeLinecap: "round",
|
|
1498
|
+
strokeLinejoin: "round"
|
|
1499
|
+
}
|
|
1500
|
+
) }),
|
|
1501
|
+
title: "Payment Voided",
|
|
1502
|
+
color: "#6B7280",
|
|
1503
|
+
bgColor: "#F3F4F6",
|
|
1504
|
+
borderColor: "#9CA3AF"
|
|
1505
|
+
};
|
|
1506
|
+
case "refunded":
|
|
1507
|
+
return {
|
|
1508
|
+
icon: /* @__PURE__ */ jsx4("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx4(
|
|
1509
|
+
"path",
|
|
1510
|
+
{
|
|
1511
|
+
d: "M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6",
|
|
1512
|
+
stroke: "currentColor",
|
|
1513
|
+
strokeWidth: "2",
|
|
1514
|
+
strokeLinecap: "round",
|
|
1515
|
+
strokeLinejoin: "round"
|
|
1516
|
+
}
|
|
1517
|
+
) }),
|
|
1518
|
+
title: "Payment Refunded",
|
|
1519
|
+
color: "#7C3AED",
|
|
1520
|
+
bgColor: "#EDE9FE",
|
|
1521
|
+
borderColor: "#8B5CF6"
|
|
1522
|
+
};
|
|
1523
|
+
case "cancelled":
|
|
1524
|
+
return {
|
|
1525
|
+
icon: /* @__PURE__ */ jsx4("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx4(
|
|
1526
|
+
"path",
|
|
1527
|
+
{
|
|
1528
|
+
d: "M6 18L18 6M6 6l12 12",
|
|
1529
|
+
stroke: "currentColor",
|
|
1530
|
+
strokeWidth: "2",
|
|
1531
|
+
strokeLinecap: "round",
|
|
1532
|
+
strokeLinejoin: "round"
|
|
1533
|
+
}
|
|
1534
|
+
) }),
|
|
1535
|
+
title: "Payment Cancelled",
|
|
1536
|
+
color: "#6B7280",
|
|
1537
|
+
bgColor: "#F3F4F6",
|
|
1538
|
+
borderColor: "#9CA3AF"
|
|
1539
|
+
};
|
|
1540
|
+
default:
|
|
1541
|
+
return {
|
|
1542
|
+
icon: /* @__PURE__ */ jsx4("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx4(
|
|
1543
|
+
"path",
|
|
1544
|
+
{
|
|
1545
|
+
d: "M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z",
|
|
1546
|
+
stroke: "currentColor",
|
|
1547
|
+
strokeWidth: "2",
|
|
1548
|
+
strokeLinecap: "round",
|
|
1549
|
+
strokeLinejoin: "round"
|
|
1550
|
+
}
|
|
1551
|
+
) }),
|
|
1552
|
+
title: "Unknown Status",
|
|
1553
|
+
color: "#6B7280",
|
|
1554
|
+
bgColor: "#F3F4F6",
|
|
1555
|
+
borderColor: "#9CA3AF"
|
|
1556
|
+
};
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
function formatAmount(amount, currency) {
|
|
1560
|
+
const majorUnits = amount / 100;
|
|
1561
|
+
const formatter = new Intl.NumberFormat("en-US", {
|
|
1562
|
+
style: "currency",
|
|
1563
|
+
currency
|
|
1564
|
+
});
|
|
1565
|
+
return formatter.format(majorUnits);
|
|
1566
|
+
}
|
|
1567
|
+
function getSizeStyles(size) {
|
|
1568
|
+
switch (size) {
|
|
1569
|
+
case "small":
|
|
1570
|
+
return { padding: "12px", iconSize: 20, fontSize: "14px" };
|
|
1571
|
+
case "large":
|
|
1572
|
+
return { padding: "24px", iconSize: 32, fontSize: "18px" };
|
|
1573
|
+
default:
|
|
1574
|
+
return { padding: "16px", iconSize: 24, fontSize: "16px" };
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
var PaymentStatus = ({
|
|
1578
|
+
status,
|
|
1579
|
+
amount,
|
|
1580
|
+
currency = "USD",
|
|
1581
|
+
transactionId,
|
|
1582
|
+
paymentMethod,
|
|
1583
|
+
title: customTitle,
|
|
1584
|
+
showAmount = true,
|
|
1585
|
+
showTransactionId = true,
|
|
1586
|
+
styles: customStyles = {},
|
|
1587
|
+
className = "",
|
|
1588
|
+
size = "medium"
|
|
1589
|
+
}) => {
|
|
1590
|
+
const config = getStatusConfig(status);
|
|
1591
|
+
const sizeStyles = getSizeStyles(size);
|
|
1592
|
+
const displayTitle = customTitle || config.title;
|
|
1593
|
+
const styles = {
|
|
1594
|
+
container: {
|
|
1595
|
+
padding: sizeStyles.padding,
|
|
1596
|
+
borderRadius: "8px",
|
|
1597
|
+
border: `1px solid ${config.borderColor}`,
|
|
1598
|
+
backgroundColor: config.bgColor,
|
|
1599
|
+
color: config.color,
|
|
1600
|
+
...customStyles.container
|
|
1601
|
+
},
|
|
1602
|
+
icon: {
|
|
1603
|
+
flexShrink: 0,
|
|
1604
|
+
...customStyles.icon
|
|
1605
|
+
},
|
|
1606
|
+
content: {
|
|
1607
|
+
flex: 1,
|
|
1608
|
+
...customStyles.content
|
|
1609
|
+
},
|
|
1610
|
+
title: {
|
|
1611
|
+
fontWeight: "600",
|
|
1612
|
+
fontSize: sizeStyles.fontSize,
|
|
1613
|
+
marginBottom: "4px",
|
|
1614
|
+
...customStyles.title
|
|
1615
|
+
},
|
|
1616
|
+
amount: {
|
|
1617
|
+
fontSize: "24px",
|
|
1618
|
+
fontWeight: "700",
|
|
1619
|
+
marginTop: "8px",
|
|
1620
|
+
...customStyles.amount
|
|
1621
|
+
},
|
|
1622
|
+
details: {
|
|
1623
|
+
marginTop: "12px",
|
|
1624
|
+
paddingTop: "12px",
|
|
1625
|
+
borderTop: `1px solid ${config.borderColor}`,
|
|
1626
|
+
...customStyles.details
|
|
1627
|
+
},
|
|
1628
|
+
detail: {
|
|
1629
|
+
display: "flex",
|
|
1630
|
+
justifyContent: "space-between",
|
|
1631
|
+
marginBottom: "4px",
|
|
1632
|
+
...customStyles.detail
|
|
1633
|
+
},
|
|
1634
|
+
detailLabel: {
|
|
1635
|
+
fontSize: "14px",
|
|
1636
|
+
opacity: 0.8,
|
|
1637
|
+
...customStyles.detailLabel
|
|
1638
|
+
},
|
|
1639
|
+
detailValue: {
|
|
1640
|
+
fontSize: "14px",
|
|
1641
|
+
fontWeight: "500",
|
|
1642
|
+
fontFamily: "monospace",
|
|
1643
|
+
...customStyles.detailValue
|
|
1644
|
+
}
|
|
1645
|
+
};
|
|
1646
|
+
return /* @__PURE__ */ jsx4("div", { style: styles.container, className, role: "status", children: /* @__PURE__ */ jsxs3("div", { style: { display: "flex", alignItems: "flex-start", gap: "12px" }, children: [
|
|
1647
|
+
/* @__PURE__ */ jsx4("div", { style: { ...styles.icon, width: sizeStyles.iconSize, height: sizeStyles.iconSize }, children: config.icon }),
|
|
1648
|
+
/* @__PURE__ */ jsxs3("div", { style: styles.content, children: [
|
|
1649
|
+
/* @__PURE__ */ jsx4("div", { style: styles.title, children: displayTitle }),
|
|
1650
|
+
showAmount && amount !== void 0 && /* @__PURE__ */ jsx4("div", { style: styles.amount, children: formatAmount(amount, currency) }),
|
|
1651
|
+
(showTransactionId || paymentMethod) && /* @__PURE__ */ jsxs3("div", { style: styles.details, children: [
|
|
1652
|
+
showTransactionId && transactionId && /* @__PURE__ */ jsxs3("div", { style: styles.detail, children: [
|
|
1653
|
+
/* @__PURE__ */ jsx4("span", { style: styles.detailLabel, children: "Transaction ID:" }),
|
|
1654
|
+
/* @__PURE__ */ jsx4("span", { style: styles.detailValue, children: transactionId })
|
|
1655
|
+
] }),
|
|
1656
|
+
paymentMethod && /* @__PURE__ */ jsxs3("div", { style: styles.detail, children: [
|
|
1657
|
+
/* @__PURE__ */ jsx4("span", { style: styles.detailLabel, children: "Payment Method:" }),
|
|
1658
|
+
/* @__PURE__ */ jsx4("span", { style: styles.detailValue, children: paymentMethod.toUpperCase() })
|
|
1659
|
+
] }),
|
|
1660
|
+
/* @__PURE__ */ jsxs3("div", { style: styles.detail, children: [
|
|
1661
|
+
/* @__PURE__ */ jsx4("span", { style: styles.detailLabel, children: "Status:" }),
|
|
1662
|
+
/* @__PURE__ */ jsx4("span", { style: styles.detailValue, children: status.toUpperCase() })
|
|
1663
|
+
] })
|
|
1664
|
+
] })
|
|
1665
|
+
] })
|
|
1666
|
+
] }) });
|
|
1667
|
+
};
|
|
1668
|
+
|
|
1669
|
+
// src/react/HostedCheckoutButton.tsx
|
|
1670
|
+
import { useState as useState4 } from "react";
|
|
1671
|
+
import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1672
|
+
var defaultStyles3 = {
|
|
1673
|
+
button: {
|
|
1674
|
+
padding: "14px 28px",
|
|
1675
|
+
fontSize: "16px",
|
|
1676
|
+
fontWeight: "600",
|
|
1677
|
+
color: "#fff",
|
|
1678
|
+
backgroundColor: "#7C3AED",
|
|
1679
|
+
border: "none",
|
|
1680
|
+
borderRadius: "8px",
|
|
1681
|
+
cursor: "pointer",
|
|
1682
|
+
transition: "all 0.2s",
|
|
1683
|
+
display: "inline-flex",
|
|
1684
|
+
alignItems: "center",
|
|
1685
|
+
justifyContent: "center",
|
|
1686
|
+
gap: "8px",
|
|
1687
|
+
minWidth: "200px"
|
|
1688
|
+
},
|
|
1689
|
+
buttonDisabled: {
|
|
1690
|
+
backgroundColor: "#A78BFA",
|
|
1691
|
+
cursor: "not-allowed",
|
|
1692
|
+
opacity: 0.7
|
|
1693
|
+
},
|
|
1694
|
+
buttonLoading: {
|
|
1695
|
+
cursor: "wait",
|
|
1696
|
+
opacity: 0.8
|
|
1697
|
+
},
|
|
1698
|
+
errorContainer: {
|
|
1699
|
+
marginTop: "16px"
|
|
1700
|
+
}
|
|
1701
|
+
};
|
|
1702
|
+
var LoadingSpinner = () => /* @__PURE__ */ jsxs4(
|
|
1703
|
+
"svg",
|
|
1704
|
+
{
|
|
1705
|
+
width: "20",
|
|
1706
|
+
height: "20",
|
|
1707
|
+
viewBox: "0 0 24 24",
|
|
1708
|
+
fill: "none",
|
|
1709
|
+
style: { animation: "spin 1s linear infinite" },
|
|
1710
|
+
children: [
|
|
1711
|
+
/* @__PURE__ */ jsx5("style", { children: `
|
|
1712
|
+
@keyframes spin {
|
|
1713
|
+
from { transform: rotate(0deg); }
|
|
1714
|
+
to { transform: rotate(360deg); }
|
|
1715
|
+
}
|
|
1716
|
+
` }),
|
|
1717
|
+
/* @__PURE__ */ jsx5(
|
|
1718
|
+
"circle",
|
|
1719
|
+
{
|
|
1720
|
+
cx: "12",
|
|
1721
|
+
cy: "12",
|
|
1722
|
+
r: "10",
|
|
1723
|
+
stroke: "currentColor",
|
|
1724
|
+
strokeWidth: "3",
|
|
1725
|
+
strokeLinecap: "round",
|
|
1726
|
+
strokeDasharray: "31.42",
|
|
1727
|
+
strokeDashoffset: "10",
|
|
1728
|
+
opacity: "0.25"
|
|
1729
|
+
}
|
|
1730
|
+
),
|
|
1731
|
+
/* @__PURE__ */ jsx5(
|
|
1732
|
+
"path",
|
|
1733
|
+
{
|
|
1734
|
+
d: "M12 2a10 10 0 0 1 10 10",
|
|
1735
|
+
stroke: "currentColor",
|
|
1736
|
+
strokeWidth: "3",
|
|
1737
|
+
strokeLinecap: "round"
|
|
1738
|
+
}
|
|
1739
|
+
)
|
|
1740
|
+
]
|
|
1741
|
+
}
|
|
1742
|
+
);
|
|
1743
|
+
var HostedCheckoutButton = ({
|
|
1744
|
+
order,
|
|
1745
|
+
returnUrl,
|
|
1746
|
+
allowedPaymentMethods,
|
|
1747
|
+
hideShipping = false,
|
|
1748
|
+
children,
|
|
1749
|
+
onCheckoutCreated,
|
|
1750
|
+
onError,
|
|
1751
|
+
styles: customStyles = {},
|
|
1752
|
+
className = "",
|
|
1753
|
+
disabled = false,
|
|
1754
|
+
showError = true,
|
|
1755
|
+
loadingComponent
|
|
1756
|
+
}) => {
|
|
1757
|
+
const { createCheckout, redirectToCheckout, isLoading, error, reset } = useCheckout();
|
|
1758
|
+
const [isRedirecting, setIsRedirecting] = useState4(false);
|
|
1759
|
+
const styles = {
|
|
1760
|
+
button: { ...defaultStyles3.button, ...customStyles.button },
|
|
1761
|
+
buttonDisabled: { ...defaultStyles3.buttonDisabled, ...customStyles.buttonDisabled },
|
|
1762
|
+
buttonLoading: { ...defaultStyles3.buttonLoading, ...customStyles.buttonLoading },
|
|
1763
|
+
errorContainer: { ...defaultStyles3.errorContainer, ...customStyles.errorContainer }
|
|
1764
|
+
};
|
|
1765
|
+
const handleClick = async () => {
|
|
1766
|
+
if (isLoading || isRedirecting) return;
|
|
1767
|
+
reset();
|
|
1768
|
+
try {
|
|
1769
|
+
const options = {
|
|
1770
|
+
order,
|
|
1771
|
+
successUrl: returnUrl,
|
|
1772
|
+
failureUrl: returnUrl,
|
|
1773
|
+
cancelUrl: returnUrl,
|
|
1774
|
+
allowedPaymentMethods,
|
|
1775
|
+
hideShipping
|
|
1776
|
+
};
|
|
1777
|
+
const checkout = await createCheckout(options);
|
|
1778
|
+
onCheckoutCreated?.(checkout);
|
|
1779
|
+
setIsRedirecting(true);
|
|
1780
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1781
|
+
redirectToCheckout();
|
|
1782
|
+
} catch (err) {
|
|
1783
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
1784
|
+
onError?.(error2);
|
|
1785
|
+
} finally {
|
|
1786
|
+
setIsRedirecting(false);
|
|
1787
|
+
}
|
|
1788
|
+
};
|
|
1789
|
+
const isDisabled = disabled || isLoading || isRedirecting;
|
|
1790
|
+
const buttonStyle = {
|
|
1791
|
+
...styles.button,
|
|
1792
|
+
...isDisabled ? styles.buttonDisabled : {},
|
|
1793
|
+
...isLoading || isRedirecting ? styles.buttonLoading : {}
|
|
1794
|
+
};
|
|
1795
|
+
return /* @__PURE__ */ jsxs4("div", { children: [
|
|
1796
|
+
/* @__PURE__ */ jsx5(
|
|
1797
|
+
"button",
|
|
1798
|
+
{
|
|
1799
|
+
onClick: handleClick,
|
|
1800
|
+
disabled: isDisabled,
|
|
1801
|
+
style: buttonStyle,
|
|
1802
|
+
className,
|
|
1803
|
+
type: "button",
|
|
1804
|
+
children: isLoading || isRedirecting ? loadingComponent || /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
1805
|
+
/* @__PURE__ */ jsx5(LoadingSpinner, {}),
|
|
1806
|
+
isRedirecting ? "Redirecting..." : "Processing..."
|
|
1807
|
+
] }) : children
|
|
1808
|
+
}
|
|
1809
|
+
),
|
|
1810
|
+
showError && error && /* @__PURE__ */ jsx5("div", { style: styles.errorContainer, children: /* @__PURE__ */ jsx5(
|
|
1811
|
+
ErrorDisplay,
|
|
1812
|
+
{
|
|
1813
|
+
error,
|
|
1814
|
+
onDismiss: reset,
|
|
1815
|
+
onRetry: handleClick
|
|
1816
|
+
}
|
|
1817
|
+
) })
|
|
1818
|
+
] });
|
|
1819
|
+
};
|
|
1820
|
+
export {
|
|
1821
|
+
APSProvider,
|
|
1822
|
+
ErrorDisplay,
|
|
1823
|
+
HostedCheckoutButton,
|
|
1824
|
+
PaymentStatus,
|
|
1825
|
+
TokenizationForm,
|
|
1826
|
+
useAPS,
|
|
1827
|
+
useCheckout,
|
|
1828
|
+
usePayment
|
|
1829
|
+
};
|