#carlos-fontana_api

1 messages · Page 1 of 1 (latest)

quaint plankBOT
#

đź‘‹ 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/1331644260033888308

📝 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.

daring bone
#

Hello, I’m experiencing an issue with the destination_payment field in the transfer object when working with a PaymentIntent that includes connected account transfers. Here’s the context:
Setup:
• We’re using Stripe Connect with on_behalf_of and transfer_data.destination set to the connected account’s stripeAccountId.
• application_fee_amount is also being used to deduct a platform fee.
• The PaymentIntent is created successfully, and the transfer object is included in the response.
Issue:
• When retrieving the PaymentIntent and expanding the transfer object (via expand: ['charges.data.transfer']), the transfer object does not include a valid destination_payment field.
• Based on the documentation, the destination_payment field should contain the connected account’s Charge ID (e.g., ch_xxx), but it’s either missing or not properly populated.
Steps Tried:
• Confirmed that the merchant.stripeAccountId is correct and active.
• Ensured the PaymentIntent was created with transfer_data.destination and on_behalf_of parameters.
• Retrieved the PaymentIntent with the necessary expanded fields.
Expected Behavior:
• The destination_payment field should reference the connected account’s charge ID.
Actual Behavior:
• The destination_payment field is missing or invalid (e.g., not starting with ch_).

echo sirenBOT
daring bone
#

I have this code when stripe calls a webhook on my server

 case 'payment_intent.succeeded': {
      const { metadata, amount, latest_charge } = event.data.object;

      if (latest_charge) {
        try {
          // Recuperar el charge con transferencia expandida
          const charge = await stripeClient.charges.retrieve(latest_charge, {
            expand: ['transfer'],
          });
      
          console1.log('charge.transfer:', JSON.stringify(charge.transfer));
          
      
          if (
            charge.transfer &&
            typeof charge.transfer !== 'string' &&
            typeof charge.transfer.destination_payment === 'string' &&
            charge.transfer.destination_payment.startsWith('ch_') // Asegurarse de que es un ID de charge
          ) {
            // Actualizar metadata en el destination_payment
            const updatedCharge = await stripeClient.charges.update(
              charge.transfer.destination_payment,
              { metadata }
            );
      
            console1.log('âś… Metadata actualizada:', updatedCharge.metadata);
          } else {
            console1.log('⚠️ No se encontró un destination_payment válido o no es un charge.');
          }
        } catch (error) {
          console1.log('🚨 Error al procesar el charge:', error.message);
        }
      } else {
        console1.log('⚠️ No se encontró latest_charge en el Payment Intent.');
 
     }
#

Basically, what I’m doing is working with Connected Accounts and implementing Direct Charges. This is working, but what I’d like to do is update the metadata on the customer’s transaction so that I can access all the relevant information, specifically the details of who made the payment

dense cradle
#

Hi there đź‘‹ stepping through what you shared.

#

You mean Destination Charges?

daring bone
#

Yes, Destination Charges

#
  /* Gets merchant if set */
      const merchant = await this.merchantInteractor.getMerchantByEventId(
        eventId
      )
      if (merchant && merchant?.stripeAccountId) {
        params.application_fee_amount = Math.ceil(trx.total - trx.donation)
        params.transfer_data = {
          destination: merchant.stripeAccountId,
        }
        params.on_behalf_of = merchant.stripeAccountId
      }

      if (!merchant || !merchant.stripeAccountId)
        throw new Error(
          '[PaymentIntent]: Merchant account must have a gateway Id.'
        

      const paymentIntent = await stripeClient.paymentIntents.create({
        ...params,
        description,
        metadata,
      })
dense cradle
#

Can you share the Transfer object contents that you see when you retrieve that object?

daring bone
#

Sure

#

{
"id": "tr_3Qk5YdEsYIHxK4oE1AVEivG3",
"object": "transfer",
"amount": 11596,
"amount_reversed": 0,
"balance_transaction": "txn_3Qk5YdEsYIHxK4oE1KuUQFIA",
"created": 1737558572,
"currency": "eur",
"description": null,
"destination": "acct_1QjgKPGaXe5d9XgQ",
"destination_payment": "py_1Qk5YeGaXe5d9XgQJPaZfgkk",
"livemode": false,
"metadata": {},
"reversals": {
"object": "list",
"data": [],
"has_more": false,
"total_count": 0,
"url": "/v1/transfers/tr_3Qk5YdEsYIHxK4oE1AVEivG3/reversals"
},
"reversed": false,
"source_transaction": "ch_3Qk5YdEsYIHxK4oE1DNxuuzB",
"source_type": "card",
"transfer_group": "group_pi_3Qk5YdEsYIHxK4oE1RajA1vm"
}

dense cradle
#

I see both destination and destination_payment are populated there. So I'm not sure I'm grasping what you're trying to convey. Is there a problem with the contents of those fields?

#

py_ is a valid prefix for Charge object IDs. You should still be able to retrieve those objects using the Charge retrieval endpoints.

daring bone
#

We are a platform that processes donations for organizations. The way it works right now is that we can see the transaction details, and our client, the organization, can also see the transaction. However, while we can see the donor’s information, they cannot

#

What I’ve been trying to do is update the transaction details once the transaction has been confirmed. Specifically, I want to update the transaction metadata so that our client can also see the donor’s information.

#

For instance, I can see this

#

But the client see this:

dense cradle
#

So the concern isn't with the contents of those fields?

What do you mean by "transaction". That isn't an object type in our ecosystem (unless you're using Issuing or Treasury, but that doesn't sound like it's the case here). metadata resides on a specific object, it isn't cascaded from one object to another (with some exceptions):
https://docs.stripe.com/metadata#copy-metadata

daring bone
#

Ah ok, i'm talking about payments

#

for instance, this one

dense cradle
#

If you want the metadata to be present on the Charge object that your Connected Account's can see (the one with the py_ prefix), then you will need to update that Charge object.

daring bone
#

If you want the metadata to be present on the Charge object that your Connected Account's can see (the one with the py_ prefix), then you will need to update that Charge object.
How can I do that? Idelly, I'd like to update the metadata, and the Details and the Customer

dense cradle
daring bone
#

Something like this?

      const { metadata, amount, latest_charge, on_behalf_of } = event.data.object
      console1.log('on_behalf_of', on_behalf_of)
      console1.log('latest_charge', latest_charge)

      try {
        const updatedCharge = await stripeClient.charges.update(
          latest_charge,
          {
            metadata: metadata,
          },
          {
            stripeAccount: on_behalf_of,
          }
        )

        console1.log(
          'Charge updated successfully:',
          JSON.stringify(updatedCharge)
        )
      } catch (error) {
        console1.log('Error updating charge:', error.message)
      }  
dense cradle
#

No, you don't want to use latest_charge, since you're trying to update the Charge object on the Connected Account from the Transfer, rather than the Charge on your Platform Account from processing the Payment Intent.

You will want to use the Charge ID from the destination_payment field on the Transfer object.

daring bone
#

Ah ok, got it.

#
const { metadata, amount, latest_charge, on_behalf_of, transfer_data } = event.data.object

  const transfer = await stripeClient.transfers.retrieve(
    transfer_data.destination,
    {
      stripeAccount: on_behalf_of,
    }
  );

  const destinationPayment = transfer.destination_payment;
    const updatedCharge = await stripeClient.charges.update(
      destinationPayment,
      {
        metadata: metadata,
      },
      {
        stripeAccount: on_behalf_of,
      }
    );
dense cradle
#

That will likely lead to errors when you run it, unless there's surrounding code changing variables that I'm not seeing.

Retrieving a Transfer object requires providing the ID of the Transfer object. If transfer_data.destination is from the Event payload you shared, then that field will contain the ID of a Connected Account, not a Transfer.

daring bone
#

Yes, I saw that... but , how can I get the transfer id?

dense cradle
#

One way is the approach you said you're using earlier in the thread:

•    When retrieving the PaymentIntent and expanding the transfer object (via expand: ['charges.data.transfer']), the transfer object does not include a valid destination_payment field.

(Though that may be outdated depending on the specific API version you're using, you'll likely want to use latest_charge.transfer instead)

daring bone
#

Ok, Stripe is sending me...
"latest_charge": "ch_3Qk5YdEsYIHxK4oE1DNxuuzB",

#

So, I can get the charge using this
const charge = await stripeClient.charges.retrieve(latest_charge);

#

Right?

dense cradle
#

Then you can use ID provided in the transfer field on that Charge to find the ID of the Transfer to the Connected Account. Then the destination_payment field on that Transfer object will have the ID of the Charge on the Connected Account.

daring bone
#
// I'm getting the charge of my organization
const charge = await stripeClient.charges.retrieve(
  latest_charge,
  {
    stripeAccount: on_behalf_of // I'm not sure if this is correct
  }
);
// I'm getting the transferId of that charge
const transferId = charge.transfer;

// I'm getting the transfer of the connected account
const transfer = await stripeClient.transfers.retrieve(
  transferId,
  {
    stripeAccount: on_behalf_of,
  }
);

// I'm getting the destination payment of that transfer
const destinationPayment = transfer.destination_payment;

// I'm updating the charge of the connected account
const updatedCharge = await stripeClient.charges.update(
  destinationPayment,
  {
    metadata: metadata,
  },
  {
    stripeAccount: on_behalf_of,
  }
);
#

Something like this?

dense cradle
#

No, the first two requests, retrieving latest_charge and the Transfer, don't need the stripeAccount header, since those objects are on your Platform account.

#

Other than that, I think it's right.

daring bone
#

Ah yeah, it makes sense

#

THanks!~ I'll test it

#

It works! thanks!

dense cradle
#

Any time! Glad to hear that helped

daring bone
#

Can I also update the Customer?

#

besides the metadata

dense cradle
#

I believe so, since it's listed as an updatable field in the API reference I linked to previously. I'm not exactly sure what happens if the Charge has already succeeded and you try that though.

daring bone
#

ok, I'll try it

#

I'm doing this
const updatedCharge = await stripeClient.charges.update(destinationPayment, { metadata: metadata, customer: customerDetails.id }, { stripeAccount: on_behalf_of });

#

Where.. customer id is cus_QRXVtXc0wuAicd

#

and it exists on my the stripe of my company

#

because I'm doing this

        const customerDetails = await stripeClient.customers.retrieve(customer);

#

And I can get the data

"Customer Details:"
"{\"id\":\"cus_QRXVtXc0wuAicd\",\"object\":\"customer\",\"address\":{\"city\":\"edinburgh\",\"country\":\"GB\",\"line1\":\"3/4 dunard garden\",\"line2\":null,\"postal_code\":\"EH9 2HZ\",\"state\":null},\"balance\":0,\"created\":1720532986,\"currency\":null,\"default_source\":null,\"delinquent\":false,\"description\":null,\"discount\":null,\"email\":\"cfontana0@gmail.com\",\"invoice_prefix\":\"B16F170D\",\"invoice_settings\":{\"custom_fields\":null,\"default_payment_method\":null,\"footer\":null,\"rendering_options\":null},\"livemode\":false,\"metadata\":{},\"name\":\"Carlos Fontana\",\"next_invoice_sequence\":1,\"phone\":null,\"preferred_locales\":[\"en-GB\"],\"shipping\":null,\"tax_exempt\":\"none\",\"test_clock\":null}"
#

But the error es

"Error updating charge:"
"No such customer: 'cus_QRXVtXc0wuAicd'"
dense cradle
#

Do you have the ID of the request throwing that error? (it should have an req_ prefix)

My suspicion is that you're trying to use the ID of a Customer from your Platform Account, which you can't do because that Customer object doesn't exist in your Connected Account so you can't associate it with a Charge that resides in your Connected Account.

daring bone
#

yes, you're right

#

So, I have to get the customer first, if it doesn't exist, create one

quaint plankBOT
gleaming ginkgo
#

Hello! Just checking in to make sure there are no outstanding questions?

daring bone
#

Hey! nope, all good!

#

It's working now!