#nikky_webhooks
1 messages ¡ Page 1 of 1 (latest)
đ 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/1361664146768269333
đ Have more to share? Add more details, code, screenshots, videos, etc. below.
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.
- nikky_webhooks, 13 hours ago, 36 messages
Here are the relevant parts of my current webhook version. try {
$event = \Stripe\Webhook::constructEvent($payload, $sig_header, $endpoint_secret);
error_log('Received event: ' . $event->type . ' ID: ' . $event->id);
if ($event->type == 'payment_intent.succeeded') {
$paymentIntent = $event['data']['object'];
$charge = $paymentIntent['charges']['data'][0];
$charge_id = $charge['id'];
$source = $charge['payment_method']; // Extract the payment method source
$metadata = $charge['metadata']['vendor_earnings'];
$transfers = json_decode($metadata, true);
foreach ($transfers as $vendor) {
$charge_params = [
'amount' => $vendor['transfer_amount'], // Amount to transfer
'currency' => 'usd',
'source' => $source, // Use the payment method source directly
'destination' => [
'account' => $vendor['stripe_account_id'], // Vendor's Stripe account
]
];
$idempotency_key = $charge_id . '_' . $vendor['stripe_account_id'];
error_log('Creating charge directly for vendor with idempotency key: ' . $idempotency_key);
try {
$response = \Stripe\Charge::create(
$charge_params,
['idempotency_key' => $idempotency_key]
);
I want the webhook to work with the 4242 test card as though I was using the 0077 test card
The card 007 7 is to bypass the pending balance
Otherwise, you'll need to set the source_transfer attribut:
https://docs.stripe.com/connect/separate-charges-and-transfers?platform=web&ui=stripe-hosted#transfer-availability
I did that req_cNTtcZq4P25PBV try {
$event = \Stripe\Webhook::constructEvent($payload, $sig_header, $endpoint_secret);
error_log('Received event: ' . $event->type . ' ID: ' . $event->id);
if ($event->type == 'payment_intent.succeeded') {
$paymentIntentId = $event['data']['object']['id']; // PaymentIntent ID
$paymentIntent = \Stripe\PaymentIntent::retrieve($paymentIntentId); // Retrieve the PaymentIntent
$latest_charge = $paymentIntent->latest_charge; // Get the latest charge
error_log('Latest charge ID: ' . $latest_charge);
$metadata = $paymentIntent['charges']['data'][0]['metadata']['vendor_earnings'];
$transfers = json_decode($metadata, true);
foreach ($transfers as $vendor) {
$transfer_params = [
'amount' => $vendor['transfer_amount'], // Amount to transfer
'currency' => 'usd',
'source_transaction' => $latest_charge, // Use latest charge as source_transaction
'destination' => $vendor['stripe_account_id'], // Vendor's connected account
]; a subsequnt test with the same webhook and 0077 test shows my data structure is still available and working as expected req_QeFUcbGfYoM9Wt
I did that req_cNTtcZq4P25PBV
you didn't added source_transaction
Are you referring to my js or my webhook? It is here in my webhook unless the structure should also have it in the event type. 'source_transaction' => $latest_charge, // Use latest charge as source_transaction
'destination' => $vendor['stripe_account_id'], // Vendor's connected account
I'm not seeing it in this request Body req_cNTtcZq4P25PBV
You are not passing the source_transaction
It is there, and I get a different error. req_SCsOauEE8GEZTI. My webhook is not retrieving the ch_ as expected and it is using cs_test_ in lieu of it. if ($event->type == 'payment_intent.succeeded') {
$paymentIntentId = $event['data']['object']['id']; // PaymentIntent ID
$paymentIntent = \Stripe\PaymentIntent::retrieve($paymentIntentId); // Retrieve the PaymentIntent
$latest_charge = $paymentIntent->latest_charge; // Get the latest charge
error_log('Latest charge ID: ' . $latest_charge);
It is there, and I get a different error. reqSCsOauEE8GEZTI. My webhook is not retrieving the ch as expected and it is using cstest in lieu of it. if ($event->type == 'payment_intent.succeeded') {
$paymentIntentId = $event['data']['object']['id']; // PaymentIntent ID
$paymentIntent = \Stripe\PaymentIntent::retrieve($paymentIntentId); // Retrieve the PaymentIntent
$latest_charge = $paymentIntent->latest_charge; // Get the latest charge
error_log('Latest charge ID: ' . $latest_charge);
req_SCsOauEE8GEZTI
You are passing the Checkout Session Id which is wrong, you should pass a Charge Id.
Right, and my webhook and js are not retrieving the ch_ as expected. The cs_test_ is being passed. This is the relevant js snippet const response = await fetch(https://api.stripe.com/v1/payment_intents/pi_xxx, {
method: 'GET',
headers: {
'Authorization': 'Bearer YOUR_SECRET_KEY',
'Content-Type': 'application/x-www-form-urlencoded'
}
});
const paymentIntent = await response.json();
const latestCharge = paymentIntent.latest_charge;
console.log('Latest Charge ID:', latestCharge);
my keys were added where needed. This is the webhook part. $paymentIntent = \Stripe\PaymentIntent::retrieve('pi_xxx');
$latestCharge = $paymentIntent->latest_charge;
error_log('Latest Charge ID: ' . $latestCharge);
So now it's fixed ?
No. My webhook and js are not retrieving the ch_ as expected. The cstest is being passed, instead.
No, it is not fixed. My webhook and js are not retrieving the ch_ as expected. The cstest is being passed, instead.
Hi. I'm taking over from my colleague. Please, give me a moment to catch up.
are not retrieving the ch_ as expected
What do you specifically mean by this?
So, I'm trying to retrieve the ch_ via my js and webhook to use it at source_transaction, but the ChargeId isn't being made available. I can see it in the charge.updated event.
Can you print the full paymentIntent?
That is part of the error. The pi_ is also returning as the cs_test. All I am gettting is the cs_test.
The pi_ is also returning as the cs_test
What do you mean by this?
What event type are you looking at?
Are you sure you're looking at payment_intent.succeeded and not checkout.session.completed? These are different events containing different objects, PaymentIntent and Checkout Session, respectively.
evt_3RDuE1GdbZlAW4WT1SYLgUOw
Okay, this is payment_intent.succeeded event with PaymentIntent as payload.
Where do you see the cs_test_ ID?
It is on checkout.session.complete and on my console/error logs. I have tried to retrieve other attributes from the events, and I can only get the cs_test.
I cannot seem to retrieve pi_ or ch_ to use in my webhook. or js.
Each event type has a different payload, you can't process them in the same way.
Right, but the webhook still isn't extracting the charge ID from the charge.updated event. if ($event->type == 'charge.updated') {
$charge = $event['data']['object']; // Extract charge object
$charge_id = $charge['id']; // Get charge ID (ch_XXX)
error_log('Charge updated event - Charge ID: ' . $charge_id);
// Send the charge ID to the front end for logging or further processing
header('Content-Type: application/json');
echo json_encode(['charge_id' => $charge_id]);
}
Why do you want to listen to all 3 events?
It is part of my webhook functionality. As of now, I need the chargeID for source_transaction.
error_log('Charge updated event - Charge ID: ' . $charge_id);
You don't see ID printed here?
Not at all.
Could you please print the full object in all cases where there's a problem.
try {
$event = \Stripe\Webhook::constructEvent($payload, $sig_header, $endpoint_secret);
error_log('Received event: ' . $event->type . ' ID: ' . $event->id);
// Handle `payment_intent.succeeded` event
if ($event->type == 'payment_intent.succeeded') {
$paymentIntentId = $event['data']['object']['id']; // PaymentIntent ID
$paymentIntent = \Stripe\PaymentIntent::retrieve($paymentIntentId); // Retrieve the PaymentIntent
$latest_charge = $paymentIntent->latest_charge; // Get the latest charge
error_log('Latest charge ID: ' . $latest_charge);
$metadata = $paymentIntent['charges']['data'][0]['metadata']['vendor_earnings'];
$transfers = json_decode($metadata, true);
foreach ($transfers as $vendor) {
$transfer_params = [
'amount' => $vendor['transfer_amount'], // Amount to transfer
'currency' => 'usd',
'source_transaction' => $latest_charge, // Use latest charge as source_transaction
'destination' => $vendor['stripe_account_id'], // Vendor's connected account
];
try {
$response = \Stripe\Transfer::create($transfer_params);
error_log('Transfer successful for vendor: ' . $vendor['stripe_account_id'] . ' Transfer ID: ' . $response->id);
} catch (Exception $e) {
error_log('Error processing transfer for vendor: ' . $vendor['stripe_account_id'] . ' - ' . $e->getMessage());
}
}
error_log('Webhook successfully processed: payment_intent.succeeded event.');
}
if ($event->type == 'charge.updated') {
$charge = $event['data']['object']; // Extract charge object
$charge_id = $charge['id']; // Get charge ID (ch_XXX)
error_log('Charge updated event - Charge ID: ' . $charge_id);
// Send the charge ID to the front end for logging or further processing
header('Content-Type: application/json');
echo json_encode(['charge_id' => $charge_id]);
}
Sorry, I mean, the Event objects, not code.
req_kVPLfuuEyWmxjO This error on the log is only with the 4242 test card, and it does not result in an event object. Subsequent testing with the 0077 test card are successful and indicating my data structure is correct. The right connected accounts are receiving the right amount. Here is the successful event object evt_1RE8vCGdbZlAW4WTG1120O41.
The issue is not fixed because I need the transfer event to be successful with the 4242 card the same as it is with the 0077. I was able to previously structure my code to operate the same with either test card, but I have since made updates and didn't retain that version.
You're passing a Checkout Session ID where a Charge ID needs to go, which produces the error.
Where do you handle the checkout.session.completed event in your code?
I am not using it because it does not pas the charge ID. That is why I am using charge.updated. I am aware that I need the charge ID at source transaction and that it is using the cs_test, instead.
I am not using checkout.session.complete because it does not pass the charge ID. That is why I am using charge.updated. I am aware that I need the charge ID at source transaction and that it is using the cs_test, instead.
I don't understand what you're trying to do here passing the sesison id into source_transaction -- can you explain more?
https://dashboard.stripe.com/test/logs/req_kVPLfuuEyWmxjO
Stepping in for vanya who had to step away
I understand completely that charge id needs to be passed at source transaction, and that is how I have structured my code, but it keeps passing the session id, instead.
Nothing in the code you shared suggests that should/would happen
$latest_charge = $paymentIntent->latest_charge; // Get the latest charge
// ...
'source_transaction' => $latest_charge, // Use latest charge as source_transaction
This seems to be the only use here, which suggests $latest_charge somehow has an unexpected value
Right, and this is the closest to what I want it to do. A previous version try {
$event = \Stripe\Webhook::constructEvent($payload, $sig_header, $endpoint_secret);
error_log('Received event: ' . $event->type . ' ID: ' . $event->id);
if ($event->type == 'payment_intent.succeeded') {
$paymentIntent = $event['data']['object'];
$payment_intent_id = $paymentIntent['id']; // Payment intent ID
$charge = $paymentIntent['charges']['data'][0];
$charge_id = $charge['id']; // Charge ID for idempotency
$metadata = $charge['metadata']['vendor_earnings'];
error_log('Payment Intent ID: ' . $payment_intent_id);
error_log('Charge ID: ' . $charge_id);
$table_name = 'processed_charges';
$processed_charge = $wpdb->get_var($wpdb->prepare(
"SELECT charge_id FROM $table_name WHERE charge_id = %s",
$charge_id
));
if ($processed_charge) {
error_log('Charge already processed. Skipping.');
http_response_code(200);
exit();
}
$transfers = json_decode($metadata, true);
foreach ($transfers as $vendor) {
// Calculate the application fee
$application_fee = $vendor['amount'] - $vendor['transfer_amount'];
// Prepare charge parameters
$charge_params = [
'amount' => $vendor['amount'], // Total amount charged to the customer
'currency' => 'usd',
'source' => $charge['payment_method'], // Source from the original charge
'destination' => [
'account' => $vendor['stripe_account_id'], // Vendor's Stripe account
],
'application_fee_amount' => $application_fee, // Fee retained by the platform
];
$idempotency_key = $charge_id . '_' . $vendor['stripe_account_id'];
error_log('Creating destination charge with idempotency key: ' . $idempotency_key);
try {
$response = \Stripe\Charge::create(
$charge_params,
['idempotency_key' => $idempotency_key]
);
error_log('Destination charge created successfully for destination: ' . $vendor['stripe_account_id'] . ' Charge ID: ' . $response->id);
} catch (Exception $e) {
error_log('Error creating destination charge for Stripe ID ' . $vendor['stripe_account_id'] . ': ' . $e->getMessage());
}
}
Before adding the source_transaction, I can use this version and have successful transfers to the right connected accounts and with the right amounts, but that is only with the 0077 test card. My intent is to have that also occur with the 4242 test card, and what I have been told is that I need the source_transaction to replicate the 0077 functionality with the 4242 test card. I can add source_transaction, but I am not able to extract the charge id to use for it.
req_FMjx5l1InqRWH2
Taking a look
This transfer request is not using source_transaction to link the pending payment
The reason this works with the 0077 bypass pending card is because it adds balance right away
while the 4242 card (and all "normal" test cards) involve a pending balance step, which is what source_transaction is meant to address
So you need to supply the charge ID there for those transfers
Which it seems you already understand, and looks like you code tries to do
I don't understand how/why you get the checkout session ID there instead
It feels like this is not the code that is running for these tests. Can you add some explicit logging to verify the code you expect is running? For example, first before the transfer creation with source_transaction , both to see that it happens and to verify the $latest_charge ID you're about to pass
Then you need to step backward to understand why it has the wrong value, if thats the case