@v7-pay/react 0.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.
package/README.md ADDED
@@ -0,0 +1,131 @@
1
+ # @v7-pay/react
2
+
3
+ React components for embedding V7 Pay checkout in your app. Stripe Embedded Checkout–style API—no manual iframe or postMessage handling.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @v7-pay/react
9
+ ```
10
+
11
+ ## Prerequisites
12
+
13
+ 1. **V7 Pay Seller Account** — Approved SELLER tenant in V7 Pay
14
+ 2. **API Credentials** — Create credentials at Desenvolvedor → Credenciais
15
+ 3. **Session creation** — Your backend must create checkout sessions via `POST /api/v1/checkout/sessions` (JWT auth)
16
+
17
+ See the V7 Pay embed documentation for session creation and webhook setup.
18
+
19
+ ## Quick Start
20
+
21
+ ### 1. Create a server action to fetch the session ID
22
+
23
+ `fetchClientSecret` must run on your server. It creates a checkout session and returns the session ID (as `clientSecret`).
24
+
25
+ ```ts
26
+ // app/actions/checkout.ts
27
+ 'use server'
28
+
29
+ const V7_PAY_BASE = process.env.V7_PAY_BASE_URL!
30
+ const CLIENT_ID = process.env.V7_PAY_CLIENT_ID!
31
+ const CLIENT_SECRET = process.env.V7_PAY_CLIENT_SECRET!
32
+
33
+ async function getAccessToken() {
34
+ const res = await fetch(`${V7_PAY_BASE}/api/auth/token`, {
35
+ method: 'POST',
36
+ headers: { 'Content-Type': 'application/json' },
37
+ body: JSON.stringify({ client_id: CLIENT_ID, client_secret: CLIENT_SECRET }),
38
+ })
39
+ const data = await res.json()
40
+ if (!res.ok) throw new Error(data.error || 'Token failed')
41
+ return data.access_token
42
+ }
43
+
44
+ export async function fetchClientSecret() {
45
+ const token = await getAccessToken()
46
+ const res = await fetch(`${V7_PAY_BASE}/api/v1/checkout/sessions`, {
47
+ method: 'POST',
48
+ headers: {
49
+ Authorization: `Bearer ${token}`,
50
+ 'Content-Type': 'application/json',
51
+ 'Idempotency-Key': `checkout-${Date.now()}`, // optional but recommended
52
+ },
53
+ body: JSON.stringify({ amount: 9990, currency: 'BRL' }),
54
+ })
55
+ const data = await res.json()
56
+ if (!res.ok) throw new Error(data.error || 'Create session failed')
57
+ return { clientSecret: data.id }
58
+ }
59
+ ```
60
+
61
+ ### 2. Use the components
62
+
63
+ ```tsx
64
+ // app/checkout/page.tsx
65
+ import { EmbeddedCheckout, EmbeddedCheckoutProvider } from '@v7-pay/react'
66
+ import { fetchClientSecret } from './actions/checkout'
67
+
68
+ const V7_PAY_BASE = process.env.NEXT_PUBLIC_V7_PAY_URL || 'https://pay.v7.app'
69
+
70
+ export default function CheckoutPage() {
71
+ return (
72
+ <div id="checkout">
73
+ <EmbeddedCheckoutProvider
74
+ options={{
75
+ baseUrl: V7_PAY_BASE,
76
+ fetchClientSecret,
77
+ onComplete: ({ sessionId }) => {
78
+ // Optional: close modal, redirect, show success
79
+ },
80
+ onCancel: ({ sessionId }) => {
81
+ // Optional: handle cancel
82
+ },
83
+ allowedOrigin: process.env.NEXT_PUBLIC_APP_URL, // Recommended in production
84
+ }}
85
+ >
86
+ <EmbeddedCheckout />
87
+ </EmbeddedCheckoutProvider>
88
+ </div>
89
+ )
90
+ }
91
+ ```
92
+
93
+ ## API
94
+
95
+ ### `EmbeddedCheckoutProvider`
96
+
97
+ Wraps the checkout and provides configuration.
98
+
99
+ | Prop | Type | Required | Description |
100
+ |------|------|----------|-------------|
101
+ | `options` | `EmbeddedCheckoutOptions` | Yes | See below |
102
+
103
+ ### `EmbeddedCheckoutOptions`
104
+
105
+ | Option | Type | Required | Description |
106
+ |--------|------|----------|-------------|
107
+ | `baseUrl` | `string` | Yes | V7 Pay base URL (e.g. `https://pay.v7.app`) |
108
+ | `fetchClientSecret` | `() => Promise<{ clientSecret: string }>` | Yes | Server action that creates a session and returns the session ID as `clientSecret` |
109
+ | `onComplete` | `(params: { sessionId: string }) => void` | No | Called when payment succeeds |
110
+ | `onCancel` | `(params: { sessionId: string }) => void` | No | Called when user cancels |
111
+ | `allowedOrigin` | `string` | No | Only accept postMessage from this origin. **Recommended in production.** |
112
+
113
+ ### `EmbeddedCheckout`
114
+
115
+ Renders the checkout iframe. Accepts optional `className` and `style` props.
116
+
117
+ ```tsx
118
+ <EmbeddedCheckout className="min-h-[500px]" style={{ borderRadius: 8 }} />
119
+ ```
120
+
121
+ ## Security
122
+
123
+ - **Never** expose `client_secret` or API tokens in frontend code. Use `fetchClientSecret` only as a server action (or server-side API route).
124
+ - Set `allowedOrigin` in production to validate postMessage origin:
125
+ ```ts
126
+ allowedOrigin: process.env.NEXT_PUBLIC_APP_URL
127
+ ```
128
+
129
+ ## Server-side confirmation
130
+
131
+ For reliable payment recording, configure a webhook URL in V7 Pay (Desenvolvedor → Webhooks). You receive `transaction.succeeded` and other events when payments complete.
@@ -0,0 +1,31 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import React from 'react';
3
+
4
+ type EmbeddedCheckoutOptions = {
5
+ /** Base URL of V7 Pay (e.g. https://pay.v7.app) */
6
+ baseUrl: string;
7
+ /** Called to get the session ID. Typically a server action that creates a session and returns it. */
8
+ fetchClientSecret: () => Promise<{
9
+ clientSecret: string;
10
+ }>;
11
+ /** Called when payment succeeds. Optional; postMessage is always sent to parent. */
12
+ onComplete?: (params: {
13
+ sessionId: string;
14
+ }) => void;
15
+ /** Called when user cancels. Optional; postMessage is always sent to parent. */
16
+ onCancel?: (params: {
17
+ sessionId: string;
18
+ }) => void;
19
+ /** If set, only accept postMessage from this origin (e.g. https://your-app.com). Recommended in production. */
20
+ allowedOrigin?: string;
21
+ };
22
+ declare function EmbeddedCheckoutProvider({ options, children, }: {
23
+ options: EmbeddedCheckoutOptions;
24
+ children: React.ReactNode;
25
+ }): react_jsx_runtime.JSX.Element;
26
+ declare function EmbeddedCheckout({ className, style }: {
27
+ className?: string;
28
+ style?: React.CSSProperties;
29
+ }): react_jsx_runtime.JSX.Element | null;
30
+
31
+ export { EmbeddedCheckout, type EmbeddedCheckoutOptions, EmbeddedCheckoutProvider };
@@ -0,0 +1,31 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import React from 'react';
3
+
4
+ type EmbeddedCheckoutOptions = {
5
+ /** Base URL of V7 Pay (e.g. https://pay.v7.app) */
6
+ baseUrl: string;
7
+ /** Called to get the session ID. Typically a server action that creates a session and returns it. */
8
+ fetchClientSecret: () => Promise<{
9
+ clientSecret: string;
10
+ }>;
11
+ /** Called when payment succeeds. Optional; postMessage is always sent to parent. */
12
+ onComplete?: (params: {
13
+ sessionId: string;
14
+ }) => void;
15
+ /** Called when user cancels. Optional; postMessage is always sent to parent. */
16
+ onCancel?: (params: {
17
+ sessionId: string;
18
+ }) => void;
19
+ /** If set, only accept postMessage from this origin (e.g. https://your-app.com). Recommended in production. */
20
+ allowedOrigin?: string;
21
+ };
22
+ declare function EmbeddedCheckoutProvider({ options, children, }: {
23
+ options: EmbeddedCheckoutOptions;
24
+ children: React.ReactNode;
25
+ }): react_jsx_runtime.JSX.Element;
26
+ declare function EmbeddedCheckout({ className, style }: {
27
+ className?: string;
28
+ style?: React.CSSProperties;
29
+ }): react_jsx_runtime.JSX.Element | null;
30
+
31
+ export { EmbeddedCheckout, type EmbeddedCheckoutOptions, EmbeddedCheckoutProvider };
package/dist/index.js ADDED
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ "use client";
3
+ var __defProp = Object.defineProperty;
4
+ var __defProps = Object.defineProperties;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
7
+ var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
9
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
11
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
12
+ var __spreadValues = (a, b) => {
13
+ for (var prop in b || (b = {}))
14
+ if (__hasOwnProp.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ if (__getOwnPropSymbols)
17
+ for (var prop of __getOwnPropSymbols(b)) {
18
+ if (__propIsEnum.call(b, prop))
19
+ __defNormalProp(a, prop, b[prop]);
20
+ }
21
+ return a;
22
+ };
23
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
24
+ var __export = (target, all) => {
25
+ for (var name in all)
26
+ __defProp(target, name, { get: all[name], enumerable: true });
27
+ };
28
+ var __copyProps = (to, from, except, desc) => {
29
+ if (from && typeof from === "object" || typeof from === "function") {
30
+ for (let key of __getOwnPropNames(from))
31
+ if (!__hasOwnProp.call(to, key) && key !== except)
32
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
33
+ }
34
+ return to;
35
+ };
36
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
37
+
38
+ // src/index.tsx
39
+ var index_exports = {};
40
+ __export(index_exports, {
41
+ EmbeddedCheckout: () => EmbeddedCheckout,
42
+ EmbeddedCheckoutProvider: () => EmbeddedCheckoutProvider
43
+ });
44
+ module.exports = __toCommonJS(index_exports);
45
+ var import_react = require("react");
46
+ var import_jsx_runtime = require("react/jsx-runtime");
47
+ var EmbeddedCheckoutContext = (0, import_react.createContext)(null);
48
+ function EmbeddedCheckoutProvider({
49
+ options,
50
+ children
51
+ }) {
52
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmbeddedCheckoutContext.Provider, { value: options, children });
53
+ }
54
+ function EmbeddedCheckout({ className, style }) {
55
+ const opts = (0, import_react.useContext)(EmbeddedCheckoutContext);
56
+ const [sessionId, setSessionId] = (0, import_react.useState)(null);
57
+ const [error, setError] = (0, import_react.useState)(null);
58
+ const [loading, setLoading] = (0, import_react.useState)(true);
59
+ const iframeRef = (0, import_react.useRef)(null);
60
+ const handledRef = (0, import_react.useRef)(false);
61
+ const handleMessage = (0, import_react.useCallback)(
62
+ (event) => {
63
+ var _a, _b, _c, _d;
64
+ if (!opts || !sessionId) return;
65
+ if (opts.allowedOrigin && event.origin !== opts.allowedOrigin) return;
66
+ const data = event.data;
67
+ if (typeof data !== "object" || !(data == null ? void 0 : data.type)) return;
68
+ if (data.type === "checkout:success") {
69
+ if (handledRef.current) return;
70
+ handledRef.current = true;
71
+ (_b = opts.onComplete) == null ? void 0 : _b.call(opts, { sessionId: (_a = data.sessionId) != null ? _a : sessionId });
72
+ }
73
+ if (data.type === "checkout:cancel") {
74
+ if (handledRef.current) return;
75
+ handledRef.current = true;
76
+ (_d = opts.onCancel) == null ? void 0 : _d.call(opts, { sessionId: (_c = data.sessionId) != null ? _c : sessionId });
77
+ }
78
+ },
79
+ [opts, sessionId]
80
+ );
81
+ (0, import_react.useEffect)(() => {
82
+ if (!opts) {
83
+ setError("EmbeddedCheckoutProvider is required");
84
+ setLoading(false);
85
+ return;
86
+ }
87
+ const fetch = opts.fetchClientSecret;
88
+ fetch().then(({ clientSecret }) => {
89
+ setSessionId(clientSecret);
90
+ }).catch((err) => {
91
+ var _a;
92
+ setError((_a = err == null ? void 0 : err.message) != null ? _a : "Failed to create checkout session");
93
+ }).finally(() => {
94
+ setLoading(false);
95
+ });
96
+ }, [opts]);
97
+ (0, import_react.useEffect)(() => {
98
+ window.addEventListener("message", handleMessage);
99
+ return () => window.removeEventListener("message", handleMessage);
100
+ }, [handleMessage]);
101
+ if (!opts) {
102
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, style, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: "EmbeddedCheckoutProvider is required" }) });
103
+ }
104
+ if (loading) {
105
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
106
+ "div",
107
+ {
108
+ className,
109
+ style: __spreadProps(__spreadValues({}, style), { display: "flex", alignItems: "center", justifyContent: "center", minHeight: 400 }),
110
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Carregando checkout..." })
111
+ }
112
+ );
113
+ }
114
+ if (error) {
115
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, style, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: error }) });
116
+ }
117
+ if (!sessionId) {
118
+ return null;
119
+ }
120
+ const base = opts.baseUrl.replace(/\/$/, "");
121
+ const iframeSrc = `${base}/checkout/${sessionId}?embed=1`;
122
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
123
+ "iframe",
124
+ {
125
+ ref: iframeRef,
126
+ src: iframeSrc,
127
+ title: "V7 Pay Checkout",
128
+ className,
129
+ style: __spreadValues({ width: "100%", minHeight: 500, border: "none" }, style),
130
+ allow: "payment"
131
+ }
132
+ );
133
+ }
134
+ // Annotate the CommonJS export names for ESM import in node:
135
+ 0 && (module.exports = {
136
+ EmbeddedCheckout,
137
+ EmbeddedCheckoutProvider
138
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,115 @@
1
+ "use client";
2
+ var __defProp = Object.defineProperty;
3
+ var __defProps = Object.defineProperties;
4
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
5
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __spreadValues = (a, b) => {
10
+ for (var prop in b || (b = {}))
11
+ if (__hasOwnProp.call(b, prop))
12
+ __defNormalProp(a, prop, b[prop]);
13
+ if (__getOwnPropSymbols)
14
+ for (var prop of __getOwnPropSymbols(b)) {
15
+ if (__propIsEnum.call(b, prop))
16
+ __defNormalProp(a, prop, b[prop]);
17
+ }
18
+ return a;
19
+ };
20
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
21
+
22
+ // src/index.tsx
23
+ import { createContext, useCallback, useContext, useEffect, useRef, useState } from "react";
24
+ import { jsx } from "react/jsx-runtime";
25
+ var EmbeddedCheckoutContext = createContext(null);
26
+ function EmbeddedCheckoutProvider({
27
+ options,
28
+ children
29
+ }) {
30
+ return /* @__PURE__ */ jsx(EmbeddedCheckoutContext.Provider, { value: options, children });
31
+ }
32
+ function EmbeddedCheckout({ className, style }) {
33
+ const opts = useContext(EmbeddedCheckoutContext);
34
+ const [sessionId, setSessionId] = useState(null);
35
+ const [error, setError] = useState(null);
36
+ const [loading, setLoading] = useState(true);
37
+ const iframeRef = useRef(null);
38
+ const handledRef = useRef(false);
39
+ const handleMessage = useCallback(
40
+ (event) => {
41
+ var _a, _b, _c, _d;
42
+ if (!opts || !sessionId) return;
43
+ if (opts.allowedOrigin && event.origin !== opts.allowedOrigin) return;
44
+ const data = event.data;
45
+ if (typeof data !== "object" || !(data == null ? void 0 : data.type)) return;
46
+ if (data.type === "checkout:success") {
47
+ if (handledRef.current) return;
48
+ handledRef.current = true;
49
+ (_b = opts.onComplete) == null ? void 0 : _b.call(opts, { sessionId: (_a = data.sessionId) != null ? _a : sessionId });
50
+ }
51
+ if (data.type === "checkout:cancel") {
52
+ if (handledRef.current) return;
53
+ handledRef.current = true;
54
+ (_d = opts.onCancel) == null ? void 0 : _d.call(opts, { sessionId: (_c = data.sessionId) != null ? _c : sessionId });
55
+ }
56
+ },
57
+ [opts, sessionId]
58
+ );
59
+ useEffect(() => {
60
+ if (!opts) {
61
+ setError("EmbeddedCheckoutProvider is required");
62
+ setLoading(false);
63
+ return;
64
+ }
65
+ const fetch = opts.fetchClientSecret;
66
+ fetch().then(({ clientSecret }) => {
67
+ setSessionId(clientSecret);
68
+ }).catch((err) => {
69
+ var _a;
70
+ setError((_a = err == null ? void 0 : err.message) != null ? _a : "Failed to create checkout session");
71
+ }).finally(() => {
72
+ setLoading(false);
73
+ });
74
+ }, [opts]);
75
+ useEffect(() => {
76
+ window.addEventListener("message", handleMessage);
77
+ return () => window.removeEventListener("message", handleMessage);
78
+ }, [handleMessage]);
79
+ if (!opts) {
80
+ return /* @__PURE__ */ jsx("div", { className, style, children: /* @__PURE__ */ jsx("p", { children: "EmbeddedCheckoutProvider is required" }) });
81
+ }
82
+ if (loading) {
83
+ return /* @__PURE__ */ jsx(
84
+ "div",
85
+ {
86
+ className,
87
+ style: __spreadProps(__spreadValues({}, style), { display: "flex", alignItems: "center", justifyContent: "center", minHeight: 400 }),
88
+ children: /* @__PURE__ */ jsx("span", { children: "Carregando checkout..." })
89
+ }
90
+ );
91
+ }
92
+ if (error) {
93
+ return /* @__PURE__ */ jsx("div", { className, style, children: /* @__PURE__ */ jsx("p", { children: error }) });
94
+ }
95
+ if (!sessionId) {
96
+ return null;
97
+ }
98
+ const base = opts.baseUrl.replace(/\/$/, "");
99
+ const iframeSrc = `${base}/checkout/${sessionId}?embed=1`;
100
+ return /* @__PURE__ */ jsx(
101
+ "iframe",
102
+ {
103
+ ref: iframeRef,
104
+ src: iframeSrc,
105
+ title: "V7 Pay Checkout",
106
+ className,
107
+ style: __spreadValues({ width: "100%", minHeight: 500, border: "none" }, style),
108
+ allow: "payment"
109
+ }
110
+ );
111
+ }
112
+ export {
113
+ EmbeddedCheckout,
114
+ EmbeddedCheckoutProvider
115
+ };
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@v7-pay/react",
3
+ "version": "0.1.0",
4
+ "description": "React components for embedding V7 Pay checkout (Stripe Embedded Checkout style)",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": ["dist"],
16
+ "scripts": {
17
+ "build": "tsup src/index.tsx --format cjs,esm --dts --clean --tsconfig tsconfig.json"
18
+ },
19
+ "peerDependencies": {
20
+ "react": ">=18.0.0"
21
+ },
22
+ "devDependencies": {
23
+ "@types/react": "^19",
24
+ "react": "^19",
25
+ "tsup": "^8"
26
+ }
27
+ }