#sxe - PI creation
1 messages · Page 1 of 1 (latest)
hi
What are you trying to do exactly?
I'm trying to run a mysql query at catch error
$paymentIntent = \Stripe\PaymentIntent::create([
'amount' => $Price*100,
'currency' => 'ron',
'payment_method_types' => ['card'],
'description' => 'username,
]);
$output = [
'clientSecret' => $paymentIntent->client_secret,
];
This is my intent
But, when I create it,
I want to get the payment_intent
So, if it gets an error, I can save it inside the query
echo json_encode($output);
} catch (Error $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
$stmt = $con->prepare("INSERT INTO Payments (UserID, PaymentCode) VALUES (?,?)");
$stmt->bind_param("is", $_SESSION['ID'], $thepaymentid);
$stmt->execute();
}
You want to get the payment intent id if creating it fails?
That's not possible as the id won't exist if creating it fails
Or are you saying something else will fail?
If, someone makes a payment,
with insufficient funds
or wrong ccv
or any other error,
I want to insert in the payments, the paymentcode
the payment_intent or some id which I can look for
for more details
You'll just need to grab the ID off the paymentintent object and store that then: https://stripe.com/docs/api/payment_intents/object#payment_intent_object-id
Complete reference documentation for the Stripe API. Includes code snippets and examples for our Python, Java, PHP, Node.js, Go, Ruby, and .NET libraries.
Thank you.
Can you help me with something too?
on create, can I get the card number, expiry date and year?
So I can save on the mysql the inputted card number & data & year & brand
No that breaks PCI compliance
You can retrieve the last 4 though: https://stripe.com/docs/api/payment_methods/retrieve
Complete reference documentation for the Stripe API. Includes code snippets and examples for our Python, Java, PHP, Node.js, Go, Ruby, and .NET libraries.
Yes, the last 4.
But if the payment is failed,
the last 4 numbers won't show.
at least on my retrieve, it doesn't show.
Yes, one sec.
pi_3Kuf1zLKPu9R0yzS06UfHbMt
On stripe dashboard it shows me.
But on my code, it shows blank.
If I use a valid payment id, it shows the 4digits,
if I use a non-valid payment id (unpaid) it shows blank.
What is the request id for the request where it's showing as blank?
Complete reference documentation for the Stripe API. Includes code snippets and examples for our Python, Java, PHP, Node.js, Go, Ruby, and .NET libraries.
Thank you,
You helpedme a lot.
helped me *
A last question.
Why doesn't my query run? With no php errors.
try {
// retrieve JSON from POST body
$jsonStr = file_get_contents('php://input');
$jsonObj = json_decode($jsonStr);
// Create a PaymentIntent with amount and currency
$paymentIntent = \Stripe\PaymentIntent::create([
'amount' => $Price*100,
'currency' => 'ron',
'payment_method_types' => ['card'],
'description' => ''.$_SESSION['Username'].'',
]);
$output = [
'clientSecret' => $paymentIntent->client_secret,
'id' => $paymentIntent->id,
];
$idoutput = $output['id'];
echo json_encode($output);
} catch (Error $e) {
$stmt = $con->prepare("INSERT INTO Table (UserID, Identity, IP) VALUES (?,?,?)");
$stmt->bind_param("iss", $_SESSION['ID'], $idoutput, $_SERVER['REMOTE_ADDR']);
$stmt->execute();
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
The error catch works, it shows me the error.
But the query is not being run.
Can you share this request id with me so I can further investigate it on my end?
One second.
req_oWfUzcnPNJyGHV
After looking at the request, it does not appear to match the above code. This request is a payment intent confirm request. Can you clarify what the ask is here?
Yes.
I'm trying to run a query inside the catch error.
The catch error works fine.
I get the error.
But, the query, does not work.
As my table does not get inserted with value(s)
Hello! To clarify, your PHP code above is creating a Payment Intent, but you gave us the request ID showing confirmation of that Payment Intent (which is coming from your client-side JavaScript, not your PHP code). Can you clarify if you're asking about the PHP code above or the client-side request?
It seems like your request to create the Payment Intent is working just fine, thus there's no error to catch server-side.
Also not sure what you mean by "I get the error." What error? Where?
Hi Rubeus.
I'm asking about the php code above.
I want to run the query in the server-side.
Okay, can you provide the request ID that matches up with that code being run?
Yes, Payment intent is working fine. I'm talking about the errors on catch error.
As in 'wrong cvv or insufficient funds'
Okay, let's back up a bit.
I think there's a mismatch here.
That request ID you just provided was not generated by this PHP code.
but by what?
The only Stripe API request coming from your PHP code above is when you call \Stripe\PaymentIntent::create to create a Payment Intent.
The request you provided above is not a Payment Intent creation request.
So, at a higher level, what are you really trying to do?
What's the goal?
Just ran the card number.
req_ibwV9g3jyzPYQv
This is the req for the last tried payment.
Yes, but that request is entirely client-side. It has nothing at all to do with your PHP code.
Have a look at this request: https://dashboard.stripe.com/test/logs/req_zlpi8a0xJ84WR2
See how it indicates it's from your PHP code?
That request to create the Payment Intent succeeded.
There is no error there.
So your PHP catch block never runs.
I'm trying to save as php variable the error, the paymentintent id, the 4 last digits, brand, exp_Month and exp_year
of this unsuccesfull payment.
I managed to save them on succesfull payments.
But on unsuccesfull, I did not manage.
You would need to write JavaScript to capture that information and relay it back to your server if that's what you want to do.
Can't I do it in php? Because I am not familiar with JS. I'm using your source-code.
You cannot do what you're describing with PHP alone, no.
Client-side, in your JavaScript, you're calling stripe.confirmPayment, correct?
const { error } = await stripe.confirmPayment({
elements,
confirmParams: {
// Make sure to change this to your payment completion page
return_url: "https://campionatlol.ro/Cos/Plata/Finalizat",
},
});
Yes.
Yep. So that error there will be populated with the error information you want. You need to write code to relay that information to your server so your PHP code can get it and write it to your database.
Do you have docs for that?
No, this is not something that's part of a typical integration.
You would need to build this part yourself. It's possible, but we don't have a guide for this specifically.
Back in 2020, 2021, I used the charge api and I managed to built it only by php.
I'll look up for what I need to do to make it happen for JS too.
Yep, that was a very different API for a very different time and payments landscape.
There are now a lot of async payment flows, with more coming, so a lot more is happening client-side these days to support those flows.
Out of curiosity, why do you want to capture this info?
Because, if a user clicks on 'Pay'
But his browser somehow quits,
Or whatever happens,
I want to make a log for every payment he ever made.
On his console on user panel.
So he can see that the last payment was accepted / denied due ... error.
Interesting use case. Thanks for sharing those details!
So, if that's your goal, you'd probably be better off listening to Events with a Webhook Endpoint.
We have a payment_intent.payment_failed Event that sounds like it would suit your use case: https://stripe.com/docs/api/events/types#event_types-payment_intent.payment_failed
You can set up a Webhook Endpoint to listen for that event and write the info to your database.
I don't want to reach my head with something more advanced, because as I said, my main language is PHP and I do not understand JS at all.
Thank you for the suggestion tho.
I'm stuck here, thinking what should I do.
Setting up a Webhook Endpoint is probably less work for you than figuring out the JavaScript, to be honest.
It would be all PHP.
My head tells me that, okay, let's see if is set const { error}, if so, run a php code, if not, do nothing, but I can not run a php code inside a js even if I add an if statement for the js case
Are webhooks live?
What do you mean? Live how?
Dumb question, nevermind.
Have a look here: https://stripe.com/docs/webhooks/quickstart
Do you have a live-demo?
The basic idea is that you set up a URL on your server, like https://example.com/webhooks.php, and you have some code there that listens for Events being sent from Stripe and processes them accordingly.
You can use Stripe CLI to test webhooks locally: https://stripe.com/docs/webhooks/test
It's not really the kind of thing that makes sense for a live demo.
Do I have to create an payment form?
This would be alongside your payment form, not a replacement for it.
There's a good overview here, including a video: https://stripe.com/docs/webhooks
Once this is set up what would happen is attempts to confirm a Payment Intent that fail (like the one in your screenshot above) would generate a payment_intent.payment_failed Event, and we would send that directly to your server.
Hmm.
In fact, those Events are already being generated, they're just not being sent anywhere. Have a look here: https://dashboard.stripe.com/test/events/evt_3Kv6xiLKPu9R0yzS14PIki5X
Do webhooks have different fees?
Nope, no fees.
To clarify, this is not a replacement for what you're building now. It's something you set up in addition to that.
Instead of creating payment intent,
Like you don't take payments with webhooks.
I create.. the webhook, right?
You still create Payment Intents.
Your existing code would not change.
Your payment form would not change.
Webhooks are a way for Stripe to send Events directly to your server.
That's all.
And where do I tell where my hook.php is ?
That's entirely up to you, you set that part up however you want.
// This is your Stripe CLI webhook secret for testing your endpoint locally.
$endpoint_secret = 'xxxxxx';
$payload = @file_get_contents('php://input');
$sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];
$event = null;
try {
$event = \Stripe\Webhook::constructEvent(
$payload, $sig_header, $endpoint_secret
);
} catch(\UnexpectedValueException $e) {
// Invalid payload
http_response_code(400);
exit();
} catch(\Stripe\Exception\SignatureVerificationException $e) {
// Invalid signature
http_response_code(400);
exit();
}
// Handle the event
switch ($event->type) {
case 'payment_intent.succeeded':
$paymentIntent = $event->data->object;
// ... handle other event types
default:
echo 'Received unknown event type ' . $event->type;
}
http_response_code(200)
Yeah, that's the example code. See that case 'payment_intent.succeeded': line? The sample code is set up to listen for payment_intent.succeeded events, but you can change that or add more cases for other event types, then add code to run when those Event types are received.
So you can have a case for payment_intent.payment_failed and run your database query when you get those events.
Yes, with modifications so the code does what you want.
Right now that code doesn't do anything.
Well, it verifies the signature, but it doesn't do anything after that.
require 'vendor/autoload.php';
// This is your Stripe CLI webhook secret for testing your endpoint locally.
$endpoint_secret = 'xxxxx';
$payload = @file_get_contents('php://input');
$sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];
$event = null;
try {
$event = \Stripe\Webhook::constructEvent(
$payload, $sig_header, $endpoint_secret
);
} catch(\UnexpectedValueException $e) {
// Invalid payload
http_response_code(400);
exit();
} catch(\Stripe\Exception\SignatureVerificationException $e) {
// Invalid signature
http_response_code(400);
exit();
}
// Handle the event
switch ($event->type) {
case 'account.updated':
$account = $event->data->object;
case 'account.external_account.created':
$externalAccount = $event->data->object;
case 'account.external_account.deleted':
$externalAccount = $event->data->object;
case 'account.external_account.updated':
$externalAccount = $event->data->object;
case 'balance.available':
$balance = $event->data->object;
case 'billing_portal.configuration.created':
default:
echo 'Received unknown event type ' . $event->type;
}
http_response_code(200);
Do I have to keep the switch ($event->type) {
?
What do you mean?
Are you familiar with how switch statements work?
in php? yes, somehow.
In js? no.
This is PHP.
Have you used switch statements before? Are you comfortable with them?
Would you be more comfortable with if statements instead?
I am more confortable with if statements.
Okay, so you can use if statements instead of the switch here. You can do if ($event->type == 'payment_intent.payment_failed') { /* Run your DB code here */ } for example.
case 'payment_intent.payment_failed':
......dosomething.....
break;
``` ?
That's all part of the switch statement.
My questions are now, how does the hook.php know the payment intent id, and, how can I get the objects?
But if you're more comfortable with if statements I would recommend using those instead.
The Payment Intent object will be inside the Event.
Add code to log out $event and then trigger an event so you can see what you get.
Or you can look in your Dashboard for details, under Developer > Webhooks
if ($event->type == 'payment_intent.payment_failed') { do }
if ($event->type == 'payment_intent.succeeded') { do }
``` ?
Yep.
Ok, my question now:
How can I get the var dump of the outcoming infos?
Like, how can I get the last 4 digits
of the intent
The Payment Intent will have a payment_method property. You can fetch that Payment Method and get the last4 and whatnot from there.
fetch it, as retrieve it?
as in
$payment_intent = $stripe->paymentIntents->retrieve($code,
['expand' => ['payment_method']]
);
``` ?
No, you can fetch the Payment Method directly using its ID: https://stripe.com/docs/api/payment_methods/retrieve
I mean, either one will work, but the Event will contain the Payment Intent itself, but whatever works for you. 🙂
The ID of the Payment Intent?
Yes.
It will be in the Event's object property: https://stripe.com/docs/api/events/object#event_object-data-object
So it would be $event->data->object->id
$intent_id = $event->data->object->id;
$stripe = new \Stripe\StripeClient(
'x'
);
$payment_intent = $stripe->paymentIntents->retrieve($intent_id,
['expand' => ['payment_method']]
);
```?
Looks like it's worth a try to see if it works as expected!
So I just place a payment now?
That depends on the type of event you want to test.
Oh, do you know what I did not think of?
But yes, you would use a test card of some sort to attempt a payment.
I'm comparing the description to $_session['Username']
if is equal, then run the query.
webhook won't be equal ever.
because it's being sent from stripe server.
I recommend you add that kind of detail to the Payment Intent's metadata so it's available when you get the event: https://stripe.com/docs/api/payment_intents/object#payment_intent_object-metadata
I've added the description, as user's username.
$paymentIntent = \Stripe\PaymentIntent::create([
'amount' => $Price*100,
'currency' => 'ron',
'payment_method_types' => ['card'],
'description' => ''.$_SESSION['Username'].'',
]);
This.
And then later I am checking if $_SESSION['Username'] == description
then { .. }
Ah, okay, that will probably work as well.
so other users can't access non authorized payments.
Isn't webhook being sent from stripe server?
I'm placing a payment in 3 minutes, a failed payment,
So I can check if query works.
Yes, webhooks are sent from Stripe's servers directly to yours.
I have placed an payment.
How do I check if the webhook sent any response?
query did not work, no new entries on the DB.
You can look in your Dashboard at the Event.
I think it was this one? https://dashboard.stripe.com/events/evt_3Kv7lV2O1y39ZmSu1P7opIFd
Pending webhook response
A webhook that is subscribed to the event hasn't successfully responded yet
evt_3Kv7lvLKPu9R0yzS1vbqHpLm
Wait, that was the wrong link!
Ah, your URL is set incorrectly.
We got a 301 redirect, which we consider a failure.
How do I modify it?
You can change it in the Dashboard. The URL looks like it should be https://campionatlol.ro/Cos/Plata/Finalizat/hook.php (no www)
Also I noticed you enabled all events on it; you should only enable the events you need to listen for.
I have to run, but @elder ibex can help you further!
Done
@unkempt urchin
Thank you.
For your time and patience
really
helpmed me a lot.
Uncaught TypeError: array_keys(): Argument #1 ($array) must be of type array, null given in /home/famiscar/public_html/domenii/campionatlol.ro/Cos/Plata/vendor/stripe/stripe-php/lib/StripeObject.php:279
👋 give me a minute to catch up
Hi, sure.
We were trying to set up the webhook.
⚠️ Webhook error while validating signature.
I found what was the error.
I was running the secret code from CLI
Now I fixed it with the one from my dashboard.
So you have it working now?
No.
I get these errors.
PHP Warning: Trying to access array offset on value of type null in campionatlol.ro/Cos/Plata/Finalizat/hook.php on line 277
PHP Warning: Trying to access array offset on value of type null in campionatlol.ro/Cos/Plata/Finalizat/hook.php on line 277
PHP Warning: Trying to access array offset on value of type null in campionatlol.ro/Cos/Plata/Finalizat/hook.php on line 278
PHP Warning: Trying to access array offset on value of type null in campionatlol.ro/Cos/Plata/Finalizat/hook.php on line 278
PHP Warning: Trying to access array offset on value of type null in campionatlol.ro/Cos/Plata/Finalizat/hook.php on line 279
PHP Warning: Trying to access array offset on value of type null in campionatlol.ro/Cos/Plata/Finalizat/hook.php on line 279
PHP Warning: Trying to access array offset on value of type null in campionatlol.ro/Cos/Plata/Finalizat/hook.php on line 280
PHP Warning: Trying to access array offset on value of type null in campionatlol.ro/Cos/Plata/Finalizat/hook.php on line 280
And also, my query still does not being run.
The error lines are these>
$brand = $payment_intent['payment_method']['card']['brand'];
$exp_month = $payment_intent['payment_method']['card']['exp_month'];
$exp_year = $payment_intent['payment_method']['card']['exp_year'];
$last4 = $payment_intent['payment_method']['card']['last4'];
What kind of event type is this code for?
for context:
$intent_id = $event->data->object->id;
$stripe = new \Stripe\StripeClient(
'x'
);
$payment_intent = $stripe->paymentIntents->retrieve($intent_id,
['expand' => ['payment_method']]
);
$amount_received = $payment_intent['amount_received'];
$currency = $payment_intent['currency'];
$description = $payment_intent['description'];
$brand = $payment_intent['payment_method']['card']['brand'];
$exp_month = $payment_intent['payment_method']['card']['exp_month'];
$exp_year = $payment_intent['payment_method']['card']['exp_year'];
$last4 = $payment_intent['payment_method']['card']['last4'];
// Handle the event
if ($event->type == 'payment_intent.payment_failed')
{
$stmt2 = $con->prepare("INSERT INTO PlatiEfectuate (UserID) VALUES (?)");
$stmt2->bind_param("i", $_SESSION['ID']);
$stmt2->execute();
}
if ($event->type == 'payment_intent.succeeded')
{
}
I'm trying to save as variables the last 4, exp year, month, brand
description, currency, amount_received.
I think now it may work, my query worked.
But,
Uncaught (Status 404) (Request req_EIsEXAuE909yiN) No such payment_intent: 'ch_3Kv82gLKPu9R0yzS03ZC3r2X'
If you're using this code for payment_intent.payment_failed events then those Payment Intents wouldn't have a Payment Method, so $payment_intent[payment_method] would be null
$intent_id = $event->data->object->id;
You need only call the Payment Intent specific code when you're dealing with payment_intent.* events - this code won't work with other event types because those return a different object
So I don't need paymentintent retrieve ?
Because I can get the last4 or other datas, from $event ?
Let me back up - as of right now your webhook event listens to a lot of different kinds of events, not just events that would return Payment Intents. From the limited amount of code you've shared it looks like your webhook handler is alwayas taking the event object ID and trying to retrieve it as a Payment Intent. This will work with event types like payment_intent.succeeded and payment_intent.payment_failed but it won't work for something like a charge.failed event because the object ID will be a Charge, not a Payment Intent.
You need to change your code to only make the request to retrieve the Payment Intent if you know it's a from a payment_intent.* event (so move your retrieval logic after the if block that checks the event type)
\Stripe\Stripe::setApiKey('x');
$endpoint_secret = 'x';
$payload = @file_get_contents('php://input');
$event = null;
try {
$event = \Stripe\Event::constructFrom(
json_decode($payload, true)
);
} catch(\UnexpectedValueException $e) {
// Invalid payload
echo '⚠️ Webhook error while parsing basic request.';
http_response_code(400);
exit();
}
if ($endpoint_secret) {
// Only verify the event if there is an endpoint secret defined
// Otherwise use the basic decoded event
$sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];
try {
$event = \Stripe\Webhook::constructEvent(
$payload, $sig_header, $endpoint_secret
);
} catch(\Stripe\Exception\SignatureVerificationException $e) {
// Invalid signature
echo '⚠️ Webhook error while validating signature.';
http_response_code(400);
exit();
}
}
$intent_id = $event->data->object->id;
// Handle the event
if ($event->type == 'payment_intent.payment_failed')
{
$stmt2 = $con->prepare("INSERT INTO PlatiEfectuate (Factura) VALUES (?)");
$stmt2->bind_param("s", $intent_id);
$stmt2->execute();
}
if ($event->type == 'payment_intent.succeeded')
{
}
http_response_code(200);
This is my code, right now.
s alwayas taking the event object ID and trying to retrieve it as a Payment Intent.
- Yes, I desired to make a retrieve so I can save as variable the transaction's informations.
My only desired events are payment_intent.succeeded and payment_intent.payment_failed
If you ONLY want to process payment_intent.succeeded and payment_intent.payment_failed events then you need to update your webhook endpoint in the dashboard to only accept those two events
Or, for what should I look for. Charge or payment intent?
My intention is to check if my desired amount got in my stripe balance.
As I said - if you jsut want those two events (since those are the only two you have code to process) you need to remove the others from your webhook
I removed the others.
But, i'm not sure what I need. Charge or payment intent.
Which one of these two, shows if the payment successfuly entered in my stripe balance?
Do you need to know when the balance is available, or just if the charge is succeeded?
I need to know,
If I ask the customer 30 RON
And he pays me 30 RON
Did the money entered in my stripe account, or it's on hold?
If his bank account holds the money, and those 30 RON did not enter in my stripe account, I see that as unpaid.
If a customer pays you those funds start off as pending and eventually transition to available (at which point you can pay them out).
I need to know if money entered in my stripe account.
Not if 'they are gonna enter, sometime'.
What should I use? Charge or payment.intent ?
You still haven't made it clear what exactly you want - do you want to know when the funds are available to pay out? Or do you just want to know when the charge is successful and the funds are pending in your account? The amount of time it takes for funds to become available depends on the country your account is in (see https://stripe.com/docs/payouts#payout-speed)
I want to know when the charge is succesful and the funds are pending in my account.
Then relying on payment_intent.succeeded is fine.
Okay, thank you!
To get access on the informations sent by the webhook,
like payment_id, last4, expire year and so on
I can use the format :
$intent_id = $event->data->object->id;
``` ?
$card = $event->card->brand;``` ?
$card = $event->card->brand;
No, this won't work - you already have code that's getting $brand above, why are you changing it to this?
I removed this part
$payment_intent = $stripe->paymentIntents->retrieve($intent_id,
['expand' => ['payment_method']]
);
``` as I thought it was unnecesarly anymore.
I need to put it back
?
Yes, you should put it back - i only suggested you remove it when you were listening to ALL event types
Ok, and the
$intent_id = $event->data->object->id;
``` should show me the intent_id ?
I still get this error:
Trying to access array offset on value of type null
Is it with a payment_intent.payment_failed event? As I mentioned earlier, you're getting an error with those events because they don't have a Payment Method to begin with.
But how can I get the last 4 digits of a card, when payment_failed ?
You can just find that under charges[0][payment_method_details]. Take a look at one of the events and you'll see all the information you need is already tehre