@xpayeg/react 2.0.0 → 2.2.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/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # @xpayeg/react
2
2
 
3
+ ## 2.2.0
4
+ ### Minor Changes
5
+
6
+
7
+
8
+ - [#462](https://github.com/xpayeg/xpay/pull/462) [`f083704`](https://github.com/xpayeg/xpay/commit/f08370434ff93c52450cfaee17f249c11b5cd46c) Thanks [@Elmosh](https://github.com/Elmosh)! - Deferred-mount Payment Element: `xpay.elements({ mode: "payment", amount, currency })` renders the payment form with no checkout session — your server creates the session with the final total when the customer clicks Pay, and its clientSecret is passed to `confirmPayment({ elements, clientSecret })` (a plain string). The session's total must equal the amount the element displays, or the confirmation fails with `amount_reconfirmation_required` and nothing is charged. Adds `elements.update({ amount, currency })` for deferred display updates. `XPayProvider` accepts the new options form; deferred amount/currency prop changes flow through `elements.update()` without recreating the instance. The existing `{ clientSecret }` path is unchanged.
9
+
10
+ Also in this release: the overlay scroll lock (3DS/action overlay and drop-in modal, now one shared implementation) pins the page at its measured geometry and preserves the scrollbar gutter, so centered boxed themes no longer shift when an overlay opens; `elements.fetchUpdates()` now genuinely re-fetches the session from the server (it previously answered from the iframe's local state; failures now resolve the error arm instead of returning stale data), and `CheckoutSession` gains optional `presentmentDetails` — the customer-facing amounts, present only when the merchant prices in a currency other than the processing currency. Read amounts presentment-first.
11
+
12
+ ## 2.1.0
13
+ ### Minor Changes
14
+
15
+
16
+
17
+ - [#224](https://github.com/xpayeg/xpay/pull/224) [`40beff5`](https://github.com/xpayeg/xpay/commit/40beff5ee4660914dff5c0f7c43480083bf331c4) Thanks [@mariamkamel](https://github.com/mariamkamel)! - `CheckoutCompleteResult` (drop-in `onComplete`) and the `session` in `confirmPayment()`'s success result now carry `paymentStatus`. Methods the customer pays afterwards, such as Fawry, complete checkout as `unpaid` and are paid later. Fulfil on `paymentStatus: "paid"`, never on completion alone.
18
+
19
+ Drop-in `onError` now fires when an attempt fails (declined, canceled, processing error) and when the session is expired or already complete on load. The modal unlocks after a failed attempt so the customer can close it; previously it stayed locked until a successful payment.
20
+
21
+ ### Patch Changes
22
+
23
+
24
+
25
+ - [#453](https://github.com/xpayeg/xpay/pull/453) [`5445718`](https://github.com/xpayeg/xpay/commit/5445718b5027cba56f1d645cba6f42904d2c2f2c) Thanks [@Elmosh](https://github.com/Elmosh)! - `@xpayeg/react` now declares its `@xpayeg/sdk` peer dependency as `^2.0.0` instead of an exact pinned version, so the two packages no longer have to be upgraded in lockstep within a major.
26
+
3
27
  ## 2.0.0
4
28
  ### Major Changes
5
29
 
package/dist/index.cjs CHANGED
@@ -64,10 +64,34 @@ function XPayProvider({ xpay: xpayProp, options, children }) {
64
64
  cancelled = true;
65
65
  };
66
66
  }, [xpayProp]);
67
+ const clientSecretOpt = options && "clientSecret" in options ? options.clientSecret : void 0;
68
+ const modeOpt = options && "mode" in options ? options.mode : void 0;
67
69
  const elements = (0, react.useMemo)(() => {
68
- if (!resolvedXPay || !options?.clientSecret) return null;
70
+ if (!resolvedXPay || !options) return null;
71
+ if (!clientSecretOpt && !modeOpt) return null;
69
72
  return resolvedXPay.elements(options);
70
- }, [resolvedXPay, options?.clientSecret]);
73
+ }, [
74
+ resolvedXPay,
75
+ clientSecretOpt,
76
+ modeOpt
77
+ ]);
78
+ const lastDeferredRef = (0, react.useRef)(null);
79
+ (0, react.useEffect)(() => {
80
+ if (!elements || !options || !("mode" in options) || !options.mode) {
81
+ lastDeferredRef.current = null;
82
+ return;
83
+ }
84
+ const prev = lastDeferredRef.current;
85
+ lastDeferredRef.current = {
86
+ amount: options.amount,
87
+ currency: options.currency
88
+ };
89
+ if (!prev) return;
90
+ const updates = {};
91
+ if (options.amount !== prev.amount) updates.amount = options.amount;
92
+ if (options.currency !== prev.currency) updates.currency = options.currency;
93
+ if (updates.amount !== void 0 || updates.currency !== void 0) elements.update(updates);
94
+ }, [elements, options]);
71
95
  const [session, setSession] = (0, react.useState)(null);
72
96
  const [sessionError, setSessionError] = (0, react.useState)(null);
73
97
  (0, react.useEffect)(() => {
package/dist/index.d.cts CHANGED
@@ -36,7 +36,13 @@ interface XPayProviderProps {
36
36
  * Use `loadXPay()` from `@xpayeg/sdk` — call it at module level, not inside a component.
37
37
  */
38
38
  xpay: XPayInstance | Promise<XPayInstance | null> | null;
39
- /** Elements options. Must include `clientSecret`. */
39
+ /**
40
+ * Elements options — either `{ clientSecret }` (session-first) or
41
+ * `{ mode: "payment", amount, currency }` (deferred — the session is created
42
+ * by your server at confirm time). In deferred mode use `useElements()` +
43
+ * `<PaymentElement>`; `useCheckout()` session data reflects the display
44
+ * amount until a session is bound at confirm.
45
+ */
40
46
  options?: ElementsOptions;
41
47
  children: ReactNode;
42
48
  }
package/dist/index.d.mts CHANGED
@@ -36,7 +36,13 @@ interface XPayProviderProps {
36
36
  * Use `loadXPay()` from `@xpayeg/sdk` — call it at module level, not inside a component.
37
37
  */
38
38
  xpay: XPayInstance | Promise<XPayInstance | null> | null;
39
- /** Elements options. Must include `clientSecret`. */
39
+ /**
40
+ * Elements options — either `{ clientSecret }` (session-first) or
41
+ * `{ mode: "payment", amount, currency }` (deferred — the session is created
42
+ * by your server at confirm time). In deferred mode use `useElements()` +
43
+ * `<PaymentElement>`; `useCheckout()` session data reflects the display
44
+ * amount until a session is bound at confirm.
45
+ */
40
46
  options?: ElementsOptions;
41
47
  children: ReactNode;
42
48
  }
package/dist/index.mjs CHANGED
@@ -63,10 +63,34 @@ function XPayProvider({ xpay: xpayProp, options, children }) {
63
63
  cancelled = true;
64
64
  };
65
65
  }, [xpayProp]);
66
+ const clientSecretOpt = options && "clientSecret" in options ? options.clientSecret : void 0;
67
+ const modeOpt = options && "mode" in options ? options.mode : void 0;
66
68
  const elements = useMemo(() => {
67
- if (!resolvedXPay || !options?.clientSecret) return null;
69
+ if (!resolvedXPay || !options) return null;
70
+ if (!clientSecretOpt && !modeOpt) return null;
68
71
  return resolvedXPay.elements(options);
69
- }, [resolvedXPay, options?.clientSecret]);
72
+ }, [
73
+ resolvedXPay,
74
+ clientSecretOpt,
75
+ modeOpt
76
+ ]);
77
+ const lastDeferredRef = useRef(null);
78
+ useEffect(() => {
79
+ if (!elements || !options || !("mode" in options) || !options.mode) {
80
+ lastDeferredRef.current = null;
81
+ return;
82
+ }
83
+ const prev = lastDeferredRef.current;
84
+ lastDeferredRef.current = {
85
+ amount: options.amount,
86
+ currency: options.currency
87
+ };
88
+ if (!prev) return;
89
+ const updates = {};
90
+ if (options.amount !== prev.amount) updates.amount = options.amount;
91
+ if (options.currency !== prev.currency) updates.currency = options.currency;
92
+ if (updates.amount !== void 0 || updates.currency !== void 0) elements.update(updates);
93
+ }, [elements, options]);
70
94
  const [session, setSession] = useState(null);
71
95
  const [sessionError, setSessionError] = useState(null);
72
96
  useEffect(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xpayeg/react",
3
- "version": "2.0.0",
3
+ "version": "2.2.0",
4
4
  "description": "XPay React components — PaymentElement, CheckoutButton, and hooks",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -33,9 +33,9 @@
33
33
  "LICENSE"
34
34
  ],
35
35
  "peerDependencies": {
36
+ "@xpayeg/sdk": "^2.0.0",
36
37
  "react": "^18 || ^19",
37
- "react-dom": "^18 || ^19",
38
- "@xpayeg/sdk": "2.0.0"
38
+ "react-dom": "^18 || ^19"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@arethetypeswrong/cli": "^0.18.2",
@@ -50,7 +50,7 @@
50
50
  "tsdown": "^0.22.0",
51
51
  "typescript": "5.9.3",
52
52
  "@xpay/tsconfig": "0.0.0",
53
- "@xpayeg/sdk": "2.0.0"
53
+ "@xpayeg/sdk": "2.2.0"
54
54
  },
55
55
  "repository": {
56
56
  "type": "git",