@xpayeg/sdk 2.4.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.types/index.d.cts +1635 -0
- package/.types/index.d.mts +1635 -0
- package/CHANGELOG.md +33 -30
- package/README.md +155 -131
- package/dist/index.cjs +0 -2
- package/dist/index.mjs +0 -2
- package/package.json +10 -23
- package/dist/index.d.cts +0 -4108
- package/dist/index.d.mts +0 -4108
package/README.md
CHANGED
|
@@ -22,36 +22,38 @@ npm install @xpayeg/sdk
|
|
|
22
22
|
<script src="https://checkout.xpay.app/v1/sdk.js"></script>
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
Both ESM (`import`) and CommonJS (`require`) are supported, with matching TypeScript declarations. Your tooling selects the appropriate entrypoint automatically.
|
|
26
|
+
|
|
25
27
|
## Step 1: Create a Checkout Session [Server-side]
|
|
26
28
|
|
|
27
29
|
On your server, create a Checkout Session and return the `clientSecret` to your frontend.
|
|
28
30
|
|
|
29
31
|
```javascript
|
|
30
32
|
// Your server (Node.js example)
|
|
31
|
-
app.post(
|
|
32
|
-
const response = await fetch(
|
|
33
|
-
method:
|
|
33
|
+
app.post("/api/create-checkout", async (req, res) => {
|
|
34
|
+
const response = await fetch("https://api.xpay.app/checkout/sessions", {
|
|
35
|
+
method: "POST",
|
|
34
36
|
headers: {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
Authorization: `Bearer ${process.env.XPAY_SECRET_KEY}`,
|
|
38
|
+
"Content-Type": "application/json",
|
|
37
39
|
},
|
|
38
40
|
body: JSON.stringify({
|
|
39
|
-
uiMode:
|
|
41
|
+
uiMode: "custom", // 'custom' for Elements SDK, 'embedded' for drop-in modal, 'hosted' for redirect
|
|
40
42
|
lineItems: [
|
|
41
43
|
{
|
|
42
44
|
priceData: {
|
|
43
|
-
unitAmount: 50000,
|
|
44
|
-
currency:
|
|
45
|
-
productData: { name:
|
|
45
|
+
unitAmount: 50000, // 500.00 EGP in piasters
|
|
46
|
+
currency: "EGP",
|
|
47
|
+
productData: { name: "Premium Plan" },
|
|
46
48
|
},
|
|
47
49
|
quantity: 1,
|
|
48
50
|
},
|
|
49
51
|
],
|
|
50
52
|
afterCompletion: {
|
|
51
|
-
type:
|
|
53
|
+
type: "redirect",
|
|
52
54
|
redirect: {
|
|
53
55
|
// {CHECKOUT_SESSION_ID} is automatically replaced with the actual session ID
|
|
54
|
-
url:
|
|
56
|
+
url: "https://yoursite.com/success?session_id={CHECKOUT_SESSION_ID}",
|
|
55
57
|
},
|
|
56
58
|
},
|
|
57
59
|
}),
|
|
@@ -69,55 +71,64 @@ app.post('/api/create-checkout', async (req, res) => {
|
|
|
69
71
|
Returns a single object with session fields and action methods merged together.
|
|
70
72
|
|
|
71
73
|
```javascript
|
|
72
|
-
import { loadXPay } from
|
|
74
|
+
import { loadXPay } from "@xpayeg/sdk";
|
|
73
75
|
|
|
74
|
-
const xpay = await loadXPay(
|
|
76
|
+
const xpay = await loadXPay("pk_test_xxx");
|
|
75
77
|
|
|
76
78
|
// Fetch client secret from your server
|
|
77
|
-
const { clientSecret } = await fetch(
|
|
79
|
+
const { clientSecret } = await fetch("/api/create-checkout", { method: "POST" }).then((r) =>
|
|
80
|
+
r.json(),
|
|
81
|
+
);
|
|
78
82
|
|
|
79
83
|
// Initialize checkout -- returns session data + action methods in one object
|
|
80
84
|
const checkout = await xpay.initCheckout({ clientSecret });
|
|
81
85
|
|
|
82
86
|
// Destructure session fields and actions together
|
|
83
87
|
const {
|
|
84
|
-
status,
|
|
85
|
-
|
|
88
|
+
status,
|
|
89
|
+
currency,
|
|
90
|
+
amountTotal,
|
|
91
|
+
amountSubtotal,
|
|
92
|
+
canConfirm,
|
|
93
|
+
paymentMethods,
|
|
94
|
+
confirm,
|
|
95
|
+
applyPromotionCode,
|
|
96
|
+
removePromotionCode,
|
|
97
|
+
updateLineItemQuantity,
|
|
86
98
|
} = checkout;
|
|
87
99
|
|
|
88
100
|
// Display in your UI
|
|
89
|
-
document.getElementById(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
`Pay with: ${paymentMethods.map(pm => pm.displayName).join(', ')}`;
|
|
101
|
+
document.getElementById("total").textContent = `${currency} ${(amountTotal / 100).toFixed(2)}`;
|
|
102
|
+
document.getElementById("methods").textContent =
|
|
103
|
+
`Pay with: ${paymentMethods.map((pm) => pm.displayName).join(", ")}`;
|
|
93
104
|
```
|
|
94
105
|
|
|
95
106
|
`clientSecret` accepts `Promise<string> | string`, so you can pass the fetch directly:
|
|
96
107
|
|
|
97
108
|
```javascript
|
|
98
109
|
const checkout = await xpay.initCheckout({
|
|
99
|
-
clientSecret: fetch(
|
|
100
|
-
.then(r => r.json())
|
|
101
|
-
.then(data => data.clientSecret),
|
|
110
|
+
clientSecret: fetch("/api/create-checkout", { method: "POST" })
|
|
111
|
+
.then((r) => r.json())
|
|
112
|
+
.then((data) => data.clientSecret),
|
|
102
113
|
});
|
|
103
114
|
```
|
|
104
115
|
|
|
105
116
|
**Session fields on the checkout object:**
|
|
106
117
|
|
|
107
|
-
| Field
|
|
108
|
-
|
|
109
|
-
| `id`
|
|
110
|
-
| `status`
|
|
111
|
-
| `canConfirm`
|
|
112
|
-
| `amountTotal`
|
|
113
|
-
| `amountSubtotal` | `number`
|
|
114
|
-
| `currency`
|
|
115
|
-
| `merchantName`
|
|
116
|
-
| `livemode`
|
|
117
|
-
| `paymentMethods` | `PaymentMethodInfo[]`
|
|
118
|
-
| `lineItems`
|
|
119
|
-
| `totalDetails`
|
|
120
|
-
| `discounts`
|
|
118
|
+
| Field | Type | Example | Description |
|
|
119
|
+
| ---------------- | ------------------------- | ----------------------------------- | --------------------------------------------- |
|
|
120
|
+
| `id` | `string` | `"cs_test_abc"` | Session ID |
|
|
121
|
+
| `status` | `SessionStatus` | `{ type: "open" }` | Structured status (see below) |
|
|
122
|
+
| `canConfirm` | `boolean` | `true` | Whether the session is ready for confirmation |
|
|
123
|
+
| `amountTotal` | `number` | `52450` | Final total in smallest currency unit |
|
|
124
|
+
| `amountSubtotal` | `number` | `50000` | Subtotal before fees/discounts |
|
|
125
|
+
| `currency` | `string` | `"EGP"` | ISO 4217 currency code |
|
|
126
|
+
| `merchantName` | `string` | `"My Store"` | Merchant display name |
|
|
127
|
+
| `livemode` | `boolean` | `false` | Whether live or test mode |
|
|
128
|
+
| `paymentMethods` | `PaymentMethodInfo[]` | `[{ type, displayName, category }]` | Available payment methods |
|
|
129
|
+
| `lineItems` | `LineItemDto[]` | | Line items with prices and quantities |
|
|
130
|
+
| `totalDetails` | `TotalDetailsResponseDto` | | Breakdown of fees, discounts, VAT |
|
|
131
|
+
| `discounts` | `DiscountResponseDto[]` | | Applied discounts |
|
|
121
132
|
|
|
122
133
|
### Session Status
|
|
123
134
|
|
|
@@ -125,19 +136,19 @@ const checkout = await xpay.initCheckout({
|
|
|
125
136
|
|
|
126
137
|
```javascript
|
|
127
138
|
// Check session status
|
|
128
|
-
if (checkout.status.type ===
|
|
139
|
+
if (checkout.status.type === "open") {
|
|
129
140
|
// Session is active, show payment form
|
|
130
141
|
}
|
|
131
142
|
|
|
132
|
-
if (checkout.status.type ===
|
|
143
|
+
if (checkout.status.type === "expired") {
|
|
133
144
|
// Session expired, show message or create a new one
|
|
134
|
-
showMessage(
|
|
145
|
+
showMessage("This checkout session has expired.");
|
|
135
146
|
}
|
|
136
147
|
|
|
137
|
-
if (checkout.status.type ===
|
|
148
|
+
if (checkout.status.type === "complete") {
|
|
138
149
|
// Payment finished -- check paymentStatus for details
|
|
139
150
|
console.log(checkout.status.paymentStatus); // "paid"
|
|
140
|
-
showMessage(
|
|
151
|
+
showMessage("Payment successful!");
|
|
141
152
|
}
|
|
142
153
|
```
|
|
143
154
|
|
|
@@ -146,29 +157,29 @@ if (checkout.status.type === 'complete') {
|
|
|
146
157
|
For event-driven initialization and lower-level control.
|
|
147
158
|
|
|
148
159
|
```javascript
|
|
149
|
-
const xpay = await loadXPay(
|
|
160
|
+
const xpay = await loadXPay("pk_test_xxx");
|
|
150
161
|
const elements = xpay.elements({ clientSecret });
|
|
151
162
|
|
|
152
|
-
elements.on(
|
|
153
|
-
console.log(session.status);
|
|
154
|
-
console.log(session.canConfirm);
|
|
155
|
-
console.log(session.amountTotal);
|
|
163
|
+
elements.on("ready", ({ session }) => {
|
|
164
|
+
console.log(session.status); // { type: "open" }
|
|
165
|
+
console.log(session.canConfirm); // true
|
|
166
|
+
console.log(session.amountTotal); // 52450
|
|
156
167
|
console.log(session.paymentMethods); // [{ type: 'card', ... }]
|
|
157
168
|
});
|
|
158
169
|
|
|
159
|
-
elements.on(
|
|
170
|
+
elements.on("loaderror", (event) => {
|
|
160
171
|
// Only fires for actual failures (invalid secret, network error, etc.)
|
|
161
172
|
// event: { type: "invalid_request_error" | "api_error" | "network_error", message, code?, param?, docUrl? }
|
|
162
|
-
console.error(
|
|
173
|
+
console.error("Failed to load:", event.message);
|
|
163
174
|
});
|
|
164
175
|
|
|
165
|
-
elements.on(
|
|
176
|
+
elements.on("error", (error) => {
|
|
166
177
|
// Fires for unsolicited errors not triggered by a merchant action.
|
|
167
178
|
// Examples: session expired during fee recalculation when switching payment methods, BIN detection failure.
|
|
168
|
-
console.log(error.type);
|
|
169
|
-
console.log(error.code);
|
|
179
|
+
console.log(error.type); // "invalid_request_error"
|
|
180
|
+
console.log(error.code); // "checkout_session_expired"
|
|
170
181
|
console.log(error.message); // "This checkout session has expired"
|
|
171
|
-
console.log(error.docUrl);
|
|
182
|
+
console.log(error.docUrl); // "https://docs.xpay.app/api/errors#checkout_session_expired"
|
|
172
183
|
});
|
|
173
184
|
```
|
|
174
185
|
|
|
@@ -180,12 +191,12 @@ elements.on('error', (error) => {
|
|
|
180
191
|
const checkout = await xpay.initCheckout({ clientSecret });
|
|
181
192
|
const elements = checkout.getElements();
|
|
182
193
|
|
|
183
|
-
const paymentElement = elements.create(
|
|
184
|
-
paymentElement.mount(
|
|
194
|
+
const paymentElement = elements.create("payment");
|
|
195
|
+
paymentElement.mount("#payment-element");
|
|
185
196
|
|
|
186
|
-
paymentElement.on(
|
|
197
|
+
paymentElement.on("change", (event) => {
|
|
187
198
|
submitButton.disabled = !event.complete;
|
|
188
|
-
console.log(event.value.type);
|
|
199
|
+
console.log(event.value.type); // 'card', 'valu', etc.
|
|
189
200
|
});
|
|
190
201
|
```
|
|
191
202
|
|
|
@@ -199,17 +210,17 @@ By default, the result is **returned to your code** (`redirect: "if_required"`).
|
|
|
199
210
|
// Default behavior: returns result to your code (no redirect)
|
|
200
211
|
const result = await checkout.confirm({
|
|
201
212
|
customerDetails: {
|
|
202
|
-
email:
|
|
203
|
-
name:
|
|
204
|
-
phone:
|
|
213
|
+
email: "customer@example.com",
|
|
214
|
+
name: "Ahmed Hassan",
|
|
215
|
+
phone: "+201234567890",
|
|
205
216
|
},
|
|
206
217
|
});
|
|
207
218
|
|
|
208
|
-
if (result.type ===
|
|
219
|
+
if (result.type === "error") {
|
|
209
220
|
errorDiv.textContent = result.error.message;
|
|
210
221
|
} else {
|
|
211
222
|
console.log(result.session.status); // { type: "complete", paymentStatus: "paid" }
|
|
212
|
-
window.location.href =
|
|
223
|
+
window.location.href = "/thank-you";
|
|
213
224
|
}
|
|
214
225
|
```
|
|
215
226
|
|
|
@@ -218,21 +229,21 @@ To redirect after success instead:
|
|
|
218
229
|
```javascript
|
|
219
230
|
await checkout.confirm({
|
|
220
231
|
customerDetails: {
|
|
221
|
-
email:
|
|
222
|
-
name:
|
|
232
|
+
email: "customer@example.com",
|
|
233
|
+
name: "Ahmed Hassan",
|
|
223
234
|
},
|
|
224
|
-
redirect:
|
|
235
|
+
redirect: "always", // Redirect to afterCompletion.redirect.url
|
|
225
236
|
});
|
|
226
237
|
// ^ If successful, the page navigates away. Code below only runs on error.
|
|
227
238
|
```
|
|
228
239
|
|
|
229
240
|
**Redirect behavior:**
|
|
230
241
|
|
|
231
|
-
| `redirect`
|
|
232
|
-
|
|
233
|
-
| Not set (default) | `"if_required"` — returns result to your code
|
|
234
|
-
| `"always"`
|
|
235
|
-
| `"if_required"`
|
|
242
|
+
| `redirect` | Behavior |
|
|
243
|
+
| ----------------- | --------------------------------------------------------- |
|
|
244
|
+
| Not set (default) | `"if_required"` — returns result to your code |
|
|
245
|
+
| `"always"` | Redirects to the session's `afterCompletion.redirect.url` |
|
|
246
|
+
| `"if_required"` | Returns result to your code — no redirect |
|
|
236
247
|
|
|
237
248
|
Your server sets that URL when it creates the session. XPay navigates there unchanged, appending nothing.
|
|
238
249
|
|
|
@@ -257,18 +268,18 @@ Action methods return `Promise<{ type: "success", session } | { type: "error", e
|
|
|
257
268
|
|
|
258
269
|
```javascript
|
|
259
270
|
// Apply promotion code
|
|
260
|
-
const result = await checkout.applyPromotionCode(
|
|
261
|
-
if (result.type ===
|
|
271
|
+
const result = await checkout.applyPromotionCode("SAVE20");
|
|
272
|
+
if (result.type === "error") {
|
|
262
273
|
errorDiv.textContent = result.error.message; // e.g., "Invalid promotion code"
|
|
263
274
|
} else {
|
|
264
|
-
console.log(
|
|
275
|
+
console.log("New total:", result.session.amountTotal);
|
|
265
276
|
}
|
|
266
277
|
|
|
267
278
|
// Remove promotion code
|
|
268
279
|
await checkout.removePromotionCode();
|
|
269
280
|
|
|
270
281
|
// Update line item quantity (object param, not positional)
|
|
271
|
-
await checkout.updateLineItemQuantity({ lineItem:
|
|
282
|
+
await checkout.updateLineItemQuantity({ lineItem: "li_abc123", quantity: 3 });
|
|
272
283
|
```
|
|
273
284
|
|
|
274
285
|
### Listening for Session Changes
|
|
@@ -277,25 +288,25 @@ Every update (promo codes, quantity changes, payment method selection, BIN detec
|
|
|
277
288
|
|
|
278
289
|
```javascript
|
|
279
290
|
// initCheckout -- use on('change', ...)
|
|
280
|
-
checkout.on(
|
|
291
|
+
checkout.on("change", (session) => {
|
|
281
292
|
totalDiv.textContent = `Total: ${session.currency} ${(session.amountTotal / 100).toFixed(2)}`;
|
|
282
293
|
|
|
283
294
|
// Amounts breakdown via totalDetails
|
|
284
|
-
console.log(session.totalDetails?.amountPlatformFee);
|
|
285
|
-
console.log(session.totalDetails?.amountCollectedVat);
|
|
286
|
-
console.log(session.totalDetails?.amountDiscount);
|
|
287
|
-
console.log(session.totalDetails?.amountShipping);
|
|
288
|
-
console.log(session.totalDetails?.amountTax);
|
|
289
|
-
|
|
290
|
-
console.log(session.discounts);
|
|
291
|
-
console.log(session.lineItems);
|
|
295
|
+
console.log(session.totalDetails?.amountPlatformFee); // Processing fee
|
|
296
|
+
console.log(session.totalDetails?.amountCollectedVat); // VAT
|
|
297
|
+
console.log(session.totalDetails?.amountDiscount); // Discount amount
|
|
298
|
+
console.log(session.totalDetails?.amountShipping); // Shipping
|
|
299
|
+
console.log(session.totalDetails?.amountTax); // Tax
|
|
300
|
+
|
|
301
|
+
console.log(session.discounts); // Applied discounts
|
|
302
|
+
console.log(session.lineItems); // Updated line items
|
|
292
303
|
});
|
|
293
304
|
```
|
|
294
305
|
|
|
295
306
|
With the classic `elements` API:
|
|
296
307
|
|
|
297
308
|
```javascript
|
|
298
|
-
elements.on(
|
|
309
|
+
elements.on("change", (session) => {
|
|
299
310
|
console.log(session.amountTotal);
|
|
300
311
|
console.log(session.totalDetails?.amountPlatformFee);
|
|
301
312
|
});
|
|
@@ -307,8 +318,8 @@ Force a fresh session fetch if needed:
|
|
|
307
318
|
|
|
308
319
|
```javascript
|
|
309
320
|
const result = await elements.fetchUpdates();
|
|
310
|
-
if (result.type ===
|
|
311
|
-
console.log(
|
|
321
|
+
if (result.type === "success") {
|
|
322
|
+
console.log("Session refreshed:", result.session.amountTotal);
|
|
312
323
|
}
|
|
313
324
|
```
|
|
314
325
|
|
|
@@ -320,11 +331,10 @@ Retrieve the session from **your server** (using your API key) to display order
|
|
|
320
331
|
|
|
321
332
|
```javascript
|
|
322
333
|
// Your server -- retrieves session using your API key
|
|
323
|
-
app.get(
|
|
324
|
-
const response = await fetch(
|
|
325
|
-
`
|
|
326
|
-
|
|
327
|
-
);
|
|
334
|
+
app.get("/api/order-status", async (req, res) => {
|
|
335
|
+
const response = await fetch(`https://api.xpay.app/checkout/sessions/${req.query.session_id}`, {
|
|
336
|
+
headers: { Authorization: `Bearer ${process.env.XPAY_SECRET_KEY}` },
|
|
337
|
+
});
|
|
328
338
|
const session = await response.json();
|
|
329
339
|
res.json(session);
|
|
330
340
|
});
|
|
@@ -333,12 +343,12 @@ app.get('/api/order-status', async (req, res) => {
|
|
|
333
343
|
**Client-side success page:**
|
|
334
344
|
|
|
335
345
|
```javascript
|
|
336
|
-
const sessionId = new URLSearchParams(window.location.search).get(
|
|
337
|
-
const session = await fetch(`/api/order-status?session_id=${sessionId}`).then(r => r.json());
|
|
346
|
+
const sessionId = new URLSearchParams(window.location.search).get("session_id");
|
|
347
|
+
const session = await fetch(`/api/order-status?session_id=${sessionId}`).then((r) => r.json());
|
|
338
348
|
|
|
339
|
-
document.getElementById(
|
|
340
|
-
session.paymentStatus ===
|
|
341
|
-
document.getElementById(
|
|
349
|
+
document.getElementById("status").textContent =
|
|
350
|
+
session.paymentStatus === "paid" ? "Payment Confirmed" : "Processing...";
|
|
351
|
+
document.getElementById("total").textContent =
|
|
342
352
|
`${session.currency} ${(session.amountTotal / 100).toFixed(2)}`;
|
|
343
353
|
|
|
344
354
|
// Amounts breakdown
|
|
@@ -349,8 +359,8 @@ document.getElementById('total').textContent =
|
|
|
349
359
|
// session.totalDetails.amountTax -- 0
|
|
350
360
|
|
|
351
361
|
// Line items
|
|
352
|
-
session.lineItems?.forEach(item => {
|
|
353
|
-
console.log(item.price.product.name,
|
|
362
|
+
session.lineItems?.forEach((item) => {
|
|
363
|
+
console.log(item.price.product.name, "x", item.quantity, "=", item.amountTotal);
|
|
354
364
|
});
|
|
355
365
|
```
|
|
356
366
|
|
|
@@ -359,11 +369,11 @@ session.lineItems?.forEach(item => {
|
|
|
359
369
|
Listen for webhook events on your server. This is the source of truth for order fulfillment.
|
|
360
370
|
|
|
361
371
|
```javascript
|
|
362
|
-
app.post(
|
|
372
|
+
app.post("/webhooks/xpay", (req, res) => {
|
|
363
373
|
const event = req.body;
|
|
364
374
|
|
|
365
|
-
if (event.type ===
|
|
366
|
-
fulfillOrder(event.data);
|
|
375
|
+
if (event.type === "checkout.session.completed") {
|
|
376
|
+
fulfillOrder(event.data); // Ship product, send email, update DB
|
|
367
377
|
}
|
|
368
378
|
|
|
369
379
|
res.json({ received: true });
|
|
@@ -379,17 +389,17 @@ The simplest integration -- opens the full checkout in a modal overlay. No form
|
|
|
379
389
|
|
|
380
390
|
<script src="https://checkout.xpay.app/v1/sdk.js"></script>
|
|
381
391
|
<script>
|
|
382
|
-
document.getElementById(
|
|
383
|
-
const xpay = XPay(
|
|
392
|
+
document.getElementById("checkout-button").addEventListener("click", async () => {
|
|
393
|
+
const xpay = XPay("pk_test_xxx");
|
|
384
394
|
|
|
385
395
|
const checkout = xpay.checkout({
|
|
386
|
-
clientSecret:
|
|
387
|
-
mode:
|
|
396
|
+
clientSecret: "cs_test_abc_secret_xyz",
|
|
397
|
+
mode: "modal",
|
|
388
398
|
onComplete: (result) => {
|
|
389
|
-
window.location.href =
|
|
399
|
+
window.location.href = "/success";
|
|
390
400
|
},
|
|
391
401
|
onClose: () => {
|
|
392
|
-
console.log(
|
|
402
|
+
console.log("Customer closed checkout");
|
|
393
403
|
},
|
|
394
404
|
});
|
|
395
405
|
|
|
@@ -403,16 +413,16 @@ The simplest integration -- opens the full checkout in a modal overlay. No form
|
|
|
403
413
|
The `@xpayeg/react` package provides a `useCheckout()` hook that wraps `initCheckout` with loading/error states:
|
|
404
414
|
|
|
405
415
|
```javascript
|
|
406
|
-
import { useCheckout } from
|
|
416
|
+
import { useCheckout } from "@xpayeg/react";
|
|
407
417
|
|
|
408
418
|
function CheckoutPage() {
|
|
409
419
|
const checkoutState = useCheckout();
|
|
410
420
|
|
|
411
|
-
if (checkoutState.type ===
|
|
421
|
+
if (checkoutState.type === "loading") {
|
|
412
422
|
return <Spinner />;
|
|
413
423
|
}
|
|
414
424
|
|
|
415
|
-
if (checkoutState.type ===
|
|
425
|
+
if (checkoutState.type === "error") {
|
|
416
426
|
return <div>Error: {checkoutState.error.message}</div>;
|
|
417
427
|
}
|
|
418
428
|
|
|
@@ -420,9 +430,13 @@ function CheckoutPage() {
|
|
|
420
430
|
|
|
421
431
|
return (
|
|
422
432
|
<div>
|
|
423
|
-
<h2>
|
|
424
|
-
|
|
425
|
-
|
|
433
|
+
<h2>
|
|
434
|
+
Total: {currency} {(amountTotal / 100).toFixed(2)}
|
|
435
|
+
</h2>
|
|
436
|
+
{lineItems?.map((item) => (
|
|
437
|
+
<div key={item.id}>
|
|
438
|
+
{item.price.product.name} x {item.quantity}
|
|
439
|
+
</div>
|
|
426
440
|
))}
|
|
427
441
|
<button onClick={() => confirm({ customerDetails: { email } })}>Pay</button>
|
|
428
442
|
</div>
|
|
@@ -438,22 +452,22 @@ Override the session's `brandingSettings` at runtime:
|
|
|
438
452
|
const checkout = await xpay.initCheckout({
|
|
439
453
|
clientSecret,
|
|
440
454
|
appearance: {
|
|
441
|
-
colorMode:
|
|
442
|
-
borderStyle:
|
|
443
|
-
inputStyle:
|
|
444
|
-
colors: { primary:
|
|
455
|
+
colorMode: "dark",
|
|
456
|
+
borderStyle: "pill",
|
|
457
|
+
inputStyle: "filled",
|
|
458
|
+
colors: { primary: "#FF6B35" },
|
|
445
459
|
},
|
|
446
460
|
});
|
|
447
461
|
|
|
448
462
|
// Update later with changeAppearance()
|
|
449
|
-
checkout.changeAppearance({ colorMode:
|
|
463
|
+
checkout.changeAppearance({ colorMode: "light" });
|
|
450
464
|
```
|
|
451
465
|
|
|
452
466
|
With the classic API:
|
|
453
467
|
|
|
454
468
|
```javascript
|
|
455
|
-
const elements = xpay.elements({ clientSecret, appearance: { colorMode:
|
|
456
|
-
elements.changeAppearance({ colorMode:
|
|
469
|
+
const elements = xpay.elements({ clientSecret, appearance: { colorMode: "dark" } });
|
|
470
|
+
elements.changeAppearance({ colorMode: "light" });
|
|
457
471
|
```
|
|
458
472
|
|
|
459
473
|
## API Reference
|
|
@@ -463,8 +477,8 @@ elements.changeAppearance({ colorMode: 'light' });
|
|
|
463
477
|
Loads the XPay SDK from CDN. Returns a Promise. Call at module level, not inside components.
|
|
464
478
|
|
|
465
479
|
```javascript
|
|
466
|
-
import { loadXPay } from
|
|
467
|
-
const xpay = await loadXPay(
|
|
480
|
+
import { loadXPay } from "@xpayeg/sdk";
|
|
481
|
+
const xpay = await loadXPay("pk_test_xxx");
|
|
468
482
|
```
|
|
469
483
|
|
|
470
484
|
### `xpay.initCheckout(options)`
|
|
@@ -473,9 +487,9 @@ Initialize a checkout session. Resolves with a merged object containing session
|
|
|
473
487
|
|
|
474
488
|
```javascript
|
|
475
489
|
const checkout = await xpay.initCheckout({
|
|
476
|
-
clientSecret:
|
|
477
|
-
appearance: { colorMode:
|
|
478
|
-
locale:
|
|
490
|
+
clientSecret: "cs_test_abc_secret_xyz", // string | Promise<string>
|
|
491
|
+
appearance: { colorMode: "dark" },
|
|
492
|
+
locale: "ar",
|
|
479
493
|
});
|
|
480
494
|
|
|
481
495
|
// Session fields: status, canConfirm, amountTotal, currency, paymentMethods, lineItems, totalDetails, ...
|
|
@@ -501,10 +515,20 @@ Full TypeScript support with all types exported:
|
|
|
501
515
|
|
|
502
516
|
```typescript
|
|
503
517
|
import type {
|
|
504
|
-
XPayInstance,
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
518
|
+
XPayInstance,
|
|
519
|
+
Elements,
|
|
520
|
+
PaymentMethodInfo,
|
|
521
|
+
CheckoutSession,
|
|
522
|
+
SessionStatus,
|
|
523
|
+
CustomerDetails,
|
|
524
|
+
Appearance,
|
|
525
|
+
InitCheckoutResult,
|
|
526
|
+
CheckoutActions,
|
|
527
|
+
ActionResult,
|
|
528
|
+
XPayError,
|
|
529
|
+
CheckoutLineItem,
|
|
530
|
+
CheckoutTotalDetails,
|
|
531
|
+
CheckoutFees,
|
|
532
|
+
CheckoutDiscount,
|
|
533
|
+
} from "@xpayeg/sdk";
|
|
510
534
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
//#region src/index.ts
|
|
3
2
|
const DEFAULT_SDK_URL = "https://checkout.xpay.app/v1/sdk.js";
|
|
4
3
|
let loadPromise = null;
|
|
5
4
|
/**
|
|
@@ -95,5 +94,4 @@ function findExistingScript() {
|
|
|
95
94
|
} catch {}
|
|
96
95
|
return null;
|
|
97
96
|
}
|
|
98
|
-
//#endregion
|
|
99
97
|
exports.loadXPay = loadXPay;
|
package/dist/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xpayeg/sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "XPay JavaScript SDK — loader and TypeScript types for embedding XPay payments",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -10,15 +10,15 @@
|
|
|
10
10
|
},
|
|
11
11
|
"main": "./dist/index.cjs",
|
|
12
12
|
"module": "./dist/index.mjs",
|
|
13
|
-
"types": "
|
|
13
|
+
"types": "./.types/index.d.mts",
|
|
14
14
|
"exports": {
|
|
15
15
|
".": {
|
|
16
16
|
"import": {
|
|
17
|
-
"types": "
|
|
17
|
+
"types": "./.types/index.d.mts",
|
|
18
18
|
"default": "./dist/index.mjs"
|
|
19
19
|
},
|
|
20
20
|
"require": {
|
|
21
|
-
"types": "
|
|
21
|
+
"types": "./.types/index.d.cts",
|
|
22
22
|
"default": "./dist/index.cjs"
|
|
23
23
|
}
|
|
24
24
|
}
|
|
@@ -27,22 +27,14 @@
|
|
|
27
27
|
"access": "public"
|
|
28
28
|
},
|
|
29
29
|
"files": [
|
|
30
|
-
"dist",
|
|
30
|
+
"dist/index.mjs",
|
|
31
|
+
"dist/index.cjs",
|
|
31
32
|
"README.md",
|
|
32
33
|
"CHANGELOG.md",
|
|
33
|
-
"LICENSE"
|
|
34
|
+
"LICENSE",
|
|
35
|
+
".types/index.d.mts",
|
|
36
|
+
".types/index.d.cts"
|
|
34
37
|
],
|
|
35
|
-
"devDependencies": {
|
|
36
|
-
"@arethetypeswrong/cli": "^0.18.2",
|
|
37
|
-
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
38
|
-
"publint": "^0.3.21",
|
|
39
|
-
"rollup": "^4.60.4",
|
|
40
|
-
"rollup-plugin-dts": "^6.4.1",
|
|
41
|
-
"tsdown": "^0.22.0",
|
|
42
|
-
"typescript": "5.9.3",
|
|
43
|
-
"@xpay/checkout-api": "0.0.0",
|
|
44
|
-
"@xpay/tsconfig": "0.0.0"
|
|
45
|
-
},
|
|
46
38
|
"repository": {
|
|
47
39
|
"type": "git",
|
|
48
40
|
"url": "git+https://github.com/xpayeg/xpay-js.git"
|
|
@@ -57,10 +49,5 @@
|
|
|
57
49
|
"egypt",
|
|
58
50
|
"checkout",
|
|
59
51
|
"sdk"
|
|
60
|
-
]
|
|
61
|
-
"scripts": {
|
|
62
|
-
"build": "tsdown && rollup -c rollup.dts.config.mjs",
|
|
63
|
-
"lint:pkg": "publint && attw --pack .",
|
|
64
|
-
"typecheck": "tsc --noEmit"
|
|
65
|
-
}
|
|
52
|
+
]
|
|
66
53
|
}
|