2
votes

My Stripe payments show up on my dashboard, but they have an 'Incomplete' status, and hovering over shows the tip, "The customer has not entered their payment method." I thought I accounted for payment method in my sessions.create() method.

My Angular component creates a StripeCheckout and sends the session data to my API. API then includes it in the response to the browser (my first attempt was to use this sessionId to redirect to checkout, but I chose this because it was smoother).

Angular/TS StripeCheckout and handler:

let headers = new Headers();
    headers.append("Content-Type", "application/json");
    headers.append(
      "authentication",
      `Bearer ${this.authServ.getAuthenticatedUserToken()}`
    );

    this.handler = StripeCheckout.configure({
      key: environment.stripe_public_key,
      locale: "auto",
      source: async source => {
        this.isLoading = true;
        this.amount = this.amount * 100;
        this.sessionURL = `${this.stringValServ.getCheckoutSessionStripeURL}/${this.activeUser.id}/${this.amount}`;
        const session = this.http
          .get(this.sessionURL, {
            headers: new HttpHeaders({
              "Content-Type": "application/json",
              authentication: `Bearer ${this.authServ.getAuthenticatedUserToken()}`
            })
          })
          .subscribe(res => {
            console.log(res);
          });
      }
    });
    //so that the charge is depicted correctly on the front end
    this.amount = this.amount / 100;
  }

  async checkout(e) {
    this.stripeAmountEvent.emit(this.amount);
    this.handler.open({
      name: "[REDACTED]",
      amount: this.amount * 100,
      description: this.description,
      email: this.activeUser.email
    });
    e.preventDefault();
  }

NodeJS API picks it up

exports.getCheckoutSession = catchAsync(async (req, res, next) => {
  const currentUserId = req.params.userId;
  const paymentAmount = req.params.amount;
  const user = await User.findById(currentUserId);

  const session = await stripe.checkout.sessions.create({
    payment_method_types: ['card'],
    success_url: `${process.env.CLIENT_URL}`,
    cancel_url: `${process.env.CLIENT_URL}`,
    customer_email: user.email,
    client_reference_id: user.userId,
    line_items: [
      {
        name: `Donation from ${user.userName}`,
        description: '[REDACTED]',
        amount: paymentAmount,
        currency: 'usd',
        quantity: 1,
        customer: user.userId
      }
    ]
  });

  const newPayment = await Payment.create({
    amount: paymentAmount,
    createdAt: Date.now(),
    createdById: user._id
  });

  res.status(200).send({
    status: 'success',
    session
  });
});

The payment gets created in my db, and the payment shows up on my Stripe dashboard. The payments show as 'Incomplete' when I expected it to charge the card.

Thanks in advance.

1

1 Answers

0
votes

The latest version of Checkout lets you accept payments directly on a Stripe-hosted payment page. This includes collecting card details, showing what's in your cart and ensuring the customer pays before being redirected to your website.

At the moment, though, your code is mixing multiple products in one place incorrectly. The code you have client-side uses Legacy Checkout. This is an older version of Stripe's product that you could use to collect card details securely. This is not something you should use anymore.

Then server-side, you are using the newer version of Checkout by creating a Session. This part is correct but you never seem to be using it.

Instead, you need to create the Session server-side, and then client-side you only need to redirect to Stripe using redirectToCheckout as documented here.