@solvapay/react 1.0.0-preview.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # @solvapay/react
2
+
3
+ Payment components for SolvaPay with Stripe integration.
4
+
5
+ ## Install
6
+ ```bash
7
+ pnpm add @solvapay/react
8
+ ```
9
+
10
+ ## Usage
11
+ ```tsx
12
+ import { SolvaPayProvider, PaymentForm } from '@solvapay/react';
13
+
14
+ export default function CheckoutPage() {
15
+ return (
16
+ <SolvaPayProvider
17
+ amount={999}
18
+ currency="USD"
19
+ planRef="pln_abc123"
20
+ >
21
+ <PaymentForm
22
+ returnUrl="/checkout/success"
23
+ onSuccess={(paymentIntent) => {
24
+ console.log('Payment successful!', paymentIntent);
25
+ }}
26
+ />
27
+ </SolvaPayProvider>
28
+ );
29
+ }
30
+ ```
31
+
32
+ Peer deps: react, react-dom
33
+
34
+ More: docs/architecture.md
package/dist/index.cjs ADDED
@@ -0,0 +1,272 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.tsx
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ PaymentForm: () => PaymentForm,
24
+ SolvaPayProvider: () => SolvaPayProvider
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // src/SolvaPayProvider.tsx
29
+ var import_react = require("react");
30
+ var import_react_stripe_js = require("@stripe/react-stripe-js");
31
+ var import_stripe_js = require("@stripe/stripe-js");
32
+ var import_jsx_runtime = require("react/jsx-runtime");
33
+ var SolvaPayProvider = ({
34
+ children,
35
+ amount = 999,
36
+ // Default $9.99
37
+ currency = "USD",
38
+ planRef,
39
+ agentRef,
40
+ apiBaseUrl = "/api/solvapay",
41
+ onPaymentReady
42
+ }) => {
43
+ const [stripePromise, setStripePromise] = (0, import_react.useState)(null);
44
+ const [clientSecret, setClientSecret] = (0, import_react.useState)("");
45
+ const [loading, setLoading] = (0, import_react.useState)(false);
46
+ const [error, setError] = (0, import_react.useState)(null);
47
+ const createPayment = async (paymentAmount) => {
48
+ setLoading(true);
49
+ setError(null);
50
+ try {
51
+ console.log("Creating SolvaPay payment intent for amount:", paymentAmount);
52
+ const response = await fetch(`${apiBaseUrl}/create-payment-intent`, {
53
+ method: "POST",
54
+ headers: {
55
+ "Content-Type": "application/json"
56
+ },
57
+ body: JSON.stringify({
58
+ planRef,
59
+ agentRef,
60
+ amount: paymentAmount,
61
+ currency,
62
+ description: "SolvaPay - Plan Purchase"
63
+ })
64
+ });
65
+ if (!response.ok) {
66
+ const errorText = await response.text();
67
+ throw new Error(`Payment intent creation failed: ${response.status} ${errorText}`);
68
+ }
69
+ const result = await response.json();
70
+ if (!result || !result.clientSecret || !result.publishableKey) {
71
+ throw new Error("Invalid response from server: missing required fields");
72
+ }
73
+ console.log("Payment intent received:", {
74
+ hasClientSecret: !!result.clientSecret,
75
+ hasPublishableKey: !!result.publishableKey,
76
+ hasAccountId: !!result.accountId,
77
+ clientSecretLength: result.clientSecret?.length || 0,
78
+ publishableKeyLength: result.publishableKey?.length || 0
79
+ });
80
+ const stripeOptions = result.accountId ? { stripeAccount: result.accountId } : {};
81
+ const stripe = await (0, import_stripe_js.loadStripe)(result.publishableKey, stripeOptions);
82
+ if (!stripe) {
83
+ throw new Error("Failed to initialize Stripe. Please check your configuration.");
84
+ }
85
+ setStripePromise(stripe);
86
+ setClientSecret(result.clientSecret);
87
+ if (onPaymentReady) {
88
+ onPaymentReady(stripe, result.clientSecret);
89
+ }
90
+ } catch (error2) {
91
+ console.error("Failed to create payment:", error2);
92
+ let errorMessage = "Payment creation failed";
93
+ if (error2 instanceof Error) {
94
+ if (error2.message.includes("publishable key")) {
95
+ errorMessage = "Server configuration error: Missing Stripe publishable key";
96
+ } else if (error2.message.includes("client secret")) {
97
+ errorMessage = "Server error: Payment intent creation failed";
98
+ } else if (error2.message.includes("Failed to initialize Stripe")) {
99
+ errorMessage = "Stripe initialization failed. Please check your configuration.";
100
+ } else {
101
+ errorMessage = error2.message;
102
+ }
103
+ }
104
+ setError(errorMessage);
105
+ } finally {
106
+ setLoading(false);
107
+ }
108
+ };
109
+ (0, import_react.useEffect)(() => {
110
+ if (amount && !stripePromise && !loading && !error) {
111
+ createPayment(amount);
112
+ }
113
+ }, [amount]);
114
+ if (error) {
115
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: {
116
+ padding: "1rem",
117
+ backgroundColor: "#fed7d7",
118
+ color: "#742a2a",
119
+ borderRadius: "0.375rem",
120
+ marginBottom: "1rem"
121
+ }, children: [
122
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: "Payment Setup Error:" }),
123
+ " ",
124
+ error,
125
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
126
+ "button",
127
+ {
128
+ onClick: () => createPayment(amount),
129
+ style: {
130
+ marginLeft: "1rem",
131
+ padding: "0.5rem 1rem",
132
+ backgroundColor: "#742a2a",
133
+ color: "white",
134
+ border: "none",
135
+ borderRadius: "0.25rem",
136
+ cursor: "pointer"
137
+ },
138
+ children: "Retry"
139
+ }
140
+ )
141
+ ] });
142
+ }
143
+ if (loading) {
144
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: {
145
+ padding: "1rem",
146
+ textAlign: "center",
147
+ color: "#4a5568"
148
+ }, children: "Setting up payment..." });
149
+ }
150
+ if (!stripePromise || !clientSecret) {
151
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: {
152
+ padding: "1rem",
153
+ textAlign: "center",
154
+ color: "#4a5568"
155
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
156
+ "button",
157
+ {
158
+ onClick: () => createPayment(amount),
159
+ style: {
160
+ padding: "0.75rem 1.5rem",
161
+ backgroundColor: "#3182ce",
162
+ color: "white",
163
+ border: "none",
164
+ borderRadius: "0.375rem",
165
+ cursor: "pointer",
166
+ fontSize: "1rem"
167
+ },
168
+ children: "Initialize Payment"
169
+ }
170
+ ) });
171
+ }
172
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_stripe_js.Elements, { stripe: stripePromise, options: { clientSecret }, children });
173
+ };
174
+
175
+ // src/PaymentForm.tsx
176
+ var import_react2 = require("react");
177
+ var import_react_stripe_js2 = require("@stripe/react-stripe-js");
178
+ var import_jsx_runtime2 = require("react/jsx-runtime");
179
+ var PaymentForm = ({
180
+ onSuccess,
181
+ onError,
182
+ returnUrl,
183
+ submitButtonText = "Pay Now",
184
+ className
185
+ }) => {
186
+ const stripe = (0, import_react_stripe_js2.useStripe)();
187
+ const elements = (0, import_react_stripe_js2.useElements)();
188
+ const [isProcessing, setIsProcessing] = (0, import_react2.useState)(false);
189
+ const [message, setMessage] = (0, import_react2.useState)(null);
190
+ const handleSubmit = async (event) => {
191
+ event.preventDefault();
192
+ if (!stripe || !elements) {
193
+ return;
194
+ }
195
+ setIsProcessing(true);
196
+ setMessage(null);
197
+ try {
198
+ const { error, paymentIntent } = await stripe.confirmPayment({
199
+ elements,
200
+ confirmParams: {
201
+ return_url: returnUrl || `${window.location.origin}/checkout/complete`
202
+ },
203
+ redirect: "if_required"
204
+ // Only redirect if required by payment method
205
+ });
206
+ if (error) {
207
+ const errorMessage = error.message || "An unexpected error occurred.";
208
+ setMessage(errorMessage);
209
+ if (onError) {
210
+ onError(new Error(errorMessage));
211
+ }
212
+ } else if (paymentIntent && paymentIntent.status === "succeeded") {
213
+ setMessage("Payment successful!");
214
+ if (onSuccess) {
215
+ onSuccess(paymentIntent);
216
+ }
217
+ } else {
218
+ setMessage(`Payment status: ${paymentIntent?.status || "processing"}`);
219
+ }
220
+ } catch (err) {
221
+ const error = err instanceof Error ? err : new Error("Unknown error occurred");
222
+ setMessage(error.message);
223
+ if (onError) {
224
+ onError(error);
225
+ }
226
+ } finally {
227
+ setIsProcessing(false);
228
+ }
229
+ };
230
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("form", { onSubmit: handleSubmit, className, children: [
231
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_stripe_js2.PaymentElement, {}),
232
+ message && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
233
+ "div",
234
+ {
235
+ style: {
236
+ marginTop: "1rem",
237
+ padding: "0.75rem",
238
+ borderRadius: "0.375rem",
239
+ backgroundColor: message.includes("successful") ? "#d1fae5" : "#fee2e2",
240
+ color: message.includes("successful") ? "#065f46" : "#7f1d1d"
241
+ },
242
+ children: message
243
+ }
244
+ ),
245
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
246
+ "button",
247
+ {
248
+ type: "submit",
249
+ disabled: !stripe || isProcessing,
250
+ style: {
251
+ marginTop: "1.5rem",
252
+ width: "100%",
253
+ padding: "0.75rem 1.5rem",
254
+ backgroundColor: isProcessing || !stripe ? "#9ca3af" : "#3b82f6",
255
+ color: "white",
256
+ border: "none",
257
+ borderRadius: "0.375rem",
258
+ fontSize: "1rem",
259
+ fontWeight: "500",
260
+ cursor: isProcessing || !stripe ? "not-allowed" : "pointer",
261
+ transition: "background-color 0.2s"
262
+ },
263
+ children: isProcessing ? "Processing..." : submitButtonText
264
+ }
265
+ )
266
+ ] });
267
+ };
268
+ // Annotate the CommonJS export names for ESM import in node:
269
+ 0 && (module.exports = {
270
+ PaymentForm,
271
+ SolvaPayProvider
272
+ });
@@ -0,0 +1,84 @@
1
+ import React from 'react';
2
+ import { Stripe } from '@stripe/stripe-js';
3
+
4
+ interface SolvaPayProviderProps {
5
+ children: React.ReactNode;
6
+ amount?: number;
7
+ currency?: string;
8
+ planRef: string;
9
+ agentRef: string;
10
+ apiBaseUrl?: string;
11
+ onPaymentReady?: (stripe: Stripe | null, clientSecret: string) => void;
12
+ }
13
+ /**
14
+ * SolvaPay Payment Provider Component
15
+ *
16
+ * Wraps children with Stripe Elements context after initializing a payment intent
17
+ * via the backend API. This component handles the secure payment flow:
18
+ *
19
+ * 1. Calls backend API route to create payment intent (backend uses secret key)
20
+ * 2. Backend returns clientSecret and publishableKey from Stripe
21
+ * 3. Initializes Stripe.js with publishableKey
22
+ * 4. Wraps children in Stripe Elements provider
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * // In your Next.js page
27
+ * import { SolvaPayProvider, PaymentForm } from '@solvapay/react';
28
+ *
29
+ * export default function CheckoutPage() {
30
+ * return (
31
+ * <SolvaPayProvider
32
+ * amount={999}
33
+ * currency="USD"
34
+ * planRef="pln_abc123"
35
+ * agentRef="agt_xyz789"
36
+ * onPaymentReady={(stripe, clientSecret) => {
37
+ * console.log('Payment ready!');
38
+ * }}
39
+ * >
40
+ * <PaymentForm />
41
+ * </SolvaPayProvider>
42
+ * );
43
+ * }
44
+ * ```
45
+ */
46
+ declare const SolvaPayProvider: React.FC<SolvaPayProviderProps>;
47
+
48
+ interface PaymentFormProps {
49
+ onSuccess?: (paymentIntent: any) => void;
50
+ onError?: (error: Error) => void;
51
+ returnUrl?: string;
52
+ submitButtonText?: string;
53
+ className?: string;
54
+ }
55
+ /**
56
+ * SolvaPay Payment Form Component
57
+ *
58
+ * Renders a payment form using Stripe's PaymentElement for secure card input.
59
+ * This component must be wrapped in a SolvaPayProvider to work properly.
60
+ *
61
+ * @example
62
+ * ```tsx
63
+ * import { SolvaPayProvider, PaymentForm } from '@solvapay/react';
64
+ *
65
+ * export default function CheckoutPage() {
66
+ * return (
67
+ * <SolvaPayProvider amount={999} planRef="pln_abc123">
68
+ * <PaymentForm
69
+ * returnUrl={`${window.location.origin}/checkout/success`}
70
+ * onSuccess={(paymentIntent) => {
71
+ * console.log('Payment successful!', paymentIntent);
72
+ * }}
73
+ * onError={(error) => {
74
+ * console.error('Payment failed:', error);
75
+ * }}
76
+ * />
77
+ * </SolvaPayProvider>
78
+ * );
79
+ * }
80
+ * ```
81
+ */
82
+ declare const PaymentForm: React.FC<PaymentFormProps>;
83
+
84
+ export { PaymentForm, type PaymentFormProps, SolvaPayProvider, type SolvaPayProviderProps };
@@ -0,0 +1,84 @@
1
+ import React from 'react';
2
+ import { Stripe } from '@stripe/stripe-js';
3
+
4
+ interface SolvaPayProviderProps {
5
+ children: React.ReactNode;
6
+ amount?: number;
7
+ currency?: string;
8
+ planRef: string;
9
+ agentRef: string;
10
+ apiBaseUrl?: string;
11
+ onPaymentReady?: (stripe: Stripe | null, clientSecret: string) => void;
12
+ }
13
+ /**
14
+ * SolvaPay Payment Provider Component
15
+ *
16
+ * Wraps children with Stripe Elements context after initializing a payment intent
17
+ * via the backend API. This component handles the secure payment flow:
18
+ *
19
+ * 1. Calls backend API route to create payment intent (backend uses secret key)
20
+ * 2. Backend returns clientSecret and publishableKey from Stripe
21
+ * 3. Initializes Stripe.js with publishableKey
22
+ * 4. Wraps children in Stripe Elements provider
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * // In your Next.js page
27
+ * import { SolvaPayProvider, PaymentForm } from '@solvapay/react';
28
+ *
29
+ * export default function CheckoutPage() {
30
+ * return (
31
+ * <SolvaPayProvider
32
+ * amount={999}
33
+ * currency="USD"
34
+ * planRef="pln_abc123"
35
+ * agentRef="agt_xyz789"
36
+ * onPaymentReady={(stripe, clientSecret) => {
37
+ * console.log('Payment ready!');
38
+ * }}
39
+ * >
40
+ * <PaymentForm />
41
+ * </SolvaPayProvider>
42
+ * );
43
+ * }
44
+ * ```
45
+ */
46
+ declare const SolvaPayProvider: React.FC<SolvaPayProviderProps>;
47
+
48
+ interface PaymentFormProps {
49
+ onSuccess?: (paymentIntent: any) => void;
50
+ onError?: (error: Error) => void;
51
+ returnUrl?: string;
52
+ submitButtonText?: string;
53
+ className?: string;
54
+ }
55
+ /**
56
+ * SolvaPay Payment Form Component
57
+ *
58
+ * Renders a payment form using Stripe's PaymentElement for secure card input.
59
+ * This component must be wrapped in a SolvaPayProvider to work properly.
60
+ *
61
+ * @example
62
+ * ```tsx
63
+ * import { SolvaPayProvider, PaymentForm } from '@solvapay/react';
64
+ *
65
+ * export default function CheckoutPage() {
66
+ * return (
67
+ * <SolvaPayProvider amount={999} planRef="pln_abc123">
68
+ * <PaymentForm
69
+ * returnUrl={`${window.location.origin}/checkout/success`}
70
+ * onSuccess={(paymentIntent) => {
71
+ * console.log('Payment successful!', paymentIntent);
72
+ * }}
73
+ * onError={(error) => {
74
+ * console.error('Payment failed:', error);
75
+ * }}
76
+ * />
77
+ * </SolvaPayProvider>
78
+ * );
79
+ * }
80
+ * ```
81
+ */
82
+ declare const PaymentForm: React.FC<PaymentFormProps>;
83
+
84
+ export { PaymentForm, type PaymentFormProps, SolvaPayProvider, type SolvaPayProviderProps };
package/dist/index.js ADDED
@@ -0,0 +1,244 @@
1
+ // src/SolvaPayProvider.tsx
2
+ import { useState, useEffect } from "react";
3
+ import { Elements } from "@stripe/react-stripe-js";
4
+ import { loadStripe } from "@stripe/stripe-js";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ var SolvaPayProvider = ({
7
+ children,
8
+ amount = 999,
9
+ // Default $9.99
10
+ currency = "USD",
11
+ planRef,
12
+ agentRef,
13
+ apiBaseUrl = "/api/solvapay",
14
+ onPaymentReady
15
+ }) => {
16
+ const [stripePromise, setStripePromise] = useState(null);
17
+ const [clientSecret, setClientSecret] = useState("");
18
+ const [loading, setLoading] = useState(false);
19
+ const [error, setError] = useState(null);
20
+ const createPayment = async (paymentAmount) => {
21
+ setLoading(true);
22
+ setError(null);
23
+ try {
24
+ console.log("Creating SolvaPay payment intent for amount:", paymentAmount);
25
+ const response = await fetch(`${apiBaseUrl}/create-payment-intent`, {
26
+ method: "POST",
27
+ headers: {
28
+ "Content-Type": "application/json"
29
+ },
30
+ body: JSON.stringify({
31
+ planRef,
32
+ agentRef,
33
+ amount: paymentAmount,
34
+ currency,
35
+ description: "SolvaPay - Plan Purchase"
36
+ })
37
+ });
38
+ if (!response.ok) {
39
+ const errorText = await response.text();
40
+ throw new Error(`Payment intent creation failed: ${response.status} ${errorText}`);
41
+ }
42
+ const result = await response.json();
43
+ if (!result || !result.clientSecret || !result.publishableKey) {
44
+ throw new Error("Invalid response from server: missing required fields");
45
+ }
46
+ console.log("Payment intent received:", {
47
+ hasClientSecret: !!result.clientSecret,
48
+ hasPublishableKey: !!result.publishableKey,
49
+ hasAccountId: !!result.accountId,
50
+ clientSecretLength: result.clientSecret?.length || 0,
51
+ publishableKeyLength: result.publishableKey?.length || 0
52
+ });
53
+ const stripeOptions = result.accountId ? { stripeAccount: result.accountId } : {};
54
+ const stripe = await loadStripe(result.publishableKey, stripeOptions);
55
+ if (!stripe) {
56
+ throw new Error("Failed to initialize Stripe. Please check your configuration.");
57
+ }
58
+ setStripePromise(stripe);
59
+ setClientSecret(result.clientSecret);
60
+ if (onPaymentReady) {
61
+ onPaymentReady(stripe, result.clientSecret);
62
+ }
63
+ } catch (error2) {
64
+ console.error("Failed to create payment:", error2);
65
+ let errorMessage = "Payment creation failed";
66
+ if (error2 instanceof Error) {
67
+ if (error2.message.includes("publishable key")) {
68
+ errorMessage = "Server configuration error: Missing Stripe publishable key";
69
+ } else if (error2.message.includes("client secret")) {
70
+ errorMessage = "Server error: Payment intent creation failed";
71
+ } else if (error2.message.includes("Failed to initialize Stripe")) {
72
+ errorMessage = "Stripe initialization failed. Please check your configuration.";
73
+ } else {
74
+ errorMessage = error2.message;
75
+ }
76
+ }
77
+ setError(errorMessage);
78
+ } finally {
79
+ setLoading(false);
80
+ }
81
+ };
82
+ useEffect(() => {
83
+ if (amount && !stripePromise && !loading && !error) {
84
+ createPayment(amount);
85
+ }
86
+ }, [amount]);
87
+ if (error) {
88
+ return /* @__PURE__ */ jsxs("div", { style: {
89
+ padding: "1rem",
90
+ backgroundColor: "#fed7d7",
91
+ color: "#742a2a",
92
+ borderRadius: "0.375rem",
93
+ marginBottom: "1rem"
94
+ }, children: [
95
+ /* @__PURE__ */ jsx("strong", { children: "Payment Setup Error:" }),
96
+ " ",
97
+ error,
98
+ /* @__PURE__ */ jsx(
99
+ "button",
100
+ {
101
+ onClick: () => createPayment(amount),
102
+ style: {
103
+ marginLeft: "1rem",
104
+ padding: "0.5rem 1rem",
105
+ backgroundColor: "#742a2a",
106
+ color: "white",
107
+ border: "none",
108
+ borderRadius: "0.25rem",
109
+ cursor: "pointer"
110
+ },
111
+ children: "Retry"
112
+ }
113
+ )
114
+ ] });
115
+ }
116
+ if (loading) {
117
+ return /* @__PURE__ */ jsx("div", { style: {
118
+ padding: "1rem",
119
+ textAlign: "center",
120
+ color: "#4a5568"
121
+ }, children: "Setting up payment..." });
122
+ }
123
+ if (!stripePromise || !clientSecret) {
124
+ return /* @__PURE__ */ jsx("div", { style: {
125
+ padding: "1rem",
126
+ textAlign: "center",
127
+ color: "#4a5568"
128
+ }, children: /* @__PURE__ */ jsx(
129
+ "button",
130
+ {
131
+ onClick: () => createPayment(amount),
132
+ style: {
133
+ padding: "0.75rem 1.5rem",
134
+ backgroundColor: "#3182ce",
135
+ color: "white",
136
+ border: "none",
137
+ borderRadius: "0.375rem",
138
+ cursor: "pointer",
139
+ fontSize: "1rem"
140
+ },
141
+ children: "Initialize Payment"
142
+ }
143
+ ) });
144
+ }
145
+ return /* @__PURE__ */ jsx(Elements, { stripe: stripePromise, options: { clientSecret }, children });
146
+ };
147
+
148
+ // src/PaymentForm.tsx
149
+ import { useState as useState2 } from "react";
150
+ import { useStripe, useElements, PaymentElement } from "@stripe/react-stripe-js";
151
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
152
+ var PaymentForm = ({
153
+ onSuccess,
154
+ onError,
155
+ returnUrl,
156
+ submitButtonText = "Pay Now",
157
+ className
158
+ }) => {
159
+ const stripe = useStripe();
160
+ const elements = useElements();
161
+ const [isProcessing, setIsProcessing] = useState2(false);
162
+ const [message, setMessage] = useState2(null);
163
+ const handleSubmit = async (event) => {
164
+ event.preventDefault();
165
+ if (!stripe || !elements) {
166
+ return;
167
+ }
168
+ setIsProcessing(true);
169
+ setMessage(null);
170
+ try {
171
+ const { error, paymentIntent } = await stripe.confirmPayment({
172
+ elements,
173
+ confirmParams: {
174
+ return_url: returnUrl || `${window.location.origin}/checkout/complete`
175
+ },
176
+ redirect: "if_required"
177
+ // Only redirect if required by payment method
178
+ });
179
+ if (error) {
180
+ const errorMessage = error.message || "An unexpected error occurred.";
181
+ setMessage(errorMessage);
182
+ if (onError) {
183
+ onError(new Error(errorMessage));
184
+ }
185
+ } else if (paymentIntent && paymentIntent.status === "succeeded") {
186
+ setMessage("Payment successful!");
187
+ if (onSuccess) {
188
+ onSuccess(paymentIntent);
189
+ }
190
+ } else {
191
+ setMessage(`Payment status: ${paymentIntent?.status || "processing"}`);
192
+ }
193
+ } catch (err) {
194
+ const error = err instanceof Error ? err : new Error("Unknown error occurred");
195
+ setMessage(error.message);
196
+ if (onError) {
197
+ onError(error);
198
+ }
199
+ } finally {
200
+ setIsProcessing(false);
201
+ }
202
+ };
203
+ return /* @__PURE__ */ jsxs2("form", { onSubmit: handleSubmit, className, children: [
204
+ /* @__PURE__ */ jsx2(PaymentElement, {}),
205
+ message && /* @__PURE__ */ jsx2(
206
+ "div",
207
+ {
208
+ style: {
209
+ marginTop: "1rem",
210
+ padding: "0.75rem",
211
+ borderRadius: "0.375rem",
212
+ backgroundColor: message.includes("successful") ? "#d1fae5" : "#fee2e2",
213
+ color: message.includes("successful") ? "#065f46" : "#7f1d1d"
214
+ },
215
+ children: message
216
+ }
217
+ ),
218
+ /* @__PURE__ */ jsx2(
219
+ "button",
220
+ {
221
+ type: "submit",
222
+ disabled: !stripe || isProcessing,
223
+ style: {
224
+ marginTop: "1.5rem",
225
+ width: "100%",
226
+ padding: "0.75rem 1.5rem",
227
+ backgroundColor: isProcessing || !stripe ? "#9ca3af" : "#3b82f6",
228
+ color: "white",
229
+ border: "none",
230
+ borderRadius: "0.375rem",
231
+ fontSize: "1rem",
232
+ fontWeight: "500",
233
+ cursor: isProcessing || !stripe ? "not-allowed" : "pointer",
234
+ transition: "background-color 0.2s"
235
+ },
236
+ children: isProcessing ? "Processing..." : submitButtonText
237
+ }
238
+ )
239
+ ] });
240
+ };
241
+ export {
242
+ PaymentForm,
243
+ SolvaPayProvider
244
+ };
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@solvapay/react",
3
+ "version": "1.0.0-preview.1",
4
+ "type": "module",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "README.md"
11
+ ],
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/solvapay/solvapay-sdk.git",
18
+ "directory": "packages/react"
19
+ },
20
+ "peerDependencies": {
21
+ "react": "^18.2.0 || ^19.0.0",
22
+ "react-dom": "^18.2.0 || ^19.0.0"
23
+ },
24
+ "dependencies": {
25
+ "@stripe/react-stripe-js": "^2.9.0",
26
+ "@stripe/stripe-js": "^4.8.0"
27
+ },
28
+ "devDependencies": {
29
+ "@types/react": "^18.2.74",
30
+ "@types/react-dom": "^18.2.25",
31
+ "typescript": "^5.5.4",
32
+ "vitest": "^2.0.5",
33
+ "@solvapay/test-utils": "0.0.0"
34
+ },
35
+ "scripts": {
36
+ "build": "tsup src/index.tsx --format esm,cjs --dts --tsconfig tsconfig.build.json",
37
+ "test": "vitest run || exit 0",
38
+ "test:unit": "vitest run || exit 0",
39
+ "test:watch": "vitest"
40
+ }
41
+ }