@fragmentpay/react 0.3.4 → 0.4.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/dist/index.d.mts CHANGED
@@ -8,7 +8,7 @@ import * as React from 'react';
8
8
 
9
9
  interface FragmentPayProviderProps {
10
10
  publicKey: string;
11
- /** Override the API/host origin. Defaults to https://frag.dev. */
11
+ /** Override the API/host origin. Defaults to https://frag.cash. */
12
12
  baseUrl?: string;
13
13
  children: React.ReactNode;
14
14
  }
@@ -25,14 +25,43 @@ interface FetchWithRetryOpts extends RequestInit {
25
25
  * with jitter for retryable failures. Exposed for advanced integrators.
26
26
  */
27
27
  declare function fetchWithRetry(input: string, init?: FetchWithRetryOpts): Promise<Response>;
28
- type IntentStatus = "created" | "requires_payment" | "quoted" | "executing" | "settled" | "failed" | "expired" | "cancelled" | "refunded";
28
+ type IntentStatus = "created" | "requires_payment" | "quoted" | "awaiting_signature" | "executing" | "partially_filled" | "settled" | "failed" | "expired" | "cancelled" | "refunded";
29
29
  interface PublicIntent {
30
30
  id: string;
31
31
  amount: string;
32
32
  currency: string;
33
33
  status: IntentStatus;
34
+ settlement_chain?: string | null;
35
+ settlement_token?: string | null;
36
+ expires_at?: string;
34
37
  metadata?: Record<string, unknown>;
35
38
  }
39
+ type RefundStatus = "pending" | "processing" | "retry_scheduled" | "succeeded" | "failed";
40
+ type RefundPhase = "queued" | "sent" | "completed" | "failed";
41
+ /**
42
+ * Bucket a raw refund status into a stable UI phase. Mirrors the identical
43
+ * helper in `@fragmentpay/server` so front-end and back-end render matching
44
+ * status pills.
45
+ */
46
+ declare function refundPhase(status: RefundStatus | string | null | undefined): RefundPhase;
47
+ interface PublicRefundStatus {
48
+ id: string;
49
+ payment_intent_id: string;
50
+ amount_usd: number;
51
+ chain: string | null;
52
+ token: string | null;
53
+ destination_tail: string;
54
+ reason: string;
55
+ status: RefundStatus;
56
+ phase: RefundPhase;
57
+ tx_hash: string | null;
58
+ failure_reason: string | null;
59
+ attempts: number;
60
+ created_at: string;
61
+ updated_at: string;
62
+ paid_at: string | null;
63
+ terminal: boolean;
64
+ }
36
65
  declare function useFragmentIntent(intentId: string | null, opts?: {
37
66
  intervalMs?: number;
38
67
  }): {
@@ -40,6 +69,21 @@ declare function useFragmentIntent(intentId: string | null, opts?: {
40
69
  status: "idle" | "loading" | "ready" | "error";
41
70
  error: string | null;
42
71
  };
72
+ /**
73
+ * Poll the payer-facing refund status endpoint at `/api/public/refunds/:id/status`.
74
+ * UNAUTHENTICATED — safe to use in customer-facing receipt / order-tracking UIs.
75
+ *
76
+ * Returns `{ data, status, error }` where `data.phase` buckets the raw refund
77
+ * state into `queued | sent | completed | failed` for a stable status pill.
78
+ * Polling stops automatically once `data.terminal === true`.
79
+ */
80
+ declare function useFragmentRefund(refundId: string | null, opts?: {
81
+ intervalMs?: number;
82
+ }): {
83
+ data: PublicRefundStatus | null;
84
+ status: "idle" | "loading" | "ready" | "error";
85
+ error: string | null;
86
+ };
43
87
  interface FragmentPayCheckoutProps {
44
88
  intentId: string;
45
89
  /** Called once the intent transitions to `settled`. */
@@ -96,4 +140,4 @@ interface FragmentPayButtonProps extends UseCreateIntentOptions {
96
140
  }
97
141
  declare function FragmentPayButton({ input, label, onSettled, onError, className, style, ...opts }: FragmentPayButtonProps): React.JSX.Element;
98
142
 
99
- export { type FetchWithRetryOpts, type FragToken, FragmentPayButton, type FragmentPayButtonProps, FragmentPayCheckout, type FragmentPayCheckoutProps, FragmentPayProvider, type FragmentPayProviderProps, type IntentStatus, type PublicIntent, type UseCreateIntentOptions, fetchWithRetry, useCreateIntent, useFragmentIntent, useFragmentTokens };
143
+ export { type FetchWithRetryOpts, type FragToken, FragmentPayButton, type FragmentPayButtonProps, FragmentPayCheckout, type FragmentPayCheckoutProps, FragmentPayProvider, type FragmentPayProviderProps, type IntentStatus, type PublicIntent, type PublicRefundStatus, type RefundPhase, type RefundStatus, type UseCreateIntentOptions, fetchWithRetry, refundPhase, useCreateIntent, useFragmentIntent, useFragmentRefund, useFragmentTokens };
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@ import * as React from 'react';
8
8
 
9
9
  interface FragmentPayProviderProps {
10
10
  publicKey: string;
11
- /** Override the API/host origin. Defaults to https://frag.dev. */
11
+ /** Override the API/host origin. Defaults to https://frag.cash. */
12
12
  baseUrl?: string;
13
13
  children: React.ReactNode;
14
14
  }
@@ -25,14 +25,43 @@ interface FetchWithRetryOpts extends RequestInit {
25
25
  * with jitter for retryable failures. Exposed for advanced integrators.
26
26
  */
27
27
  declare function fetchWithRetry(input: string, init?: FetchWithRetryOpts): Promise<Response>;
28
- type IntentStatus = "created" | "requires_payment" | "quoted" | "executing" | "settled" | "failed" | "expired" | "cancelled" | "refunded";
28
+ type IntentStatus = "created" | "requires_payment" | "quoted" | "awaiting_signature" | "executing" | "partially_filled" | "settled" | "failed" | "expired" | "cancelled" | "refunded";
29
29
  interface PublicIntent {
30
30
  id: string;
31
31
  amount: string;
32
32
  currency: string;
33
33
  status: IntentStatus;
34
+ settlement_chain?: string | null;
35
+ settlement_token?: string | null;
36
+ expires_at?: string;
34
37
  metadata?: Record<string, unknown>;
35
38
  }
39
+ type RefundStatus = "pending" | "processing" | "retry_scheduled" | "succeeded" | "failed";
40
+ type RefundPhase = "queued" | "sent" | "completed" | "failed";
41
+ /**
42
+ * Bucket a raw refund status into a stable UI phase. Mirrors the identical
43
+ * helper in `@fragmentpay/server` so front-end and back-end render matching
44
+ * status pills.
45
+ */
46
+ declare function refundPhase(status: RefundStatus | string | null | undefined): RefundPhase;
47
+ interface PublicRefundStatus {
48
+ id: string;
49
+ payment_intent_id: string;
50
+ amount_usd: number;
51
+ chain: string | null;
52
+ token: string | null;
53
+ destination_tail: string;
54
+ reason: string;
55
+ status: RefundStatus;
56
+ phase: RefundPhase;
57
+ tx_hash: string | null;
58
+ failure_reason: string | null;
59
+ attempts: number;
60
+ created_at: string;
61
+ updated_at: string;
62
+ paid_at: string | null;
63
+ terminal: boolean;
64
+ }
36
65
  declare function useFragmentIntent(intentId: string | null, opts?: {
37
66
  intervalMs?: number;
38
67
  }): {
@@ -40,6 +69,21 @@ declare function useFragmentIntent(intentId: string | null, opts?: {
40
69
  status: "idle" | "loading" | "ready" | "error";
41
70
  error: string | null;
42
71
  };
72
+ /**
73
+ * Poll the payer-facing refund status endpoint at `/api/public/refunds/:id/status`.
74
+ * UNAUTHENTICATED — safe to use in customer-facing receipt / order-tracking UIs.
75
+ *
76
+ * Returns `{ data, status, error }` where `data.phase` buckets the raw refund
77
+ * state into `queued | sent | completed | failed` for a stable status pill.
78
+ * Polling stops automatically once `data.terminal === true`.
79
+ */
80
+ declare function useFragmentRefund(refundId: string | null, opts?: {
81
+ intervalMs?: number;
82
+ }): {
83
+ data: PublicRefundStatus | null;
84
+ status: "idle" | "loading" | "ready" | "error";
85
+ error: string | null;
86
+ };
43
87
  interface FragmentPayCheckoutProps {
44
88
  intentId: string;
45
89
  /** Called once the intent transitions to `settled`. */
@@ -96,4 +140,4 @@ interface FragmentPayButtonProps extends UseCreateIntentOptions {
96
140
  }
97
141
  declare function FragmentPayButton({ input, label, onSettled, onError, className, style, ...opts }: FragmentPayButtonProps): React.JSX.Element;
98
142
 
99
- export { type FetchWithRetryOpts, type FragToken, FragmentPayButton, type FragmentPayButtonProps, FragmentPayCheckout, type FragmentPayCheckoutProps, FragmentPayProvider, type FragmentPayProviderProps, type IntentStatus, type PublicIntent, type UseCreateIntentOptions, fetchWithRetry, useCreateIntent, useFragmentIntent, useFragmentTokens };
143
+ export { type FetchWithRetryOpts, type FragToken, FragmentPayButton, type FragmentPayButtonProps, FragmentPayCheckout, type FragmentPayCheckoutProps, FragmentPayProvider, type FragmentPayProviderProps, type IntentStatus, type PublicIntent, type PublicRefundStatus, type RefundPhase, type RefundStatus, type UseCreateIntentOptions, fetchWithRetry, refundPhase, useCreateIntent, useFragmentIntent, useFragmentRefund, useFragmentTokens };
package/dist/index.js CHANGED
@@ -34,8 +34,10 @@ __export(index_exports, {
34
34
  FragmentPayCheckout: () => FragmentPayCheckout,
35
35
  FragmentPayProvider: () => FragmentPayProvider,
36
36
  fetchWithRetry: () => fetchWithRetry,
37
+ refundPhase: () => refundPhase,
37
38
  useCreateIntent: () => useCreateIntent,
38
39
  useFragmentIntent: () => useFragmentIntent,
40
+ useFragmentRefund: () => useFragmentRefund,
39
41
  useFragmentTokens: () => useFragmentTokens
40
42
  });
41
43
  module.exports = __toCommonJS(index_exports);
@@ -56,7 +58,7 @@ function FragmentPayProvider({ publicKey, baseUrl, children }) {
56
58
  }
57
59
  }
58
60
  const value = React.useMemo(
59
- () => ({ publicKey, baseUrl: (baseUrl ?? "https://frag.dev").replace(/\/$/, "") }),
61
+ () => ({ publicKey, baseUrl: (baseUrl ?? "https://frag.cash").replace(/\/$/, "") }),
60
62
  [publicKey, baseUrl]
61
63
  );
62
64
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FragContext.Provider, { value, children });
@@ -119,6 +121,18 @@ async function fetchWithRetry(input, init = {}) {
119
121
  attempt += 1;
120
122
  }
121
123
  }
124
+ function refundPhase(status) {
125
+ switch (status) {
126
+ case "succeeded":
127
+ return "completed";
128
+ case "processing":
129
+ return "sent";
130
+ case "failed":
131
+ return "failed";
132
+ default:
133
+ return "queued";
134
+ }
135
+ }
122
136
  function useFragmentIntent(intentId, opts = {}) {
123
137
  const { baseUrl } = useFrag();
124
138
  if (intentId !== null && (typeof intentId !== "string" || intentId.length < 3)) {
@@ -165,6 +179,51 @@ function useFragmentIntent(intentId, opts = {}) {
165
179
  }, [intentId, baseUrl, interval]);
166
180
  return state;
167
181
  }
182
+ function useFragmentRefund(refundId, opts = {}) {
183
+ const { baseUrl } = useFrag();
184
+ if (refundId !== null && (typeof refundId !== "string" || refundId.length < 3)) {
185
+ throw new Error("[fragmentpay] useFragmentRefund: refundId must be a non-empty string or null");
186
+ }
187
+ const interval = opts.intervalMs ?? 3e3;
188
+ if (typeof interval !== "number" || interval < 500 || interval > 6e4) {
189
+ throw new Error("[fragmentpay] useFragmentRefund: intervalMs must be between 500 and 60000");
190
+ }
191
+ const [state, setState] = React.useState({ data: null, status: "idle", error: null });
192
+ React.useEffect(() => {
193
+ if (!refundId) return;
194
+ let alive = true;
195
+ let timer;
196
+ async function tick() {
197
+ try {
198
+ const res = await fetchWithRetry(
199
+ `${baseUrl}/api/public/refunds/${refundId}/status`,
200
+ { maxRetries: 4 }
201
+ );
202
+ if (!res.ok) {
203
+ const retryAfter = parseRetryAfter(res.headers.get("retry-after")) ?? interval * 2;
204
+ throw Object.assign(new Error(`HTTP ${res.status}`), { retryAfter });
205
+ }
206
+ const raw = await res.json();
207
+ const data = { ...raw, phase: refundPhase(raw.status) };
208
+ if (!alive) return;
209
+ setState({ data, status: "ready", error: null });
210
+ if (!data.terminal) timer = setTimeout(tick, interval);
211
+ } catch (e) {
212
+ if (!alive) return;
213
+ const wait = e.retryAfter ?? interval * 2;
214
+ setState((s) => ({ ...s, status: "error", error: e.message }));
215
+ timer = setTimeout(tick, wait);
216
+ }
217
+ }
218
+ setState({ data: null, status: "loading", error: null });
219
+ tick();
220
+ return () => {
221
+ alive = false;
222
+ clearTimeout(timer);
223
+ };
224
+ }, [refundId, baseUrl, interval]);
225
+ return state;
226
+ }
168
227
  function FragmentPayCheckout({
169
228
  intentId,
170
229
  onSettled,
@@ -299,7 +358,9 @@ function FragmentPayButton({
299
358
  FragmentPayCheckout,
300
359
  FragmentPayProvider,
301
360
  fetchWithRetry,
361
+ refundPhase,
302
362
  useCreateIntent,
303
363
  useFragmentIntent,
364
+ useFragmentRefund,
304
365
  useFragmentTokens
305
366
  });
package/dist/index.mjs CHANGED
@@ -16,7 +16,7 @@ function FragmentPayProvider({ publicKey, baseUrl, children }) {
16
16
  }
17
17
  }
18
18
  const value = React.useMemo(
19
- () => ({ publicKey, baseUrl: (baseUrl ?? "https://frag.dev").replace(/\/$/, "") }),
19
+ () => ({ publicKey, baseUrl: (baseUrl ?? "https://frag.cash").replace(/\/$/, "") }),
20
20
  [publicKey, baseUrl]
21
21
  );
22
22
  return /* @__PURE__ */ jsx(FragContext.Provider, { value, children });
@@ -79,6 +79,18 @@ async function fetchWithRetry(input, init = {}) {
79
79
  attempt += 1;
80
80
  }
81
81
  }
82
+ function refundPhase(status) {
83
+ switch (status) {
84
+ case "succeeded":
85
+ return "completed";
86
+ case "processing":
87
+ return "sent";
88
+ case "failed":
89
+ return "failed";
90
+ default:
91
+ return "queued";
92
+ }
93
+ }
82
94
  function useFragmentIntent(intentId, opts = {}) {
83
95
  const { baseUrl } = useFrag();
84
96
  if (intentId !== null && (typeof intentId !== "string" || intentId.length < 3)) {
@@ -125,6 +137,51 @@ function useFragmentIntent(intentId, opts = {}) {
125
137
  }, [intentId, baseUrl, interval]);
126
138
  return state;
127
139
  }
140
+ function useFragmentRefund(refundId, opts = {}) {
141
+ const { baseUrl } = useFrag();
142
+ if (refundId !== null && (typeof refundId !== "string" || refundId.length < 3)) {
143
+ throw new Error("[fragmentpay] useFragmentRefund: refundId must be a non-empty string or null");
144
+ }
145
+ const interval = opts.intervalMs ?? 3e3;
146
+ if (typeof interval !== "number" || interval < 500 || interval > 6e4) {
147
+ throw new Error("[fragmentpay] useFragmentRefund: intervalMs must be between 500 and 60000");
148
+ }
149
+ const [state, setState] = React.useState({ data: null, status: "idle", error: null });
150
+ React.useEffect(() => {
151
+ if (!refundId) return;
152
+ let alive = true;
153
+ let timer;
154
+ async function tick() {
155
+ try {
156
+ const res = await fetchWithRetry(
157
+ `${baseUrl}/api/public/refunds/${refundId}/status`,
158
+ { maxRetries: 4 }
159
+ );
160
+ if (!res.ok) {
161
+ const retryAfter = parseRetryAfter(res.headers.get("retry-after")) ?? interval * 2;
162
+ throw Object.assign(new Error(`HTTP ${res.status}`), { retryAfter });
163
+ }
164
+ const raw = await res.json();
165
+ const data = { ...raw, phase: refundPhase(raw.status) };
166
+ if (!alive) return;
167
+ setState({ data, status: "ready", error: null });
168
+ if (!data.terminal) timer = setTimeout(tick, interval);
169
+ } catch (e) {
170
+ if (!alive) return;
171
+ const wait = e.retryAfter ?? interval * 2;
172
+ setState((s) => ({ ...s, status: "error", error: e.message }));
173
+ timer = setTimeout(tick, wait);
174
+ }
175
+ }
176
+ setState({ data: null, status: "loading", error: null });
177
+ tick();
178
+ return () => {
179
+ alive = false;
180
+ clearTimeout(timer);
181
+ };
182
+ }, [refundId, baseUrl, interval]);
183
+ return state;
184
+ }
128
185
  function FragmentPayCheckout({
129
186
  intentId,
130
187
  onSettled,
@@ -258,7 +315,9 @@ export {
258
315
  FragmentPayCheckout,
259
316
  FragmentPayProvider,
260
317
  fetchWithRetry,
318
+ refundPhase,
261
319
  useCreateIntent,
262
320
  useFragmentIntent,
321
+ useFragmentRefund,
263
322
  useFragmentTokens
264
323
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fragmentpay/react",
3
- "version": "0.3.4",
3
+ "version": "0.4.0",
4
4
  "description": "React components for Frag hosted checkout — accept any token, on any chain.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",