Skip to content

Commit

Permalink
Merge branch 'master' into admin-prefetch
Browse files Browse the repository at this point in the history
  • Loading branch information
blueyed authored Oct 17, 2017
2 parents 23c04d0 + fda68d4 commit 2e0e23b
Show file tree
Hide file tree
Showing 34 changed files with 2,868 additions and 278 deletions.
27 changes: 0 additions & 27 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,6 @@ jobs:
- image: circleci/python:2.7
environment:
TOXENV=py27-dj18
py27dj19:
<<: *common
docker:
- image: circleci/python:2.7
environment:
TOXENV=py27-dj19
py27dj110:
<<: *common
docker:
Expand All @@ -73,12 +67,6 @@ jobs:
- image: circleci/python:3.4
environment:
TOXENV=py34-dj18
py34dj19:
<<: *common
docker:
- image: circleci/python:3.4
environment:
TOXENV=py34-dj19
py34dj110:
<<: *common
docker:
Expand All @@ -103,12 +91,6 @@ jobs:
- image: circleci/python:3.5
environment:
TOXENV=py35-dj18
py35dj19:
<<: *common
docker:
- image: circleci/python:3.5
environment:
TOXENV=py35-dj19
py35dj110:
<<: *common
docker:
Expand Down Expand Up @@ -148,9 +130,6 @@ workflows:
- py27dj18:
requires:
- lint
- py27dj19:
requires:
- lint
- py27dj110:
requires:
- lint
Expand All @@ -163,9 +142,6 @@ workflows:
- py34dj18:
requires:
- lint
- py34dj19:
requires:
- lint
- py34dj110:
requires:
- lint
Expand All @@ -179,9 +155,6 @@ workflows:
- py35dj18:
requires:
- lint
- py35dj19:
requires:
- lint
- py35dj110:
requires:
- lint
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
[![](https://img.shields.io/pypi/v/pinax-stripe.svg)](https://pypi.python.org/pypi/pinax-stripe/)
[![](https://img.shields.io/badge/license-MIT-blue.svg)](https://pypi.python.org/pypi/pinax-stripe/)

[![](https://img.shields.io/travis/pinax/pinax-stripe.svg)](https://travis-ci.org/pinax/pinax-stripe)
[![Codecov](https://img.shields.io/codecov/c/github/pinax/pinax-stripe.svg)](https://codecov.io/gh/pinax/pinax-stripe)
![](https://img.shields.io/github/contributors/pinax/pinax-stripe.svg)
![](https://img.shields.io/github/issues-pr/pinax/pinax-stripe.svg)
Expand Down
235 changes: 235 additions & 0 deletions docs/user-guide/connect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
# Using Stripe Connect

[Stripe Connect](https://stripe.com/connect) allows you to perform charges on behalf of your
users and then payout to thier bank accounts.

There are several ways to integrate Connect and these result in the creation of different
[account types](https://stripe.com/connect/account-types). Before you begin your Connect
integration, it is crucial you identify which strategy makes sense for your project as which
you choose has great implications in terms of development effort and how much of your users'
experience you can customize.

This project allows use of any account type.

!!! tip "Using Connect requires you to receive webhooks"

Regardless of which integration you use you will need to enable webhooks so that you can
immediately know when an Account has changed. It may also be necessary for Standard and
Express integrations in order to detect when an Account has been created.


## Standard and Express Accounts

Users go through an OAuth-like flow hosted by Stripe and set up their own Stripe account.
Stripe will send an event via webhook that will create the account instance in your database.

You can then create a credit card charge on behalf of a standard account by specifying
the `destination_account` parameter:

```python
from pinax.stripe.models import Account
from pinax.stripe.actions.charges import create

account = Account.objects.get(pk=123)
charge = create(5.00, customer, destination_account=account.stripe_id)
```

As a result doing this, the charge will be deposited into the specified Account and
paid out to the user via their configured payout settings.


## Custom Accounts

Custom accounts are created, updated and transacted with fully via Stripe's APIs. This
gives you full control over the user experience but places a high developmental burden
on your project.

You must collect information from your users to setup their Accounts. To this end, this
library includes forms that will help you create accounts and keep them verified.

### Verification

When you create a custom Connect account, you can initially supply the minimum details
and immediately be able to transfer funds to the account. After a certain amount has
been transferred, Stripe will request further verification for an account and at this
point you need to ask your user to supply that information. One of the main advantages
of going the Standard or Express routes is that this verification dialogue happens
between your customer and Stripe.

### Forms

To create a Custom account, you must capture your users' banking information and supply it
to Stripe. The information you must capture varies by country. Be sure to read Stripe's
[documentation on required info](https://stripe.com/docs/connect/required-verification-information)
before proceeding.

This library includes two forms intended to ease the process of collecting the right information
from your users when both creating and updating Custom accounts.

#### Creating a Custom Account

To create an Account for the currently logged in user, you can use the `InitialCustomAccountForm`
along with a `FormView`, as below. Assuming the user enters valid data, this form
will create a custom Account that you can immediately begin processing charges for and paying out
to.

```python
from django.views.generic.edit import FormView
from pinax.stripe.forms import InitialCustomAccountForm


class CreateCustomAccountView(FormView):
"""Prompt a user to enter their bank account details."""

form_class = InitialCustomAccountForm
template_name = '<path to your template>'

def get_form_kwargs(self, *args, **kwargs):
form_kwargs = super(
CreateBankAccountView, self
).get_form_kwargs(
*args, **kwargs
)
initial = form_kwargs.pop('initial', {})
form_kwargs['request'] = self.request
form_kwargs['country'] = 'US'
return form_kwargs

def form_valid(self, form):
try:
form.save()
except:
if form.errors:
# means we've converted the exception into errors on the
# form so we just redisplay the form in this case
return self.form_invalid(form)
else:
# some untranslatable error occurred, log it and
# inform user you're looking into it
pass
else:
# success
pass
# redirect to success url
return super(self, CreateCustomAccountView).form_valid(form)

```

#### Updating a Custom Account with Further Verification Information

After a Custom account has had a certain amount of charges created or funds paid out
Stripe will request additional verification info. They will set a due date after which
the ability to create charges for and pay out to this account may be restricted.

You will need to detect the webhook for `account.updated` and based on several fields,
determine whether or not you need to initiate an information collection process for
your user. For example:

```python
from pinax.stripe.models import Account
from pinax.stripe.signals import WEBHOOK_SIGNALS


@receiver(WEBHOOK_SIGNALS["account.updated"])
@receiver_switch
def stripe_account_updated(sender, event, **kwargs):
account = Account.objects.get(
stripe_id=event.validated_message['data']['object']['id']
)
# if this is not a custom account, it's probably our platform
# account or an express or standard account, so do nothing
if not account.type == "custom":
return
if account.verification_due_by and account.verification_fields_needed:
# then Stripe is asking us for some info!
# notify the user about this, flag their account so when they login
# they can see they need to enter further info
pass

```

When the user next accesses your website, you will want to be able to request
them to provide further information if they wish to continue receiving payments
and possibly payouts.

This library includes the `AdditionalCustomAccountForm` in order to make it easy
to dynamically request the right extra information from the user. Using a `FormView`
as with the previous example, you simply need to initialize the form with a keyword
argument `account`, which should be the Account instance you need to collect
further information for. This form will automatically parse `Account.verification_fields_needed`
and build the fields dynamically.


```python
from django.views.generic.edit import FormView
from pinax.stripe.forms import AdditionalCustomAccountForm


class UpdateCustomAccountView(FormView):
"""Prompt a user to enter further info to keep their account verified."""

form_class = AdditionalCustomAccountForm
template_name = '<path to your template>'

def get_form_kwargs(self, *args, **kwargs):
form_kwargs = super(
UpdateCustomAccountView, self
).get_form_kwargs(
*args, **kwargs
)
initial = form_kwargs.pop('initial', {})
form_kwargs['account'] = <Account instance>
return form_kwargs

def form_valid(self, form):
try:
form.save()
except:
if form.errors:
# means we've converted the exception into errors on the
# form so we just redisplay the form in this case
return self.form_invalid(form)
else:
# some untranslatable error occurred, log it and
# inform user you're looking into it
pass
else:
# success
pass
# redirect to success url
return super(self, UpdateCustomAccountView).form_valid(form)

```

#### Manually paying out a Custom account

You may decide to keep your users' payout schedules simple and on a rolling basis, but
using Custom accounts frees you up to fully control this aspect of your product.

When you have a user with a Custom account in good standing, you can create a payout for
the user as below. For the sake of this example, we'll assume you're using the `destination_account`
parametre when creating the charges, such that the payment balance is automatically being
deposited into the Custom account's balance.

```python
from pinax.stripe.models import Account

account = Account.objects.get(pk=<target account id>)

# we choose the first external account the user has configured
external_account = stripe_account.external_accounts.data[0]

external_transfer = transfers.create(
5.00,
'USD',
external_account.id,
"A payout to a bank account!",
stripe_account=account.stripe_id, # this tells Stripe to transfer from the balance of the Custom account
)
assert external_transfer.status in ('paid', 'pending')

```

In most cases, the transfer (to an external account, these are commonly referred to as `payouts`) will be
initially in a `pending` state. After several days, this will shift to `paid` and your user should see the
amount on their bank account statement.
1 change: 1 addition & 0 deletions makemigrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
DATABASES={
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": ":memory:",
}
},
MIDDLEWARE_CLASSES=[
Expand Down
Loading

0 comments on commit 2e0e23b

Please sign in to comment.