Skip to content
Newer
Older
100644 335 lines (233 sloc) 14.3 KB
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
1 Django PayPal
2 =============
3
4
5 About
6 -----
7
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 26, 2009
8 Django PayPal is a pluggable application that implements with PayPal Payments Standard and Payments Pro.
7273c50 @johnboxall readmeformatting
johnboxall authored Feb 1, 2009
9
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
10 Before diving in, a quick review of PayPal's payment methods is in order! [PayPal Payments Standard](https://cms.paypal.com/cms_content/US/en_US/files/developer/PP_WebsitePaymentsStandard_IntegrationGuide.pdf) is the "Buy it Now" buttons you may have
11 seen floating around the internets. Buyers click on the button and are taken to PayPal's website where they can pay for the product. After completing the purchase PayPal makes an HTTP POST to your `notify_url`. PayPal calls this process [Instant Payment Notification](https://cms.paypal.com/cms_content/US/en_US/files/developer/PP_OrderMgmt_IntegrationGuide.pdf) (IPN) but you may know it as [webhooks](http://blog.webhooks.org). This method kinda sucks because it drops your customers off at PayPal's website but it's easy to implement and doesn't require SSL.
7273c50 @johnboxall readmeformatting
johnboxall authored Feb 1, 2009
12
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
13 PayPal Payments Pro allows you to accept payments on your website. It contains two distinct payment flows - Direct Payment allows the user to enter credit card information on your website and pay on your website. Express Checkout sends the user over to PayPal to confirm their payment method before redirecting back to your website for confirmation. PayPal rules state that both methods must be implemented.
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
14
4039574 @johnboxall Fixed a bug in the IPN view. Thanks Oliver. Also added tests so it ne…
johnboxall authored May 27, 2009
15 There is currently an active discussion over the handling of some of the finer points of the PayPal API and the evolution of this code base - check it out over at [Django PayPal on Google Groups](http://groups.google.com/group/django-paypal).
98460e0 @johnboxall Move `get_hexdigest` inside `make_secret` function to avoid `contrib.…
johnboxall authored May 25, 2009
16
481f120 @johnboxall Added NVP model to keep track of all the things...
johnboxall authored Feb 2, 2009
17
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
18 Using PayPal Payments Standard IPN:
19 -------------------------------
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
20
21 1. Download the code from GitHub:
22
23 git clone git://github.com/johnboxall/django-paypal.git paypal
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
24
616f56f @johnboxall + Updated README & include pdt signals.
johnboxall authored May 4, 2009
25 1. Edit `settings.py` and add `paypal.standard.ipn` to your `INSTALLED_APPS` and `PAYPAL_RECEIVER_EMAIL`:
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
26
27 # settings.py
28 ...
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
29 INSTALLED_APPS = (... 'paypal.standard.ipn', ...)
616f56f @johnboxall + Updated README & include pdt signals.
johnboxall authored May 4, 2009
30 ...
31 PAYPAL_RECEIVER_EMAIL = "yourpaypalemail@example.com"
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
32
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
33 1. Create an instance of the `PayPalPaymentsForm` in the view where you would like the custom to pay.
34 Call `render` on the instance in your template to write out the HTML.
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
35
36 # views.py
37 ...
38 from paypal.standard.forms import PayPalPaymentsForm
39
40 def view_that_asks_for_money(request):
41
42 # What you want the button to do.
43 paypal_dict = {
44 "business": "yourpaypalemail@example.com",
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
45 "amount": "10000000.00",
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
46 "item_name": "name of the item",
47 "invoice": "unique-invoice-id",
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
48 "notify_url": "http://www.example.com/your-ipn-location/",
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
49 "return_url": "http://www.example.com/your-return-location/",
50 "cancel_return": "http://www.example.com/your-cancel-location/",
51
52 }
53
54 # Create the instance.
55 form = PayPalPaymentsForm(initial=paypal_dict)
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
56 return render_to_response("payment.html", {"form":form})
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
57
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
58
59 # payment.html
60 ...
61 <h1>Show me the money!</h1>
62 {{ form.render }}
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
63
64 1. When someone uses this button to buy something PayPal makes a HTTP POST to
65 your "notify_url". PayPal calls this Instant Payment Notification (IPN).
254f1d3 @mthornhill add some pdt documentation and update ipn docs from refactor
mthornhill authored Apr 21, 2009
66 The view `paypal.standard.ipn.views.ipn` handles IPN processing. To set the
67 correct `notify_url` add the following to your `urls.py`:
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
68
69 # urls.py
70 ...
71 urlpatterns = patterns('',
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
72 (r'^something/hard/to/guess/', include('paypal.standard.ipn.urls')),
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
73 )
74
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
75 1. Whenever an IPN is processed a signal will be sent with the result of the
76 transaction. Connect the signals to actions to perform the needed operations
77 when a successful payment is recieved.
78
79 There are two signals for basic transactions:
61f9dbc @johnboxall Slowly merging in subscription stuff - this is a work in progress.
johnboxall authored Apr 26, 2009
80 - `payment_was_succesful`
81 - `payment_was_flagged`
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
82
83 And four signals for subscriptions:
84 - `subscription_cancel` - Sent when a subscription is cancelled.
85 - `subscription_eot` - Sent when a subscription expires.
86 - `subscription_modify` - Sent when a subscription is modified.
87 - `subscription_signup` - Sent when a subscription is created.
61f9dbc @johnboxall Slowly merging in subscription stuff - this is a work in progress.
johnboxall authored Apr 26, 2009
88
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
89 You can connect to these signals and update your data accordingly [Django Signals Documentation](http://docs.djangoproject.com/en/dev/topics/signals/).
31f7f6e @johnboxall Added a few templates to help start getting the tests together. Updat…
johnboxall authored Feb 2, 2009
90
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
91 # models.py
92 ...
0d5f1dd @mthornhill refactored ipn and pdt into separate applications
mthornhill authored Apr 21, 2009
93 from paypal.standard.ipn.signals import payment_was_successful
31f7f6e @johnboxall Added a few templates to help start getting the tests together. Updat…
johnboxall authored Feb 3, 2009
94
95 def show_me_the_money(sender, **kwargs):
96 ipn_obj = sender
bbd0163 @johnboxall fix for recurring payments bug.
johnboxall authored Feb 3, 2009
97 # Undertake some action depending upon `ipn_obj`.
254f1d3 @mthornhill add some pdt documentation and update ipn docs from refactor
mthornhill authored Apr 21, 2009
98 if ipn_obj.custom == "Upgrade all users!":
99 Users.objects.update(paid=True)
100 payment_was_successful.connect(show_me_the_money)
101
102
103 Using PayPal Payments Standard PDT:
104 -------------------------------
105
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
106 Paypal Payment Data Transfer (PDT) allows you to display transaction details to a customer immediately on return to your site unlike PayPal IPN which may take some seconds. [You will need to enable PDT in your PayPal account to use it.your PayPal account to use it](https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/howto_html_paymentdatatransfer).
254f1d3 @mthornhill add some pdt documentation and update ipn docs from refactor
mthornhill authored Apr 21, 2009
107
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
108 1. Download the code from GitHub:
254f1d3 @mthornhill add some pdt documentation and update ipn docs from refactor
mthornhill authored Apr 21, 2009
109
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
110 git clone git://github.com/johnboxall/django-paypal.git paypal
111
c37239b @johnboxall + Added note about PAYPAL_IDENTITY_TOKEN to README.
johnboxall authored May 17, 2009
112 1. Edit `settings.py` and add `paypal.standard.pdt` to your `INSTALLED_APPS`. Also set `PAYPAL_IDENTITY_TOKEN` - you can find the correct value of this setting from the PayPal website:
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
113
114 # settings.py
254f1d3 @mthornhill add some pdt documentation and update ipn docs from refactor
mthornhill authored Apr 21, 2009
115 ...
c37239b @johnboxall + Added note about PAYPAL_IDENTITY_TOKEN to README.
johnboxall authored May 17, 2009
116 INSTALLED_APPS = (... 'paypal.standard.pdt', ...)
117
118 PAYPAL_IDENTITY_TOKEN = "xxx"
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
119
120 1. Create a view that uses `PayPalPaymentsForm` just like in PayPal IPN.
254f1d3 @mthornhill add some pdt documentation and update ipn docs from refactor
mthornhill authored Apr 21, 2009
121
122 1. After someone uses this button to buy something PayPal will return the user to your site at
123 your "return_url" with some extra GET parameters. PayPal calls this Payment Data Transfer (PDT).
124 The view `paypal.standard.pdt.views.pdt` handles PDT processing. to specify the correct
125 `return_url` add the following to your `urls.py`:
126
127 # urls.py
128 ...
129 urlpatterns = patterns('',
130 (r'^paypal/pdt/', include('paypal.standard.pdt.urls')),
131 ...
132 )
133
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
134 Using PayPal Payments Standard with Subscriptions:
135 -------------------------------
254f1d3 @mthornhill add some pdt documentation and update ipn docs from refactor
mthornhill authored Apr 21, 2009
136
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
137 1. For subscription actions, you'll need to add a parameter to tell it to use the subscription buttons and the command, plus any
138 subscription-specific settings:
31f7f6e @johnboxall Added a few templates to help start getting the tests together. Updat…
johnboxall authored Feb 3, 2009
139
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
140 # views.py
141 ...
61f9dbc @johnboxall Slowly merging in subscription stuff - this is a work in progress.
johnboxall authored Apr 26, 2009
142 paypal_dict = {
143 "cmd": "_xclick-subscriptions",
144 "business": "your_account@paypal",
145 "a3": "9.99", # monthly price
146 "p3": 1, # duration of each unit (depends on unit)
147 "t3": "M", # duration unit ("M for Month")
148 "src": "1", # make payments recur
149 "sra": "1", # reattempt payment on payment error
150 "no_note": "1", # remove extra notes (optional)
151 "item_name": "my cool subscription",
152 "notify_url": "http://www.example.com/your-ipn-location/",
153 "return_url": "http://www.example.com/your-return-location/",
154 "cancel_return": "http://www.example.com/your-cancel-location/",
155 }
156
157 # Create the instance.
158 form = PayPalPaymentsForm(initial=paypal_dict, button_type="subscribe")
159
160 # Output the button.
161 form.render()
162
163
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
164 Using PayPal Payments Standard with Encrypted Buttons:
165 ------------------------------------------------------
166
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
167 Use this method to encrypt your button so sneaky gits don't try to hack it. Thanks to [Jon Atkinson](http://jonatkinson.co.uk/) for the [tutorial](http://jonatkinson.co.uk/paypal-encrypted-buttons-django/).
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
168
169 1. Encrypted buttons require the `M2Crypto` library:
170
171 easy_install M2Crypto
172
173
174 1. Encrypted buttons require certificates. Create a private key:
175
176 openssl genrsa -out paypal.pem 1024
177
178 1. Create a public key:
179
180 openssl req -new -key paypal.pem -x509 -days 365 -out pubpaypal.pem
181
182 1. Upload your public key to the paypal website (sandbox or live).
183
7273c50 @johnboxall readmeformatting
johnboxall authored Feb 1, 2009
184 [https://www.paypal.com/us/cgi-bin/webscr?cmd=_profile-website-cert](https://www.paypal.com/us/cgi-bin/webscr?cmd=_profile-website-cert)
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
185
7273c50 @johnboxall readmeformatting
johnboxall authored Feb 1, 2009
186 [https://www.paypal.com/us/cgi-bin/webscr?cmd=_profile-website-cert](https://www.sandbox.paypal.com/us/cgi-bin/webscr?cmd=_profile-website-cert)
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
187
188 1. Copy your `cert id` - you'll need it in two steps. It's on the screen where
189 you uploaded your public key.
190
191 1. Download PayPal's public certificate - it's also on that screen.
192
193 1. Edit your `settings.py` to include cert information:
194
195 # settings.py
196 PAYPAL_PRIVATE_CERT = '/path/to/paypal.pem'
197 PAYPAL_PUBLIC_CERT = '/path/to/pubpaypal.pem'
198 PAYPAL_CERT = '/path/to/paypal_cert.pem'
199 PAYPAL_CERT_ID = 'get-from-paypal-website'
200
201 1. Swap out your unencrypted button for a `PayPalEncryptedPaymentsForm`:
202
203 # views.py
204 from paypal.standard.forms import PayPalEncryptedPaymentsForm
205
206 def view_that_asks_for_money(request):
207 ...
208 # Create the instance.
209 form = PayPalPaymentsForm(initial=paypal_dict)
210 # Works just like before!
211 form.render()
7273c50 @johnboxall readmeformatting
johnboxall authored Feb 1, 2009
212
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
213
214 Using PayPal Payments Standard with Encrypted Buttons and Shared Secrets:
cce14ce @johnboxall condensed item/recurring into one parameter
johnboxall authored Feb 2, 2009
215 -------------------------------------------------------------------------
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
216
217 This method uses Shared secrets instead of IPN postback to verify that transactions
218 are legit. PayPal recommends you should use Shared Secrets if:
219
220 * You are not using a shared website hosting service.
221 * You have enabled SSL on your web server.
222 * You are using Encrypted Website Payments.
223 * You use the notify_url variable on each individual payment transaction.
224
225 Use postbacks for validation if:
226 * You rely on a shared website hosting service
227 * You do not have SSL enabled on your web server
228
229 1. Swap out your button for a `PayPalSharedSecretEncryptedPaymentsForm`:
230
7273c50 @johnboxall readmeformatting
johnboxall authored Feb 1, 2009
231 # views.py
232 from paypal.standard.forms import PayPalSharedSecretEncryptedPaymentsForm
233
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
234 def view_that_asks_for_money(request):
235 ...
236 # Create the instance.
237 form = PayPalSharedSecretEncryptedPaymentsForm(initial=paypal_dict)
238 # Works just like before!
239 form.render()
240
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
241 1. Verify that your IPN endpoint is running on SSL - `request.is_secure()` should return `True`!
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
242
cce14ce @johnboxall condensed item/recurring into one parameter
johnboxall authored Feb 2, 2009
243
244 Using PayPal Payments Pro
245 -------------------------
246
faa0ed3 @johnboxall + Moved all TEST settings to a settings variable that can be more eas…
johnboxall authored Mar 13, 2009
247 PayPal Payments Pro is the more awesome version of PayPal that lets you accept payments on your site. Note that PayPal Pro uses a lot of the code from `paypal.standard` so you'll need to include both apps. Specifically IPN is still used for payment confirmation. There is a fairly good [explanation of the WPP basics on the PayPal Forums](http://www.pdncommunity.com/pdn/board/message?board.id=wppro&thread.id=192).
cce14ce @johnboxall condensed item/recurring into one parameter
johnboxall authored Feb 2, 2009
248
faa0ed3 @johnboxall + Moved all TEST settings to a settings variable that can be more eas…
johnboxall authored Mar 13, 2009
249
250 1. Edit `settings.py` and add `paypal.standard` and `paypal.pro` to your `INSTALLED_APPS`, also set your PayPal settings:
cce14ce @johnboxall condensed item/recurring into one parameter
johnboxall authored Feb 2, 2009
251
252 # settings.py
253 ...
254 INSTALLED_APPS = (... 'paypal.standard', 'paypal.pro', ...)
faa0ed3 @johnboxall + Moved all TEST settings to a settings variable that can be more eas…
johnboxall authored Mar 13, 2009
255 PAYPAL_TEST = True # Start in Testing Mode
256 PAYPAL_WPP_USER = ??? # Get from PayPal website!
257 PAYPAL_WPP_PASSWORD = ???
258 PAYPAL_WPP_SIGNATURE = ???
259
31f7f6e @johnboxall Added a few templates to help start getting the tests together. Updat…
johnboxall authored Feb 3, 2009
260
3c961c0 @johnboxall Updated README w/ a better description of putting in paypal pro.
johnboxall authored Feb 10, 2009
261 1. Write a view wrapper for `paypal.pro.views.PayPalPro` and add it to your `urls.py`:
31f7f6e @johnboxall Added a few templates to help start getting the tests together. Updat…
johnboxall authored Feb 3, 2009
262
cce14ce @johnboxall condensed item/recurring into one parameter
johnboxall authored Feb 2, 2009
263 from paypal.pro.views import PayPalPro
3c961c0 @johnboxall Updated README w/ a better description of putting in paypal pro.
johnboxall authored Feb 10, 2009
264
265 def buy_my_item(request):
266 item = {'amt':"10.00", # amount to charge for item
267 'inv':"inventory#", # unique tracking variable paypal
268 'custom':"tracking#", # custom tracking variable for you
269 'cancelurl':"http://...", # Express checkout cancel url
270 'returnurl':"http://..."} # Express checkout return url
cce14ce @johnboxall condensed item/recurring into one parameter
johnboxall authored Feb 2, 2009
271
3c961c0 @johnboxall Updated README w/ a better description of putting in paypal pro.
johnboxall authored Feb 10, 2009
272 kw = {'item':'item', # what you're selling
273 'payment_template': 'template', # template to use for payment form
274 'confirm_template': 'confirm_template', # form class to use for Express checkout confirmation
275 'payment_form_cls': 'payment_form_cls', # form class to use for payment
276 'success_url': '/success', # where to redirect after successful payment
277 }
278 ppp = PayPalPro(**kw)
279 return ppp(request)
cce14ce @johnboxall condensed item/recurring into one parameter
johnboxall authored Feb 2, 2009
280
281 # urls.py
282
283 urlpatterns = ('',
284 ...
285 (r'^payment-url/$', 'myproject.views.pro')
286 ...
287 )
288
289
faa0ed3 @johnboxall + Moved all TEST settings to a settings variable that can be more eas…
johnboxall authored Mar 13, 2009
290 1. Add the IPN endpoints to your `urls.py` to receive callbacks from PayPal.
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
291
faa0ed3 @johnboxall + Moved all TEST settings to a settings variable that can be more eas…
johnboxall authored Mar 13, 2009
292 1. Profit.
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
293
98460e0 @johnboxall Move `get_hexdigest` inside `make_secret` function to avoid `contrib.…
johnboxall authored May 26, 2009
294
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
295 ToDo:
296 =====
297
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
298 * A a shared conf file for all settings.
64a4c9c @johnboxall readme
johnboxall authored Feb 1, 2009
299
d6b783b @johnboxall + Fix up readme...
johnboxall authored Apr 27, 2009
300 * Shared Secrets: The implementation is untested.
cce14ce @johnboxall condensed item/recurring into one parameter
johnboxall authored Feb 2, 2009
301
76e9cbb @johnboxall pro/views: Made context a parameter for WPP so you can pass it into t…
johnboxall authored Feb 11, 2009
302 * Query Fields: Lots of fields store QueryDict dumps b/c we're not sure exactly what we're getting - would be cool to be able to access those fields like they were a dict (JSONField)
cce14ce @johnboxall condensed item/recurring into one parameter
johnboxall authored Feb 2, 2009
303
76e9cbb @johnboxall pro/views: Made context a parameter for WPP so you can pass it into t…
johnboxall authored Feb 11, 2009
304 * Feeds: Would also be awesome to have a feed of successful payments so you keep up to date with how rich you're getting.
31f7f6e @johnboxall Added a few templates to help start getting the tests together. Updat…
johnboxall authored Feb 3, 2009
305
2e0ec2f @johnboxall + Formatting updates.
johnboxall authored Mar 4, 2009
306 Links:
307 ------
308
309 1. [Set your IPN Endpoint on the PayPal Sandbox](https://www.sandbox.paypal.com/us/cgi-bin/webscr?cmd=_profile-ipn-notify)
310
cce14ce @johnboxall condensed item/recurring into one parameter
johnboxall authored Feb 2, 2009
311 License (MIT)
312 =============
313
314 Copyright (c) 2009 Handi Mobility Inc.
315
316 Permission is hereby granted, free of charge, to any person
317 obtaining a copy of this software and associated documentation
318 files (the "Software"), to deal in the Software without
319 restriction, including without limitation the rights to use,
320 copy, modify, merge, publish, distribute, sublicense, and/or sell
321 copies of the Software, and to permit persons to whom the
322 Software is furnished to do so, subject to the following
323 conditions:
324
325 The above copyright notice and this permission notice shall be
326 included in all copies or substantial portions of the Software.
327
328 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
329 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
330 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
331 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
332 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
333 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
334 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
335 OTHER DEALINGS IN THE SOFTWARE.
Something went wrong with that request. Please try again.