diff --git a/public/package-lock.json b/public/package-lock.json
index b6fdb0900..3f98ccf46 100644
--- a/public/package-lock.json
+++ b/public/package-lock.json
@@ -9,8 +9,9 @@
"version": "1.0.0",
"license": "GPL-3.0",
"dependencies": {
+ "@stripe/react-stripe-js": "^2.7.0",
+ "@stripe/stripe-js": "^3.3.0",
"@types/markdown-it": "^12.2.1",
- "@types/stripe-v3": "^3.1.23",
"chart.js": "^2.9.4",
"d3": "^7.8.3",
"d3-sankey": "^0.12.3",
@@ -105,6 +106,27 @@
"integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==",
"dev": true
},
+ "node_modules/@stripe/react-stripe-js": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-2.7.0.tgz",
+ "integrity": "sha512-kTkIZl2ZleBuDR9c6fDy/s4m33llII8a5al6BDAMSTrfVq/4gSZv3RBO5KS/xvnxS+fDapJ3bKvjD8Lqj+AKdQ==",
+ "dependencies": {
+ "prop-types": "^15.7.2"
+ },
+ "peerDependencies": {
+ "@stripe/stripe-js": "^1.44.1 || ^2.0.0 || ^3.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/@stripe/stripe-js": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-3.3.0.tgz",
+ "integrity": "sha512-dUgAsko9KoYC1U2TIawHzbkQJzPoApxCc1Qf6/j318d1ArViyh6ROHVYTxnU3RlOQL/utUD9I4/QoyiCowsgrw==",
+ "engines": {
+ "node": ">=12.16"
+ }
+ },
"node_modules/@types/body-parser": {
"version": "1.19.2",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
@@ -568,11 +590,6 @@
"@types/node": "*"
}
},
- "node_modules/@types/stripe-v3": {
- "version": "3.1.23",
- "resolved": "https://registry.npmjs.org/@types/stripe-v3/-/stripe-v3-3.1.23.tgz",
- "integrity": "sha512-fqCnai832M6o6qH6A+Dqk1I/SXAzx629bknbrDigyvkuAPGZGrjkLCMxfnrtWswHXNPjj/TtuGf4lQnDXc815w=="
- },
"node_modules/@types/ws": {
"version": "8.5.4",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz",
@@ -2876,8 +2893,7 @@
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "peer": true
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"node_modules/json-parse-even-better-errors": {
"version": "2.3.1",
@@ -2968,7 +2984,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
- "peer": true,
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
@@ -5833,6 +5848,14 @@
"inBundle": true,
"license": "ISC"
},
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/object-inspect": {
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
@@ -6068,6 +6091,16 @@
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"dev": true
},
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -6185,6 +6218,11 @@
"react": "^18.2.0"
}
},
+ "node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ },
"node_modules/readable-stream": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
diff --git a/public/package.json b/public/package.json
index 2cea58138..f459b10d2 100644
--- a/public/package.json
+++ b/public/package.json
@@ -17,8 +17,9 @@
"webpack-merge": "^5.8.0"
},
"dependencies": {
+ "@stripe/react-stripe-js": "^2.7.0",
+ "@stripe/stripe-js": "^3.3.0",
"@types/markdown-it": "^12.2.1",
- "@types/stripe-v3": "^3.1.23",
"chart.js": "^2.9.4",
"d3": "^7.8.3",
"d3-sankey": "^0.12.3",
diff --git a/public/ts/cartpage.tsx b/public/ts/cartpage.tsx
index 0e87dd667..9e30783d1 100644
--- a/public/ts/cartpage.tsx
+++ b/public/ts/cartpage.tsx
@@ -154,10 +154,10 @@ const PaymentButton = ({
e.preventDefault();
setInProgress(true);
try {
- const result = await stripe.createPaymentMethod(
- "card",
- element,
- );
+ const result = await stripe!.createPaymentMethod({
+ type: "card",
+ card: element,
+ });
if (result.error) {
throw result.error;
} else {
diff --git a/public/ts/payment_common.tsx b/public/ts/payment_common.tsx
index 2394a2ba0..b1c3aedd5 100644
--- a/public/ts/payment_common.tsx
+++ b/public/ts/payment_common.tsx
@@ -1,4 +1,11 @@
-///
+import { useStripe } from "@stripe/react-stripe-js";
+import {
+ loadStripe,
+ PaymentMethod,
+ Stripe,
+ StripeCardElement,
+ StripeError,
+} from "@stripe/stripe-js";
import { useEffect, useRef } from "preact/hooks";
import Cart, { Item } from "./cart";
import * as common from "./common";
@@ -13,20 +20,20 @@ import { useTranslation } from "./translations";
declare var UIkit: any;
-export var stripe: stripe.Stripe;
-var card: stripe.elements.Element;
+export var stripe: Stripe | null;
+var card: StripeCardElement;
var spinner: any;
var payButton: HTMLInputElement;
var errorElement: any;
-export function initializeStripe() {
+export async function initializeStripe() {
// Create a Stripe client.
- stripe = Stripe(window.stripeKey);
+ stripe = await loadStripe(window.stripeKey);
}
export function mountStripe() {
// Create an instance of Elements.
- const elements = stripe.elements({ locale: "sv" });
+ const elements = stripe!.elements({ locale: "sv" });
// Custom styling can be passed to options when creating an Element.
const stripeStyle = {
base: {
@@ -64,7 +71,7 @@ interface ResponseFunction {
export interface PaymentFlowDefinition {
initiate_payment: InitializePaymentFunction;
before_initiate_payment?: Function;
- on_stripe_error?: (error: stripe.Error) => void;
+ on_stripe_error?: (error: StripeError) => void;
handle_backend_response?: (
json: ServerResponse,
) => void;
@@ -337,7 +344,7 @@ export function ProductDataFromProducts(
export const StripeCardInput = ({
element,
}: {
- element: stripe.elements.Element;
+ element: StripeCardElement;
}) => {
const mountPoint = useRef(null);
@@ -350,7 +357,9 @@ export const StripeCardInput = ({
export const createStripeCardInput = () => {
// Create an instance of Elements.
- const elements = stripe.elements({ locale: "sv" });
+ const stripe = useStripe();
+ const elements = stripe?.elements({ locale: "sv" });
+
// Custom styling can be passed to options when creating an Element.
const stripeStyle = {
base: {
@@ -370,7 +379,7 @@ export const createStripeCardInput = () => {
};
// Create an instance of the card Element.
- return elements.create("card", {
+ return elements?.create("card", {
style: stripeStyle,
hidePostalCode: true,
});
@@ -566,10 +575,12 @@ export const extractRelevantProducts = (
};
export async function createPaymentMethod(
- element: stripe.elements.Element,
+ element: StripeCardElement,
memberInfo: member_t,
-): Promise {
- const result = await stripe.createPaymentMethod("card", element, {
+): Promise {
+ const result = await stripe!.createPaymentMethod({
+ type: "card",
+ card: element,
billing_details: {
name: `${memberInfo.firstname} ${memberInfo.lastname}`,
email: memberInfo.email,
@@ -649,7 +660,7 @@ export async function handleStripeSetupIntent<
res.data.action_info!.type ===
PaymentIntentNextActionType.USE_STRIPE_SDK
) {
- const stripeResult = await stripe.confirmCardSetup(
+ const stripeResult = await stripe!.confirmCardSetup(
res.data.action_info!.client_secret,
);
if (stripeResult.error) {
@@ -705,7 +716,7 @@ export async function negotiatePayment<
res.data.action_info!.type ===
PaymentIntentNextActionType.USE_STRIPE_SDK
) {
- const stripeResult = await stripe.handleCardAction(
+ const stripeResult = await stripe!.handleCardAction(
res.data.action_info!.client_secret,
);
if (stripeResult.error) {
@@ -729,7 +740,7 @@ export async function negotiatePayment<
}
export async function pay(
- paymentMethod: stripe.paymentMethod.PaymentMethod,
+ paymentMethod: PaymentMethod,
cart: Cart,
productData: ProductData,
discount: Discount,
diff --git a/public/ts/register.tsx b/public/ts/register.tsx
index cffbe375a..d26a0fbb1 100644
--- a/public/ts/register.tsx
+++ b/public/ts/register.tsx
@@ -1,3 +1,9 @@
+import { Elements } from "@stripe/react-stripe-js";
+import {
+ loadStripe,
+ PaymentMethod,
+ StripeCardElement,
+} from "@stripe/stripe-js";
import { ComponentChildren, render } from "preact";
import { StateUpdater, useEffect, useMemo, useState } from "preact/hooks";
import { PopupModal, useCalendlyEventListener } from "react-calendly";
@@ -7,7 +13,12 @@ import * as common from "./common";
import { ServerResponse, trackPlausible } from "./common";
import { LoadCurrentMemberInfo, member_t } from "./member_common";
import {
+ calculateAmountToPay,
+ createPaymentMethod,
+ createStripeCardInput,
Discount,
+ extractRelevantProducts,
+ pay,
PaymentFailedError,
PriceLevel,
Product,
@@ -16,12 +27,6 @@ import {
RegisterPageData,
StripeCardInput,
ToPayPreview,
- calculateAmountToPay,
- createPaymentMethod,
- createStripeCardInput,
- extractRelevantProducts,
- initializeStripe,
- pay,
} from "./payment_common";
import { TranslationWrapper, Translator, useTranslation } from "./translations";
import { URL_RELATIVE_MEMBER_PORTAL } from "./urls";
@@ -423,7 +428,7 @@ const Confirmation = ({
productData: ProductData;
discount: Discount;
discountInfo: DiscountsInfo;
- card: stripe.elements.Element;
+ card: StripeCardElement;
onRegistered: (r: RegistrationSuccess) => void;
onBack: () => void;
}) => {
@@ -538,7 +543,7 @@ type RegistrationSuccess = {
};
async function registerMember(
- paymentMethod: stripe.paymentMethod.PaymentMethod,
+ paymentMethod: PaymentMethod,
productData: ProductData,
memberInfo: MemberInfoValidated,
selectedPlan: Plan,
@@ -1188,12 +1193,14 @@ const RegisterPage = ({}: {}) => {
common.documentLoaded().then(() => {
const root = document.getElementById("root");
- initializeStripe();
+ const stripe = loadStripe(window.stripeKey);
if (root != null) {
render(
-
+
+
+
,
root,
diff --git a/public/ts/subscriptions.tsx b/public/ts/subscriptions.tsx
index 15372b0f5..3c629873a 100644
--- a/public/ts/subscriptions.tsx
+++ b/public/ts/subscriptions.tsx
@@ -1,3 +1,4 @@
+import { StripeCardElement } from "@stripe/stripe-js";
import { render } from "preact";
import { useState } from "preact/hooks";
import Cart from "./cart";
@@ -60,7 +61,7 @@ const PayDialog = ({
onCancel,
onPay,
}: {
- stripe: stripe.elements.Element;
+ stripe: StripeCardElement;
productData: ProductData;
products: Product[];
currentMemberships: SubscriptionType[];