Broadleaf Microservices
  • v1.0.0-latest-prod

Stripe Payment Element Integration

Stripe.js

This example contain example on how to implement Stripe payment integration using React Stripe.js. Please read Stripe’s documentation before proceeding.

Example Integration

Create Payment form

import {
  CardNumberElement,
  CardCvcElement,
  CardExpiryElement,
  useElements,
  useStripe,
  Elements,
  StripeElementsOptions
} from '@stripe/react-stripe-js';

import { loadStripe } from '@stripe/stripe-js/pure';

const StripePaymentForm = () => {
    ...

  // load Stripe scripts
  const stripePromise = loadStripe('publicApiKey');

  // build options
  const options = {
    mode: 'payment',
    amount: 1010,
    currency: 'USD',
    paymentMethodCreation: 'manual',
    //if don't set this prop or value doesn't correlate with the one on server
    //com/broadleafcommerce/payment/service/gateway/DefaultStripeExternalCallService.java:348
    //PaymentIntent.create(createParams, requestOptions); specifically in createParams
    //.setCaptureMethod(captureMethod)
    captureMethod: 'manual'/'automatic',
    //card type automatically includes apple_pay and google_pay, this is the only type we need to include so far
    //this should correlate with PaymentIntentCreateParams.Builder.addPaymentMethodType("card")
    //on server side, or remove this property so that methods will be read from the dashboard
    paymentMethodTypes: ['card'],
  } as StripeElementsOptions;

    // get saved payment method using "useSavedPaymentMethodInfo" from "@broadleaf/payment-react"
    const { error, onSubmit } = useHandleSubmit({ selectedSavedPaymentMethod });

    return (
      <Elements stripe={stripePromise} options={options}>
          <Formik
            initialValues={...}
            onSubmit={onSubmit}
            validateOnBlur
            validateOnChange={false}
          >
            {props => (
              <FormikForm>
                <CheckboxField name="savePaymentForFutureUse" label="Save new payment?" />
                <CardNumberElement ... />
                <InputField name="nameOnAccount" autoComplete="cc-name" />
                <CardCvcElement ... />
                <CardExpiryElement ... />
                 ...
                <button type="submit">Submit</button>
              </FormikForm>
            )}
          </Formik>
      </Elements>
    )
}

Handle Submit

const useHandleSubmit = ({ selectedSavedPaymentMethod }) => {
  const stripe = useStripe();
  const elements = useElements();
  const [error, setError] = useState();
  const [stripeError, setStripeError] = useState();

  const onSubmit = async (data, actions): Promise<void> => {
    const { savePaymentForFutureUse, ...billingAddress } = data;

    const billing_details = {
      ...billingAddress,
      email: emailAddress,
    };

    let paymentMethod;
    let displayAttributes= {};
    let paymentName;

    // selectedSavedPaymentMethod - one of the created saved payment method from PaymentTransactionServices
    if (!isEmpty(selectedSavedPaymentMethod)) {
      paymentMethod = {
        id: selectedSavedPaymentMethod.id,
      };

      displayAttributes = selectedSavedPaymentMethod.displayAttributes;

      paymentName = selectedSavedPaymentMethod.name;
    } else {
      const stripeResponse = await stripe.createPaymentMethod({
        type: 'card',
        card: elements.getElement(CardNumberElement),
        billing_details,
      });

      if (!isEmpty(stripeResponse.error)) {
        setStripeError(stripeResponse.error);
        return;
      }

      paymentMethod = stripeResponse.paymentMethod;

      // these are optional and only support displaying info about the card on the frontend
      displayAttributes = {
        creditCardType: paymentMethod.card.brand.toUpperCase(),
        creditCardNumber: maskCardNumber(paymentMethod.card.last4),
        creditCardExpDateMonth: padStart(
          `${paymentMethod.card.exp_month}`,
          2,
          '0'
        ),
        creditCardExpDateYear: `${paymentMethod.card.exp_year}`,
      };

      paymentName = `${displayAttributes.creditCardType} | ${displayAttributes.creditCardNumber} ${displayAttributes.creditCardExpDateMonth}/${displayAttributes.creditCardExpDateYear}`;
    }

    // these are required to pass to the backend as part of the payment
    const paymentMethodProperties = {
      PAYMENT_METHOD_ID: paymentMethod.id,
      CUSTOMER_ID: paymentMethod.customer,
      // The URL to redirect your customer back to after they authenticate or cancel their payment
      PAYMENT_INTENT_RETURN_URL: `${baseUrl}/api/cart-operations/checkout/${cart.id}/payment-callback/STRIPE?tenantId=${tenantId}&applicationId=${applicationId}`,
    };

    const paymentRequest = {
      name: paymentName,
      savedPaymentMethodId: selectedSavedPaymentMethod.id,
      type: 'CREDIT_CARD',
      gatewayType: 'STRIPE',
      amount: { amount: 11, currency: 'USD' },
      subtotal: cart.cartPricing.subtotal,
      adjustmentsTotal: cart.cartPricing.adjustmentsTotal,
      fulfillmentTotal: cart.cartPricing.fulfillmentTotal,
      taxTotal: cart.cartPricing.totalTax,
      isSingleUsePaymentMethod: true,
      billingAddress,
      shouldSavePaymentForFutureUse: savePaymentForFutureUse,
      paymentMethodProperties,
      displayAttributes,
    };

    try {
        // submit the payment request
        // "handleSubmitPaymentInfo" - can be obtained from our payment SDK - useSubmitPaymentRequest(). It creates a Payment in PaymentTransactionServices
        const paymentSummary = await handleSubmitPaymentInfo(paymentRequest);
        ...
    } catch (err) {
      setError(err);
    } finally {
      ...
    }
  };

  return { error, onSubmit, stripeError };
};
Note
When specifying paymentMethodTypes, the "card" type should be included to enable support for credit card forms, Apple Pay, and Google Pay. This is currently the only required payment method type. The list of paymentMethodTypes should correlate with PaymentIntentCreateParams.Builder.addPaymentMethodType("card") on the server side in com.broadleafcommerce.payment.service.gateway.DefaultStripeExternalCallService#populatePaymentIntentCreateParams. Alternatively, you can omit this client-side property and configure payment methods dynamically by setting the server-side property broadleaf.stripe.payment-methods-from-stripe-dashboard to true, allowing Stripe to determine available methods via the Dashboard.