AW-22-deleteChargebee

This commit is contained in:
Денис Катаев 2024-04-19 16:30:40 +00:00 committed by Daniil Chemerkin
parent d77a21efc0
commit b5aabf16cc
37 changed files with 2 additions and 675 deletions

View File

@ -89,7 +89,6 @@
<img src="/leo.png" alt="Aura - Energy of your Horoscope" />
</div>
</div>
<script src="https://js.chargebee.com/v2/chargebee.js"></script>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

29
package-lock.json generated
View File

@ -8,7 +8,6 @@
"name": "aurawebapp",
"version": "0.0.0",
"dependencies": {
"@chargebee/chargebee-js-react-wrapper": "^0.6.3",
"@reduxjs/toolkit": "^1.9.5",
"@smakss/react-scroll-direction": "^4.0.4",
"@stripe/react-stripe-js": "^2.3.1",
@ -30,7 +29,6 @@
"unique-names-generator": "^4.7.1"
},
"devDependencies": {
"@chargebee/chargebee-js-types": "^1.0.1",
"@types/node": "^20.5.1",
"@types/react": "^18.2.6",
"@types/react-dom": "^18.2.4",
@ -417,21 +415,6 @@
"node": ">=6.9.0"
}
},
"node_modules/@chargebee/chargebee-js-react-wrapper": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/@chargebee/chargebee-js-react-wrapper/-/chargebee-js-react-wrapper-0.6.3.tgz",
"integrity": "sha512-U3KJLZZiUxxkW4HAkAZr2bs6r4HqL1S59zo3AAp4UYtJZbxmBpfX5e/MlsSENvsRxd2GvOpumcHr8rR4V0D5vw==",
"peerDependencies": {
"react": "^18.0.0 || ^17.0.0",
"react-dom": "^18.0.0 || ^17.0.0"
}
},
"node_modules/@chargebee/chargebee-js-types": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@chargebee/chargebee-js-types/-/chargebee-js-types-1.0.1.tgz",
"integrity": "sha512-JsHlIAjZDwX2Q/vDGN4xzKRC8n1K4xCwzKl7wZOOwUH9ow030CRspVRkP3OWHrY5gLmpbmICc/iK2aptF3t/Ow==",
"dev": true
},
"node_modules/@emotion/is-prop-valid": {
"version": "0.8.8",
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
@ -3865,18 +3848,6 @@
"to-fast-properties": "^2.0.0"
}
},
"@chargebee/chargebee-js-react-wrapper": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/@chargebee/chargebee-js-react-wrapper/-/chargebee-js-react-wrapper-0.6.3.tgz",
"integrity": "sha512-U3KJLZZiUxxkW4HAkAZr2bs6r4HqL1S59zo3AAp4UYtJZbxmBpfX5e/MlsSENvsRxd2GvOpumcHr8rR4V0D5vw==",
"requires": {}
},
"@chargebee/chargebee-js-types": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@chargebee/chargebee-js-types/-/chargebee-js-types-1.0.1.tgz",
"integrity": "sha512-JsHlIAjZDwX2Q/vDGN4xzKRC8n1K4xCwzKl7wZOOwUH9ow030CRspVRkP3OWHrY5gLmpbmICc/iK2aptF3t/Ow==",
"dev": true
},
"@emotion/is-prop-valid": {
"version": "0.8.8",
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",

View File

@ -10,7 +10,6 @@
"preview": "vite preview"
},
"dependencies": {
"@chargebee/chargebee-js-react-wrapper": "^0.6.3",
"@reduxjs/toolkit": "^1.9.5",
"@smakss/react-scroll-direction": "^4.0.4",
"@stripe/react-stripe-js": "^2.3.1",
@ -32,7 +31,6 @@
"unique-names-generator": "^4.7.1"
},
"devDependencies": {
"@chargebee/chargebee-js-types": "^1.0.1",
"@types/node": "^20.5.1",
"@types/react": "^18.2.6",
"@types/react-dom": "^18.2.4",

View File

@ -13,7 +13,6 @@ import {
SubscriptionCheckout,
SubscriptionReceipts,
SubscriptionStatus,
PaymentIntents,
AICompatCategories,
AICompats,
AIRequests,
@ -50,7 +49,6 @@ const api = {
getSubscriptionStatus: createMethod<SubscriptionStatus.Payload, SubscriptionStatus.Response>(SubscriptionStatus.createRequest),
getSubscriptionReceipt: createMethod<SubscriptionReceipts.GetPayload, SubscriptionReceipts.Response>(SubscriptionReceipts.createGetRequest),
createSubscriptionReceipt: createMethod<SubscriptionReceipts.Payload, SubscriptionReceipts.Response>(SubscriptionReceipts.createRequest),
createPaymentIntent: createMethod<PaymentIntents.Payload, PaymentIntents.Response>(PaymentIntents.createRequest),
getAiCompatCategories: createMethod<AICompatCategories.Payload, AICompatCategories.Response>(AICompatCategories.createRequest),
getAiCompat: createMethod<AICompats.Payload, AICompats.Response>(AICompats.createRequest),
getAiRequest: createMethod<AIRequests.Payload, AIRequests.Response>(AIRequests.createRequest),

View File

@ -13,7 +13,6 @@ export interface Response {
active_iaps: ActiveIAps[]
version: string
apple_music_api: AppleMusicApi
chargebee: ChargebeeConfig
first_open_subscription_popup: boolean
runs_before_subscription_popup: number
appirater_alerts: AppiraterAlertAppiraterAlert[]
@ -53,12 +52,6 @@ export interface AppleMusicApi {
jwt: string
}
export interface ChargebeeConfig {
site: string
publishableKey: string
gatewayAccountId: string
}
export const createRequest = ({ bundleId }: Payload): Request => {
const url = new URL(routes.server.apps(bundleId))
return new Request(url, { method: 'GET', headers: getBaseHeaders() })

View File

@ -1,49 +0,0 @@
import { PaymentIntent } from '@chargebee/chargebee-js-types'
import { AuthPayload } from '../types'
import { getAuthHeaders } from '../utils'
import routes from '@/routes'
export interface Payload extends AuthPayload {
paymentMethod: PaymentMethod
currencyCode: CurrencyCode
}
export interface Response {
payment_intent: PaymentIntent
customer: Customer
}
export type PaymentMethod = 'apple_pay' | 'google_pay' | 'card'
export type CurrencyCode = 'USD'
export interface Customer {
id: string
email: string
auto_collection: 'on' | 'off'
net_term_days: number
allow_direct_debit: boolean
taxability: 'taxable' | 'exempt'
created_at: number
updated_at: number
pii_cleared: 'active' | 'scheduled_for_clear' | 'cleared'
channel: 'web' | 'app_store' | 'play_store'
resource_version: number
deleted: boolean
card_status: 'no_card' | 'valid' | 'expiring' | 'expired'
promotional_credits: number
refundable_credits: number
excess_payments: number
unbilled_charges: number
preferred_currency_code: CurrencyCode
}
export const createRequest = ({ token, paymentMethod, currencyCode }: Payload): Request => {
const url = new URL(routes.server.paymentIntents())
const body = JSON.stringify({
payment_intent: {
payment_method_type: paymentMethod,
currency_code: currencyCode,
}
})
return new Request(url, { method: 'POST', headers: getAuthHeaders(token), body })
}

View File

@ -6,12 +6,6 @@ export interface GetPayload extends AuthPayload {
id: string;
}
export interface ChargebeeReceiptPayload extends AuthPayload {
itemPriceId: string;
gwToken: string;
referenceId?: string;
}
export interface AppleReceiptPayload extends AuthPayload {
receiptData: string;
autorenewable?: boolean;
@ -33,7 +27,6 @@ export interface PayPalReceiptPayload extends AuthPayload {
}
export type Payload =
| ChargebeeReceiptPayload
| AppleReceiptPayload
| StripeReceiptPayload
| PayPalReceiptPayload;
@ -78,12 +71,6 @@ interface IPayPalLink {
method: "GET" | "PATCH";
}
function createRequest({
token,
itemPriceId,
gwToken,
referenceId,
}: ChargebeeReceiptPayload): Request;
function createRequest({
token,
receiptData,
@ -104,16 +91,6 @@ function createRequest(payload: Payload): Request {
}
function getDataPayload(payload: Payload) {
if ("itemPriceId" in payload && "gwToken" in payload) {
return {
way: "chargebee",
subscription_receipt: {
item_price_id: payload.itemPriceId,
gw_token: payload.gwToken,
reference_id: payload.referenceId,
},
};
}
if ("receiptData" in payload) {
return {
way: "apple",

View File

@ -11,7 +11,6 @@ export * as SubscriptionItems from "./UserSubscriptionItemPrices";
export * as SubscriptionCheckout from "./UserSubscriptionCheckout";
export * as SubscriptionStatus from "./UserSubscriptionStatus";
export * as SubscriptionReceipts from "./UserSubscriptionReceipts";
export * as PaymentIntents from "./UserPaymentIntents";
export * as AICompatCategories from "./AICompatCategories";
export * as AICompats from "./AICompats";
export * as AIRequests from "./AIRequests";

View File

@ -1,74 +1,15 @@
import { usePayment } from "@/payment";
import routes from "@/routes";
import { selectors } from "@/store";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import Header from "../Header";
import Loader from "../Loader";
import Title from "../Title";
import UserHeader from "../UserHeader";
import ErrorModal from "./ErrorModal";
import { ApplePayBanner, GooglePayBanner } from "./methods";
import { StripeButton } from "./methods/Stripe";
import secure from "./secure.png";
import "./styles.css";
// import { PayPalButton } from "./methods/PayPal/Button";
// import { useAuth } from "@/auth";
// import { useApi } from "@/api";
// import { PayPalReceiptPayload } from "@/api/resources/UserSubscriptionReceipts";
import { ISubscriptionPlan } from "@/api/resources/SubscriptionPlans";
const getPrice = (activeSubPlan: ISubscriptionPlan | null) => {
if (!activeSubPlan) return 0;
return String(
activeSubPlan?.trial?.price_cents
? activeSubPlan.trial.price_cents / 100
: 0
);
};
function PaymentPage(): JSX.Element {
const { t } = useTranslation();
const { applePay } = usePayment();
// const api = useApi();
// const { token } = useAuth();
const [openErrorModal, setOpenErrorModal] = useState(false);
const navigate = useNavigate();
const isLoading = applePay === null;
const isApplePayAvailable =
import.meta.env.PROD && applePay?.canMakePayments();
const email = useSelector(selectors.selectEmail);
const activeSubPlan = useSelector(selectors.selectActiveSubPlan);
// Do not allow to go to this page if there is no other payment methods
useEffect(() => {
// Have only Stripe for now
navigate(routes.client.paymentStripe());
}, []);
const navigateToStripe = () => {
navigate(routes.client.paymentStripe());
};
// const navigateToPayPal = async () => {
// const { subscription_receipt } = await api.createSubscriptionReceipt({
// token,
// itemInterval: "year",
// way: "paypal",
// subscription_receipt: {
// sub_plan_id: activeSubPlan?.id || "",
// },
// } as PayPalReceiptPayload);
// const url = subscription_receipt.data.links?.find(
// (link) => link.rel === "approve"
// )?.href;
// if (!url?.length) {
// return setOpenErrorModal(true);
// }
// window.location.href = url;
// };
return (
<>
@ -78,38 +19,7 @@ function PaymentPage(): JSX.Element {
/>
<UserHeader email={email} />
<section className="page">
{isLoading ? (
<Loader />
) : (
<>
<div className="page-header">
{isApplePayAvailable ? <ApplePayBanner /> : <GooglePayBanner />}
<img src={secure} alt="100% Secure" />
</div>
<Title variant="h1" className="mb-45">
{t("choose_payment")}
</Title>
<div className="payment-buttons-container">
{/* <PayPalButton onClick={navigateToPayPal} /> */}
<StripeButton onClick={navigateToStripe} />
</div>
<p className="payment-warining">
{t("will_be_charged", {
strongText: (
<strong>
{t("trial_price", {
price: getPrice(activeSubPlan || null),
})}
</strong>
),
})}
</p>
<ErrorModal
open={openErrorModal}
onClose={() => setOpenErrorModal(false)}
/>
</>
)}
</section>
</>
);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

View File

@ -1,5 +0,0 @@
import applePaySafeCheckout from './Apple-Pay.png'
export function ApplePayBanner (): JSX.Element {
return <img src={applePaySafeCheckout} alt='Guaranteed safe checkout' />
}

View File

@ -1,67 +0,0 @@
import { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { PaymentIntent } from '@chargebee/chargebee-js-types'
import { useApi, useApiCall, extractErrorMessage, SubscriptionReceipts } from '@/api'
import { usePayment, ApplePayButtonOptions } from '@/payment'
import { useAuth } from '@/auth'
import Loader, { LoaderColor } from '@/components/Loader'
import ErrorText from '@/components/ErrorText'
const currencyCode = 'USD'
const paymentMethod = 'apple_pay'
const buttonId = 'apple-pay-btn'
interface ApplePayButtonProps {
onSuccess: (receipt: SubscriptionReceipts.SubscriptionReceipt) => void
onError: (error: Error) => void
}
export function ApplePayButton({ onSuccess, onError }: ApplePayButtonProps): JSX.Element {
const api = useApi()
const { i18n } = useTranslation()
const { token } = useAuth()
const { applePay } = usePayment()
const [isMounting, setIsMounting] = useState<boolean>(true)
const loadData = useCallback(() => {
return api.createPaymentIntent({ token, paymentMethod, currencyCode })
.then(({ payment_intent }) => payment_intent)
}, [api, token])
const { data, error, isPending } = useApiCall<PaymentIntent>(loadData)
if (error) console.error(error)
useEffect(() => {
if (data === null) return
const buttonOptions: ApplePayButtonOptions = {
buttonColor: 'black',
buttonType: 'plain',
locale: i18n.language
}
applePay?.setPaymentIntent(data)
applePay?.mountPaymentButton(`#${buttonId}`, buttonOptions)
.then(() => setIsMounting(false))
.then(() => applePay?.handlePayment())
.then((paymentIntent) => {
return api.createSubscriptionReceipt({
token, receiptData: paymentIntent.id, autorenewable: true, sandbox: true,
})
})
.then(({ subscription_receipt }: SubscriptionReceipts.Response) => onSuccess(subscription_receipt))
.catch((error: Error) => onError(error))
}, [data, applePay, i18n.language, api, token, onSuccess, onError])
return (
<div id={buttonId} className='pay-btn'>
{isPending || isMounting ? <FakeApplePayButton /> : null}
{error ? <ErrorText message={extractErrorMessage(error)} isShown={true} size='large'/> : null}
</div>
)
}
function FakeApplePayButton() {
return (
<div className='apple-pay-button-placeholder'>
<Loader color={LoaderColor.White} />
</div>
)
}

View File

@ -1,2 +0,0 @@
export * from './Banner'
export * from './Button'

View File

@ -1,18 +0,0 @@
import { useTranslation } from 'react-i18next'
import MainButton from '@/components/MainButton'
import card from './card.svg'
interface CardButtonProps {
onClick: () => void
}
export function CardButton({ onClick }: CardButtonProps): JSX.Element {
const { t } = useTranslation()
return (
<MainButton color='blue' onClick={onClick}>
<img className='payment-card' src={card} alt='Credit / Debit Card' />
{t('card')}
</MainButton>
)
}

View File

@ -1,146 +0,0 @@
import { useCallback, useEffect, useRef, useState, ChangeEvent } from 'react'
import { useTranslation } from 'react-i18next'
import {
CardCVV, CardComponent, CardExpiry, CardNumber, Provider
} from '@chargebee/chargebee-js-react-wrapper'
import ChargebeeComponents from '@chargebee/chargebee-js-react-wrapper/dist/components/ComponentGroup'
import { PaymentIntent } from '@chargebee/chargebee-js-types'
import { usePayment } from '@/payment'
import { useApi, SubscriptionReceipts, useApiCall } from '@/api'
import { useAuth } from '@/auth'
import Modal from '@/components/Modal'
import Title from '@/components/Title'
import MainButton from '@/components/MainButton'
import Loader from '@/components/Loader'
import visa from './visa.svg'
import mastercard from './mastercard.svg'
import amex from './amex.svg'
import diners from './diners.svg'
import discover from './discover.svg'
import { cardStyles } from './styles'
interface CardModalProps {
open: boolean
onClose: () => void
onSuccess: (receipt: SubscriptionReceipts.SubscriptionReceipt) => void
onError: (error: Error) => void
}
interface Field {
cardType: string | undefined
complete: boolean
empty: boolean
error: Error | undefined
field: 'number' | 'expiry' | 'cvv'
key: string | undefined
type: string
}
type Status = 'idle' | 'loading' | 'filling' | 'subscribing' | 'ready' | 'success' | 'error'
const initCompletedFields = {
number: false,
expiry: false,
cvv: false,
}
type CompletedFields = typeof initCompletedFields
const isReady = (fields: CompletedFields) => Object.values(fields).every((complete: boolean) => complete)
const currencyCode = 'USD'
const paymentMethod = 'card'
const itemPriceId = 'aura-membership-2-week-USD'
export function CardModal({ open, onClose, onSuccess, onError }: CardModalProps): JSX.Element {
const api = useApi()
const cardRef = useRef<ChargebeeComponents>(null)
const [status, setStatus] = useState<Status>('idle')
const [fields, setFields] = useState(initCompletedFields)
const { token, user } = useAuth()
const { t, i18n } = useTranslation()
const locale = i18n.language
const isInit = status === 'idle'
const isLoading = status === 'loading'
const { chargebee } = usePayment()
const loadData = useCallback(() => {
return api.createPaymentIntent({ token, paymentMethod, currencyCode })
.then(({ payment_intent }) => payment_intent)
}, [api, token])
const { data, error } = useApiCall<PaymentIntent>(loadData)
const handleReady = () => setStatus('filling')
const handleClose = () => {
setStatus('loading')
onClose()
}
const handleChange = ({ field, complete, error }: ChangeEvent & Field) => {
setFields((state) => ({ ...state, [field]: complete && !error }))
}
const payWithCard = () => {
if (data === null) return
setStatus('subscribing')
cardRef.current?.authorizeWith3ds(data, { email: user?.email }, {})
.then((paymentIntent: PaymentIntent) => {
return api.createSubscriptionReceipt({ token, itemPriceId, gwToken: paymentIntent.id })
})
.then(({ subscription_receipt }: SubscriptionReceipts.Response) => {
setStatus('success')
onSuccess(subscription_receipt)
})
.catch((error: Error) => {
setStatus('error')
onError(error)
})
}
if (error) console.error(error)
useEffect(() => {
if (status !== 'filling' && status !== 'ready' && status !== 'error') return
setStatus(isReady(fields) ? 'ready' : 'filling')
}, [fields, status])
useEffect(() => {
if (isInit) setStatus('loading')
}, [isInit])
return (
<Modal open={open} onClose={handleClose}>
{ isLoading ? <div className='payment-loader'><Loader /></div> : null}
<div style={{ display: isLoading ? 'none' : 'block' }}>
<div className='payment-modal-header'>
<Title variant='h3' className='mb-0'>{t('card')}</Title>
<div className='payment-card-list'>
<img src={visa} alt='Visa Card' />
<img src={mastercard} alt='Mastercard Card' />
<img src={amex} alt='Amex Card' />
<img src={diners} alt='Diners Card' />
<img src={discover} alt='Discover Card' />
</div>
</div>
<Provider cbInstance={chargebee}>
<CardComponent ref={cardRef} locale={locale} styles={cardStyles} onReady={handleReady}>
<div className="payment-input"><CardNumber onChange={handleChange} /></div>
<div className='payment-group'>
<div className="payment-input"><CardExpiry onChange={handleChange} /></div>
<div className="payment-input"><CardCVV onChange={handleChange} /></div>
</div>
</CardComponent>
</Provider>
<p className='payment-inforamtion'>{t('charged_only')}</p>
<MainButton color='blue' onClick={payWithCard} disabled={status !== 'ready'}>
{ status === 'subscribing' ? <Loader /> : <><LockIcon />{t('start_trial')}</> }
</MainButton>
</div>
</Modal>
)
}
function LockIcon(): JSX.Element {
return (
<svg width="13" height="16" viewBox="0 0 13 16" xmlns="http://www.w3.org/2000/svg">
<path d="M11.5556 6.24219H1.44444C0.6467 6.24219 0 6.97481 0 7.87855V13.6058C0 14.5096 0.6467 15.2422 1.44444 15.2422H11.5556C12.3533 15.2422 13 14.5096 13 13.6058V7.87855C13 6.97481 12.3533 6.24219 11.5556 6.24219Z"></path>
<path fillRule="evenodd" clipRule="evenodd" d="M6.5 0.242188C4.29086 0.242188 2.5 2.03305 2.5 4.24219V8.24219H10.5V4.24219C10.5 2.03305 8.70914 0.242188 6.5 0.242188ZM6.5 1.24219C4.84315 1.24219 3.5 2.58533 3.5 4.24219V7.24219H9.5V4.24219C9.5 2.58533 8.15685 1.24219 6.5 1.24219Z"></path>
</svg>
)
}

View File

@ -1,4 +0,0 @@
<svg width="40" height="26" viewBox="0 0 40 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1.14258" y="0.50708" width="38.2857" height="24" rx="3.5" fill="#1F72CD" stroke="#C7C7C7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.31366 8.86157L6 16.4103H9.96691L10.4587 15.2067H11.5828L12.0746 16.4103H16.441V15.4917L16.8301 16.4103H19.0888L19.4779 15.4722V16.4103H28.5589L29.6631 15.2379L30.6971 16.4103L35.3613 16.42L32.0371 12.657L35.3613 8.86157H30.7694L29.6945 10.0122L28.6931 8.86157H18.8141L17.9658 10.81L17.0976 8.86157H13.139V9.74892L12.6986 8.86157H9.31366ZM10.0812 9.93314H12.0149L14.2128 15.0519V9.93314H16.3311L18.0287 13.6033L19.5933 9.93314H21.701V15.3498H20.4185L20.408 11.1053L18.5383 15.3498H17.3911L15.5109 11.1053V15.3498H12.8726L12.3724 14.1355H9.67018L9.17103 15.3488H7.75745L10.0812 9.93314ZM28.0891 9.93314H22.8743V15.3466H28.0084L29.6631 13.5525L31.2581 15.3466H32.9254L30.502 12.6556L32.9254 9.93314H31.3304L29.6841 11.7067L28.0891 9.93314ZM11.0218 10.8504L10.1315 13.0137H11.911L11.0218 10.8504ZM24.1622 12.0433V11.0545V11.0536H27.416L28.8358 12.6349L27.3531 14.2249H24.1622V13.1454H27.0071V12.0433H24.1622Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,5 +0,0 @@
<svg width="23" height="16" viewBox="0 0 23 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" width="22" height="16" rx="2" fill="white"/>
<rect x="0.5" y="2.66406" width="22" height="2.66667" fill="#9FB8FF"/>
<rect x="3" y="7.35938" width="17" height="2" rx="1" fill="#CEDBFF"/>
</svg>

Before

Width:  |  Height:  |  Size: 303 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1,11 +0,0 @@
<svg width="40" height="25" viewBox="0 0 40 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.927734" y="0.5" width="38.2857" height="24" rx="3.5" fill="white" stroke="#C7C7C7"/>
<path d="M21.7056 14.5831C23.1438 14.5831 24.3097 13.4173 24.3097 11.9791C24.3097 10.5409 23.1438 9.375 21.7056 9.375C20.2674 9.375 19.1016 10.5409 19.1016 11.9791C19.1016 13.4173 20.2674 14.5831 21.7056 14.5831Z" fill="#F26E21"/>
<path d="M7.68056 13.2265C7.37371 13.5012 6.97776 13.6203 6.34895 13.6203H6.08805V10.3509H6.34895C6.97767 10.3509 7.35801 10.4622 7.68056 10.7509C8.0168 11.0475 8.21741 11.5066 8.21741 11.981C8.21741 12.4556 8.0168 12.9309 7.68056 13.2265ZM6.54397 9.51367H5.11523V14.4568H6.53679C7.2912 14.4568 7.83649 14.2799 8.31496 13.8862C8.88246 13.4194 9.21937 12.7153 9.21937 11.9875C9.21929 10.5281 8.11994 9.51367 6.54397 9.51367Z" fill="#0B161F"/>
<path d="M9.66602 14.4568H10.6373V9.51367H9.66602V14.4568Z" fill="#0B161F"/>
<path d="M13.0165 11.4102C12.4328 11.1954 12.2609 11.0542 12.2609 10.7886C12.2609 10.477 12.5668 10.2398 12.9859 10.2398C13.2775 10.2398 13.5162 10.3583 13.7715 10.6392L14.2787 9.98043C13.8607 9.61672 13.36 9.43164 12.8138 9.43164C11.9325 9.43164 11.2593 10.0395 11.2593 10.8463C11.2593 11.5292 11.5728 11.8773 12.4853 12.2042C12.8665 12.337 13.0603 12.4254 13.1578 12.4857C13.3519 12.612 13.4497 12.7891 13.4497 12.9967C13.4497 13.3976 13.1281 13.6935 12.694 13.6935C12.2306 13.6935 11.8572 13.4642 11.6327 13.0342L11.0059 13.6351C11.4533 14.2863 11.9915 14.5766 12.7321 14.5766C13.7408 14.5766 14.4505 13.9085 14.4505 12.9529C14.451 12.1667 14.1228 11.8108 13.0165 11.4102Z" fill="#0B161F"/>
<path d="M14.7578 11.9872C14.7578 13.4416 15.9091 14.5682 17.3891 14.5682C17.8079 14.5682 18.166 14.486 18.6076 14.2797V13.1444C18.2185 13.5304 17.8744 13.6857 17.4335 13.6857C16.4544 13.6857 15.7591 12.9813 15.7591 11.9804C15.7591 11.032 16.4766 10.2829 17.3891 10.2829C17.8516 10.2829 18.2035 10.4457 18.6076 10.8389V9.70445C18.1819 9.49006 17.8298 9.40161 17.411 9.40161C15.939 9.40195 14.7578 10.551 14.7578 11.9872Z" fill="#0B161F"/>
<path d="M26.4857 12.8341L25.155 9.51367H24.0938L26.2098 14.5834H26.7322L28.8854 9.51367H27.8313L26.4857 12.8341Z" fill="#0B161F"/>
<path d="M29.3281 14.4568H32.0856V13.6203H30.2997V12.2847H32.0172V11.4481H30.2997V10.3509H32.0856V9.51367H29.3281V14.4568Z" fill="#0B161F"/>
<path d="M33.9802 11.789H33.6971V10.2909H33.9955C34.6021 10.2909 34.9311 10.544 34.9311 11.0249C34.9311 11.5212 34.6021 11.789 33.9802 11.789ZM35.9308 10.9725C35.9308 10.0465 35.2887 9.51367 34.1668 9.51367H32.7246V14.4568H33.6968V12.4703H33.8244L35.1687 14.4568H36.3647L34.7944 12.3743C35.528 12.2262 35.9308 11.7291 35.9308 10.9725Z" fill="#0B161F"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,2 +0,0 @@
export * from './Button'
export * from './Modal'

View File

@ -1,6 +0,0 @@
<svg width="40" height="26" viewBox="0 0 40 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.857422" y="0.50708" width="38.2857" height="24" rx="3.5" fill="white" stroke="#C7C7C7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.6198 19.2054C28.5208 19.2054 31.6831 16.0807 31.6831 12.2262C31.6831 8.37175 28.5208 5.24707 24.6198 5.24707C22.8715 5.24707 21.2715 5.87473 20.0381 6.91445C18.8048 5.87492 17.2049 5.24738 15.4568 5.24738C11.5559 5.24738 8.39355 8.37206 8.39355 12.2266C8.39355 16.081 11.5559 19.2057 15.4568 19.2057C17.2051 19.2057 18.8051 18.5781 20.0385 17.5383C21.2719 18.5779 22.8717 19.2054 24.6198 19.2054Z" fill="#ED0006"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.0381 17.538C21.5568 16.2579 22.5198 14.353 22.5198 12.226C22.5198 10.0989 21.5568 8.19413 20.0381 6.91402C21.2715 5.87441 22.8714 5.24683 24.6196 5.24683C28.5205 5.24683 31.6828 8.37151 31.6828 12.226C31.6828 16.0805 28.5205 19.2052 24.6196 19.2052C22.8714 19.2052 21.2715 18.5776 20.0381 17.538Z" fill="#F9A000"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.0383 17.5382C21.557 16.258 22.52 14.3533 22.52 12.2262C22.52 10.0992 21.557 8.19441 20.0383 6.91431C18.5196 8.19441 17.5566 10.0992 17.5566 12.2262C17.5566 14.3533 18.5196 16.258 20.0383 17.5382Z" fill="#FF5E00"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,18 +0,0 @@
export const cardStyles = {
base: {
color: '#121620',
lineHeight: '18px',
fontSize: '16px',
fontWeight: '400',
fontFamily: 'SF Pro Text, system-ui, sans-serif',
'::placeholder': {
color: '#8E8E93',
}
},
invalid: {
color: '#FF5758',
},
empty: {
fontWeight: '400',
},
}

View File

@ -1,4 +0,0 @@
<svg width="40" height="26" viewBox="0 0 40 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.571289" y="0.50708" width="38.2857" height="24" rx="3.5" fill="white" stroke="#C7C7C7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5123 16.6824H10.0603L8.22151 10.3638C8.13424 10.0731 7.94893 9.81616 7.67634 9.69505C6.99608 9.3907 6.24647 9.14849 5.42871 9.02633V8.78306H9.37881C9.92398 8.78306 10.3329 9.14849 10.401 9.57289L11.3551 14.1307L13.8059 8.78306H16.1899L12.5123 16.6824ZM17.5528 16.6824H15.2371L17.144 8.78306H19.4597L17.5528 16.6824ZM22.4557 10.9714C22.5238 10.5459 22.9327 10.3026 23.4098 10.3026C24.1594 10.2416 24.9759 10.3637 25.6574 10.667L26.0663 8.96626C25.3848 8.72299 24.6352 8.60083 23.9549 8.60083C21.7073 8.60083 20.0718 9.695 20.0718 11.2136C20.0718 12.3688 21.2303 12.9754 22.048 13.3408C22.9327 13.7052 23.2735 13.9485 23.2053 14.3129C23.2053 14.8594 22.5238 15.1027 21.8436 15.1027C21.0258 15.1027 20.2081 14.9205 19.4597 14.6162L19.0508 16.318C19.8685 16.6213 20.7532 16.7434 21.571 16.7434C24.0912 16.8034 25.6574 15.7103 25.6574 14.0696C25.6574 12.0034 22.4557 11.8823 22.4557 10.9714ZM33.7621 16.6824L31.9233 8.78306H29.9483C29.5394 8.78306 29.1305 9.02633 28.9942 9.3907L25.5893 16.6824H27.9732L28.449 15.5282H31.3781L31.6507 16.6824H33.7621ZM30.289 10.9106L30.9693 13.8877H29.0624L30.289 10.9106Z" fill="#172B85"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,5 +0,0 @@
import gPaySafeCheckout from './Google-Pay.png'
export function GooglePayBanner(): JSX.Element {
return <img src={gPaySafeCheckout} alt='Guaranteed safe checkout' />
}

View File

@ -1,67 +0,0 @@
import { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { PaymentIntent } from '@chargebee/chargebee-js-types'
import { SubscriptionReceipts, extractErrorMessage, useApi, useApiCall } from '@/api'
import { usePayment, GooglePayButtonOptions } from '@/payment'
import { useAuth } from '@/auth'
import Loader, { LoaderColor } from '@/components/Loader'
import ErrorText from '@/components/ErrorText'
const currencyCode = 'USD'
const paymentMethod = 'google_pay'
const buttonId = 'google-pay-btn'
interface GooglePayButtonProps {
onSuccess: (receipt: SubscriptionReceipts.SubscriptionReceipt) => void
onError: (error: Error) => void
}
export function GooglePayButton({ onSuccess, onError }: GooglePayButtonProps): JSX.Element {
const api = useApi()
const { i18n } = useTranslation()
const { token } = useAuth()
const { googlePay } = usePayment()
const [isMounting, setIsMounting] = useState<boolean>(true)
const loadData = useCallback(() => {
return api.createPaymentIntent({ token, paymentMethod, currencyCode })
.then(({ payment_intent }) => payment_intent)
}, [api, token])
const { data, error, isPending } = useApiCall<PaymentIntent>(loadData)
if (error) console.error(error)
useEffect(() => {
if (data === null) return
const buttonOptions: GooglePayButtonOptions = {
buttonColor: 'black',
buttonType: 'plain',
buttonLocale: i18n.language,
buttonSizeMode: 'fill',
}
googlePay?.setPaymentIntent(data)
googlePay?.mountPaymentButton(`#${buttonId}`, buttonOptions)
.then(() => setIsMounting(false))
.then(() => googlePay?.handlePayment())
.then((result) => {
console.log('Success payment by GooglePay', result)
// TODO: implement api.createSubscriptionReceipt for GooglePay
})
.then(() => onSuccess({} as SubscriptionReceipts.SubscriptionReceipt))
.catch((error: Error) => onError(error))
}, [data, googlePay, i18n.language, onSuccess, onError])
return (
<div id={buttonId} className='pay-btn'>
{isPending || isMounting ? <FakeGPayButton /> : null}
{error ? <ErrorText message={extractErrorMessage(error)} isShown={true} size='large'/> : null}
</div>
)
}
function FakeGPayButton() {
return (
<div className='gpay-button-fake-loader'>
<Loader color={LoaderColor.White} />
</div>
)
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

View File

@ -1 +0,0 @@
<svg width="41" height="17" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="M19.526 2.635v4.083h2.518c.6 0 1.096-.202 1.488-.605.403-.402.605-.882.605-1.437 0-.544-.202-1.018-.605-1.422-.392-.413-.888-.62-1.488-.62h-2.518zm0 5.52v4.736h-1.504V1.198h3.99c1.013 0 1.873.337 2.582 1.012.72.675 1.08 1.497 1.08 2.466 0 .991-.36 1.819-1.08 2.482-.697.665-1.559.996-2.583.996h-2.485v.001zm7.668 2.287c0 .392.166.718.499.98.332.26.722.391 1.168.391.633 0 1.196-.234 1.692-.701.497-.469.744-1.019.744-1.65-.469-.37-1.123-.555-1.962-.555-.61 0-1.12.148-1.528.442-.409.294-.613.657-.613 1.093m1.946-5.815c1.112 0 1.989.297 2.633.89.642.594.964 1.408.964 2.442v4.932h-1.439v-1.11h-.065c-.622.914-1.45 1.372-2.486 1.372-.882 0-1.621-.262-2.215-.784-.594-.523-.891-1.176-.891-1.96 0-.828.313-1.486.94-1.976s1.463-.735 2.51-.735c.892 0 1.629.163 2.206.49v-.344c0-.522-.207-.966-.621-1.33a2.132 2.132 0 0 0-1.455-.547c-.84 0-1.504.353-1.995 1.062l-1.324-.834c.73-1.045 1.81-1.568 3.238-1.568m11.853.262l-5.02 11.53H34.42l1.864-4.034-3.302-7.496h1.635l2.387 5.749h.032l2.322-5.75z" fill="#FFF"/><path d="M13.448 7.134c0-.473-.04-.93-.116-1.366H6.988v2.588h3.634a3.11 3.11 0 0 1-1.344 2.042v1.68h2.169c1.27-1.17 2.001-2.9 2.001-4.944" fill="#4285F4"/><path d="M6.988 13.7c1.816 0 3.344-.595 4.459-1.621l-2.169-1.681c-.603.406-1.38.643-2.29.643-1.754 0-3.244-1.182-3.776-2.774H.978v1.731a6.728 6.728 0 0 0 6.01 3.703" fill="#34A853"/><path d="M3.212 8.267a4.034 4.034 0 0 1 0-2.572V3.964H.978A6.678 6.678 0 0 0 .261 6.98c0 1.085.26 2.11.717 3.017l2.234-1.731z" fill="#FABB05"/><path d="M6.988 2.921c.992 0 1.88.34 2.58 1.008v.001l1.92-1.918C10.324.928 8.804.262 6.989.262a6.728 6.728 0 0 0-6.01 3.702l2.234 1.731c.532-1.592 2.022-2.774 3.776-2.774" fill="#E94235"/></g></svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1,2 +0,0 @@
export * from './Banner'
export * from './Button'

View File

@ -1,3 +0,0 @@
export * from './ApplePay'
export * from './GooglePay'
export * from './Card'

View File

@ -44,10 +44,6 @@
letter-spacing: 0.0008em;
}
.payment-chargebee {
width: 100%;
}
.payment-card-list {
display: flex;
justify-content: space-between;

View File

@ -2,7 +2,6 @@ import { useTranslation } from "react-i18next";
import styles from "./styles.module.css";
import { ISubscriptionPlan } from "@/api/resources/SubscriptionPlans";
import TotalToday from "./TotalToday";
// import ApplePayButton from "../StripePage/ApplePayButton";
import MainButton from "../MainButton";
import { useApi } from "@/api";
import { useAuth } from "@/auth";

View File

@ -8,7 +8,6 @@ import { store } from "./store";
import { AuthProvider } from "./auth";
import { ApiContext, createApi } from "./api";
import { LegalContext, buildLegal } from "./legal";
import { PaymentContext } from "./payment";
import { getClientLocale, buildResources, fallbackLng } from "./locales";
import App from "./components/App";
@ -35,7 +34,6 @@ const init = async () => {
.use(initReactI18next)
.use(new ReactPostprocessor())
.init(options);
window.Chargebee.init(config.chargebee);
const isProduction = import.meta.env.MODE === "production";
@ -72,11 +70,7 @@ const init = async () => {
<ApiContext.Provider value={api}>
<AuthProvider>
<LegalContext.Provider value={legal}>
<PaymentContext.Provider
value={window.Chargebee.getInstance()}
>
<App />
</PaymentContext.Provider>
</LegalContext.Provider>
</AuthProvider>
</ApiContext.Provider>

View File

@ -1,4 +0,0 @@
import { createContext } from 'react'
import { ChargebeeInstance } from '@chargebee/chargebee-js-types'
export const PaymentContext = createContext<ChargebeeInstance>({} as ChargebeeInstance)

View File

@ -1,3 +0,0 @@
export * from './PaymentContext'
export * from './usePayment'
export * from './types'

View File

@ -1,43 +0,0 @@
import { PaymentIntent } from '@chargebee/chargebee-js-types'
interface Handler {
handlePayment: () => Promise<PaymentIntent>
setPaymentIntent: (paymentIntent: PaymentIntent) => void
}
export interface GooglePayButtonOptions {
buttonColor: 'default' | 'black' | 'white'
buttonType: 'long' | 'short' | 'book' | 'buy' | 'checkout' | 'donate' | 'order' | 'pay' | 'plain' | 'subscribe'
buttonSizeMode: 'static' | 'fill'
buttonLocale: string
}
export interface PaymentOptions {
requestPayerEmail: boolean
requestBillingAddress: boolean
requestShippingAddress: boolean
}
export interface ApplePayButtonOptions {
locale: string
buttonColor: 'black' | 'white' | 'white-outline'
buttonType: 'add-money' | 'book' | 'buy' | 'check-out' | 'continue' | 'contribute' | 'donate' | 'order' | 'pay' | 'plain' | 'reload' | 'rent' | 'set-up' | 'subscribe' | 'support' | 'tip' | 'top-up'
}
interface ApplePay extends Handler {
canMakePayments: () => boolean
mountPaymentButton: (selector: string, options?: ApplePayButtonOptions) => Promise<void>
}
interface GooglePay extends Handler {
getPaymentIntent: () => PaymentIntent
updatePaymentIntent: (paymentIntent: PaymentIntent) => void
mountPaymentButton: (
selector: string,
buttonOptions?: GooglePayButtonOptions,
paymentOptions?: PaymentOptions
) => Promise<void>
}
export type ApplePayHandler = ApplePay | null
export type GooglePayHandler = GooglePay | null

View File

@ -1,21 +0,0 @@
import { useContext, useEffect, useState } from 'react'
import { PaymentContext } from './PaymentContext'
import { ApplePayHandler, GooglePayHandler } from './types'
export const usePayment = () => {
const chargebee = useContext(PaymentContext)
const [googlePay, setGooglePay] = useState<GooglePayHandler>(null)
const [applePay, setApplePay] = useState<ApplePayHandler>(null)
useEffect(() => {
Promise.all([
chargebee.load('google-pay'),
chargebee.load('apple-pay'),
]).then(([googlePay, applePay]) => {
setGooglePay(googlePay)
setApplePay(applePay)
})
}, [chargebee])
return { chargebee, googlePay, applePay }
}

View File

@ -1,11 +1,3 @@
import { Chargebee } from "@chargebee/chargebee-js-types";
declare global {
interface Window {
Chargebee: typeof Chargebee;
}
}
export enum EDirectionOnboarding {
LEFT = "left",
RIGHT = "right",