UNPKG

@shopgate/engage

Version:
195 lines (185 loc) • 5.62 kB
import React, { useEffect, useContext } from 'react'; import { i18n } from '@shopgate/engage/core/helpers'; import { Elements, CardNumberElement, useStripe, useElements } from '@stripe/react-stripe-js'; import PropTypes from 'prop-types'; import Context from "./StripeProvider.context"; import connect from "./StripeProvider.connector"; import { promise as stripePromise, loadSdk } from "./sdk"; import { useCheckoutContext } from "../../hooks/common"; import PaymentContext from "../context"; /** * Prepares checkout with stripe request and creates event data. * @param {Stripe} stripe Stripe * @param {Object} req Stripe request. * @param {Order} order Order * @return {Promise} */ import { jsx as _jsx } from "react/jsx-runtime"; export const prepareStripeRequestCheckout = (stripe, req, order) => new Promise(async (resolve, reject) => { // Update totals if they updated due to promotions etc. req.update({ total: { amount: Math.round(order.total * 100), label: 'Checkout' } }); // Recheck availability again. const availability = await req.canMakePayment(); if (!availability) { reject(availability); return; } // Handle success req.on('paymentmethod', async event => { resolve(event); }); // Handle cancellation req.on('cancel', err => { reject(err); }); // Show to user req.show(); }); /** * Triggers the payment using stripe payment request api. * @param {Object} stripe Stripe * @param {Object} req Stripe Request * @param {Object} order Order * @param {Object} activeTransaction Active request * @returns {Promise} */ const triggerStripeRequest = async (stripe, req, order, activeTransaction) => { // Create checkout event on stripe. // For apple pay the event is already created beforehands // This is due to safaris limitation that the `show` needs // to happen directly inside a gesture handler (click, ..) let event = req.preparedEvent; if (!event) { event = await prepareStripeRequestCheckout(stripe, req, order); } // Send over to stripe and let it validate. const intent = activeTransaction?.checkoutParams?.paymentIntent; const result = await stripe.confirmCardPayment(intent, { payment_method: event.paymentMethod.id }, { handleActions: false }); if (result.error) { event.complete('fail'); throw result.error; } event.complete('success'); if (result.paymentIntent.status === 'requires_action') { const step = await stripe.confirmCardPayment(intent); if (step.error) { throw step.error; } return step; } return result; }; /** * A Provider that is needed for all stripe based * @param {Object} props The components props. * @param {Object} props.children The child components. * @returns {JSX.Element} */ const StripeProvider = ({ children }) => { const [error, setError] = React.useState(null); const { order, paymentData } = useCheckoutContext(); const stripe = useStripe(); const elements = useElements(); const contextApi = React.useMemo(() => ({ error, setError, fulfillTransaction: async ({ paymentTransactions }) => { // Make sure api responded with a new payment transaction. const activeTransaction = paymentTransactions[0]; if (!activeTransaction?.checkoutParams?.paymentIntent) { setError(i18n.text('checkout.errors.noPaymentTransaction')); return false; } // Handle stripe payment button request. if (paymentData?.meta && paymentData?.meta.stripeRequest) { try { const { paymentIntent } = await triggerStripeRequest(stripe, paymentData.meta.stripeRequest, order, activeTransaction); return [{ id: activeTransaction.id, checkoutParams: { paymentIntentId: paymentIntent.id } }]; } catch (_error) { return false; } } // Handle regular payment. const { error: incomingError, paymentIntent } = await stripe.confirmCardPayment(activeTransaction.checkoutParams.paymentIntent, { payment_method: { card: elements.getElement(CardNumberElement) } }); if (incomingError) { console.error(incomingError, activeTransaction.checkoutParams.paymentIntent); setError(incomingError.message); return false; } return [{ id: activeTransaction.id, checkoutParams: { paymentIntentId: paymentIntent.id } }]; } }), [elements, error, order, paymentData, stripe]); const { registerPaymentMethod } = useContext(PaymentContext); useEffect(() => { registerPaymentMethod(contextApi); }, [contextApi, registerPaymentMethod]); return /*#__PURE__*/_jsx(Context.Provider, { value: contextApi, children: children }); }; /** * A Provider that is needed for all stripe based * @param {Object} props The components props. * @param {string} [props.publishableKey] The publishable key. * @param {Object} props.children The child components. * @returns {JSX.Element} */ const StripeProviderWrapper = ({ publishableKey, children }) => { React.useEffect(() => { if (!publishableKey) { return; } loadSdk(publishableKey); }, [publishableKey]); return /*#__PURE__*/_jsx(Elements, { stripe: stripePromise, children: /*#__PURE__*/_jsx(StripeProvider, { children: children }) }); }; StripeProviderWrapper.defaultProps = { publishableKey: null }; export default connect(StripeProviderWrapper);