For a cross border transfer to a connected account from the platform, how do you find the exchange rate and amount in connected account's currency?

88 views
Skip to first unread message

Dashiell Bark-Huss

unread,
Jan 19, 2021, 5:32:27 PM1/19/21
to Stripe API Discussion
My platform is USD and the connected account is in CAD. In the stripe GUI I can find the exchange rate and amount transferred in CAD, as circled in the attached screenshot. But I can't find these attributes in the API.

The only object I've seen with an exchange rate attribute is the balance transaction. However, when I get the balance transaction for the transaction in the screenshot, I get this response object:

Request: https://api.stripe.com/v1/balance_transactions/txn_1IBNiNLLBOhef2QNqIeaNg9o
Response:
{
"id": "txn_1IBNiNLLBOhef2QNqIeaNg9o",
"object": "balance_transaction",
"amount": -7777,
"available_on": 1611619200,
"created": 1611076199,
"currency": "usd",
"description": null,
"exchange_rate": null,
"fee": 0,
"fee_details": [],
"net": -7777,
"reporting_category": "transfer",
"source": "tr_1IBNiNLLBOhef2QNcNqv3IlS",
"status": "pending",
"type": "transfer" }

The problem here is that balance transaction object only shows this transaction in USD: $77.77 USD came out of my platform account.

But it doesn't show the conversion rate or the amount in CAD when this $77.00 went into the CAD connected account. As we can see in the GUI screenshot, that $77.77 was converted to 
$98.02 CAD and the exchange rate was 1.26039 (USD->CAD). 

How can I find this CAD amount and exchange rate through the API?
Screen Shot 2021-01-19 at 2.26.21 PM.png

Remi J.

unread,
Jan 19, 2021, 7:23:41 PM1/19/21
to Stripe API Discussion
Hello Dashiell,

Your integration is using Checkout and passing `transfer_data` which corresponds to Destination charges [1]. This means the Charge is created on the platform and funds are automatically sent to the connected account directly while you keep a portion for your platform. You're also not using the `on_behalf_of` parameter [2] which means the funds are first settled in the platform's currency and then they are sent in that currency to the connected account where they end up being converted again to their own currency if needed.

In your example you created £67.35 GBP payment which is what your platform gets (minus our fee). Since your platform is US-based, you got USD instead of GBP which converted to $90.76 USD in your balance after all (again minus our fee). You also configured the Session to send £57.71 GBP to the connected account via the `transfer_data` parameter. Since you got USD from the transaction though we transferred USD to the connected account instead so the Transfer is in USD at this point (what you see on that BT). As a result, those funds created a Charge in the connected account (py_123) which got automatically converted to CAD as the currency the connected account can accept. The USD->CAD conversion is stored on that Charge's BT instead of the Transfer's BT.

The Charge on the connected account is linked to the Transfer itself. You get access to it via the `destination_payment` property on the Transfer resource [3]. To summarize there are 3 BTs that exists today:
1/ The Charge's BT on the platform that shows the GBP->USD conversion
2/ The Transfer's BT that shows USD moving from the platform to the connected account
3/ The Charge's BT on the connected account that shows the USD->CAD conversion.

I hope this helps clarify how all of this works!
Remi


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

Dashiell Bark-Huss

unread,
Jan 19, 2021, 10:12:14 PM1/19/21
to Stripe API Discussion
Thanks Remi. That makes a lot of sense but I'm still running into a problem.

So now I'm trying to get the 3rd BT object, as you suggested. But I'm getting an error.

I used the endpoint `https://api.stripe.com/v1/transfers/tr_1IBNiNLLBOhef2QNcNqv3IlS?expand[]=destination_payment` to expand the destination payment.
Response:
{
    "id": "tr_1IBNiNLLBOhef2QNcNqv3IlS",
    "object": "transfer",
    "amount": 7777,
    "amount_reversed": 0,
    "balance_transaction": "txn_1IBNiNLLBOhef2QNqIeaNg9o",
    "created": 1611076199,
    "currency": "usd",
    "description": null,
    "destination": "acct_1Heou2LKojKNEaYI",
    "destination_payment": {
        "id": "py_1IBNiNLKojKNEaYIjCnlyzyy",
        "object": "charge",
        "amount": 7777,
        "amount_captured": 7777,
        "amount_refunded": 0,
        "application": "ca_IDpLL959m3xLKwEBQTIYssGh7UFQZbOu",
        "application_fee": null,
        "application_fee_amount": null,
        "balance_transaction": "txn_1IBNiOLKojKNEaYINS8SFq4t",
        "billing_details": {
            "address": {
                "city": null,
                "country": null,
                "line1": null,
                "line2": null,
                "postal_code": null,
                "state": null
            },
            "email": null,
            "name": null,
            "phone": null
        },
        "calculated_statement_descriptor": null,
        "captured": true,
        "created": 1611076200,
        "currency": "usd",
        "customer": null,
        "description": null,
        "destination": null,
        "dispute": null,
        "disputed": false,
        "failure_code": null,
        "failure_message": null,
        "fraud_details": {},
        "invoice": null,
        "livemode": false,
        "metadata": {},
        "on_behalf_of": null,
        "order": null,
        "outcome": null,
        "paid": true,
        "payment_intent": null,
        "payment_method": null,
        "payment_method_details": {
            "stripe_account": {},
            "type": "stripe_account"
        },
        "receipt_email": null,
        "receipt_number": null,
        "refunded": false,
        "refunds": {
            "object": "list",
            "data": [],
            "has_more": false,
            "total_count": 0,
            "url": "/v1/charges/py_1IBNiNLKojKNEaYIjCnlyzyy/refunds"
        },
        "review": null,
        "shipping": null,
        "source": {
            "id": "acct_1HAi5vLLBOhef2QN",
            "object": "account",
            "application_name": "WishTender LLC",
            "application_url": "http://wishtender.com/"
        },
        "source_transfer": "tr_1IBNiNLLBOhef2QNcNqv3IlS",
        "statement_descriptor": null,
        "statement_descriptor_suffix": null,
        "status": "succeeded",
        "transfer_data": null,
        "transfer_group": null
    },
    "livemode": false,
    "metadata": {},
    "reversals": {
        "object": "list",
        "data": [],
        "has_more": false,
        "total_count": 0,
        "url": "/v1/transfers/tr_1IBNiNLLBOhef2QNcNqv3IlS/reversals"
    },
    "reversed": false,
    "source_transaction": "ch_1IBNiNLLBOhef2QN9Itws9zP",
    "source_type": "card",
    "transfer_group": "group_pi_1IBNhXLLBOhef2QN0GV5dFE3"
}

Here I see the BT id is txn_1IBNiOLKojKNEaYINS8SFq4t.

So next I made a request for that BT: https://api.stripe.com/v1/balance_transactions/txn_1IBNiOLKojKNEaYINS8SFq4t
But that gives me an error:
{
    "error": {
        "code": "resource_missing",
        "message": "No such balance transaction: 'txn_1IBNiOLKojKNEaYINS8SFq4t'",
        "param": "id",
        "type": "invalid_request_error"
    }
}

I keep looking it over to see if I made a typo in the request but the id is straight out of the response and the request works if I use a different BT id. 

To further investigate I did a request for all balance transfers: https://api.stripe.com/v1/balance_transactions. This gave me a long list of balance transfers but none with `currency: "cad"`. So I didn't see the balance transfer there either.

Do you have any idea what I'm doing wrong? Why can't I find this balance transfer object?
To unsubscribe from this group and stop receiving emails from it, send an email to api-d...@lists.stripe.com.

Remi J.

unread,
Jan 19, 2021, 10:16:11 PM1/19/21
to Stripe API Discussion
The Transfer lives on your own account (the platform) but the destination payment lives on the connected account instead and not your own account. To retrieve the Balance Transaction (or the Charge itself) you have to use the `Stripe-Account` header to indicate that you make the API request on behalf of the connected account. This is documented here: https://stripe.com/docs/connect/authentication#authentication-via-the-stripe-account-header

Relatedly, you can expand multiple levels at once in the request. So instead of expanding `destination_payment`, you can expand `destination_payment.balance_transaction` and it will expand both objects in one API request which will allow you to view all objects at once. This is documented here: https://stripe.com/docs/expand#multiple-levels

I hope this helps!

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

Dashiell Bark-Huss

unread,
Jan 19, 2021, 10:40:08 PM1/19/21
to Stripe API Discussion
Ah yes, thank you for the solution and the multilevel expand suggestion!
To unsubscribe from this group and stop receiving emails from it, send an email to api-d...@lists.stripe.com.
Reply all
Reply to author
Forward
0 new messages