@valuepay/react 1.0.0 → 2.1.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.
@@ -3,11 +3,15 @@
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var useValuePay = require('../hooks/useValuePay.js');
5
5
 
6
- const ValuePayButton = ({ text, className, style, disabled, children, ...config }) => {
6
+ const ValuePayButton = ({ text, className, style, containerStyle, containerClassName, disabled, children, ...config }) => {
7
7
  const { initialize, isReady, isProcessing } = useValuePay.useValuePay(config);
8
8
  const defaultText = `Pay \u20A6${config.amount.toLocaleString()} Now`;
9
9
  const buttonText = children || text || defaultText;
10
- return (jsxRuntime.jsx("button", { type: "button", onClick: () => initialize(), disabled: disabled || !isReady || isProcessing, className: className, style: style, "aria-busy": isProcessing, "aria-label": typeof buttonText === "string" ? buttonText : "Pay Now", children: isProcessing ? "Processing..." : buttonText }));
10
+ const button = (jsxRuntime.jsx("button", { type: "button", onClick: () => initialize(), disabled: disabled || !isReady || isProcessing, className: className, style: style, "aria-busy": isProcessing, "aria-label": typeof buttonText === "string" ? buttonText : "Pay Now", children: isProcessing ? "Processing..." : buttonText }));
11
+ if (containerStyle || containerClassName) {
12
+ return (jsxRuntime.jsx("div", { style: containerStyle, className: containerClassName, "data-testid": "valuepay-button-container", children: button }));
13
+ }
14
+ return button;
11
15
  };
12
16
 
13
17
  exports.ValuePayButton = ValuePayButton;
@@ -1 +1 @@
1
- {"version":3,"file":"ValuePayButton.js","sources":["../../../../src/components/ValuePayButton.tsx"],"sourcesContent":["import React from \"react\";\nimport { useValuePay } from \"../hooks/useValuePay\";\nimport type { ValuePayButtonProps } from \"../types\";\n\nexport const ValuePayButton: React.FC<ValuePayButtonProps> = ({\n text,\n className,\n style,\n disabled,\n children,\n ...config\n}) => {\n const { initialize, isReady, isProcessing } = useValuePay(config);\n\n const defaultText = `Pay \\u20A6${config.amount.toLocaleString()} Now`;\n const buttonText = children || text || defaultText;\n\n return (\n <button\n type=\"button\"\n onClick={() => initialize()}\n disabled={disabled || !isReady || isProcessing}\n className={className}\n style={style}\n aria-busy={isProcessing}\n aria-label={typeof buttonText === \"string\" ? buttonText : \"Pay Now\"}\n >\n {isProcessing ? \"Processing...\" : buttonText}\n </button>\n );\n};\n"],"names":["useValuePay","_jsx"],"mappings":";;;;;MAIa,cAAc,GAAkC,CAAC,EAC5D,IAAI,EACJ,SAAS,EACT,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,GAAG,MAAM,EACV,KAAI;AACH,IAAA,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,GAAGA,uBAAW,CAAC,MAAM,CAAC;IAEjE,MAAM,WAAW,GAAG,CAAA,UAAA,EAAa,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,CAAA,IAAA,CAAM;AACrE,IAAA,MAAM,UAAU,GAAG,QAAQ,IAAI,IAAI,IAAI,WAAW;IAElD,QACEC,cAAA,CAAA,QAAA,EAAA,EACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,MAAM,UAAU,EAAE,EAC3B,QAAQ,EAAE,QAAQ,IAAI,CAAC,OAAO,IAAI,YAAY,EAC9C,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,KAAK,EAAA,WAAA,EACD,YAAY,EAAA,YAAA,EACX,OAAO,UAAU,KAAK,QAAQ,GAAG,UAAU,GAAG,SAAS,EAAA,QAAA,EAElE,YAAY,GAAG,eAAe,GAAG,UAAU,EAAA,CACrC;AAEb;;;;"}
1
+ {"version":3,"file":"ValuePayButton.js","sources":["../../../../src/components/ValuePayButton.tsx"],"sourcesContent":["import React from \"react\";\r\nimport { useValuePay } from \"../hooks/useValuePay\";\r\nimport type { ValuePayButtonProps } from \"../types\";\r\n\r\nexport const ValuePayButton: React.FC<ValuePayButtonProps> = ({\r\n text,\r\n className,\r\n style,\r\n containerStyle,\r\n containerClassName,\r\n disabled,\r\n children,\r\n ...config\r\n}) => {\r\n const { initialize, isReady, isProcessing } = useValuePay(config);\r\n\r\n const defaultText = `Pay \\u20A6${config.amount.toLocaleString()} Now`;\r\n const buttonText = children || text || defaultText;\r\n\r\n const button = (\r\n <button\r\n type=\"button\"\r\n onClick={() => initialize()}\r\n disabled={disabled || !isReady || isProcessing}\r\n className={className}\r\n style={style}\r\n aria-busy={isProcessing}\r\n aria-label={typeof buttonText === \"string\" ? buttonText : \"Pay Now\"}\r\n >\r\n {isProcessing ? \"Processing...\" : buttonText}\r\n </button>\r\n );\r\n\r\n if (containerStyle || containerClassName) {\r\n return (\r\n <div style={containerStyle} className={containerClassName} data-testid=\"valuepay-button-container\">\r\n {button}\r\n </div>\r\n );\r\n }\r\n\r\n return button;\r\n};\r\n"],"names":["useValuePay","_jsx"],"mappings":";;;;;AAIO,MAAM,cAAc,GAAkC,CAAC,EAC5D,IAAI,EACJ,SAAS,EACT,KAAK,EACL,cAAc,EACd,kBAAkB,EAClB,QAAQ,EACR,QAAQ,EACR,GAAG,MAAM,EACV,KAAI;AACH,IAAA,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,GAAGA,uBAAW,CAAC,MAAM,CAAC;IAEjE,MAAM,WAAW,GAAG,CAAA,UAAA,EAAa,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,CAAA,IAAA,CAAM;AACrE,IAAA,MAAM,UAAU,GAAG,QAAQ,IAAI,IAAI,IAAI,WAAW;IAElD,MAAM,MAAM,IACVC,cAAA,CAAA,QAAA,EAAA,EACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,MAAM,UAAU,EAAE,EAC3B,QAAQ,EAAE,QAAQ,IAAI,CAAC,OAAO,IAAI,YAAY,EAC9C,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,KAAK,EAAA,WAAA,EACD,YAAY,EAAA,YAAA,EACX,OAAO,UAAU,KAAK,QAAQ,GAAG,UAAU,GAAG,SAAS,EAAA,QAAA,EAElE,YAAY,GAAG,eAAe,GAAG,UAAU,EAAA,CACrC,CACV;AAED,IAAA,IAAI,cAAc,IAAI,kBAAkB,EAAE;AACxC,QAAA,QACEA,cAAA,CAAA,KAAA,EAAA,EAAK,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,kBAAkB,iBAAc,2BAA2B,EAAA,QAAA,EAC/F,MAAM,EAAA,CACH;IAEV;AAEA,IAAA,OAAO,MAAM;AACf;;;;"}
@@ -5,6 +5,32 @@ var useScript = require('./useScript.js');
5
5
  var helpers = require('../utils/helpers.js');
6
6
  var constants = require('../utils/constants.js');
7
7
 
8
+ /**
9
+ * Checks whether any callback function is provided in the config,
10
+ * using case-insensitive detection to handle both SDK-style (onCallback)
11
+ * and raw checkout-style (oncallback) casing.
12
+ */
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
+ function hasCallback(cfg, ...names) {
15
+ return names.some((name) => typeof cfg[name] === "function" || typeof cfg[name.toLowerCase()] === "function");
16
+ }
17
+ /**
18
+ * Returns true if the config has ANY callback defined (case-insensitive).
19
+ * When true, callbacks take precedence and redirectUrl is ignored.
20
+ */
21
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
+ function hasAnyCallback(cfg) {
23
+ return hasCallback(cfg, "onSuccess", "onCallback", "onCancelled", "onClose", "onError");
24
+ }
25
+ /**
26
+ * Redirects the browser to the given URL with ref and status query params.
27
+ */
28
+ function redirectTo(redirectUrl, ref, status) {
29
+ const url = new URL(redirectUrl);
30
+ url.searchParams.set("ref", ref);
31
+ url.searchParams.set("status", status);
32
+ window.location.href = url.toString();
33
+ }
8
34
  const useValuePay = (config) => {
9
35
  const scriptUrl = config.scriptUrl || constants.DEFAULT_SCRIPT_URL;
10
36
  const isReady = useScript.useScript(scriptUrl);
@@ -23,24 +49,33 @@ const useValuePay = (config) => {
23
49
  setIsProcessing(true);
24
50
  const normalized = helpers.normalizeConfig(cfg, overrides);
25
51
  const ref = normalized.transactionRef;
52
+ const useRedirect = !hasAnyCallback(cfg) && !!cfg.redirectUrl;
26
53
  window.ValuepayCheckout({
27
54
  ...normalized,
28
55
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
56
+ onsuccess: (response) => {
57
+ var _a;
58
+ const res = helpers.normalizeResponse(response, ref, cfg);
59
+ setIsProcessing(false);
60
+ if (useRedirect) {
61
+ redirectTo(cfg.redirectUrl, res.ref, res.status);
62
+ return;
63
+ }
64
+ if (res.status !== "SUCCESS" && res.status !== "COMPLETED") {
65
+ res.status = "SUCCESS";
66
+ }
67
+ (_a = cfg.onSuccess) === null || _a === void 0 ? void 0 : _a.call(cfg, res);
68
+ },
69
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
70
  oncallback: (response) => {
30
- var _a, _b;
71
+ var _a;
31
72
  const res = helpers.normalizeResponse(response, ref, cfg);
32
73
  setIsProcessing(false);
33
- if (cfg.redirectUrl) {
34
- const url = new URL(cfg.redirectUrl);
35
- url.searchParams.set("ref", res.ref);
36
- url.searchParams.set("status", res.status);
37
- window.location.href = url.toString();
74
+ if (useRedirect) {
75
+ redirectTo(cfg.redirectUrl, res.ref, res.status);
38
76
  return;
39
77
  }
40
78
  (_a = cfg.onCallback) === null || _a === void 0 ? void 0 : _a.call(cfg, res);
41
- if (res.status === "SUCCESS" || res.status === "COMPLETED") {
42
- (_b = cfg.onSuccess) === null || _b === void 0 ? void 0 : _b.call(cfg, res);
43
- }
44
79
  },
45
80
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
81
  onCancelled: (response) => {
@@ -48,11 +83,8 @@ const useValuePay = (config) => {
48
83
  const res = helpers.normalizeResponse(response, ref, cfg);
49
84
  res.status = "CANCELLED";
50
85
  setIsProcessing(false);
51
- if (cfg.redirectUrl) {
52
- const url = new URL(cfg.redirectUrl);
53
- url.searchParams.set("ref", res.ref);
54
- url.searchParams.set("status", "CANCELLED");
55
- window.location.href = url.toString();
86
+ if (useRedirect) {
87
+ redirectTo(cfg.redirectUrl, res.ref, "CANCELLED");
56
88
  return;
57
89
  }
58
90
  (_a = cfg.onCancelled) === null || _a === void 0 ? void 0 : _a.call(cfg, res);
@@ -70,11 +102,8 @@ const useValuePay = (config) => {
70
102
  var _a;
71
103
  const res = helpers.normalizeResponse(response, ref, cfg);
72
104
  setIsProcessing(false);
73
- if (cfg.redirectUrl) {
74
- const url = new URL(cfg.redirectUrl);
75
- url.searchParams.set("ref", res.ref);
76
- url.searchParams.set("status", "CLOSED");
77
- window.location.href = url.toString();
105
+ if (useRedirect) {
106
+ redirectTo(cfg.redirectUrl, res.ref, "CLOSED");
78
107
  return;
79
108
  }
80
109
  (_a = cfg.onClose) === null || _a === void 0 ? void 0 : _a.call(cfg, res);
@@ -1 +1 @@
1
- {"version":3,"file":"useValuePay.js","sources":["../../../../src/hooks/useValuePay.ts"],"sourcesContent":["import { useCallback, useState, useRef } from \"react\";\nimport { useScript } from \"./useScript\";\nimport { normalizeConfig, normalizeResponse } from \"../utils/helpers\";\nimport { DEFAULT_SCRIPT_URL } from \"../utils/constants\";\nimport type { ValuePayConfig, UseValuePayReturn } from \"../types\";\n\nexport const useValuePay = (config: ValuePayConfig): UseValuePayReturn => {\n const scriptUrl = config.scriptUrl || DEFAULT_SCRIPT_URL;\n const isReady = useScript(scriptUrl);\n const [isProcessing, setIsProcessing] = useState(false);\n const configRef = useRef(config);\n configRef.current = config;\n\n const initialize = useCallback(\n (overrides?: Partial<ValuePayConfig>) => {\n const cfg = configRef.current;\n\n if (typeof window === \"undefined\" || !window.ValuepayCheckout) {\n cfg.onError?.(new Error(\"ValuePay script not loaded. Ensure the script URL is accessible.\"));\n return;\n }\n\n if (isProcessing) return;\n\n setIsProcessing(true);\n\n const normalized = normalizeConfig(cfg, overrides);\n const ref = normalized.transactionRef;\n\n window.ValuepayCheckout({\n ...normalized,\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n oncallback: (response: any) => {\n const res = normalizeResponse(response, ref, cfg);\n setIsProcessing(false);\n\n if (cfg.redirectUrl) {\n const url = new URL(cfg.redirectUrl);\n url.searchParams.set(\"ref\", res.ref);\n url.searchParams.set(\"status\", res.status);\n window.location.href = url.toString();\n return;\n }\n\n cfg.onCallback?.(res);\n\n if (res.status === \"SUCCESS\" || res.status === \"COMPLETED\") {\n cfg.onSuccess?.(res);\n }\n },\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onCancelled: (response: any) => {\n const res = normalizeResponse(response, ref, cfg);\n res.status = \"CANCELLED\";\n setIsProcessing(false);\n\n if (cfg.redirectUrl) {\n const url = new URL(cfg.redirectUrl);\n url.searchParams.set(\"ref\", res.ref);\n url.searchParams.set(\"status\", \"CANCELLED\");\n window.location.href = url.toString();\n return;\n }\n\n cfg.onCancelled?.(res);\n },\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onAborted: (response: any) => {\n const res = normalizeResponse(response, ref, cfg);\n res.status = \"CANCELLED\";\n setIsProcessing(false);\n cfg.onCancelled?.(res);\n },\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onClosed: (response: any) => {\n const res = normalizeResponse(response, ref, cfg);\n setIsProcessing(false);\n\n if (cfg.redirectUrl) {\n const url = new URL(cfg.redirectUrl);\n url.searchParams.set(\"ref\", res.ref);\n url.searchParams.set(\"status\", \"CLOSED\");\n window.location.href = url.toString();\n return;\n }\n\n cfg.onClose?.(res);\n },\n });\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [isReady, isProcessing],\n );\n\n return { initialize, isReady, isProcessing };\n};\n"],"names":["DEFAULT_SCRIPT_URL","useScript","useState","useRef","useCallback","normalizeConfig","normalizeResponse"],"mappings":"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"}
1
+ {"version":3,"file":"useValuePay.js","sources":["../../../../src/hooks/useValuePay.ts"],"sourcesContent":["import { useCallback, useState, useRef } from \"react\";\r\nimport { useScript } from \"./useScript\";\r\nimport { normalizeConfig, normalizeResponse } from \"../utils/helpers\";\r\nimport { DEFAULT_SCRIPT_URL } from \"../utils/constants\";\r\nimport type { ValuePayConfig, UseValuePayReturn } from \"../types\";\r\n\r\n/**\r\n * Checks whether any callback function is provided in the config,\r\n * using case-insensitive detection to handle both SDK-style (onCallback)\r\n * and raw checkout-style (oncallback) casing.\r\n */\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\nfunction hasCallback(cfg: any, ...names: string[]): boolean {\r\n return names.some(\r\n (name) => typeof cfg[name] === \"function\" || typeof cfg[name.toLowerCase()] === \"function\",\r\n );\r\n}\r\n\r\n/**\r\n * Returns true if the config has ANY callback defined (case-insensitive).\r\n * When true, callbacks take precedence and redirectUrl is ignored.\r\n */\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\nfunction hasAnyCallback(cfg: any): boolean {\r\n return hasCallback(cfg, \"onSuccess\", \"onCallback\", \"onCancelled\", \"onClose\", \"onError\");\r\n}\r\n\r\n/**\r\n * Redirects the browser to the given URL with ref and status query params.\r\n */\r\nfunction redirectTo(redirectUrl: string, ref: string, status: string) {\r\n const url = new URL(redirectUrl);\r\n url.searchParams.set(\"ref\", ref);\r\n url.searchParams.set(\"status\", status);\r\n window.location.href = url.toString();\r\n}\r\n\r\nexport const useValuePay = (config: ValuePayConfig): UseValuePayReturn => {\r\n const scriptUrl = config.scriptUrl || DEFAULT_SCRIPT_URL;\r\n const isReady = useScript(scriptUrl);\r\n const [isProcessing, setIsProcessing] = useState(false);\r\n const configRef = useRef(config);\r\n configRef.current = config;\r\n\r\n const initialize = useCallback(\r\n (overrides?: Partial<ValuePayConfig>) => {\r\n const cfg = configRef.current;\r\n\r\n if (typeof window === \"undefined\" || !window.ValuepayCheckout) {\r\n cfg.onError?.(\r\n new Error(\"ValuePay script not loaded. Ensure the script URL is accessible.\"),\r\n );\r\n return;\r\n }\r\n\r\n if (isProcessing) return;\r\n\r\n setIsProcessing(true);\r\n\r\n const normalized = normalizeConfig(cfg, overrides);\r\n const ref = normalized.transactionRef;\r\n const useRedirect = !hasAnyCallback(cfg) && !!cfg.redirectUrl;\r\n\r\n window.ValuepayCheckout({\r\n ...normalized,\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n onsuccess: (response: any) => {\r\n const res = normalizeResponse(response, ref, cfg);\r\n setIsProcessing(false);\r\n\r\n if (useRedirect) {\r\n redirectTo(cfg.redirectUrl!, res.ref, res.status);\r\n return;\r\n }\r\n\r\n if (res.status !== \"SUCCESS\" && res.status !== \"COMPLETED\") {\r\n res.status = \"SUCCESS\";\r\n }\r\n cfg.onSuccess?.(res);\r\n },\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n oncallback: (response: any) => {\r\n const res = normalizeResponse(response, ref, cfg);\r\n setIsProcessing(false);\r\n\r\n if (useRedirect) {\r\n redirectTo(cfg.redirectUrl!, res.ref, res.status);\r\n return;\r\n }\r\n\r\n cfg.onCallback?.(res);\r\n },\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n onCancelled: (response: any) => {\r\n const res = normalizeResponse(response, ref, cfg);\r\n res.status = \"CANCELLED\";\r\n setIsProcessing(false);\r\n\r\n if (useRedirect) {\r\n redirectTo(cfg.redirectUrl!, res.ref, \"CANCELLED\");\r\n return;\r\n }\r\n\r\n cfg.onCancelled?.(res);\r\n },\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n onAborted: (response: any) => {\r\n const res = normalizeResponse(response, ref, cfg);\r\n res.status = \"CANCELLED\";\r\n setIsProcessing(false);\r\n cfg.onCancelled?.(res);\r\n },\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n onClosed: (response: any) => {\r\n const res = normalizeResponse(response, ref, cfg);\r\n setIsProcessing(false);\r\n\r\n if (useRedirect) {\r\n redirectTo(cfg.redirectUrl!, res.ref, \"CLOSED\");\r\n return;\r\n }\r\n\r\n cfg.onClose?.(res);\r\n },\r\n });\r\n },\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n [isReady, isProcessing],\r\n );\r\n\r\n return { initialize, isReady, isProcessing };\r\n};\r\n"],"names":["DEFAULT_SCRIPT_URL","useScript","useState","useRef","useCallback","normalizeConfig","normalizeResponse"],"mappings":";;;;;;;AAMA;;;;AAIG;AACH;AACA,SAAS,WAAW,CAAC,GAAQ,EAAE,GAAG,KAAe,EAAA;AAC/C,IAAA,OAAO,KAAK,CAAC,IAAI,CACf,CAAC,IAAI,KAAK,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,UAAU,IAAI,OAAO,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,UAAU,CAC3F;AACH;AAEA;;;AAGG;AACH;AACA,SAAS,cAAc,CAAC,GAAQ,EAAA;AAC9B,IAAA,OAAO,WAAW,CAAC,GAAG,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,CAAC;AACzF;AAEA;;AAEG;AACH,SAAS,UAAU,CAAC,WAAmB,EAAE,GAAW,EAAE,MAAc,EAAA;AAClE,IAAA,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC;IAChC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC;IAChC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC;IACtC,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,GAAG,CAAC,QAAQ,EAAE;AACvC;AAEO,MAAM,WAAW,GAAG,CAAC,MAAsB,KAAuB;AACvE,IAAA,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAIA,4BAAkB;AACxD,IAAA,MAAM,OAAO,GAAGC,mBAAS,CAAC,SAAS,CAAC;IACpC,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAGC,cAAQ,CAAC,KAAK,CAAC;AACvD,IAAA,MAAM,SAAS,GAAGC,YAAM,CAAC,MAAM,CAAC;AAChC,IAAA,SAAS,CAAC,OAAO,GAAG,MAAM;AAE1B,IAAA,MAAM,UAAU,GAAGC,iBAAW,CAC5B,CAAC,SAAmC,KAAI;;AACtC,QAAA,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO;QAE7B,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAAE;YAC7D,CAAA,EAAA,GAAA,GAAG,CAAC,OAAO,MAAA,IAAA,IAAA,EAAA,KAAA,MAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,GAAA,EACT,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAC9E;YACD;QACF;AAEA,QAAA,IAAI,YAAY;YAAE;QAElB,eAAe,CAAC,IAAI,CAAC;QAErB,MAAM,UAAU,GAAGC,uBAAe,CAAC,GAAG,EAAE,SAAS,CAAC;AAClD,QAAA,MAAM,GAAG,GAAG,UAAU,CAAC,cAAc;AACrC,QAAA,MAAM,WAAW,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,WAAW;QAE7D,MAAM,CAAC,gBAAgB,CAAC;AACtB,YAAA,GAAG,UAAU;;AAGb,YAAA,SAAS,EAAE,CAAC,QAAa,KAAI;;gBAC3B,MAAM,GAAG,GAAGC,yBAAiB,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,CAAC;gBACjD,eAAe,CAAC,KAAK,CAAC;gBAEtB,IAAI,WAAW,EAAE;AACf,oBAAA,UAAU,CAAC,GAAG,CAAC,WAAY,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC;oBACjD;gBACF;AAEA,gBAAA,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,IAAI,GAAG,CAAC,MAAM,KAAK,WAAW,EAAE;AAC1D,oBAAA,GAAG,CAAC,MAAM,GAAG,SAAS;gBACxB;AACA,gBAAA,CAAA,EAAA,GAAA,GAAG,CAAC,SAAS,MAAA,IAAA,IAAA,EAAA,KAAA,MAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,GAAA,EAAG,GAAG,CAAC;YACtB,CAAC;;AAGD,YAAA,UAAU,EAAE,CAAC,QAAa,KAAI;;gBAC5B,MAAM,GAAG,GAAGA,yBAAiB,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,CAAC;gBACjD,eAAe,CAAC,KAAK,CAAC;gBAEtB,IAAI,WAAW,EAAE;AACf,oBAAA,UAAU,CAAC,GAAG,CAAC,WAAY,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC;oBACjD;gBACF;AAEA,gBAAA,CAAA,EAAA,GAAA,GAAG,CAAC,UAAU,MAAA,IAAA,IAAA,EAAA,KAAA,MAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,GAAA,EAAG,GAAG,CAAC;YACvB,CAAC;;AAGD,YAAA,WAAW,EAAE,CAAC,QAAa,KAAI;;gBAC7B,MAAM,GAAG,GAAGA,yBAAiB,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,CAAC;AACjD,gBAAA,GAAG,CAAC,MAAM,GAAG,WAAW;gBACxB,eAAe,CAAC,KAAK,CAAC;gBAEtB,IAAI,WAAW,EAAE;oBACf,UAAU,CAAC,GAAG,CAAC,WAAY,EAAE,GAAG,CAAC,GAAG,EAAE,WAAW,CAAC;oBAClD;gBACF;AAEA,gBAAA,CAAA,EAAA,GAAA,GAAG,CAAC,WAAW,MAAA,IAAA,IAAA,EAAA,KAAA,MAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,GAAA,EAAG,GAAG,CAAC;YACxB,CAAC;;AAGD,YAAA,SAAS,EAAE,CAAC,QAAa,KAAI;;gBAC3B,MAAM,GAAG,GAAGA,yBAAiB,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,CAAC;AACjD,gBAAA,GAAG,CAAC,MAAM,GAAG,WAAW;gBACxB,eAAe,CAAC,KAAK,CAAC;AACtB,gBAAA,CAAA,EAAA,GAAA,GAAG,CAAC,WAAW,MAAA,IAAA,IAAA,EAAA,KAAA,MAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,GAAA,EAAG,GAAG,CAAC;YACxB,CAAC;;AAGD,YAAA,QAAQ,EAAE,CAAC,QAAa,KAAI;;gBAC1B,MAAM,GAAG,GAAGA,yBAAiB,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,CAAC;gBACjD,eAAe,CAAC,KAAK,CAAC;gBAEtB,IAAI,WAAW,EAAE;oBACf,UAAU,CAAC,GAAG,CAAC,WAAY,EAAE,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC;oBAC/C;gBACF;AAEA,gBAAA,CAAA,EAAA,GAAA,GAAG,CAAC,OAAO,MAAA,IAAA,IAAA,EAAA,KAAA,MAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,GAAA,EAAG,GAAG,CAAC;YACpB,CAAC;AACF,SAAA,CAAC;IACJ,CAAC;;AAED,IAAA,CAAC,OAAO,EAAE,YAAY,CAAC,CACxB;AAED,IAAA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE;AAC9C;;;;"}
package/dist/cjs/index.js CHANGED
@@ -12,5 +12,6 @@ exports.ValuePayButton = ValuePayButton.ValuePayButton;
12
12
  exports.ValuePayProvider = ValuePayProvider.ValuePayProvider;
13
13
  exports.useValuePay = useValuePay.useValuePay;
14
14
  exports.useScript = useScript.useScript;
15
+ exports.ensureRefPrefix = helpers.ensureRefPrefix;
15
16
  exports.generateTransactionRef = helpers.generateTransactionRef;
16
17
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;"}
@@ -13,12 +13,22 @@ const generateTransactionRef = (length = 20) => {
13
13
  }
14
14
  return result;
15
15
  };
16
+ /**
17
+ * Ensure the transaction reference always carries the VPS_TX_ prefix.
18
+ * If the merchant supplies their own ref, the prefix is prepended (unless already present).
19
+ * If omitted, a fully random reference is generated.
20
+ */
21
+ const ensureRefPrefix = (ref) => {
22
+ return ref.startsWith(constants.TRANSACTION_REF_PREFIX) ? ref : `${constants.TRANSACTION_REF_PREFIX}${ref}`;
23
+ };
16
24
  /**
17
25
  * Normalize the SDK config into the shape expected by window.ValuepayCheckout.
18
26
  */
19
27
  const normalizeConfig = (config, overrides) => {
20
28
  const merged = { ...config, ...overrides };
21
- const ref = merged.transactionRef || generateTransactionRef();
29
+ const ref = merged.transactionRef
30
+ ? ensureRefPrefix(merged.transactionRef)
31
+ : generateTransactionRef();
22
32
  return {
23
33
  public_key: merged.publicKey,
24
34
  transactionRef: ref,
@@ -61,6 +71,7 @@ const normalizeResponse = (raw, ref, config) => {
61
71
  };
62
72
  };
63
73
 
74
+ exports.ensureRefPrefix = ensureRefPrefix;
64
75
  exports.generateTransactionRef = generateTransactionRef;
65
76
  exports.normalizeConfig = normalizeConfig;
66
77
  exports.normalizeResponse = normalizeResponse;
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.js","sources":["../../../../src/utils/helpers.ts"],"sourcesContent":["import type { ValuePayConfig, ValuePayResponse } from \"../types\";\nimport { DEFAULT_CURRENCY, DEFAULT_CHANNELS, TRANSACTION_REF_PREFIX } from \"./constants\";\n\n/**\n * Generate a unique transaction reference.\n */\nexport const generateTransactionRef = (length: number = 20): string => {\n const chars = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n let result = TRANSACTION_REF_PREFIX;\n for (let i = 0; i < length; i++) {\n result += chars.charAt(Math.floor(Math.random() * chars.length));\n }\n return result;\n};\n\n/**\n * Normalize the SDK config into the shape expected by window.ValuepayCheckout.\n */\nexport const normalizeConfig = (config: ValuePayConfig, overrides?: Partial<ValuePayConfig>) => {\n const merged = { ...config, ...overrides };\n const ref = merged.transactionRef || generateTransactionRef();\n\n return {\n public_key: merged.publicKey,\n transactionRef: ref,\n amount: merged.amount,\n currency: merged.currency || DEFAULT_CURRENCY,\n channels: merged.channels || DEFAULT_CHANNELS,\n type: \"default\",\n redirect_url: merged.redirectUrl,\n metaData: merged.metaData,\n customer: {\n email: merged.customer.email,\n fullName: merged.customer.fullName,\n phone: merged.customer.phone,\n },\n customisedCheckout: merged.customization\n ? {\n title: merged.customization.title,\n description: merged.customization.description,\n logoLink: merged.customization.logoUrl,\n }\n : undefined,\n };\n};\n\n/**\n * Normalize the raw ValuePay response into our SDK response shape.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const normalizeResponse = (raw: any, ref: string, config: ValuePayConfig): ValuePayResponse => {\n return {\n ref: raw?.ref || raw?.reference || raw?.tx_ref || ref,\n transactionRef: raw?.transactionRef || ref,\n status: raw?.status || raw?.validation?.status || \"PENDING\",\n amount: config.amount,\n currency: config.currency || DEFAULT_CURRENCY,\n customer: config.customer,\n paymentMethod: raw?.paymentMethod,\n validation: raw?.validation,\n raw,\n };\n};\n"],"names":["TRANSACTION_REF_PREFIX","DEFAULT_CURRENCY","DEFAULT_CHANNELS"],"mappings":"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"}
1
+ {"version":3,"file":"helpers.js","sources":["../../../../src/utils/helpers.ts"],"sourcesContent":["import type { ValuePayConfig, ValuePayResponse } from \"../types\";\r\nimport { DEFAULT_CURRENCY, DEFAULT_CHANNELS, TRANSACTION_REF_PREFIX } from \"./constants\";\r\n\r\n/**\r\n * Generate a unique transaction reference.\r\n */\r\nexport const generateTransactionRef = (length: number = 20): string => {\r\n const chars = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\r\n let result = TRANSACTION_REF_PREFIX;\r\n for (let i = 0; i < length; i++) {\r\n result += chars.charAt(Math.floor(Math.random() * chars.length));\r\n }\r\n return result;\r\n};\r\n\r\n/**\r\n * Ensure the transaction reference always carries the VPS_TX_ prefix.\r\n * If the merchant supplies their own ref, the prefix is prepended (unless already present).\r\n * If omitted, a fully random reference is generated.\r\n */\r\nexport const ensureRefPrefix = (ref: string): string => {\r\n return ref.startsWith(TRANSACTION_REF_PREFIX) ? ref : `${TRANSACTION_REF_PREFIX}${ref}`;\r\n};\r\n\r\n/**\r\n * Normalize the SDK config into the shape expected by window.ValuepayCheckout.\r\n */\r\nexport const normalizeConfig = (config: ValuePayConfig, overrides?: Partial<ValuePayConfig>) => {\r\n const merged = { ...config, ...overrides };\r\n const ref = merged.transactionRef\r\n ? ensureRefPrefix(merged.transactionRef)\r\n : generateTransactionRef();\r\n\r\n return {\r\n public_key: merged.publicKey,\r\n transactionRef: ref,\r\n amount: merged.amount,\r\n currency: merged.currency || DEFAULT_CURRENCY,\r\n channels: merged.channels || DEFAULT_CHANNELS,\r\n type: \"default\",\r\n redirect_url: merged.redirectUrl,\r\n metaData: merged.metaData,\r\n customer: {\r\n email: merged.customer.email,\r\n fullName: merged.customer.fullName,\r\n phone: merged.customer.phone,\r\n },\r\n customisedCheckout: merged.customization\r\n ? {\r\n title: merged.customization.title,\r\n description: merged.customization.description,\r\n logoLink: merged.customization.logoUrl,\r\n }\r\n : undefined,\r\n };\r\n};\r\n\r\n/**\r\n * Normalize the raw ValuePay response into our SDK response shape.\r\n */\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\nexport const normalizeResponse = (raw: any, ref: string, config: ValuePayConfig): ValuePayResponse => {\r\n return {\r\n ref: raw?.ref || raw?.reference || raw?.tx_ref || ref,\r\n transactionRef: raw?.transactionRef || ref,\r\n status: raw?.status || raw?.validation?.status || \"PENDING\",\r\n amount: config.amount,\r\n currency: config.currency || DEFAULT_CURRENCY,\r\n customer: config.customer,\r\n paymentMethod: raw?.paymentMethod,\r\n validation: raw?.validation,\r\n raw,\r\n };\r\n};\r\n"],"names":["TRANSACTION_REF_PREFIX","DEFAULT_CURRENCY","DEFAULT_CHANNELS"],"mappings":"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"}
@@ -1,11 +1,15 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import { useValuePay } from '../hooks/useValuePay.js';
3
3
 
4
- const ValuePayButton = ({ text, className, style, disabled, children, ...config }) => {
4
+ const ValuePayButton = ({ text, className, style, containerStyle, containerClassName, disabled, children, ...config }) => {
5
5
  const { initialize, isReady, isProcessing } = useValuePay(config);
6
6
  const defaultText = `Pay \u20A6${config.amount.toLocaleString()} Now`;
7
7
  const buttonText = children || text || defaultText;
8
- return (jsx("button", { type: "button", onClick: () => initialize(), disabled: disabled || !isReady || isProcessing, className: className, style: style, "aria-busy": isProcessing, "aria-label": typeof buttonText === "string" ? buttonText : "Pay Now", children: isProcessing ? "Processing..." : buttonText }));
8
+ const button = (jsx("button", { type: "button", onClick: () => initialize(), disabled: disabled || !isReady || isProcessing, className: className, style: style, "aria-busy": isProcessing, "aria-label": typeof buttonText === "string" ? buttonText : "Pay Now", children: isProcessing ? "Processing..." : buttonText }));
9
+ if (containerStyle || containerClassName) {
10
+ return (jsx("div", { style: containerStyle, className: containerClassName, "data-testid": "valuepay-button-container", children: button }));
11
+ }
12
+ return button;
9
13
  };
10
14
 
11
15
  export { ValuePayButton };
@@ -1 +1 @@
1
- {"version":3,"file":"ValuePayButton.js","sources":["../../../../src/components/ValuePayButton.tsx"],"sourcesContent":["import React from \"react\";\nimport { useValuePay } from \"../hooks/useValuePay\";\nimport type { ValuePayButtonProps } from \"../types\";\n\nexport const ValuePayButton: React.FC<ValuePayButtonProps> = ({\n text,\n className,\n style,\n disabled,\n children,\n ...config\n}) => {\n const { initialize, isReady, isProcessing } = useValuePay(config);\n\n const defaultText = `Pay \\u20A6${config.amount.toLocaleString()} Now`;\n const buttonText = children || text || defaultText;\n\n return (\n <button\n type=\"button\"\n onClick={() => initialize()}\n disabled={disabled || !isReady || isProcessing}\n className={className}\n style={style}\n aria-busy={isProcessing}\n aria-label={typeof buttonText === \"string\" ? buttonText : \"Pay Now\"}\n >\n {isProcessing ? \"Processing...\" : buttonText}\n </button>\n );\n};\n"],"names":["_jsx"],"mappings":";;;MAIa,cAAc,GAAkC,CAAC,EAC5D,IAAI,EACJ,SAAS,EACT,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,GAAG,MAAM,EACV,KAAI;AACH,IAAA,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC;IAEjE,MAAM,WAAW,GAAG,CAAA,UAAA,EAAa,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,CAAA,IAAA,CAAM;AACrE,IAAA,MAAM,UAAU,GAAG,QAAQ,IAAI,IAAI,IAAI,WAAW;IAElD,QACEA,GAAA,CAAA,QAAA,EAAA,EACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,MAAM,UAAU,EAAE,EAC3B,QAAQ,EAAE,QAAQ,IAAI,CAAC,OAAO,IAAI,YAAY,EAC9C,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,KAAK,EAAA,WAAA,EACD,YAAY,EAAA,YAAA,EACX,OAAO,UAAU,KAAK,QAAQ,GAAG,UAAU,GAAG,SAAS,EAAA,QAAA,EAElE,YAAY,GAAG,eAAe,GAAG,UAAU,EAAA,CACrC;AAEb;;;;"}
1
+ {"version":3,"file":"ValuePayButton.js","sources":["../../../../src/components/ValuePayButton.tsx"],"sourcesContent":["import React from \"react\";\r\nimport { useValuePay } from \"../hooks/useValuePay\";\r\nimport type { ValuePayButtonProps } from \"../types\";\r\n\r\nexport const ValuePayButton: React.FC<ValuePayButtonProps> = ({\r\n text,\r\n className,\r\n style,\r\n containerStyle,\r\n containerClassName,\r\n disabled,\r\n children,\r\n ...config\r\n}) => {\r\n const { initialize, isReady, isProcessing } = useValuePay(config);\r\n\r\n const defaultText = `Pay \\u20A6${config.amount.toLocaleString()} Now`;\r\n const buttonText = children || text || defaultText;\r\n\r\n const button = (\r\n <button\r\n type=\"button\"\r\n onClick={() => initialize()}\r\n disabled={disabled || !isReady || isProcessing}\r\n className={className}\r\n style={style}\r\n aria-busy={isProcessing}\r\n aria-label={typeof buttonText === \"string\" ? buttonText : \"Pay Now\"}\r\n >\r\n {isProcessing ? \"Processing...\" : buttonText}\r\n </button>\r\n );\r\n\r\n if (containerStyle || containerClassName) {\r\n return (\r\n <div style={containerStyle} className={containerClassName} data-testid=\"valuepay-button-container\">\r\n {button}\r\n </div>\r\n );\r\n }\r\n\r\n return button;\r\n};\r\n"],"names":["_jsx"],"mappings":";;;AAIO,MAAM,cAAc,GAAkC,CAAC,EAC5D,IAAI,EACJ,SAAS,EACT,KAAK,EACL,cAAc,EACd,kBAAkB,EAClB,QAAQ,EACR,QAAQ,EACR,GAAG,MAAM,EACV,KAAI;AACH,IAAA,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC;IAEjE,MAAM,WAAW,GAAG,CAAA,UAAA,EAAa,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,CAAA,IAAA,CAAM;AACrE,IAAA,MAAM,UAAU,GAAG,QAAQ,IAAI,IAAI,IAAI,WAAW;IAElD,MAAM,MAAM,IACVA,GAAA,CAAA,QAAA,EAAA,EACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,MAAM,UAAU,EAAE,EAC3B,QAAQ,EAAE,QAAQ,IAAI,CAAC,OAAO,IAAI,YAAY,EAC9C,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,KAAK,EAAA,WAAA,EACD,YAAY,EAAA,YAAA,EACX,OAAO,UAAU,KAAK,QAAQ,GAAG,UAAU,GAAG,SAAS,EAAA,QAAA,EAElE,YAAY,GAAG,eAAe,GAAG,UAAU,EAAA,CACrC,CACV;AAED,IAAA,IAAI,cAAc,IAAI,kBAAkB,EAAE;AACxC,QAAA,QACEA,GAAA,CAAA,KAAA,EAAA,EAAK,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,kBAAkB,iBAAc,2BAA2B,EAAA,QAAA,EAC/F,MAAM,EAAA,CACH;IAEV;AAEA,IAAA,OAAO,MAAM;AACf;;;;"}
@@ -3,6 +3,32 @@ import { useScript } from './useScript.js';
3
3
  import { normalizeConfig, normalizeResponse } from '../utils/helpers.js';
4
4
  import { DEFAULT_SCRIPT_URL } from '../utils/constants.js';
5
5
 
6
+ /**
7
+ * Checks whether any callback function is provided in the config,
8
+ * using case-insensitive detection to handle both SDK-style (onCallback)
9
+ * and raw checkout-style (oncallback) casing.
10
+ */
11
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
+ function hasCallback(cfg, ...names) {
13
+ return names.some((name) => typeof cfg[name] === "function" || typeof cfg[name.toLowerCase()] === "function");
14
+ }
15
+ /**
16
+ * Returns true if the config has ANY callback defined (case-insensitive).
17
+ * When true, callbacks take precedence and redirectUrl is ignored.
18
+ */
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ function hasAnyCallback(cfg) {
21
+ return hasCallback(cfg, "onSuccess", "onCallback", "onCancelled", "onClose", "onError");
22
+ }
23
+ /**
24
+ * Redirects the browser to the given URL with ref and status query params.
25
+ */
26
+ function redirectTo(redirectUrl, ref, status) {
27
+ const url = new URL(redirectUrl);
28
+ url.searchParams.set("ref", ref);
29
+ url.searchParams.set("status", status);
30
+ window.location.href = url.toString();
31
+ }
6
32
  const useValuePay = (config) => {
7
33
  const scriptUrl = config.scriptUrl || DEFAULT_SCRIPT_URL;
8
34
  const isReady = useScript(scriptUrl);
@@ -21,24 +47,33 @@ const useValuePay = (config) => {
21
47
  setIsProcessing(true);
22
48
  const normalized = normalizeConfig(cfg, overrides);
23
49
  const ref = normalized.transactionRef;
50
+ const useRedirect = !hasAnyCallback(cfg) && !!cfg.redirectUrl;
24
51
  window.ValuepayCheckout({
25
52
  ...normalized,
26
53
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
54
+ onsuccess: (response) => {
55
+ var _a;
56
+ const res = normalizeResponse(response, ref, cfg);
57
+ setIsProcessing(false);
58
+ if (useRedirect) {
59
+ redirectTo(cfg.redirectUrl, res.ref, res.status);
60
+ return;
61
+ }
62
+ if (res.status !== "SUCCESS" && res.status !== "COMPLETED") {
63
+ res.status = "SUCCESS";
64
+ }
65
+ (_a = cfg.onSuccess) === null || _a === void 0 ? void 0 : _a.call(cfg, res);
66
+ },
67
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
27
68
  oncallback: (response) => {
28
- var _a, _b;
69
+ var _a;
29
70
  const res = normalizeResponse(response, ref, cfg);
30
71
  setIsProcessing(false);
31
- if (cfg.redirectUrl) {
32
- const url = new URL(cfg.redirectUrl);
33
- url.searchParams.set("ref", res.ref);
34
- url.searchParams.set("status", res.status);
35
- window.location.href = url.toString();
72
+ if (useRedirect) {
73
+ redirectTo(cfg.redirectUrl, res.ref, res.status);
36
74
  return;
37
75
  }
38
76
  (_a = cfg.onCallback) === null || _a === void 0 ? void 0 : _a.call(cfg, res);
39
- if (res.status === "SUCCESS" || res.status === "COMPLETED") {
40
- (_b = cfg.onSuccess) === null || _b === void 0 ? void 0 : _b.call(cfg, res);
41
- }
42
77
  },
43
78
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
79
  onCancelled: (response) => {
@@ -46,11 +81,8 @@ const useValuePay = (config) => {
46
81
  const res = normalizeResponse(response, ref, cfg);
47
82
  res.status = "CANCELLED";
48
83
  setIsProcessing(false);
49
- if (cfg.redirectUrl) {
50
- const url = new URL(cfg.redirectUrl);
51
- url.searchParams.set("ref", res.ref);
52
- url.searchParams.set("status", "CANCELLED");
53
- window.location.href = url.toString();
84
+ if (useRedirect) {
85
+ redirectTo(cfg.redirectUrl, res.ref, "CANCELLED");
54
86
  return;
55
87
  }
56
88
  (_a = cfg.onCancelled) === null || _a === void 0 ? void 0 : _a.call(cfg, res);
@@ -68,11 +100,8 @@ const useValuePay = (config) => {
68
100
  var _a;
69
101
  const res = normalizeResponse(response, ref, cfg);
70
102
  setIsProcessing(false);
71
- if (cfg.redirectUrl) {
72
- const url = new URL(cfg.redirectUrl);
73
- url.searchParams.set("ref", res.ref);
74
- url.searchParams.set("status", "CLOSED");
75
- window.location.href = url.toString();
103
+ if (useRedirect) {
104
+ redirectTo(cfg.redirectUrl, res.ref, "CLOSED");
76
105
  return;
77
106
  }
78
107
  (_a = cfg.onClose) === null || _a === void 0 ? void 0 : _a.call(cfg, res);
@@ -1 +1 @@
1
- {"version":3,"file":"useValuePay.js","sources":["../../../../src/hooks/useValuePay.ts"],"sourcesContent":["import { useCallback, useState, useRef } from \"react\";\nimport { useScript } from \"./useScript\";\nimport { normalizeConfig, normalizeResponse } from \"../utils/helpers\";\nimport { DEFAULT_SCRIPT_URL } from \"../utils/constants\";\nimport type { ValuePayConfig, UseValuePayReturn } from \"../types\";\n\nexport const useValuePay = (config: ValuePayConfig): UseValuePayReturn => {\n const scriptUrl = config.scriptUrl || DEFAULT_SCRIPT_URL;\n const isReady = useScript(scriptUrl);\n const [isProcessing, setIsProcessing] = useState(false);\n const configRef = useRef(config);\n configRef.current = config;\n\n const initialize = useCallback(\n (overrides?: Partial<ValuePayConfig>) => {\n const cfg = configRef.current;\n\n if (typeof window === \"undefined\" || !window.ValuepayCheckout) {\n cfg.onError?.(new Error(\"ValuePay script not loaded. Ensure the script URL is accessible.\"));\n return;\n }\n\n if (isProcessing) return;\n\n setIsProcessing(true);\n\n const normalized = normalizeConfig(cfg, overrides);\n const ref = normalized.transactionRef;\n\n window.ValuepayCheckout({\n ...normalized,\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n oncallback: (response: any) => {\n const res = normalizeResponse(response, ref, cfg);\n setIsProcessing(false);\n\n if (cfg.redirectUrl) {\n const url = new URL(cfg.redirectUrl);\n url.searchParams.set(\"ref\", res.ref);\n url.searchParams.set(\"status\", res.status);\n window.location.href = url.toString();\n return;\n }\n\n cfg.onCallback?.(res);\n\n if (res.status === \"SUCCESS\" || res.status === \"COMPLETED\") {\n cfg.onSuccess?.(res);\n }\n },\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onCancelled: (response: any) => {\n const res = normalizeResponse(response, ref, cfg);\n res.status = \"CANCELLED\";\n setIsProcessing(false);\n\n if (cfg.redirectUrl) {\n const url = new URL(cfg.redirectUrl);\n url.searchParams.set(\"ref\", res.ref);\n url.searchParams.set(\"status\", \"CANCELLED\");\n window.location.href = url.toString();\n return;\n }\n\n cfg.onCancelled?.(res);\n },\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onAborted: (response: any) => {\n const res = normalizeResponse(response, ref, cfg);\n res.status = \"CANCELLED\";\n setIsProcessing(false);\n cfg.onCancelled?.(res);\n },\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onClosed: (response: any) => {\n const res = normalizeResponse(response, ref, cfg);\n setIsProcessing(false);\n\n if (cfg.redirectUrl) {\n const url = new URL(cfg.redirectUrl);\n url.searchParams.set(\"ref\", res.ref);\n url.searchParams.set(\"status\", \"CLOSED\");\n window.location.href = url.toString();\n return;\n }\n\n cfg.onClose?.(res);\n },\n });\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [isReady, isProcessing],\n );\n\n return { initialize, isReady, isProcessing };\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useValuePay.js","sources":["../../../../src/hooks/useValuePay.ts"],"sourcesContent":["import { useCallback, useState, useRef } from \"react\";\r\nimport { useScript } from \"./useScript\";\r\nimport { normalizeConfig, normalizeResponse } from \"../utils/helpers\";\r\nimport { DEFAULT_SCRIPT_URL } from \"../utils/constants\";\r\nimport type { ValuePayConfig, UseValuePayReturn } from \"../types\";\r\n\r\n/**\r\n * Checks whether any callback function is provided in the config,\r\n * using case-insensitive detection to handle both SDK-style (onCallback)\r\n * and raw checkout-style (oncallback) casing.\r\n */\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\nfunction hasCallback(cfg: any, ...names: string[]): boolean {\r\n return names.some(\r\n (name) => typeof cfg[name] === \"function\" || typeof cfg[name.toLowerCase()] === \"function\",\r\n );\r\n}\r\n\r\n/**\r\n * Returns true if the config has ANY callback defined (case-insensitive).\r\n * When true, callbacks take precedence and redirectUrl is ignored.\r\n */\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\nfunction hasAnyCallback(cfg: any): boolean {\r\n return hasCallback(cfg, \"onSuccess\", \"onCallback\", \"onCancelled\", \"onClose\", \"onError\");\r\n}\r\n\r\n/**\r\n * Redirects the browser to the given URL with ref and status query params.\r\n */\r\nfunction redirectTo(redirectUrl: string, ref: string, status: string) {\r\n const url = new URL(redirectUrl);\r\n url.searchParams.set(\"ref\", ref);\r\n url.searchParams.set(\"status\", status);\r\n window.location.href = url.toString();\r\n}\r\n\r\nexport const useValuePay = (config: ValuePayConfig): UseValuePayReturn => {\r\n const scriptUrl = config.scriptUrl || DEFAULT_SCRIPT_URL;\r\n const isReady = useScript(scriptUrl);\r\n const [isProcessing, setIsProcessing] = useState(false);\r\n const configRef = useRef(config);\r\n configRef.current = config;\r\n\r\n const initialize = useCallback(\r\n (overrides?: Partial<ValuePayConfig>) => {\r\n const cfg = configRef.current;\r\n\r\n if (typeof window === \"undefined\" || !window.ValuepayCheckout) {\r\n cfg.onError?.(\r\n new Error(\"ValuePay script not loaded. Ensure the script URL is accessible.\"),\r\n );\r\n return;\r\n }\r\n\r\n if (isProcessing) return;\r\n\r\n setIsProcessing(true);\r\n\r\n const normalized = normalizeConfig(cfg, overrides);\r\n const ref = normalized.transactionRef;\r\n const useRedirect = !hasAnyCallback(cfg) && !!cfg.redirectUrl;\r\n\r\n window.ValuepayCheckout({\r\n ...normalized,\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n onsuccess: (response: any) => {\r\n const res = normalizeResponse(response, ref, cfg);\r\n setIsProcessing(false);\r\n\r\n if (useRedirect) {\r\n redirectTo(cfg.redirectUrl!, res.ref, res.status);\r\n return;\r\n }\r\n\r\n if (res.status !== \"SUCCESS\" && res.status !== \"COMPLETED\") {\r\n res.status = \"SUCCESS\";\r\n }\r\n cfg.onSuccess?.(res);\r\n },\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n oncallback: (response: any) => {\r\n const res = normalizeResponse(response, ref, cfg);\r\n setIsProcessing(false);\r\n\r\n if (useRedirect) {\r\n redirectTo(cfg.redirectUrl!, res.ref, res.status);\r\n return;\r\n }\r\n\r\n cfg.onCallback?.(res);\r\n },\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n onCancelled: (response: any) => {\r\n const res = normalizeResponse(response, ref, cfg);\r\n res.status = \"CANCELLED\";\r\n setIsProcessing(false);\r\n\r\n if (useRedirect) {\r\n redirectTo(cfg.redirectUrl!, res.ref, \"CANCELLED\");\r\n return;\r\n }\r\n\r\n cfg.onCancelled?.(res);\r\n },\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n onAborted: (response: any) => {\r\n const res = normalizeResponse(response, ref, cfg);\r\n res.status = \"CANCELLED\";\r\n setIsProcessing(false);\r\n cfg.onCancelled?.(res);\r\n },\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n onClosed: (response: any) => {\r\n const res = normalizeResponse(response, ref, cfg);\r\n setIsProcessing(false);\r\n\r\n if (useRedirect) {\r\n redirectTo(cfg.redirectUrl!, res.ref, \"CLOSED\");\r\n return;\r\n }\r\n\r\n cfg.onClose?.(res);\r\n },\r\n });\r\n },\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n [isReady, isProcessing],\r\n );\r\n\r\n return { initialize, isReady, isProcessing };\r\n};\r\n"],"names":[],"mappings":"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"}
package/dist/esm/index.js CHANGED
@@ -2,5 +2,5 @@ export { ValuePayButton } from './components/ValuePayButton.js';
2
2
  export { ValuePayProvider } from './components/ValuePayProvider.js';
3
3
  export { useValuePay } from './hooks/useValuePay.js';
4
4
  export { useScript } from './hooks/useScript.js';
5
- export { generateTransactionRef } from './utils/helpers.js';
5
+ export { ensureRefPrefix, generateTransactionRef } from './utils/helpers.js';
6
6
  //# sourceMappingURL=index.js.map
@@ -11,12 +11,22 @@ const generateTransactionRef = (length = 20) => {
11
11
  }
12
12
  return result;
13
13
  };
14
+ /**
15
+ * Ensure the transaction reference always carries the VPS_TX_ prefix.
16
+ * If the merchant supplies their own ref, the prefix is prepended (unless already present).
17
+ * If omitted, a fully random reference is generated.
18
+ */
19
+ const ensureRefPrefix = (ref) => {
20
+ return ref.startsWith(TRANSACTION_REF_PREFIX) ? ref : `${TRANSACTION_REF_PREFIX}${ref}`;
21
+ };
14
22
  /**
15
23
  * Normalize the SDK config into the shape expected by window.ValuepayCheckout.
16
24
  */
17
25
  const normalizeConfig = (config, overrides) => {
18
26
  const merged = { ...config, ...overrides };
19
- const ref = merged.transactionRef || generateTransactionRef();
27
+ const ref = merged.transactionRef
28
+ ? ensureRefPrefix(merged.transactionRef)
29
+ : generateTransactionRef();
20
30
  return {
21
31
  public_key: merged.publicKey,
22
32
  transactionRef: ref,
@@ -59,5 +69,5 @@ const normalizeResponse = (raw, ref, config) => {
59
69
  };
60
70
  };
61
71
 
62
- export { generateTransactionRef, normalizeConfig, normalizeResponse };
72
+ export { ensureRefPrefix, generateTransactionRef, normalizeConfig, normalizeResponse };
63
73
  //# sourceMappingURL=helpers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.js","sources":["../../../../src/utils/helpers.ts"],"sourcesContent":["import type { ValuePayConfig, ValuePayResponse } from \"../types\";\nimport { DEFAULT_CURRENCY, DEFAULT_CHANNELS, TRANSACTION_REF_PREFIX } from \"./constants\";\n\n/**\n * Generate a unique transaction reference.\n */\nexport const generateTransactionRef = (length: number = 20): string => {\n const chars = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n let result = TRANSACTION_REF_PREFIX;\n for (let i = 0; i < length; i++) {\n result += chars.charAt(Math.floor(Math.random() * chars.length));\n }\n return result;\n};\n\n/**\n * Normalize the SDK config into the shape expected by window.ValuepayCheckout.\n */\nexport const normalizeConfig = (config: ValuePayConfig, overrides?: Partial<ValuePayConfig>) => {\n const merged = { ...config, ...overrides };\n const ref = merged.transactionRef || generateTransactionRef();\n\n return {\n public_key: merged.publicKey,\n transactionRef: ref,\n amount: merged.amount,\n currency: merged.currency || DEFAULT_CURRENCY,\n channels: merged.channels || DEFAULT_CHANNELS,\n type: \"default\",\n redirect_url: merged.redirectUrl,\n metaData: merged.metaData,\n customer: {\n email: merged.customer.email,\n fullName: merged.customer.fullName,\n phone: merged.customer.phone,\n },\n customisedCheckout: merged.customization\n ? {\n title: merged.customization.title,\n description: merged.customization.description,\n logoLink: merged.customization.logoUrl,\n }\n : undefined,\n };\n};\n\n/**\n * Normalize the raw ValuePay response into our SDK response shape.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const normalizeResponse = (raw: any, ref: string, config: ValuePayConfig): ValuePayResponse => {\n return {\n ref: raw?.ref || raw?.reference || raw?.tx_ref || ref,\n transactionRef: raw?.transactionRef || ref,\n status: raw?.status || raw?.validation?.status || \"PENDING\",\n amount: config.amount,\n currency: config.currency || DEFAULT_CURRENCY,\n customer: config.customer,\n paymentMethod: raw?.paymentMethod,\n validation: raw?.validation,\n raw,\n };\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"helpers.js","sources":["../../../../src/utils/helpers.ts"],"sourcesContent":["import type { ValuePayConfig, ValuePayResponse } from \"../types\";\r\nimport { DEFAULT_CURRENCY, DEFAULT_CHANNELS, TRANSACTION_REF_PREFIX } from \"./constants\";\r\n\r\n/**\r\n * Generate a unique transaction reference.\r\n */\r\nexport const generateTransactionRef = (length: number = 20): string => {\r\n const chars = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\r\n let result = TRANSACTION_REF_PREFIX;\r\n for (let i = 0; i < length; i++) {\r\n result += chars.charAt(Math.floor(Math.random() * chars.length));\r\n }\r\n return result;\r\n};\r\n\r\n/**\r\n * Ensure the transaction reference always carries the VPS_TX_ prefix.\r\n * If the merchant supplies their own ref, the prefix is prepended (unless already present).\r\n * If omitted, a fully random reference is generated.\r\n */\r\nexport const ensureRefPrefix = (ref: string): string => {\r\n return ref.startsWith(TRANSACTION_REF_PREFIX) ? ref : `${TRANSACTION_REF_PREFIX}${ref}`;\r\n};\r\n\r\n/**\r\n * Normalize the SDK config into the shape expected by window.ValuepayCheckout.\r\n */\r\nexport const normalizeConfig = (config: ValuePayConfig, overrides?: Partial<ValuePayConfig>) => {\r\n const merged = { ...config, ...overrides };\r\n const ref = merged.transactionRef\r\n ? ensureRefPrefix(merged.transactionRef)\r\n : generateTransactionRef();\r\n\r\n return {\r\n public_key: merged.publicKey,\r\n transactionRef: ref,\r\n amount: merged.amount,\r\n currency: merged.currency || DEFAULT_CURRENCY,\r\n channels: merged.channels || DEFAULT_CHANNELS,\r\n type: \"default\",\r\n redirect_url: merged.redirectUrl,\r\n metaData: merged.metaData,\r\n customer: {\r\n email: merged.customer.email,\r\n fullName: merged.customer.fullName,\r\n phone: merged.customer.phone,\r\n },\r\n customisedCheckout: merged.customization\r\n ? {\r\n title: merged.customization.title,\r\n description: merged.customization.description,\r\n logoLink: merged.customization.logoUrl,\r\n }\r\n : undefined,\r\n };\r\n};\r\n\r\n/**\r\n * Normalize the raw ValuePay response into our SDK response shape.\r\n */\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\nexport const normalizeResponse = (raw: any, ref: string, config: ValuePayConfig): ValuePayResponse => {\r\n return {\r\n ref: raw?.ref || raw?.reference || raw?.tx_ref || ref,\r\n transactionRef: raw?.transactionRef || ref,\r\n status: raw?.status || raw?.validation?.status || \"PENDING\",\r\n amount: config.amount,\r\n currency: config.currency || DEFAULT_CURRENCY,\r\n customer: config.customer,\r\n paymentMethod: raw?.paymentMethod,\r\n validation: raw?.validation,\r\n raw,\r\n };\r\n};\r\n"],"names":[],"mappings":"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"}
@@ -2,5 +2,5 @@ export { ValuePayButton } from "./components/ValuePayButton";
2
2
  export { ValuePayProvider } from "./components/ValuePayProvider";
3
3
  export { useValuePay } from "./hooks/useValuePay";
4
4
  export { useScript } from "./hooks/useScript";
5
- export { generateTransactionRef } from "./utils/helpers";
5
+ export { generateTransactionRef, ensureRefPrefix } from "./utils/helpers";
6
6
  export type { ValuePayConfig, ValuePayResponse, ValuePayStatus, ValuePayCustomer, ValuePayCustomization, PaymentChannel, UseValuePayReturn, ValuePayButtonProps, ValuePayProviderProps, ValuePayProviderRef, } from "./types";
@@ -30,14 +30,21 @@ export interface ValuePayConfig {
30
30
  customer: ValuePayCustomer;
31
31
  currency?: string;
32
32
  channels?: PaymentChannel[];
33
+ /** Optional custom transaction reference. The VPS_TX_ prefix is auto-prepended if missing. Auto-generated when omitted. */
33
34
  transactionRef?: string;
35
+ /** Optional redirect URL after payment. Only used as a fallback when no callbacks (onSuccess, onCallback, onCancelled, onClose, onError) are provided. Callbacks always take precedence. */
34
36
  redirectUrl?: string;
35
37
  metaData?: Record<string, unknown>;
36
38
  customization?: ValuePayCustomization;
39
+ /** Called on successful payment. Use this OR `onCallback` — not both. When provided, this is passed as `onsuccess` to the checkout and only fires for successful transactions. */
37
40
  onSuccess?: (response: ValuePayResponse) => void;
41
+ /** Called when the user closes the checkout modal. */
38
42
  onClose?: (response: ValuePayResponse) => void;
43
+ /** Called when the user explicitly cancels the payment. */
39
44
  onCancelled?: (response: ValuePayResponse) => void;
45
+ /** General-purpose callback for ALL payment events (success, failure, etc.). Use this OR `onSuccess` — not both. When provided, this is passed as `oncallback` to the checkout and fires for every event. */
40
46
  onCallback?: (response: ValuePayResponse) => void;
47
+ /** Called on SDK-level errors (script load failure, etc.). */
41
48
  onError?: (error: Error) => void;
42
49
  scriptUrl?: string;
43
50
  }
@@ -52,10 +59,14 @@ export interface UseValuePayReturn {
52
59
  export interface ValuePayButtonProps extends ValuePayConfig {
53
60
  /** Button text override. Default: "Pay ₦{amount}" */
54
61
  text?: string;
55
- /** Additional CSS class names */
62
+ /** Additional CSS class names for the button element */
56
63
  className?: string;
57
- /** Additional inline styles */
64
+ /** Inline styles applied to the button element (text color, font, padding, etc.) */
58
65
  style?: CSSProperties;
66
+ /** Inline styles applied to the outer container div (width, height, margin, borders, radius, background, etc.) */
67
+ containerStyle?: CSSProperties;
68
+ /** Additional CSS class names for the outer container div */
69
+ containerClassName?: string;
59
70
  /** Disable the button */
60
71
  disabled?: boolean;
61
72
  /** Custom children override the default button text */
@@ -3,6 +3,12 @@ import type { ValuePayConfig, ValuePayResponse } from "../types";
3
3
  * Generate a unique transaction reference.
4
4
  */
5
5
  export declare const generateTransactionRef: (length?: number) => string;
6
+ /**
7
+ * Ensure the transaction reference always carries the VPS_TX_ prefix.
8
+ * If the merchant supplies their own ref, the prefix is prepended (unless already present).
9
+ * If omitted, a fully random reference is generated.
10
+ */
11
+ export declare const ensureRefPrefix: (ref: string) => string;
6
12
  /**
7
13
  * Normalize the SDK config into the shape expected by window.ValuepayCheckout.
8
14
  */
package/package.json CHANGED
@@ -1,13 +1,18 @@
1
1
  {
2
2
  "name": "@valuepay/react",
3
- "version": "1.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Official React SDK for ValuePay payment gateway",
5
5
  "author": "Value Payment Solutions Limited",
6
6
  "license": "MIT",
7
7
  "main": "dist/cjs/index.js",
8
8
  "module": "dist/esm/index.js",
9
9
  "types": "dist/types/index.d.ts",
10
- "files": ["dist", "README.md", "LICENSE", "CHANGELOG.md"],
10
+ "files": [
11
+ "dist",
12
+ "README.md",
13
+ "LICENSE",
14
+ "CHANGELOG.md"
15
+ ],
11
16
  "exports": {
12
17
  ".": {
13
18
  "import": "./dist/esm/index.js",