#masudrhossain-cc-mandates

1 messages · Page 1 of 1 (latest)

fresh skyBOT
#

Hello! We'll be with you shortly. Below are links to other discussions we've had with you in the past week in case you want to review that information. If your question is related to one of these previous discussions, please provide a comprehensive summary of the current state and what you need help with now. We help many users simultaneously, so a summary allows us to resolve your issue as soon as possible.

timber vigil
#

Hello

#

Can you share the PaymentIntent ID where you are seeing the Charge fail?

atomic schooner
#

"payment_intent": "pi_3OKov9G02uXfwr5H11CPxX8i",

#

(test mode)

timber vigil
#

Looks like you are using the 4000003800000446 test card which requires 3DS for any on-session payments like starting a Subscription

atomic schooner
#

So it should work fine if it's a single charge?

timber vigil
#

This card is already set up for off-session use. It requires authentication for one-time and other on-session payments. However, all off-session payments succeed as if the card has been previously set up.

atomic schooner
timber vigil
#

Yes that's from the SetupIntent

#

You could also call confirmPayment() client-side using the PaymentIntent's client_secret to handle 3DS for the PaymentIntent

#

Let's back up a second.

atomic schooner
#

If i'm doing the SetupIntent do i need to do PaymentIntent's too? I thought it was either or

timber vigil
#

What are you trying to simulate here?

#

Is the customer on-session when you create this Subscription?

#

And are you trying to collect a new card or used a previously saved PaymentMethod?

atomic schooner
#

We had an issue with an indian connect customer who was testing with an indian card. and they got an error for 3DS. So i added SetupIntent to show the 3DS popup to confirm it. And so I'm using these cards to test that 3DS.

And the connect customer was testing with an indian for a subscription (but he also has single charge plans too, so we need it to work with either type)

timber vigil
#

Okay well that's a whole different thing.

atomic schooner
timber vigil
#

So if you are attempting to set up a PaymentMethod for an Indian cardholder then you need to pass through the necessary payment_method_options parameters as shown in the above doc

atomic schooner
timber vigil
#

No, you use a SetupIntent to setup a PaymentMethod for future use

#

You can also setup the PaymentMethod when you charge it initially by certain parameters on the PaymentIntent as well

#

It really depends on what you are trying to do.

#

So I'll ask again whether the customer is on-session in this case and whether you are trying to charge them using a new card or a saved card?

atomic schooner
#
Stripe.api_key = 'REMOVED'

Stripe::SetupIntent.create({
  payment_method_types: ['card'], 
  payment_method_options: {
    card: {
      # Add specific options for the card here if needed
      # For example, to request a 3D Secure authentication
      request_three_d_secure: 'any'
    }
  } 
})

like this?

timber vigil
#

This is specifically for an Indian cardholder?

atomic schooner
timber vigil
#

On-session means the customer is in your checkout flow at that moment.

atomic schooner
timber vigil
#

Okay and you are trying to charge a card they had already saved in the past?

atomic schooner
#

Yes

timber vigil
#

Okay how did you originally save that card?

#

Like really at that point you shouldn't need to do anything but create/confirm the PaymentIntent or Subscription. 3DS shouldn't need to be completed again -- it should already have been handled in the original setup.

atomic schooner
#

Stripe elements, submit form with reactjs

const handleSubmit = async (e) => {
    e.preventDefault();
    setDisabled(true)

    if (!stripe || !elements) {
      return;
    }

    setIsLoading(true);

    const cardElement = elements.getElement(CardElement);

    // Create a Payment Method using the Card Element
    stripe.confirmCardSetup(setupIntentClientSecret, {
      payment_method: {
        card: cardElement
      },
    })
    .then(function(result) {
      if (result.error) {
        // Handle error (e.g., display an error message to the user)
        console.error(result.error.message);
      } else {
        // Handle success (e.g., redirect to a success page or perform additional actions)
        // You can access the SetupIntent using result.setupIntent
        console.log("SetupIntent confirmed successfully:", result.setupIntent);
        axios.post(`/api/o/${service.organization.token}/services/${match.params.service_id}/service_checkouts`, {
          paymentMethodId: result.setupIntent.payment_method,
          name,
          email,
          address,
          price_id: price, 
          coupon_code: coupon_code, 
          intent_due_amount: amount_due, 
          selected_addons: selectedAddons,
          project_id: new URLSearchParams(new URL(window.location.href).search).get('project_id'),
          project_service_checkout_id: new URLSearchParams(new URL(window.location.href).search).get('psc'), 
          new: true, 
          referral_code: referralCode
        })
        .then(function(response){
          console.log(response);
          if(response.data.success){
            notice("Checkout successful");
            history.push(`/portal/o/${service.organization.token}/projects/${response.data.project_id}/tasks?show_task=true`);
            notice("You can now add a task")
          } else {
            setDisabled(false)
          }
        })
        .catch(function(error){
          console.log(error)
          notice("An error occured");
          setDisabled(false)
        })
        .then(function () {
          setDisabled(false);
        });
    
        setIsLoading(false);
      }
    })
    .catch(function(error) {
      // Handle any unexpected errors
      console.error(error);
    });
  };

I created a SetupIntent like this and passed that in to the form

@setup_intent = Stripe::SetupIntent.create(
          payment_method_types: ['card'],
          usage: 'off_session' # Set to 'off_session' for recurring payments
        )

When user submits the form on the frontend, i do this on the backend>

 # Create a customer with the payment method
        customer = Stripe::Customer.create(
          email: current_user.email,
          name: current_user.username
        )

# Attach the payment method to the customers profile
          payment_method = Stripe::PaymentMethod.attach(
            params[:paymentMethodId],
            {customer: customer.id},
          )

          # Make the payment_method the default for the customer
          Stripe::Customer.update(customer.id,
            {
              invoice_settings: {
                default_payment_method: params[:paymentMethodId]
              }, 
            },
          )

and then i try to charge it like this if it's a subscription

subscription = Stripe::Subscription.create({
            customer: customer.id, # Replace with the actual customer ID
            items: [
              { price: params[:price_id] }
            ], 
            coupon: stripe_coupon, 
            metadata: {
              service_id: @service_price.token
            }, 
            expand: ['latest_invoice.payment_intent'],
            description: "#{@service.title} -#{@service_price.token}", 
            application_fee_percent: affiliate_application_fee_percent
          })
timber vigil
#

Okay let's pause for a second

#

First, you don't need to do # Attach the payment method to the customers profile payment_method = Stripe::PaymentMethod.attach( params[:paymentMethodId], {customer: customer.id}, ) at all

#

You should create the Customer when you create the SetupIntent

atomic schooner
timber vigil
#

Then the PaymentMethod will be automatically attached to the Customer upon successful confirmation on your frontend and you reduce the number of API requests you are making.

atomic schooner
timber vigil
#

Yes that's correct.

#

Now let's pause again

#

Are you specifically working on handling an Indian customer?

fresh skyBOT
atomic schooner
#

Only person that had this issue was 1 indian connect account.

#

(we're a platform that lets people create customizable checkout forms for their service business)

timber vigil
#

So you want to add those parameters to your SetupIntent creation request

#

So that a India Mandate is created when it is required for Indian customers/accounts.

atomic schooner
#

Is this mandate required for indian CONNECT ACCOUNTS or indian card users (ie customer)?

timber vigil
#

Pause and read the doc above end to end

atomic schooner
#

Because i'm curious if this issue will arise if an indian person uses their card with a usa account

distant coyote
#

Hi 👋

I'm stepping in as my colleague needs to go. The documentation provided extends to Customers in India as well.

atomic schooner
#

Does stripe have a way of automatically knowing if it's an indian card or is that up to me?

distant coyote
#

No that is up to you.

atomic schooner
#

Okay so i did this to setup the mandate

@setup_intent = Stripe::SetupIntent.create({
            customer: @customer.stripe_customer,
            usage: 'off_session',
            payment_method_types: ['card'],
            payment_method_options: {
              card: {
                mandate_options: {
                  reference: '{{REFERENCE}}',
                  description: '{{DESCRIPTION}}',
                  amount: 2000000,
                  currency: 'inr',
                  start_date: 1675238400,
                  amount_type: 'maximum',
                  interval: 'month',
                  interval_count: 1,
                  supported_types: ['india'],
                },
              },
            },
          })

Which gave me the client_secret which i then used in

 stripe.confirmCardSetup(setupIntentClientSecret, {
      payment_method: {
        card: cardElement
      },
    })

And then i submitted a charge for $100 USD and i got this again

#

am i suppose to use PaymentIntent instead of SetupIntent? I thought it was one or the other.

distant coyote
#

Wait wait wait

#

What do you mean

And then i submitted a charge for $100 USD and i got this again

#

Setup Intents are used to save payment methods for future use. They do not collect funds

atomic schooner
distant coyote
#

So if you are trying to make a charge, you need to use a PaymentIntent

atomic schooner
#

so paymentintent for single charges and setupintent for recurring charges

distant coyote
#

No

#

You need to read the documents we provide thoroughly

#

Setup Intent creates a Payment Method and attaches it to a Customer

#

It does not charge funds, ever

atomic schooner
#

Oh yeah, I know that
That's what i used it for

#

So i added a payment method to a customer.

#

which showed up correclty

distant coyote
#

Okay, so if you are going to charge the customer using that payment method then you need to use a Payment Intent

atomic schooner
#

good lord, it finally worked.

#

all i had to do was use PaymentIntent and not SetupIntent.

#

Do you guys have a list of countries that have these weird requirements?

Also, what's the point of having these if the charges goes through regardless of these amount and start_date

distant coyote
#

Please read the docs. All of this is explained completely in our docs