Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,71 @@ For full documentation, see our `API reference`_.

.. _API reference: https://developer.gocardless.com/api-reference

Handling webhooks
`````````````````

GoCardless supports webhooks, allowing you to receive real-time notifications when
things happen in your account, so you can take automatic actions in response, for example:

* When a customer cancels their mandate with the bank, suspend their club membership
* When a payment fails due to lack of funds, mark their invoice as unpaid
* When a customer's subscription generates a new payment, log it in their "past payments" list

The client allows you to validate that a webhook you receive is genuinely from GoCardless,
and to parse it into Event objects which are easy to work with:

.. code:: python

import gocardless_pro

# When you create a webhook endpoint, you can specify a secret. When GoCardless sends
# you a webhook, it will sign the body using that secret. Since only you and GoCardless
# know the secret, you can check the signature and ensure that the webhook is truly
# from GoCardless.
webhook_endpoint_secret = os.environ['GOCARDLESS_WEBHOOK_ENDPOINT_SECRET']

# In your webhook handler (e.g. Flask route)
@app.route('/webhooks', methods=['POST'])
def handle_webhook():
request_body = request.data
signature_header = request.headers.get('Webhook-Signature')

try:
events = gocardless_pro.Webhook.parse(
request_body,
signature_header,
webhook_endpoint_secret
)

for event in events:
print(event.id)

return '', 200
except gocardless_pro.errors.InvalidSignatureError:
# The webhook doesn't appear to be genuinely from GoCardless
return '', 498

Accessing the webhook ID
''''''''''''''''''''''''

If you need to access the webhook ID for debugging purposes, you can use ``parse_with_meta`` instead:

.. code:: python

result = gocardless_pro.Webhook.parse_with_meta(
request_body,
signature_header,
webhook_endpoint_secret
)
events = result.events
webhook_id = result.webhook_id # e.g. "WB123" - useful for debugging

Note: The webhook ID is intended for debugging and logging purposes only. It should not be
used for deduplication - instead, use the event IDs to deduplicate, as each event has a unique
ID that remains consistent if the same event is sent multiple times.

For more details on working with webhooks, see our `"Getting Started" guide <https://developer.gocardless.com/getting-started/api/introduction/?lang=python>`_.


Available resources
```````````````````
Expand Down
2 changes: 1 addition & 1 deletion gocardless_pro/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

from .client import Client

__version__ = '3.4.0'
__version__ = '3.4.1'

4 changes: 2 additions & 2 deletions gocardless_pro/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ def _default_headers(self):
'Authorization': 'Bearer {0}'.format(self.access_token),
'Content-Type': 'application/json',
'GoCardless-Client-Library': 'gocardless-pro-python',
'GoCardless-Client-Version': '3.4.0',
'GoCardless-Client-Version': '3.4.1',
'User-Agent': self._user_agent(),
'GoCardless-Version': '2015-07-06',
}
Expand All @@ -181,7 +181,7 @@ def _user_agent(self):
python_version = '.'.join(platform.python_version_tuple()[0:2])
vm_version = '{}.{}.{}-{}{}'.format(*sys.version_info)
return ' '.join([
'gocardless-pro-python/3.4.0',
'gocardless-pro-python/3.4.1',
'python/{0}'.format(python_version),
'{0}/{1}'.format(platform.python_implementation(), vm_version),
'{0}/{1}'.format(platform.system(), platform.release()),
Expand Down
2 changes: 1 addition & 1 deletion gocardless_pro/services/billing_request_flows_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def initialise(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand Down
18 changes: 9 additions & 9 deletions gocardless_pro/services/billing_requests_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def collect_customer_details(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand Down Expand Up @@ -126,7 +126,7 @@ def collect_bank_account(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand All @@ -153,7 +153,7 @@ def confirm_payer_details(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand All @@ -179,7 +179,7 @@ def fulfil(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand All @@ -205,7 +205,7 @@ def cancel(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand Down Expand Up @@ -284,7 +284,7 @@ def notify(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand All @@ -309,7 +309,7 @@ def fallback(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand Down Expand Up @@ -339,7 +339,7 @@ def choose_currency(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand All @@ -363,7 +363,7 @@ def select_institution(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand Down
6 changes: 3 additions & 3 deletions gocardless_pro/services/blocks_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def disable(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand All @@ -138,7 +138,7 @@ def enable(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand All @@ -162,7 +162,7 @@ def block_by_ref(self,params=None, headers=None):
path = '/blocks/block_by_ref'

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand Down
2 changes: 1 addition & 1 deletion gocardless_pro/services/creditor_bank_accounts_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def disable(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand Down
2 changes: 1 addition & 1 deletion gocardless_pro/services/customer_bank_accounts_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def disable(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand Down
2 changes: 1 addition & 1 deletion gocardless_pro/services/customer_notifications_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def handle(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand Down
2 changes: 1 addition & 1 deletion gocardless_pro/services/funds_availabilities_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class FundsAvailabilitiesService(base_service.BaseService):
"""

RESOURCE_CLASS = resources.FundsAvailability
RESOURCE_NAME = 'funds_availability'
RESOURCE_NAME = 'funds_availabilities'


def check(self,identity,params=None, headers=None):
Expand Down
2 changes: 1 addition & 1 deletion gocardless_pro/services/instalment_schedules_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ def cancel(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand Down
4 changes: 2 additions & 2 deletions gocardless_pro/services/mandate_imports_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def submit(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand Down Expand Up @@ -133,7 +133,7 @@ def cancel(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand Down
2 changes: 1 addition & 1 deletion gocardless_pro/services/mandate_pdfs_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def create(self,params=None, headers=None):
To generate a PDF mandate in another language, set the
`Accept-Language` header when creating the PDF mandate to the relevant
[ISO 639-1](http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)
[ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)
language code supported for the scheme.
| Scheme | Supported languages
Expand Down
4 changes: 2 additions & 2 deletions gocardless_pro/services/mandates_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ def cancel(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand Down Expand Up @@ -178,7 +178,7 @@ def reinstate(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand Down
8 changes: 4 additions & 4 deletions gocardless_pro/services/outbound_payments_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def withdraw(self,params=None, headers=None):
path = '/outbound_payments/withdrawal'

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand Down Expand Up @@ -88,7 +88,7 @@ def cancel(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand All @@ -98,7 +98,7 @@ def approve(self,identity,params=None, headers=None):
"""Approve an outbound payment.

Approves an outbound payment. Only outbound payments with the
pending_approval status can be approved.
"pending_approval" status can be approved.

Args:
identity (string): Unique identifier of the outbound payment.
Expand All @@ -113,7 +113,7 @@ def approve(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand Down
4 changes: 2 additions & 2 deletions gocardless_pro/services/payer_authorisations_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def submit(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand Down Expand Up @@ -164,7 +164,7 @@ def confirm(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand Down
4 changes: 2 additions & 2 deletions gocardless_pro/services/payments_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ def cancel(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand Down Expand Up @@ -182,7 +182,7 @@ def retry(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand Down
2 changes: 1 addition & 1 deletion gocardless_pro/services/redirect_flows_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def complete(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand Down
2 changes: 1 addition & 1 deletion gocardless_pro/services/scenario_simulators_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def run(self,identity,params=None, headers=None):
})

if params is not None:
params = {self._envelope_key(): params}
params = {'data': params}
response = self._perform_request('POST', path, params, headers,
retry_failures=False)
return self._resource_for(response)
Expand Down
Loading
Loading