capacitor-community / stripe

Stripe Mobile SDK wrapper for Capacitor
https://capacitor-community-stripe.netlify.app/
MIT License
196 stars 77 forks source link

Ionic React app issue in paymet sheet and payment flow #187

Closed mostafa3ly closed 2 years ago

mostafa3ly commented 2 years ago

Platform

Describe the bug Accept payment is not working on ionic react js app. i have two issues: 1 - when i present payment sheet for the first time it appears like this

1

but when close and open again it appears fine and this is a bad experience for the user

2 - when i try to test the card payment using the test api keys and test data on stripe official documentation the page is redirect to the same page appended to it the query of zip code http://localhost:8100?zipCode=00000 and no completation callback is called and i try this for payment sheet and payment flow and i think my server code is fine and my stripe account is fine, cause i test it on the official react js api and it success. also when i click on presentPaymentSheet it logs this error

2

client side package.json


  "dependencies": {
    "@capacitor-community/stripe": "^3.7.2",
    "@capacitor-firebase/authentication": "^0.3.0",
    "@capacitor/android": "3.5.1",
    "@capacitor/app": "1.1.1",
    "@capacitor/core": "3.5.1",
    "@capacitor/geolocation": "^1.3.1",
    "@capacitor/google-maps": "^1.0.0",
    "@capacitor/haptics": "1.1.4",
    "@capacitor/keyboard": "1.2.2",
    "@capacitor/status-bar": "1.0.8",
    "@ionic/react": "^6.1.7",
    "@ionic/react-router": "^6.1.7",
    "@reduxjs/toolkit": "^1.8.2",
    "@stripe-elements/stripe-elements": "^1.1.0",
    "@stripe/react-stripe-js": "^1.8.1",
    "@stripe/stripe-js": "^1.31.0",
    "@testing-library/jest-dom": "^5.11.9",
    "@testing-library/react": "^13.3.0",
    "@testing-library/user-event": "^14.2.0",
    "@types/jest": "^27.5.1",
    "@types/node": "^17.0.36",
    "@types/react": "^16.14.3",
    "@types/react-dom": "^16.9.10",
    "@types/react-router": "^5.1.11",
    "@types/react-router-dom": "^5.1.7",
    "firebase": "^9.8.2",
    "ionicons": "^6.0.1",
    "prop-types": "^15.8.1",
    "react": "^17.0.1",
    "react-dom": "^17.0.1",
    "react-redux": "^8.0.2",
    "react-router": "^5.2.0",
    "react-router-dom": "^5.2.0",
    "react-scripts": "^5.0.0",
    "typescript": "^4.7.2",
    "web-vitals": "^2.1.4",
    "workbox-background-sync": "^6.5.3",
    "workbox-broadcast-update": "^6.5.3",
    "workbox-cacheable-response": "^6.5.3",
    "workbox-core": "^6.5.3",
    "workbox-expiration": "^6.5.3",
    "workbox-google-analytics": "^6.5.3",
    "workbox-navigation-preload": "^6.5.3",
    "workbox-precaching": "^6.5.3",
    "workbox-range-requests": "^6.5.3",
    "workbox-routing": "^6.5.3",
    "workbox-strategies": "^6.5.3",
    "workbox-streams": "^6.5.3"
  },

client side code

App.tsx

import { IonApp, setupIonicReact } from "@ionic/react";
import { FC } from "react";
import { FirebaseAuthProvider } from "./contexts/FirebaseAuthContext";
import { CapacitorStripeProvider } from "@capacitor-community/stripe/dist/esm/react/provider";
import Routes from "./routes";
import PageLoading from "./components/PageLoading";

/* Core CSS required for Ionic components to work properly */
import "@ionic/react/css/core.css";

/* Basic CSS for apps built with Ionic */
import "@ionic/react/css/normalize.css";
import "@ionic/react/css/structure.css";
import "@ionic/react/css/typography.css";

/* Optional CSS utils that can be commented out */
import "@ionic/react/css/padding.css";
import "@ionic/react/css/float-elements.css";
import "@ionic/react/css/text-alignment.css";
import "@ionic/react/css/text-transformation.css";
import "@ionic/react/css/flex-utils.css";
import "@ionic/react/css/display.css";

/* Theme variables */
import "./theme/variables.css";

setupIonicReact();

const App: FC = () => (
  <CapacitorStripeProvider
    publishableKey={process.env.REACT_APP_STRIPE_API_KEY}
    fallback={<PageLoading />}
  >
    <IonApp>
      <FirebaseAuthProvider>
        <Routes />
      </FirebaseAuthProvider>
    </IonApp>
  </CapacitorStripeProvider>
);

export default App;
import { useCapacitorStripe } from "@capacitor-community/stripe/dist/esm/react/provider";
import { PaymentSheetEventsEnum } from "@capacitor-community/stripe";
import { PaymentIntentResult } from "src/interfaces/PaymentIntentResult";
import { IonContent, IonButton } from "@ionic/react";
import { FC, useCallback, useEffect, useState } from "react";

const Content: FC = () => {
  const {stripe} = useCapacitorStripe();

  const [isLoading, setIsLoading] = useState(true);
  const [paymentIntentResult, setPaymentIntentResult] =
    useState<PaymentIntentResult>();

  const createPaymentIntent = useCallback(async () => {
    const result = await fetch(
      process.env.REACT_APP_API_BASE_URL,
      { method: "GET" }
    );
      setPaymentIntentResult((await result.json()) as PaymentIntentResult);
      setIsLoading(false);
  }, []);

  useEffect(() => {
    createPaymentIntent();
    stripe.addListener(PaymentSheetEventsEnum.Loaded, () => {
      console.log("loaded");
    });
    stripe.addListener(PaymentSheetEventsEnum.Canceled, () => {
      console.log("Canceled");
    });
    stripe.addListener(PaymentSheetEventsEnum.Completed, () => {
      console.log("Complete");
    });
    stripe.addListener(PaymentSheetEventsEnum.FailedToLoad, () => {
      console.log("failed to laod");
    });
  }, [createPaymentIntent, stripe]);

  const handlePay = async (
  ): Promise<void> => {
      if (!paymentIntentResult) return;
      const {
        customerEphemeralKeySecret,
        customerId,
        paymentIntentClientSecret,
      } = paymentIntentResult;
      await stripe.createPaymentSheet({
        paymentIntentClientSecret,
        customerId,
        customerEphemeralKeySecret,
        merchantDisplayName: "app name",
      });
      await stripe.presentPaymentSheet();
  };

  return (
    <IonContent fullscreen>
      <IonButton onClick={handlePay} disabled={isLoading}>PAY</IonButton>
    </IonContent>
  );
};

export default Content;

server side code which has no issues by the way

    const stripe = new Stripe(STRIPE_TEST_SECRET_KEY, {
      apiVersion: "2020-08-27",
    });
    const customer = await stripe.customers.create();
    const { secret: customerEphemeralKeySecret } =
      await stripe.ephemeralKeys.create(
        { customer: customer.id },
        { apiVersion: "2020-08-27"}
      );
    const { client_secret: paymentIntentClientSecret } =
      await stripe.paymentIntents.create({
        amount: 2000,
        currency: "usd",
        customer: customer.id,
      });
    if (!customerEphemeralKeySecret || !paymentIntentClientSecret) {
      throw Error();
    }

    const result: PaymentIntentResult = {
      paymentIntentClientSecret,
      customerEphemeralKeySecret,
      customerId: customer.id,
    };

    res.json(result);
rdlabo commented 2 years ago

@mostafa3ly Thanks for issue. I released next version, please try this:

npm i @capacitor-community/stripe@latest @stripe-elements/stripe-elements@latest

This is https://github.com/stripe-elements/stripe-elements issue. If you have next issue, please ask here.

hmarquez-solutions commented 2 years ago

This worked for me!

rdlabo commented 2 years ago

@hmarquez-solutions @mostafa3ly I'm glad you got it sorted out! Did you use this plugin for production? If yes, please tell us what app. This help development this plugin.

👉 https://github.com/capacitor-community/stripe/issues/145

Thanks.