How to do a Refund of a Charge created on behalf of Standalone Account?

2,223 views
Skip to first unread message

Daniel Almeida

unread,
Dec 14, 2016, 3:05:09 PM12/14/16
to Stripe API Discussion

How to do a Refund a Charge created on behalf of Standalone Account?

The documentation specifies that to proceed with Refund::create() a charge with prefix "ch_" must be provided. But when I retrieve the Charge using the standalone account's Secret Key, I get only charges with prefix "py_" and when I try to refund this type of charge (which is in fact a payment), the return I get is:

"This payment comes from a charge with a destination, so it can't be refunded unless the charge (ch_19PHYjHQipuDgpcpPD8hNYI1) has been refunded first".

But the point is that I don't find anywhere in the documentation how can I do to retrieve the charge with prefix "ch_" through the API. 

I even tried to retrieve the ch_ by using the py_ as a charger ID on Charge:retrieve using the my application Secret Key, but the answer is that can't find the charge py_.

Does anybody knows how to retrieve ch_ of a standalone py_?

Thank you all.



Remi J.

unread,
Dec 14, 2016, 3:10:39 PM12/14/16
to api-d...@lists.stripe.com
Hey Daniel,

When you create a charge through the platform [1] using `destination`, multiple objects are created automatically:

1/ You create the $100 charge with the `destination` parameter and take $10 as an application fee.
2/ This deposits $100 minus Stripe's fees ($3.2 in the US) in your pending balance and creates a Charge object [2] (ch_XXX) in your account.
3/ We create a transfer automatically for those funds to remove $100 from your balance and send it to the connected account.
4/ A Charge of type "payment" (py_XXX) is created on the connected account corresponding to those $100 minus your application fee added to their pending balance.
5/ That $10 application fee (fee_XXXX) is then deposited in your pending balance and created in your account.

If you want to refund that charge, you wouldn't refund the payment. Instead, you would refund the charge that was created in *your* account. You'd also reverse the associated transfer to pull the funds back from the connected account by passing `reverse_transfer: true`. If you want to refund your application fee you would also pass `refund_application_fee: true`. This is covered in details in our refund documentation [3]

It's fairly uncommon to create charges with `destination` when using Standalone accounts though. The reason is that, when you do this, you hold the liability for any disputes for your connected account(s) which is rarely what you want with Standalone accounts. In general, platforms in your situation create charges directly on the connected accounts [4] instead. You can read more about the risks in our support article [5] about this.

Hope this helps!
Remi


--
You received this message because you are subscribed to the Google Groups "Stripe API Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to api-discuss+unsubscribe@lists.stripe.com.
To post to this group, send email to api-d...@lists.stripe.com.
Visit this group at https://groups.google.com/a/lists.stripe.com/group/api-discuss/.

Daniel Almeida

unread,
Dec 14, 2016, 3:18:17 PM12/14/16
to Stripe API Discussion
Thank you for your answer Remi.

The entire process you're mentioning I'm covering on my backend. I just would like to be able to retrieve the ch_ ID using a py_ from a standalone account - is it possible?

If not, is it possible to make a charge directly to the user's account and yet apply my application fee?

Thanks again
To unsubscribe from this group and stop receiving emails from it, send an email to api-discuss...@lists.stripe.com.

Remi J.

unread,
Dec 14, 2016, 3:25:55 PM12/14/16
to api-d...@lists.stripe.com
The entire process you're mentioning I'm covering on my backend. I just would like to be able to retrieve the ch_ ID using a py_ from a standalone account - is it possible?

I'm not fully sure I follow what you mean. The connected account always has the payment with the id (py_XXXX). The charge with the id (ch_XXXX) lives on your platform instead. In most cases, you would store those ids in your own system/database so that you know exactly what to refund.

You can go from the py_XXX to the ch_YYY though using the `expand` feature (https://stripe.com/docs/api#expanding_objects). What you want is expand the original transfer which exists in your platform and created this payment and then look at the `source_transaction` property. In PHP you would do this:

$payment = \Stripe\Charge::retrieve(
    array("id" => "py_XXYYZZ", "expand" => array("source_transfer.source_transaction")),
    array("stripe_account" => "acct_AABBCC")
);

$original_charge_id = $payment->source_transfer->source_transaction;



If not, is it possible to make a charge directly to the user's account and yet apply my application fee?

This is definitely possible and the preferred way when using Standalone accounts. That's what I called creating a charge directly on the connected account. This is documented here: https://stripe.com/docs/connect/payments-fees#charging-directly

To unsubscribe from this group and stop receiving emails from it, send an email to api-discuss+unsubscribe@lists.stripe.com.

Daniel Almeida

unread,
Dec 14, 2016, 5:24:47 PM12/14/16
to Stripe API Discussion

Thanks for sending me the expand object code, it worked properly, I expanded the object and got the ch_ ID.

It's a pity the direct charge with application fee didn't work - I would rather leave disputes to the user. I even think it should be possible to apply application fees to direct charges as well, after all, "Creating Payment and Fees" documentation section is supposedly explaining two ways of creating payments with Fees but only one of the ways allows us to really apply fees, it doesn't make sense. On my point of view the text is inconsistent.

Thank you once again Remi.

Remi J.

unread,
Dec 14, 2016, 5:27:43 PM12/14/16
to api-d...@lists.stripe.com
It's a pity the direct charge with application fee didn't work - I would rather leave disputes to the user. I even think it should be possible to apply application fees to direct charges as well, after all, "Creating Payment and Fees" documentation section is supposedly explaining two ways of creating payments with Fees but only one of the ways allows us to really apply fees, it doesn't make sense. On my point of view the text is inconsistent.


I'm sorry if I was confusing before but what you are trying to do is exactly what we offer. You create a charge directly on the connected account, they pay Stripe fee *and* they pay you own application fee. The charge exists on their account and they own the liability for refunds and disputes. This is what's explained in details here: https://stripe.com/docs/connect/payments-fees#charging-directly

Here's an example in PHP:

$charge = \Stripe\Charge::create(array(
  'amount' => 12345,
  'currency' => 'USD',
  'source' => "tok_XXXYYYZZZ",
  'description' => 'Testing charge on connected account',
  'application_fee' => 1000
),
array('stripe_account' => "acct_AABBCC")
);

This code creates a $123.45 charge on the connected account acct_AABBCC and keeps $10 for you as your own application fee.
 
To unsubscribe from this group and stop receiving emails from it, send an email to api-discuss+unsubscribe@lists.stripe.com.

Daniel Almeida

unread,
Dec 14, 2016, 6:37:05 PM12/14/16
to Stripe API Discussion
Nice! Thanks Remi, you were of great help, now it's working as I intended. Thanks again!

José Fernandes

unread,
Dec 15, 2016, 9:09:55 AM12/15/16
to api-d...@lists.stripe.com
Hi guys,

I've some questions on the payments Object architecture from Stripe. I've already emailed support for this, but they specifically didn't answer "Question 2" and weren't sure about the other two questions. Now I'm trying this mailing list hopping this is read by someone more technical savvy.

Problem: match Stripe connected account bank account transfer to related managed account payments.

Architecture: 
Charges are created in the platform account with "destination" parameter pointing to the connected account.
Transfer Schedule: automatic, Daily — 2 business day rolling basis
We don't want to use manual transfer schedule.

When we charge a customer we create a captured charge with the `destination` parameter. Stripe creates the following objects based on this Charge object.

Transfer object on the platform account:
This represents a transfer from the customer charge to the connected account
- type `stripe_account`
- with `destination_payment` field

Payment (Charge) object in the Connected Account
This represents the payment charge based on the platform transfer.
- Includes a `source_transfer` field which we use to relate with the platform transfer and associated customer Charge.

Transfer object in the Connected Account
This is a bulk transfer on available Payment objects on the Connected Account.
- type `card` or `bank_account`
- Don't include any field we could use to relate to the corresponding Payment objects

For each of this objects Stripe also creates BalanceTransaction objects. In the dashboard we can find a representation on this objects in the Balance page.

Inspecting the Balance Transaction object referred in `balance_transaction` for the Connected Account Payment object we can find the Available On date , which tell us when Stripe backend will create the Connected Account bank Transfer.

Inspecting the Balance Transaction object referred in `balance_transaction` for the Connected Account bank Transfer we check we have an empty `source_transfers` field.

We know it's possible to relate the Connected Account bank Transfer objects with the Connected Account Payment object, because you do so on the Transfer page.

Question 1: Can we trust the Available On date on the Payments Balance Transaction, for the exact date the bank account Transfer will be created?

Question 2: Is it possible and how do we relate a Connected Account bank transfer with the available Payment objects? We need some code that based on a transfer ID allow us to fetch all payments (charge) IDs.

Question 3: Will the transferred amounts be available on our merchants bank account statement the same day the Connected Account Transfer is `status` paid. Will this happen on the same day that shows on Available On from the associated Payment objects?


Thank you guys!
José


To unsubscribe from this group and stop receiving emails from it, send an email to api-discuss+unsubscribe@lists.stripe.com.

Remi J.

unread,
Dec 15, 2016, 9:41:26 AM12/15/16
to api-d...@lists.stripe.com
On Thu, Dec 15, 2016 at 8:54 AM, José Fernandes <jose...@gmail.com> wrote:
Hi guys,

I've some questions on the payments Object architecture from Stripe. I've already emailed support for this, but they specifically didn't answer "Question 2" and weren't sure about the other two questions. Now I'm trying this mailing list hopping this is read by someone more technical savvy.

Sorry to hear that! In the future, I'd recommend simply replying to the email you got back from support to clarify the points that weren't addressed yet. It's possible that the person who answered you thought they answered all questions and missed that you were still confused.

Problem: match Stripe connected account bank account transfer to related managed account payments.

Architecture: 
Charges are created in the platform account with "destination" parameter pointing to the connected account.
Transfer Schedule: automatic, Daily — 2 business day rolling basis
We don't want to use manual transfer schedule.

When we charge a customer we create a captured charge with the `destination` parameter. Stripe creates the following objects based on this Charge object.

Transfer object on the platform account:
This represents a transfer from the customer charge to the connected account
- type `stripe_account`
- with `destination_payment` field

Payment (Charge) object in the Connected Account
This represents the payment charge based on the platform transfer.
- Includes a `source_transfer` field which we use to relate with the platform transfer and associated customer Charge.

Transfer object in the Connected Account
This is a bulk transfer on available Payment objects on the Connected Account.
- type `card` or `bank_account`
- Don't include any field we could use to relate to the corresponding Payment objects

For each of this objects Stripe also creates BalanceTransaction objects. In the dashboard we can find a representation on this objects in the Balance page.

Inspecting the Balance Transaction object referred in `balance_transaction` for the Connected Account Payment object we can find the Available On date , which tell us when Stripe backend will create the Connected Account bank Transfer.

Inspecting the Balance Transaction object referred in `balance_transaction` for the Connected Account bank Transfer we check we have an empty `source_transfers` field.

We know it's possible to relate the Connected Account bank Transfer objects with the Connected Account Payment object, because you do so on the Transfer page.

Question 1: Can we trust the Available On date on the Payments Balance Transaction, for the exact date the bank account Transfer will be created?

Not really no. The `available_on` property [1] on the Balance Transaction object [2] indicates when the funds will be made available in your balance. This is a unix timestamp that corresponds to the day the funds will be available, at 12am UTC. On that day, we run some background jobs on our end that go through all the transactions that are still pending and find the ones that are supposed to be available. Once we have all of them, they get released at once. this means that the funds could be available a few hours after 12am UTC instead of right at that time.

What you should focus on here is the transfer that is created and when we think it's paid. This is done by listening to the `transfer.*` events that are created in all of your connected accounts. You would add a Connect webhook endpoint [3] on your platform so that you get the events of all your connected accounts. Each event would have a `user_id` key that would contain the account id (acct_XXX) of your connected account that the event is coming from. You should rely on those events to inform your users.


Question 2: Is it possible and how do we relate a Connected Account bank transfer with the available Payment objects? We need some code that based on a transfer ID allow us to fetch all payments (charge) IDs.

What you are looking for is the Balance History API [4]. If you provide the transfer identifier in the `transfer` parameter you will get a list of transactions that were transferred out on the specified transfer [5].

Since the transfer is on the connected account, you'll need to use a few things. First off, you'll use the `Stripe-Account` header [6] to make the API request on behalf of that account. Then that transfer bundles payments (py_XXX) and what you really want is the original charge. For this, you'll use the Expand feature [7] to retrieve the transfer in your platform that created the payment and the charge that created the transfer.

In PHP your code would look like this:

$balance = \Stripe\BalanceTransaction::all(
    array(
        "limit" => 3,
        "transfer" => "tr_XXYYZZ",
        "expand" => array("data.source.source_transfer.source_transaction")
    ),
    array("stripe_account" => "acct_AAAA")
);


Question 3: Will the transferred amounts be available on our merchants bank account statement the same day the Connected Account Transfer is `status` paid. Will this happen on the same day that shows on Available On from the associated Payment objects?

It should though it's not always a guarantee as it depends on the bank. We try to predict as best as we can when the funds are made available and that's what the `transfer.paid` event represents but some banks might take longer to process the transfer than others.
 
I hope this helps make things clearer!
Remi

José Fernandes

unread,
Dec 15, 2016, 10:46:34 AM12/15/16
to api-d...@lists.stripe.com
Thanks Remi!

Great answer, totally clear my questions.

Thanks,
José
Reply all
Reply to author
Forward
0 new messages