I have two types of users in Stripe:
Let's assume in an example below user is a model instance(Django):
# Connect Account
instance = stripe.Account.create(
type="custom",
country="PL",
email=user.email,
default_currency="pln",
business_type="individual",
requested_capabilities=["card_payments", "transfers",],
individual={
"first_name": user.first_name.strip(),
"last_name": user.last_name.strip(),
"email": user.email,
"dob": {
"day": user.profile.date_of_birth.day,
"month": user.profile.date_of_birth.month,
"year": user.profile.date_of_birth.year,
},
"phone": user.profile.phone_number,
"address": {
"city": user.city.strip(),
"postal_code": user.profile.postal_code.strip(),
"country": "PL",
"line1": user.profile.address.strip(),
},
},
)
# Customer Account
instance = stripe.Customer.create(
email=user.email,
description="desc",
name=user.get_full_name.strip(),
phone=user.profile.phone_number.strip(),
)
Those two API calls work properly. Then I'm able to retrieve the unique Stripe ID for new user:
stripe_customer_id = instance.get("id")
For stripe.Account type, I need to provide an identity verification: front and back of ID Card (as an example):
document_images_data = [
"data:image/png;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",
"data:image/png;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",
]
_front = stripe.File.create(
purpose="identity_document",
file=Base64ImageField().to_internal_value(base64_data=document_images_data[0]),
stripe_account=stripe_customer_id,
)
_back = stripe.File.create(
purpose="identity_document",
file=Base64ImageField().to_internal_value(base64_data=document_images_data[1]),
stripe_account=stripe_customer_id,
)
individual = {
"verification": {
"document": {"front": _front.get("id"), "back": _back.get("id")},
"additional_document": {"front": _front.get("id"), "back": _back.get("id")},
}
}
Then I need to attach uploaded files to stripe.Account instance:
stripe.Account.modify(stripe_customer_id, individual=individual)
And accept Stripe terms through API:
stripe.Account.modify(stripe_customer_id, tos_acceptance={"date": int(time.time()), "ip": user.user_ip,})
After a successful creation of stripe.Account instance, I can see on the Stripe's dashboard, there are two things left to be filled, so that the account can be completely verified:
Is there a way to somehow fill these data in API call ? What is more, I'm trying to create an individual type of account, so why does it require those two things to be filled as well ?
From now, I have two users and I'd like to add a Card to Customer Account and Bank Account to Connect Account.
In both situations I'm using a card/bank account details provided by a token, like this (Stripe test card/bank account):
stripe.Customer.create_source(stripe_customer_id, source="tok_amex") # example token card details
stripe.Account.create_external_account(
stripe_customer_id,
external_account="btok_1H7J0nHksFGR44vruZzOpnnD", # example token bank account details
)
And now, let's assume I want to charge a Customer card and transfer those money into Connect Account.
As I understood from documentation, it should be:
stripe.Charge.create(
amount=10000,
currency="pln",
customer=stripe_customer_id, # stripe.Customer unique Stripe ID
description="some desc",
)
This operation also works as I can see the result on Stripe's Dashboard.
Now I want to transfer those money into a Connect Account:
stripe.Transfer.create(amount=10000, currency="pln", destination=connect_account_stripe_id)
Even though I added some credits to stripe.Customer balance I got:
You have insufficient funds in your Stripe account. One likely reason you have insufficient funds is that your funds are automatically being paid out
How to enable manual transfers? And the most important question - Is it a proper approach or the entire process should be handled in different way? Thanks in advance!
When you process a payment against the customer(stripe.Charge.create
), this adds funds to your platform account's balance. Funds are initially pending
for some time(https://stripe.com/docs/payouts#standard-payout-timing).
Calling /v1/transfers (stripe.Transfers.create
) attempts to take funds from your available
balance and transfer them to the destination Stripe account. It will fail if you have no available
balance as is most likely the case here.
If your platform is on automatic daily payouts(the default setting), you will never actually have an available
balance. That's because Stripe will move the funds directly from your pending
balance into your payouts so you can get that money as quickly as possible, which is what most accounts would want(as a Connect platform with this funds flow, you are a bit more advanced).
If you want to do this kind of transfer, I'd suggest the following, in order:
source_transaction
when calling stripe.Transfer.create
: https://stripe.com/docs/connect/charges-transfers#transfer-availability . That lets the transfer succeed right now, and the funds from the incoming charge will move to the connected account when they're available.available
balance from your pay-ins. You can then just make transfers directly from that balance, like the API call you shared in the question. In test mode you can use a special test card to simulate having available
balance without waiting days.And the most important question - Is it a proper approach or the entire process should be handled in different way?
This model(having a marketplace where each user of the platform has a Stripe Customer and a Stripe Account associated with them that you tie together) is fairly normal, but it's tricky to build. Similarly, really you're just building a standard "separate charges and transfers" integration : https://stripe.com/docs/connect/charges#separate-charges-transfers But you're building one of the most complex things on Stripe (Custom Connect + separate Charges and Transfers) so it's a bit of an undertaking.
I would try to simplify it as much as possible by using Destination charges unless you absolutely can't, and using Express or Stripe's hosted onboarding for Custom accounts (https://stripe.com/docs/connect/connect-onboarding) unless you really need to whitelabel this.
Also you should be using PaymentIntents(https://stripe.com/docs/payments/accept-a-payment) for the pay-in part, not Charge.create
and Tokens as those don't support 3D Secure which is important in Europe, but it's maybe ok to use them just to test out the Connect side of things.