I am trying to figure out why I seem to be getting this error when trying to call a Stripe API. The error I am getting is this:
The provided key 'sk_test_***********************************************************************************************JQQa' does not have access to account 'cus_PN***' (or that account does not exist). Application access may have been revoked
And my code is as follows:
const stripe = require('stripe')(
process.env.STRIPE_REAL_API_SECRET_KEY || process.env.STRIPE_TEST_API_SECRET_KEY
);
const eligibleBalanceDueData = res[1].rows.map((row) => ({
balanceDueId: row.balance_due_id,
affiliateStripeId: row.stripe_customer_id?.trim(),
totalAmount: parseFloat(row.total_amount),
}));
for (const item of eligibleBalanceDueData) {
// Retrieve the Connected Account ID associated with the Customer ID
const connectedAccountId = await stripe.accounts.listExternalAccounts(
item.affiliateStripeId,
{ limit: 1, type: 'custom_account' }
);
const transfer = await stripe.transfers.create(
{
amount: 2, // item.totalAmount * 100, // Amount in cents
currency: 'usd',
destination: connectedAccountId.data[0].id,
transfer_group: 'affiliate_payout',
},
{
idempotencyKey: 'unique_key_here'
}
);
}
It seems that if I have any of the above Stripe API calls (i.e. await stripe.accounts.listExternalAccounts
or await stripe.transfers.create
) then I get the error.
This is supposed to be code to pay affiliates, which are set up as customers in stripe. So I guess technically I just want to be able to send a customer a payment via the API. If I am correct, I assume that transfers are what should be used in my case, not payouts or paymentIntents, and that the destination in the transfer call should be the account id of the stripe customer id.
Valid API Key:
I have checked the secret keys (in test mode), and by the stripe dashboard it says "Standard keys – These keys will allow you to authenticate API requests". I assume the secret key is therefore valid, presumably with full permissions and has no issue.
Valid Account:
The account in the error (i.e. 'cus_PN***...'
) does exist (i.e. there is a customer with that id) – and the value is stored inside item.affiliateStripeId
.
Access?
I assume that the Api calls should work with item.affiliateStripeId
, because it’s a customer.
I assume this code will create a test payment rather than a real payment.
Stripe API Response:
{
type: 'StripePermissionError',
raw: {
message: "The provided key 'sk_test_***********************************************************************************************JQQa' does not have access to account 'cus_PNaqkPyNm7hjp4' (or that account does not exist). Application access may have been revoked.",
type: 'invalid_request_error',
code: 'account_invalid',
doc_url: 'https://stripe.com/docs/error-codes/account-invalid',
headers: {
server: 'nginx',
date: 'Tue, 16 Jan 2024 09:11:48 GMT',
'content-type': 'application/json',
'content-length': '432',
connection: 'keep-alive',
'access-control-allow-credentials': 'true',
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
'access-control-allow-origin': '*',
'access-control-expose-headers': 'Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, X-Stripe-Privileged-Session-Required',
'access-control-max-age': '300',
'cache-control': 'no-cache, no-store',
vary: 'Origin',
'strict-transport-security': 'max-age=63072000; includeSubDomains; preload'
},
statusCode: 403,
requestId: undefined
},
rawType: 'invalid_request_error',
code: 'account_invalid',
doc_url: 'https://stripe.com/docs/error-codes/account-invalid',
param: undefined,
detail: undefined,
headers: {
server: 'nginx',
date: 'Tue, 16 Jan 2024 09:11:48 GMT',
'content-type': 'application/json',
'content-length': '432',
connection: 'keep-alive',
'access-control-allow-credentials': 'true',
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
'access-control-allow-origin': '*',
'access-control-expose-headers': 'Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, X-Stripe-Privileged-Session-Required',
'access-control-max-age': '300',
'cache-control': 'no-cache, no-store',
vary: 'Origin',
'strict-transport-security': 'max-age=63072000; includeSubDomains; preload'
},
requestId: undefined,
statusCode: 403,
charge: undefined,
decline_code: undefined,
payment_intent: undefined,
payment_method: undefined,
payment_method_type: undefined,
setup_intent: undefined,
source: undefined
}
It seems to link to this: https://stripe.com/docs/error-codes/account-invalid – "The account ID provided as a value for the Stripe-Account header is invalid. Check that your requests are specifying a valid account ID."
To be honest, I am completely lost here, especially after reading the docs and other posts. Any help would be greatly appreciated.
2
Answers
It’s hard to investigate as the problem is specific to your Stripe account. You should reach out to Stripe support and tell them the request ID, so that they can help you troubleshoot.
You seem to have a fundamental misunderstanding of how Stripe Connect works — the value you’re passing is
cus_xxx
, which is a Customer ID. It is absolutely not a Connected accountacct_xxx
which is what you should be passing to such APIs.All of these points are incorrect unfortunately and not how this works at all. All of those things should be Accounts, not Customers.
A Customer
cus_xxx
is the object that represents a buyer, someone who pays you or who pays another merchant. It can not receive money, it’s someone who pays you.An Account
acct_xxx
is the object that represents a seller(merchant) or an entity you send money to/takes a cut of some payments; i.e an affiliate in your model.An Account might have (potentially multiple) ExternalAccounts (
ba_xxx
for bank accounts for example) which are where the balance transferred to/accumulated by that Stripe account get paid out to, you never transfer to those though, you transfer to the Account and then it’s the payout schedule/settings of the Account that interact with the ExternalAccount.===
To use Connect you have your sellers/affiliates/employees/etc (people receiving money from your platform) sign up for Stripe accounts connected to your own platform account(you can manage that signup/connection in varying levels viz-a-viz Standard/Express/Custom accounts), complete KYC by providing identity verification docs, and then you save their account IDs in your system.
Separate to that, you’ll have Customers who buy things, you charge them with PaymentIntents for example, and then you use things like the Transfers API to send funds from that payment you processed to the relevant Account ID.
I highly highly suggest you start by implementing a complete guide like https://stripe.com/docs/connect/collect-then-transfer-guide from scratch.