Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/stripe/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.env*
!.env.example
**/*.log
coverage
dist
dist-ssr
node_modules
19 changes: 19 additions & 0 deletions packages/stripe/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import reactConfig from "@prefabs.tech/eslint-config/react.js";

export default [
...reactConfig,
{
rules: {
"unicorn/filename-case": [
"error",
{
cases: {
camelCase: true,
kebabCase: true,
pascalCase: true,
},
},
],
},
},
];
84 changes: 84 additions & 0 deletions packages/stripe/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
{
"name": "@prefabs.tech/react-stripe",
"version": "0.72.1",
"description": "React Stripe Plugin",
"type": "module",
"exports": {
".": {
"types": "./dist/src/index.d.ts",
"import": "./dist/PrefabsTechReactStripe.es.js",
"require": "./dist/PrefabsTechReactStripe.umd.js"
},
"./dist/PrefabsTechReactStripe.css": "./dist/react-stripe.css"
},
"main": "./dist/PrefabsTechReactStripe.umd.js",
"module": "./dist/PrefabsTechReactStripe.es.js",
"types": "./dist/src/index.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "vite build && tsc --emitDeclarationOnly",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"snapshot:update": "vitest --environment jsdom run --update --passWithNoTests",
"sort-package": "npx sort-package-json",
"stylelint": "stylelint \"src/**/*.{css}\" --allow-empty-input",
"stylelint:fix": "stylelint \"src/**/*.{css}\" --fix --allow-empty-input",
"test": "pnpm build && vitest --environment jsdom run --coverage --passWithNoTests",
"test:component": "vitest --environment jsdom run component/ --passWithNoTests",
"test:snapshot": "vitest --environment jsdom run snapshot/ --passWithNoTests",
"test:unit": "vitest --environment jsdom run unit/ --passWithNoTests",
"test:watch": "vitest --environment jsdom",
"typecheck": "tsc --noEmit -p tsconfig.vitest.json --composite false"
},
"dependencies": {},
"devDependencies": {
"@prefabs.tech/eslint-config": "0.7.0",
"@prefabs.tech/react-i18n": "0.72.1",
"@prefabs.tech/react-ui": "0.72.1",
"@prefabs.tech/react-user": "0.72.1",
"@prefabs.tech/stylelint-config": "0.7.0",
"@prefabs.tech/tsconfig": "0.7.0",
"@testing-library/dom": "10.4.1",
"@testing-library/react": "16.3.2",
"@testing-library/user-event": "14.6.1",
"@types/jsdom": "21.1.7",
"@types/node": "25.6.0",
"@types/react": "18.3.28",
"@types/react-dom": "18.3.7",
"@vitejs/plugin-react": "5.2.0",
"@vitest/coverage-v8": "3.2.4",
"axios": "1.16.0",
"eslint": "9.39.4",
"jsdom": "27.4.0",
"prettier": "3.8.3",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-router-dom": "6.30.3",
"stylelint": "17.10.0",
"stylelint-config-standard": "40.0.0",
"stylelint-order": "8.1.1",
"typescript": "5.9.3",
"vite": "7.3.2",
"vitest": "3.2.4"
},
"peerDependencies": {
"@prefabs.tech/react-i18n": "0.72.1",
"@prefabs.tech/react-ui": "0.72.1",
"@prefabs.tech/react-user": "0.72.1",
"axios": "1.16.0",
"react": ">=18.3",
"react-dom": ">=18.3",
"react-router-dom": ">=6.30"
},
"peerDependenciesMeta": {
"@prefabs.tech/react-user": {
"optional": true
}
},
"engines": {
"node": ">=18",
"pnpm": ">=9"
}
}
30 changes: 30 additions & 0 deletions packages/stripe/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { AxiosInstance } from "axios";

import { create } from "axios";

import type { StripeConfig } from "../types";

export const getAxiosClient = async (
apiBaseUrl: string,
config?: StripeConfig,
): Promise<AxiosInstance> => {
if (config?.axiosClient) {
return config.axiosClient(apiBaseUrl);
}

try {
const { axiosClient } = await import("@prefabs.tech/react-user");

return axiosClient(apiBaseUrl);
} catch {
return create({
baseURL: apiBaseUrl,
headers: {
"Content-Type": "application/json",
},
withCredentials: true,
});
}
};

export { checkoutSession, getStatus } from "./payment";
37 changes: 37 additions & 0 deletions packages/stripe/src/api/payment/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { CheckoutSessionPayload, StripeConfig } from "../../types";

import { getAxiosClient } from "..";
import { API_PATH_CHECKOUT_SESSION, API_PATH_STATUS } from "../../constants";

export const checkoutSession = async (
payload: CheckoutSessionPayload,
apiBaseUrl: string,
config?: StripeConfig,
) => {
const path = config?.apiRoutes?.checkoutSession || API_PATH_CHECKOUT_SESSION;

const client = await getAxiosClient(apiBaseUrl, config);
const response = await client.post(path, payload);

if ("error" in response.data) {
throw new Error(response.data);
}

const redirectUrl = response.data.url as string;
window.location.href = redirectUrl;

return response.data;
};

export const getStatus = async (apiBaseUrl: string, config?: StripeConfig) => {
const path = config?.apiRoutes?.status || API_PATH_STATUS;

const client = await getAxiosClient(apiBaseUrl, config);
const response = await client.get(path);

if ("error" in response.data) {
throw new Error(response.data);
}

return response.data;
};
95 changes: 95 additions & 0 deletions packages/stripe/src/assets/css/cancelled-page.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
.cancelled-page[data-centered="true"] {
--_page-max-width: 30rem;
}

.cancelled-page .dz-card {
animation: fadeInUp 0.6s ease-out;
display: flex;
flex-direction: column;
gap: 1.5rem;
max-width: 30rem;
padding: 2rem 1.5rem;
text-align: center;
width: 100%;
}

.cancelled-page .crossmark {
margin: 0 auto 0.5rem;
stroke-width: 2;
width: 5rem;
}

.cancelled-page .crossmark-circle {
animation: stroke 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards;
stroke: #f44336;
stroke-dasharray: 166;
stroke-dashoffset: 166;
stroke-width: 2;
}

.cancelled-page .crossmark-line {
animation: stroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.6s forwards;
stroke: var(--dz-danger-color, #dc3545);
stroke-dasharray: 28;
stroke-dashoffset: 28;
stroke-linecap: round;
stroke-width: 3;
transform-origin: 50% 50%;
}

.cancelled-page h1.title {
color: var(--dz-secondary-color, #475569);
font-size: 1.5rem;
font-weight: 700;
margin-block: 1rem;
}

.cancelled-page .message {
color: var(--dz-secondary-color, #475569);;
display: flex;
flex-direction: column;
font-size: 1rem;
gap: 0.5rem;
line-height: 1.5;
margin-block: auto;
}

.cancelled-page .message p {
margin: 0;
}

.cancelled-page button {
min-width: 12rem;
}

@keyframes stroke {
100% {
stroke-dashoffset: 0;
}
}

@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}

to {
opacity: 1;
transform: translateY(0);
}
}

@media (width >= 576px) {
.cancelled-page .dz-card {
padding: 3rem 2rem;
}

.cancelled-page .crossmark {
width: 7rem;
}

.cancelled-page h1.title {
font-size: 2rem;
}
}
2 changes: 2 additions & 0 deletions packages/stripe/src/assets/css/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@import url("./cancelled-page.css");
@import url("./success-page.css");
106 changes: 106 additions & 0 deletions packages/stripe/src/assets/css/success-page.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
.success-page[data-centered="true"] {
--_page-max-width: 30rem;
}

.success-page .dz-card {
animation: fadeInUp 0.6s ease-out;
display: flex;
flex-direction: column;
gap: 1.5rem;
max-width: 30rem;
padding: 2rem 1.5rem;
text-align: center;
width: 100%;
}

.success-page .checkmark {
margin: 0 auto 0.5rem;
stroke-width: 2;
width: 5rem;
}

.success-page .checkmark-circle {
animation: stroke 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards;
stroke: var(--dz-success-color, #22c55e);
stroke-dasharray: 166;
stroke-dashoffset: 166;
stroke-width: 2;
}

.success-page .checkmark-check {
animation: stroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.6s forwards;
stroke: var(--dz-success-color, #22c55e);
stroke-dasharray: 48;
stroke-dashoffset: 48;
stroke-linecap: round;
stroke-width: 3;
transform-origin: 50% 50%;
}

.success-page h1.title {
color: var(--dz-secondary-color, #475569);;
font-size: 1.5rem;
font-weight: 700;
margin-block: 1rem;
}

.success-page p.subtitle {
color: #4caf50;
font-size: 1.125rem;
font-weight: 600;
margin: 0;
}

.success-page .message {
color: #666;
display: flex;
flex-direction: column;
font-size: 1rem;
gap: 0.5rem;
line-height: 1.5;
margin-block: auto;
}

.success-page .message p {
margin: 0;
}

.success-page button {
min-width: 12rem;
}

@keyframes stroke {
100% {
stroke-dashoffset: 0;
}
}

@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}

to {
opacity: 1;
transform: translateY(0);
}
}

@media (width >= 576px) {
.success-page .card {
padding: 3rem 2rem;
}

.success-page .checkmark {
width: 7rem;
}

.success-page h1.title {
font-size: 2rem;
}

.success-page p.subtitle {
font-size: 1.25rem;
}
}
Loading
Loading