#codename-mid9it_code

1 messages ยท Page 1 of 1 (latest)

slow patrolBOT
misty viperBOT
#

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.

slow patrolBOT
#

๐Ÿ‘‹ Welcome to your new thread!

โฒ๏ธ We'll be here soon! Typically we respond in a few minutes, but sometimes we might take a bit longer if the server is busy or if you have a particularly tricky question.

โฑ๏ธ We close idle threads, which makes them read-only. Once a thread is closed it won't be reopened, but you can always start a new thread if you have another question.

๐Ÿ”— This thread will always be available, even after it's closed. You can find it again using Discord's search, or you can save this link: https://discord.com/channels/841573134531821608/1220416441736495185

๐Ÿ“ Have more to share? Add more details, code, screenshots, videos, etc. below.

misty viperBOT
wheat wolf
#

The is my backend logic using PHP

if($_SERVER['REQUEST_METHOD'] == "POST"){
set_value($_POST);

$token = (isset($_POST['stripeToken'])) ? $_POST['stripeToken'] : null;
$crad_number = (isset($_POST['crad_number'])) ? $_POST['crad_number'] : null;
$month = (isset($_POST['month'])) ? $_POST['month'] : null;
$year = (isset($_POST['year'])) ? $_POST['year'] : null;
$cvv = (isset($_POST['cvv'])) ? $_POST['cvv'] : null;

// Validation
$errors = array();
if(empty($token)){
    $errors[] = 'Token is required';
}
if(empty($crad_number)){
    $errors[] = 'Crad Number is required';
}
if(empty($month)){
    $errors[] = 'MONTH name is required';
}
if(empty($year)){
    $errors[] = 'YEAR is required';
}
if(empty($cvv)){
    $errors[] = 'CVV is required';
}
if(empty($errors)){
        
    try{
        
         $stripe = new \Stripe\StripeClient('stripe test key');
        // Creat Customer In Stripe
        $customer = \Stripe\Customer::create(array(
            "email" => $email,
            "name" => $membernamedata,
            'source'  => $token,
        ));

        $finalamount = $amount*100;
        
        $paydesc = "Members Online Directory";

        // Create the subscription with the found product ID
         $subscription = $stripe->subscriptions->create([
            'customer' => $customer,
            'items' => [['price' => $product_id]],
            "currency" => $_currency,
            "description" => $paydesc,
            //'amount' => $finalamount,
        ]);
#

// Create Payment Intent
try {
$stripe = new \Stripe\StripeClient('stripe test key');

    // Create PaymentIntent 
    $intent = $stripe->paymentIntents->create([
        'amount' => $finalamount,
        'currency' => $_currency,
        'customer' => $customer->id, // If customer already exists
        'payment_method_types' => ['card'],
        'confirmation_method' => 'manual',
        'confirm' => true,
    ]);

// Check if 3DS is required:
if ($intent->status === 'requires_action') {
echo json_encode([
'requires_action' => true,
'client_secret' => $intent->client_secret,
'redirect_url' => $intent->next_action->redirect_to_url->url // Stripe will provide the redirect URL
]);
}

        } catch (\Stripe\Exception\Exception $e) {
            // Handle Stripe errors
            echo json_encode(['error' => $e->getMessage()]);
            
        }
#

<!-- Stripe JavaScript library -->
<script src="https://js.stripe.com/v2/"></script>
<script>
// Set your publishable key
Stripe.setPublishableKey('stripe test key');

// Callback to handle the response from stripe
function stripeResponseHandler(status, response) {
    if (response.error) {
        // Enable the submit button
        $('#payBtn').removeAttr("disabled");
        // Display the errors on the form
        $(".payment-status").html('<p>'+response.error.message+'</p>');
    }
    else{
        var form$ = $("#payment-form");
        // Get token id
        var token = response.id;
        // Insert the token into the form
        form$.append("<input type='hidden' name='stripeToken' value='" + token + "' />");
        // Submit form to the server
        form$.get(0).submit();
    }
}

$(document).ready(function(){
    // On form submit
$("#payment-form").submit(function (event) {
event.preventDefault(); // Prevent default form submission

// Disable the submit button 
$('#payBtn').attr("disabled", "disabled");

$.post("/join-us/payment.php") // Assuming payment.php is the form's action
    .done(function (data) { 
        if (data.requires_action) {
            // Redirect to Stripe for 3DS
            window.location.href = data.redirect_url; 
        } else {
            // Payment successful (no 3DS)
            console.log("Payment successful!");
        }
    })
    .fail(function () {
        // Re-enable submit button
        $('#payBtn').removeAttr("disabled");
        console.log("Error submitting to backend");
    });

});

        // Submit from callback
        return false;
    });

</script>

#

currently the only thing i see on my network tab using browser dev tools "Payment Successful" but then no stripe customer or subscription is created, neither am i redirected to an authentication page.

iron haven
#

Have you debugged what is happening on your server? It sounds like something may be going wrong there that is failing in a way that your frontend isn't expecting

#

Like are your create subscription and intent calls being made at all?

#

Also quick question: is there a reason that you are creating both a subscription and a payment intent here? Like are these two separate payments? Depending on how much you can change about your site, you can simplify this to only use the subscription's initial payment intent.

misty viperBOT
wheat wolf
finite robin
#

๐Ÿ‘‹ hopping in here since pompey has to head out

finite robin
#

If you're seeing "Payment Successful" in your console logs that means your client-side is getting data.requires_action: false, right? Have you checked what the response was from your servver and confirmed it's sending the response you expect?

wheat wolf
finite robin
#

Have you checked your stripe account to make sure thre requests are goingn through? If you have an account ID or example request ID I could take a look

wheat wolf
#

let me get you an example id

finite robin
#

Yeah an example ID would help a lot

wheat wolf
#

this is the one using a 3DS card

sub_1OwkCkDLt3ELpQcmnD19emZt

#

this is a non 3ds card

sub_1OwkE8DLt3ELpQcmmaCOSwp3

finite robin
#

For sub_1OwkCkDLt3ELpQcmnD19emZt I don't see you creating a separate payment at all so it doesn't seem like that part of your code is being hit

#

and backing up for a bit - if you're creating a subscription you shouldn't need to create a separate PI. You should be able to reuse the Payment Intent that is generated on subscription creation

wheat wolf
# finite robin For sub_1OwkCkDLt3ELpQcmnD19emZt I don't see you creating a separate payment at ...

yes it's not a separate payment. i'm new to setting up stripe. so i usually go to the docs as reference point but sometimes i really don't understand it all, like when i wanted to use subscription schedule for setting future subscription, meanwhile i should have just maintained my original create subscription code. it took days for me to finally get it. just as you and pompey have said about this paymentIntent thing

wheat wolf
#

or you could say the authentication for 3DS card. so say i revert my code back to the point i was able to successfully create customers and subscription successfully using non 3DS cards. (because i can no longer do so since i started using the paymentIntent). what will i do next

finite robin
#

Yeah I'd rip out the Payment Intent creation code, and then:

  • update your subscription creation code to expand latest_invoice.payment_intent
  • and then add logic that check latest_invoice.payment_intent.status = 'requires_action' to know if 3ds is required or not
wheat wolf
finite robin
#

Yes, but the key thing is expanding latest_invoice.payment_intent. Try it out - after adding the expansino you should be able to see the full Payment Intent in your sub creation response

wheat wolf
#

and what about the js side?

finite robin
#

Yeah, you'd still need to implement the next action redirect on your end

#

You had a lot of it already in the code you shared earlier

wheat wolf
#

at this point i don't even know anymore.

#

but at least i can remove paymentIntent block

finite robin
#

Yeah you weren't really reach the next action redirect code at all though since your server-side code wasn't behaving as you'd expect. If I were you I'd make the server-side changes, make sure your endpoint is returning the response you expect, and then move on to the client-side stuff

#

Also I know you mentioned this earlier but if you can convince your client to upgrade I'd strongly recommend stripejs v3. It'll make the client-side piece much easier

wheat wolf
#

okay just so we can round this up immediately.

#

i should

  1. Rip out the Payment Intent creation code
  2. Update your subscription creation code to expand latest_invoice.payment_intent
  3. add logic that check latest_invoice.payment_intent.status = 'requires_action' to know if 3ds is required or not
    4)............
finite robin
slow patrolBOT
tidal bough
#

You handling the 3DS redirect or iframe display manually should work, yes, but not the automatic handling

wheat wolf
tidal bough
#

That sounds right, and then inspect to handle the 3ds/action case

#

One little gotcha to be aware of this using that flow: after the 3ds action is handled, you need re-confirm the payment intent to complete the payment within 1 hour

#

If you wait more than 1 hour after that happens, the payment intent will revert back to requires_payment_method

wheat wolf
tidal bough
#

But yes, you might need to handle 3ds authentication for future subscription payments, that is a possibility

#

Stripe also offers a flow to handle this for you with customer emails to authenticate the payment

#

Let me grab information about that

wheat wolf
#

actually i'm using stripe.js v2 so i can't use a few of the new objects or methods

wheat wolf
#

but if you more versatile with v2 you could save my life rn lol

tidal bough
#

We strongly recommend upgrading to v3 as karbi said, v2 is far out of date and does not support much of our current functionality

wheat wolf
#

else i'd be long done with this job

#

please help me look at my js part of the code, is there anything i should change?

#

i forgot to add this part

// Create single-use token to charge the user
Stripe.createToken({
number: $('#crad_number').val(),
exp_month: $('#month').val(),
exp_year: $('#year').val(),
cvc: $('#cvv').val()
}, stripeResponseHandler);

        // Submit from callback
        return false;
tidal bough
#

This is the information about getting webhooks to handle authentication (3ds) when needed

#

Other that being an old pattern we no longer recommend, the token creation is not the problem, its that you need to handle authentication later.

wheat wolf
tidal bough
#

You really should get webhooks set up to learn about future payment failures

wheat wolf
tidal bough
#

you can configure your billing settings to send a hosted link to customers so they can complete the flow.

#

The links there are to the relevant dashboard settings

#

Confirm the PaymentIntent again
Server-side
Using the same endpoint you set up earlier, confirm the PaymentIntent again to finalize the payment and fulfill the order. The payment attempt fails and transitions back to requires_payment_method if it is not confirmed again within one hour.

wheat wolf
#

i'm looking at all the docs.

#

but these docs are mostly for the new stripe yeah? so i don't waste time on implementing things that won't be compatible with the stripe i use now

#

also is there a doc that shows how i can use webhooks to send the customers a hosted link to confirm payment and what happens after that?

#

theres so much docs and only got until tomorrow to finish up lol

tidal bough