recurly webhooks

webhooks:

A webhook in web development is a method of augmenting or altering the behavior of a web page, or web application, with custom callbacks. These callbacks may be maintained, modified, and managed by third-party users and developers who may not necessarily be affiliated with the originating website or application more on wiki.

webhooks on reculry:

Recurly allows us to use webhooks to get the updates about account, take the case of a user who has cancelled the subscription which will expire at the end of current billing cycle (say end of the month) and you want to get notified when this will happen so that you can perform some buisness logic.

setup:

To setup webhooks on recurly we have to login into recurly console > Developers > webhooks. There we will add the url to which recurly will try to POST say http://mysite.com/test_recurly_webhook/ and then add our credentials. After this we need to setup a view which will listen to this POST for django.Since we are using @csrf_exempt we will try to only allow requests from the ip list (from docs)

Recurly Webhook Cosole

mentioned under:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
from django.http import HttpResponse,
from django.views.decorators.csrf import csrf_exempt
import recurly

#you can use some libraries too check [this](http://stackoverflow.com/questions/819355/how-can-i-check-if-an-ip-is-in-a-network-in-python)
allowedIps=['64.74.141.1', '8.36.93.1', '64.74.141.2', '8.36.93.2', '64.74.141.3', '8.36.93.3', '64.74.141.4',
    '8.36.93.4', '64.74.141.5', '8.36.93.5', '64.74.141.6', '8.36.93.6', '64.74.141.7', '8.36.93.7', '64.74.141.8',
    '8.36.93.8', '64.74.141.9', '8.36.93.9', '64.74.141.10', '8.36.93.10', '64.74.141.11', '8.36.93.11', '64.74.141.12',
    '8.36.93.12', '64.74.141.13', '8.36.93.13', '64.74.141.14', '8.36.93.14', '64.74.141.15', '8.36.93.15', '64.74.141.16',
    '8.36.93.16', '64.74.141.17', '8.36.93.17', '64.74.141.18', '8.36.93.18', '64.74.141.19', '8.36.93.19', '64.74.141.20',
    '8.36.93.20', '64.74.141.21', '8.36.93.21', '64.74.141.22', '8.36.93.22', '64.74.141.23', '8.36.93.23', '64.74.141.24',
    '8.36.93.24', '64.74.141.25', '8.36.93.25', '64.74.141.26', '8.36.93.26', '64.74.141.27', '8.36.93.27', '64.74.141.28',
    '8.36.93.28', '64.74.141.29', '8.36.93.29', '64.74.141.30', '8.36.93.30', '64.74.141.31', '8.36.93.31', '64.74.141.32',
    '8.36.93.32', '64.74.141.33', '8.36.93.33', '64.74.141.34', '8.36.93.34', '64.74.141.35', '8.36.93.35', '64.74.141.36',
    '8.36.93.36', '64.74.141.37', '8.36.93.37', '64.74.141.38', '8.36.93.38', '64.74.141.39', '8.36.93.39', '64.74.141.40',
    '8.36.93.40', '64.74.141.41', '8.36.93.41', '64.74.141.42', '8.36.93.42', '64.74.141.43', '8.36.93.43', '64.74.141.44',
    '8.36.93.44', '64.74.141.45', '8.36.93.45', '64.74.141.46', '8.36.93.46', '64.74.141.47', '8.36.93.47', '64.74.141.48',
    '8.36.93.48', '64.74.141.49', '8.36.93.49', '64.74.141.50', '8.36.93.50', '64.74.141.51', '8.36.93.51', '64.74.141.52',
    '8.36.93.52', '64.74.141.53', '8.36.93.53', '64.74.141.54', '8.36.93.54', '64.74.141.55', '8.36.93.55', '64.74.141.56',
    '8.36.93.56', '64.74.141.57', '8.36.93.57', '64.74.141.58', '8.36.93.58', '64.74.141.59', '8.36.93.59', '64.74.141.60',
    '8.36.93.60', '64.74.141.61', '8.36.93.61', '64.74.141.62', '8.36.93.62', '64.74.141.63', '8.36.93.63', '64.74.141.64',
    '8.36.93.64', '64.74.141.65', '8.36.93.65', '64.74.141.66', '8.36.93.66', '64.74.141.67', '8.36.93.67', '64.74.141.68',
    '8.36.93.68', '64.74.141.69', '8.36.93.69', '64.74.141.70', '8.36.93.70', '64.74.141.71', '8.36.93.71', '64.74.141.72',
    '8.36.93.72', '64.74.141.73', '8.36.93.73', '64.74.141.74', '8.36.93.74', '64.74.141.75', '8.36.93.75', '64.74.141.76',
    '8.36.93.76', '64.74.141.77', '8.36.93.77', '64.74.141.78', '8.36.93.78', '64.74.141.79', '8.36.93.79', '64.74.141.80',
    '8.36.93.80', '64.74.141.81', '8.36.93.81', '64.74.141.82', '8.36.93.82', '64.74.141.83', '8.36.93.83', '64.74.141.84',
    '8.36.93.84', '64.74.141.85', '8.36.93.85', '64.74.141.86', '8.36.93.86', '64.74.141.87', '8.36.93.87', '64.74.141.88',
    '8.36.93.88', '64.74.141.89', '8.36.93.89', '64.74.141.90', '8.36.93.90', '64.74.141.91', '8.36.93.91', '64.74.141.92',
    '8.36.93.92', '64.74.141.93', '8.36.93.93', '64.74.141.94', '8.36.93.94', '64.74.141.95', '8.36.93.95', '64.74.141.96',
    '8.36.93.96', '64.74.141.97', '8.36.93.97', '64.74.141.98', '8.36.93.98', '64.74.141.99', '8.36.93.99', '64.74.141.100',
    '8.36.93.100', '64.74.141.101', '8.36.93.101', '64.74.141.102', '8.36.93.102', '64.74.141.103', '8.36.93.103', '64.74.141.104',
    '8.36.93.104', '64.74.141.105', '8.36.93.105', '64.74.141.106', '8.36.93.106', '64.74.141.107', '8.36.93.107', '64.74.141.108',
    '8.36.93.108', '64.74.141.109', '8.36.93.109', '64.74.141.110', '8.36.93.110', '64.74.141.111', '8.36.93.111', '64.74.141.112',
    '8.36.93.112', '64.74.141.113', '8.36.93.113', '64.74.141.114', '8.36.93.114', '64.74.141.115', '8.36.93.115', '64.74.141.116',
    '8.36.93.116', '64.74.141.117', '8.36.93.117', '64.74.141.118', '8.36.93.118', '64.74.141.119', '8.36.93.119', '64.74.141.120',
    '8.36.93.120', '64.74.141.121', '8.36.93.121', '64.74.141.122', '8.36.93.122', '64.74.141.123', '8.36.93.123', '64.74.141.124',
    '8.36.93.124', '64.74.141.125', '8.36.93.125', '64.74.141.126', '8.36.93.126', '64.74.141.127', '8.36.93.127', '64.74.141.128',
    '8.36.93.128', '64.74.141.129', '8.36.93.129', '64.74.141.130', '8.36.93.130', '64.74.141.131', '8.36.93.131', '64.74.141.132',
    '8.36.93.132', '64.74.141.133', '8.36.93.133', '64.74.141.134', '8.36.93.134', '64.74.141.135', '8.36.93.135', '64.74.141.136',
    '8.36.93.136', '64.74.141.137', '8.36.93.137', '64.74.141.138', '8.36.93.138', '64.74.141.139', '8.36.93.139', '64.74.141.140',
    '8.36.93.140', '64.74.141.141', '8.36.93.141', '64.74.141.142', '8.36.93.142', '64.74.141.143', '8.36.93.143', '64.74.141.144',
    '8.36.93.144', '64.74.141.145', '8.36.93.145', '64.74.141.146', '8.36.93.146', '64.74.141.147', '8.36.93.147', '64.74.141.148',
    '8.36.93.148', '64.74.141.149', '8.36.93.149', '64.74.141.150', '8.36.93.150', '64.74.141.151', '8.36.93.151', '64.74.141.152',
    '8.36.93.152', '64.74.141.153', '8.36.93.153', '64.74.141.154', '8.36.93.154', '64.74.141.155', '8.36.93.155', '64.74.141.156',
    '8.36.93.156', '64.74.141.157', '8.36.93.157', '64.74.141.158', '8.36.93.158', '64.74.141.159', '8.36.93.159', '64.74.141.160',
    '8.36.93.160', '64.74.141.161', '8.36.93.161', '64.74.141.162', '8.36.93.162', '64.74.141.163', '8.36.93.163', '64.74.141.164',
    '8.36.93.164', '64.74.141.165', '8.36.93.165', '64.74.141.166', '8.36.93.166', '64.74.141.167', '8.36.93.167', '64.74.141.168',
    '8.36.93.168', '64.74.141.169', '8.36.93.169', '64.74.141.170', '8.36.93.170', '64.74.141.171', '8.36.93.171', '64.74.141.172',
    '8.36.93.172', '64.74.141.173', '8.36.93.173', '64.74.141.174', '8.36.93.174', '64.74.141.175', '8.36.93.175', '64.74.141.176',
    '8.36.93.176', '64.74.141.177', '8.36.93.177', '64.74.141.178', '8.36.93.178', '64.74.141.179', '8.36.93.179', '64.74.141.180',
    '8.36.93.180', '64.74.141.181', '8.36.93.181', '64.74.141.182', '8.36.93.182', '64.74.141.183', '8.36.93.183', '64.74.141.184',
    '8.36.93.184', '64.74.141.185', '8.36.93.185', '64.74.141.186', '8.36.93.186', '64.74.141.187', '8.36.93.187', '64.74.141.188',
    '8.36.93.188', '64.74.141.189', '8.36.93.189', '64.74.141.190', '8.36.93.190', '64.74.141.191', '8.36.93.191', '64.74.141.192',
    '8.36.93.192', '64.74.141.193', '8.36.93.193', '64.74.141.194', '8.36.93.194', '64.74.141.195', '8.36.93.195', '64.74.141.196',
    '8.36.93.196', '64.74.141.197', '8.36.93.197', '64.74.141.198', '8.36.93.198', '64.74.141.199', '8.36.93.199', '64.74.141.200',
    '8.36.93.200', '64.74.141.201', '8.36.93.201', '64.74.141.202', '8.36.93.202', '64.74.141.203', '8.36.93.203', '64.74.141.204',
    '8.36.93.204', '64.74.141.205', '8.36.93.205', '64.74.141.206', '8.36.93.206', '64.74.141.207', '8.36.93.207', '64.74.141.208',
    '8.36.93.208', '64.74.141.209', '8.36.93.209', '64.74.141.210', '8.36.93.210', '64.74.141.211', '8.36.93.211', '64.74.141.212',
    '8.36.93.212', '64.74.141.213', '8.36.93.213', '64.74.141.214', '8.36.93.214', '64.74.141.215', '8.36.93.215', '64.74.141.216',
    '8.36.93.216', '64.74.141.217', '8.36.93.217', '64.74.141.218', '8.36.93.218', '64.74.141.219', '8.36.93.219', '64.74.141.220',
    '8.36.93.220', '64.74.141.221', '8.36.93.221', '64.74.141.222', '8.36.93.222', '64.74.141.223', '8.36.93.223', '64.74.141.224',
    '8.36.93.224', '64.74.141.225', '8.36.93.225', '64.74.141.226', '8.36.93.226', '64.74.141.227', '8.36.93.227', '64.74.141.228',
    '8.36.93.228', '64.74.141.229', '8.36.93.229', '64.74.141.230', '8.36.93.230', '64.74.141.231', '8.36.93.231', '64.74.141.232',
    '8.36.93.232', '64.74.141.233', '8.36.93.233', '64.74.141.234', '8.36.93.234', '64.74.141.235', '8.36.93.235', '64.74.141.236',
    '8.36.93.236', '64.74.141.237', '8.36.93.237', '64.74.141.238', '8.36.93.238', '64.74.141.239', '8.36.93.239', '64.74.141.240',
    '8.36.93.240', '64.74.141.241', '8.36.93.241', '64.74.141.242', '8.36.93.242', '64.74.141.243', '8.36.93.243', '64.74.141.244',
    '8.36.93.244', '64.74.141.245', '8.36.93.245', '64.74.141.246', '8.36.93.246', '64.74.141.247', '8.36.93.247', '64.74.141.248',
    '8.36.93.248', '64.74.141.249', '8.36.93.249', '64.74.141.250', '8.36.93.250', '64.74.141.251', '8.36.93.251', '64.74.141.252',
    '8.36.93.252', '64.74.141.253', '8.36.93.253']


def get_client_ip(request):
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[0]
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip

def allow_by_ip(view_func):
    def authorize(request, *args, **kwargs):
        #user_ip = request.META['REMOTE_ADDR']
        user_ip = get_client_ip(request)
        for ip in allowedIps:
            if ip==user_ip:
                return view_func(request, *args, **kwargs)
        return HttpResponse('Invalid Ip Access!')
    return authorize

@csrf_exempt
@allow_by_ip
def recurly_webhook(request):

    #get not needed
    if request.method == 'GET':
        logger.info('THIS IS THE MESSAGE')
        return HttpResponse('get is up')

    if request.method == 'POST':
        print(request.body)
        #to parse the xml payload
        notification=recurly.objects_for_push_notification(request.body)
        if notification['type']=='expired_subscription_notification':
            #this is an expired susbscription notification, get user account
            #your buisness logic
        logger.info('log for %s' % notification['type'])
        return HttpResponse('success')

You can remove the @allow_by_ip decorator if you dont want authentication by ip.Hope you liked the post

some refrences: recurly docs 1, recurly docs 2, recurly docs 3.