Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 372 lines (266 sloc) 15.662 kb
64a4c9c @johnboxall readme
johnboxall authored
1 Django PayPal
2 =============
3
4
5 About
6 -----
7
e528252 @johnboxall Updated the README with some common FAQ.
johnboxall authored
8 Django PayPal is a pluggable application that implements with PayPal Payments
9 Standard and Payments Pro.
7273c50 @johnboxall readmeformatting
johnboxall authored
10
d6b783b @johnboxall + Fix up readme...
johnboxall authored
11 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
12 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
13
d6b783b @johnboxall + Fix up readme...
johnboxall authored
14 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
15
4039574 @johnboxall Fixed a bug in the IPN view. Thanks Oliver. Also added tests so it never...
johnboxall authored
16 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.aut...
johnboxall authored
17
481f120 @johnboxall Added NVP model to keep track of all the things...
johnboxall authored
18
d6b783b @johnboxall + Fix up readme...
johnboxall authored
19 Using PayPal Payments Standard IPN:
20 -------------------------------
64a4c9c @johnboxall readme
johnboxall authored
21
22 1. Download the code from GitHub:
23
24 git clone git://github.com/johnboxall/django-paypal.git paypal
d6b783b @johnboxall + Fix up readme...
johnboxall authored
25
e528252 @johnboxall Updated the README with some common FAQ.
johnboxall authored
26 1. Edit `settings.py` and add `paypal.standard.ipn` to your `INSTALLED_APPS`
27 and `PAYPAL_RECEIVER_EMAIL`:
64a4c9c @johnboxall readme
johnboxall authored
28
29 # settings.py
30 ...
d6b783b @johnboxall + Fix up readme...
johnboxall authored
31 INSTALLED_APPS = (... 'paypal.standard.ipn', ...)
616f56f @johnboxall + Updated README & include pdt signals.
johnboxall authored
32 ...
33 PAYPAL_RECEIVER_EMAIL = "yourpaypalemail@example.com"
64a4c9c @johnboxall readme
johnboxall authored
34
e528252 @johnboxall Updated the README with some common FAQ.
johnboxall authored
35 1. Create an instance of the `PayPalPaymentsForm` in the view where you would
36 like to collect money. Call `render` on the instance in your template to
37 write out the HTML.
64a4c9c @johnboxall readme
johnboxall authored
38
39 # views.py
40 ...
41 from paypal.standard.forms import PayPalPaymentsForm
42
43 def view_that_asks_for_money(request):
44
45 # What you want the button to do.
46 paypal_dict = {
47 "business": "yourpaypalemail@example.com",
d6b783b @johnboxall + Fix up readme...
johnboxall authored
48 "amount": "10000000.00",
64a4c9c @johnboxall readme
johnboxall authored
49 "item_name": "name of the item",
50 "invoice": "unique-invoice-id",
d6b783b @johnboxall + Fix up readme...
johnboxall authored
51 "notify_url": "http://www.example.com/your-ipn-location/",
64a4c9c @johnboxall readme
johnboxall authored
52 "return_url": "http://www.example.com/your-return-location/",
53 "cancel_return": "http://www.example.com/your-cancel-location/",
54
55 }
56
57 # Create the instance.
58 form = PayPalPaymentsForm(initial=paypal_dict)
e528252 @johnboxall Updated the README with some common FAQ.
johnboxall authored
59 context = {"form": form}
60 return render_to_response("payment.html", context)
64a4c9c @johnboxall readme
johnboxall authored
61
d6b783b @johnboxall + Fix up readme...
johnboxall authored
62
e528252 @johnboxall Updated the README with some common FAQ.
johnboxall authored
63 <!-- payment.html -->
d6b783b @johnboxall + Fix up readme...
johnboxall authored
64 ...
65 <h1>Show me the money!</h1>
e528252 @johnboxall Updated the README with some common FAQ.
johnboxall authored
66 <!-- writes out the form tag automatically -->
d6b783b @johnboxall + Fix up readme...
johnboxall authored
67 {{ form.render }}
64a4c9c @johnboxall readme
johnboxall authored
68
69 1. When someone uses this button to buy something PayPal makes a HTTP POST to
70 your "notify_url". PayPal calls this Instant Payment Notification (IPN).
254f1d3 @mthornhill add some pdt documentation and update ipn docs from refactor
mthornhill authored
71 The view `paypal.standard.ipn.views.ipn` handles IPN processing. To set the
72 correct `notify_url` add the following to your `urls.py`:
64a4c9c @johnboxall readme
johnboxall authored
73
74 # urls.py
75 ...
76 urlpatterns = patterns('',
d6b783b @johnboxall + Fix up readme...
johnboxall authored
77 (r'^something/hard/to/guess/', include('paypal.standard.ipn.urls')),
64a4c9c @johnboxall readme
johnboxall authored
78 )
79
d6b783b @johnboxall + Fix up readme...
johnboxall authored
80 1. Whenever an IPN is processed a signal will be sent with the result of the
81 transaction. Connect the signals to actions to perform the needed operations
82 when a successful payment is recieved.
83
84 There are two signals for basic transactions:
53ddc9b payment_was_succes[s]ful
David Cramer authored
85 - `payment_was_successful`
61f9dbc @johnboxall Slowly merging in subscription stuff - this is a work in progress.
johnboxall authored
86 - `payment_was_flagged`
d6b783b @johnboxall + Fix up readme...
johnboxall authored
87
88 And four signals for subscriptions:
89 - `subscription_cancel` - Sent when a subscription is cancelled.
90 - `subscription_eot` - Sent when a subscription expires.
91 - `subscription_modify` - Sent when a subscription is modified.
92 - `subscription_signup` - Sent when a subscription is created.
61f9dbc @johnboxall Slowly merging in subscription stuff - this is a work in progress.
johnboxall authored
93
e43f734 Added 3 more recurring payment signals.
David Cramer authored
94 Several more exist for recurring payments:
2fe1afe Updated signal list.
David Cramer authored
95 - `recurring_create` - Sent when a recurring payment is created.
96 - `recurring_payment` - Sent when a payment is received from a recurring payment.
e43f734 Added 3 more recurring payment signals.
David Cramer authored
97 - `recurring_cancel` - Sent when a recurring payment is cancelled.
98 - `recurring_suspend` - Sent when a recurring payment is suspended.
99 - `recurring_reactivate` - Sent when a recurring payment is reactivated.
2fe1afe Updated signal list.
David Cramer authored
100
101 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. Updates ...
johnboxall authored
102
d6b783b @johnboxall + Fix up readme...
johnboxall authored
103 # models.py
104 ...
0d5f1dd @mthornhill refactored ipn and pdt into separate applications
mthornhill authored
105 from paypal.standard.ipn.signals import payment_was_successful
31f7f6e @johnboxall Added a few templates to help start getting the tests together. Updates ...
johnboxall authored
106
107 def show_me_the_money(sender, **kwargs):
108 ipn_obj = sender
bbd0163 @johnboxall fix for recurring payments bug.
johnboxall authored
109 # Undertake some action depending upon `ipn_obj`.
254f1d3 @mthornhill add some pdt documentation and update ipn docs from refactor
mthornhill authored
110 if ipn_obj.custom == "Upgrade all users!":
111 Users.objects.update(paid=True)
112 payment_was_successful.connect(show_me_the_money)
113
114
115 Using PayPal Payments Standard PDT:
24cb747 @dcramer Clean up docs a bit
authored
116 -----------------------------------
254f1d3 @mthornhill add some pdt documentation and update ipn docs from refactor
mthornhill authored
117
d6b783b @johnboxall + Fix up readme...
johnboxall authored
118 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
119
d6b783b @johnboxall + Fix up readme...
johnboxall authored
120 1. Download the code from GitHub:
254f1d3 @mthornhill add some pdt documentation and update ipn docs from refactor
mthornhill authored
121
d6b783b @johnboxall + Fix up readme...
johnboxall authored
122 git clone git://github.com/johnboxall/django-paypal.git paypal
123
c37239b @johnboxall + Added note about PAYPAL_IDENTITY_TOKEN to README.
johnboxall authored
124 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
125
126 # settings.py
254f1d3 @mthornhill add some pdt documentation and update ipn docs from refactor
mthornhill authored
127 ...
c37239b @johnboxall + Added note about PAYPAL_IDENTITY_TOKEN to README.
johnboxall authored
128 INSTALLED_APPS = (... 'paypal.standard.pdt', ...)
129
130 PAYPAL_IDENTITY_TOKEN = "xxx"
d6b783b @johnboxall + Fix up readme...
johnboxall authored
131
132 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
133
134 1. After someone uses this button to buy something PayPal will return the user to your site at
135 your "return_url" with some extra GET parameters. PayPal calls this Payment Data Transfer (PDT).
136 The view `paypal.standard.pdt.views.pdt` handles PDT processing. to specify the correct
137 `return_url` add the following to your `urls.py`:
138
139 # urls.py
140 ...
141 urlpatterns = patterns('',
142 (r'^paypal/pdt/', include('paypal.standard.pdt.urls')),
143 ...
144 )
145
d6b783b @johnboxall + Fix up readme...
johnboxall authored
146 Using PayPal Payments Standard with Subscriptions:
24cb747 @dcramer Clean up docs a bit
authored
147 --------------------------------------------------
254f1d3 @mthornhill add some pdt documentation and update ipn docs from refactor
mthornhill authored
148
d6b783b @johnboxall + Fix up readme...
johnboxall authored
149 1. For subscription actions, you'll need to add a parameter to tell it to use the subscription buttons and the command, plus any
150 subscription-specific settings:
31f7f6e @johnboxall Added a few templates to help start getting the tests together. Updates ...
johnboxall authored
151
d6b783b @johnboxall + Fix up readme...
johnboxall authored
152 # views.py
153 ...
61f9dbc @johnboxall Slowly merging in subscription stuff - this is a work in progress.
johnboxall authored
154 paypal_dict = {
155 "cmd": "_xclick-subscriptions",
156 "business": "your_account@paypal",
157 "a3": "9.99", # monthly price
158 "p3": 1, # duration of each unit (depends on unit)
159 "t3": "M", # duration unit ("M for Month")
160 "src": "1", # make payments recur
161 "sra": "1", # reattempt payment on payment error
162 "no_note": "1", # remove extra notes (optional)
163 "item_name": "my cool subscription",
164 "notify_url": "http://www.example.com/your-ipn-location/",
165 "return_url": "http://www.example.com/your-return-location/",
166 "cancel_return": "http://www.example.com/your-cancel-location/",
167 }
168
169 # Create the instance.
170 form = PayPalPaymentsForm(initial=paypal_dict, button_type="subscribe")
171
172 # Output the button.
173 form.render()
174
175
64a4c9c @johnboxall readme
johnboxall authored
176 Using PayPal Payments Standard with Encrypted Buttons:
177 ------------------------------------------------------
178
d6b783b @johnboxall + Fix up readme...
johnboxall authored
179 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
180
181 1. Encrypted buttons require the `M2Crypto` library:
182
183 easy_install M2Crypto
184
185
186 1. Encrypted buttons require certificates. Create a private key:
187
188 openssl genrsa -out paypal.pem 1024
189
190 1. Create a public key:
191
192 openssl req -new -key paypal.pem -x509 -days 365 -out pubpaypal.pem
193
194 1. Upload your public key to the paypal website (sandbox or live).
195
7273c50 @johnboxall readmeformatting
johnboxall authored
196 [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
197
7273c50 @johnboxall readmeformatting
johnboxall authored
198 [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
199
200 1. Copy your `cert id` - you'll need it in two steps. It's on the screen where
201 you uploaded your public key.
202
203 1. Download PayPal's public certificate - it's also on that screen.
204
205 1. Edit your `settings.py` to include cert information:
206
207 # settings.py
208 PAYPAL_PRIVATE_CERT = '/path/to/paypal.pem'
209 PAYPAL_PUBLIC_CERT = '/path/to/pubpaypal.pem'
210 PAYPAL_CERT = '/path/to/paypal_cert.pem'
211 PAYPAL_CERT_ID = 'get-from-paypal-website'
212
213 1. Swap out your unencrypted button for a `PayPalEncryptedPaymentsForm`:
214
215 # views.py
216 from paypal.standard.forms import PayPalEncryptedPaymentsForm
217
218 def view_that_asks_for_money(request):
219 ...
220 # Create the instance.
221 form = PayPalPaymentsForm(initial=paypal_dict)
222 # Works just like before!
223 form.render()
7273c50 @johnboxall readmeformatting
johnboxall authored
224
64a4c9c @johnboxall readme
johnboxall authored
225
226 Using PayPal Payments Standard with Encrypted Buttons and Shared Secrets:
cce14ce @johnboxall condensed item/recurring into one parameter
johnboxall authored
227 -------------------------------------------------------------------------
64a4c9c @johnboxall readme
johnboxall authored
228
229 This method uses Shared secrets instead of IPN postback to verify that transactions
230 are legit. PayPal recommends you should use Shared Secrets if:
231
232 * You are not using a shared website hosting service.
233 * You have enabled SSL on your web server.
234 * You are using Encrypted Website Payments.
235 * You use the notify_url variable on each individual payment transaction.
236
237 Use postbacks for validation if:
238 * You rely on a shared website hosting service
239 * You do not have SSL enabled on your web server
240
241 1. Swap out your button for a `PayPalSharedSecretEncryptedPaymentsForm`:
242
7273c50 @johnboxall readmeformatting
johnboxall authored
243 # views.py
244 from paypal.standard.forms import PayPalSharedSecretEncryptedPaymentsForm
245
64a4c9c @johnboxall readme
johnboxall authored
246 def view_that_asks_for_money(request):
247 ...
248 # Create the instance.
249 form = PayPalSharedSecretEncryptedPaymentsForm(initial=paypal_dict)
250 # Works just like before!
251 form.render()
252
d6b783b @johnboxall + Fix up readme...
johnboxall authored
253 1. Verify that your IPN endpoint is running on SSL - `request.is_secure()` should return `True`!
64a4c9c @johnboxall readme
johnboxall authored
254
cce14ce @johnboxall condensed item/recurring into one parameter
johnboxall authored
255
e528252 @johnboxall Updated the README with some common FAQ.
johnboxall authored
256 Using PayPal Payments Pro (WPP)
257 -------------------------------
cce14ce @johnboxall condensed item/recurring into one parameter
johnboxall authored
258
e528252 @johnboxall Updated the README with some common FAQ.
johnboxall authored
259 WPP is the more awesome version of PayPal that lets you accept payments on your
260 site. WPP reuses code from `paypal.standard` so you'll need to include both
f1452cf @kevin1024 updated README file to remove dead link and add missing step to WPP inst...
kevin1024 authored
261 apps.
cce14ce @johnboxall condensed item/recurring into one parameter
johnboxall authored
262
28c618e @wolever Adding instructions for getting PayPal API credentials.
wolever authored
263 1. Obtain PayPal Pro API credentials: login to PayPal, click *My Account*,
264 *Profile*, *Request API credentials*, *Set up PayPal API credentials and
265 permissions*, *View API Signature*.
faa0ed3 @johnboxall + Moved all TEST settings to a settings variable that can be more easily...
johnboxall authored
266
24cb747 @dcramer Clean up docs a bit
authored
267 2. Edit `settings.py` and add `paypal.standard` and `paypal.pro` to your
28c618e @wolever Adding instructions for getting PayPal API credentials.
wolever authored
268 `INSTALLED_APPS` and put in your PayPal Pro API credentials.
cce14ce @johnboxall condensed item/recurring into one parameter
johnboxall authored
269
270 # settings.py
271 ...
272 INSTALLED_APPS = (... 'paypal.standard', 'paypal.pro', ...)
28c618e @wolever Adding instructions for getting PayPal API credentials.
wolever authored
273 PAYPAL_TEST = True
274 PAYPAL_WPP_USER = "???"
e528252 @johnboxall Updated the README with some common FAQ.
johnboxall authored
275 PAYPAL_WPP_PASSWORD = "???"
276 PAYPAL_WPP_SIGNATURE = "???"
faa0ed3 @johnboxall + Moved all TEST settings to a settings variable that can be more easily...
johnboxall authored
277
24cb747 @dcramer Clean up docs a bit
authored
278 3. Run `python manage.py syncdb` to add the required tables.
31f7f6e @johnboxall Added a few templates to help start getting the tests together. Updates ...
johnboxall authored
279
24cb747 @dcramer Clean up docs a bit
authored
280 4. Write a wrapper view for `paypal.pro.views.PayPalPro`:
31f7f6e @johnboxall Added a few templates to help start getting the tests together. Updates ...
johnboxall authored
281
e528252 @johnboxall Updated the README with some common FAQ.
johnboxall authored
282 # views.py
cce14ce @johnboxall condensed item/recurring into one parameter
johnboxall authored
283 from paypal.pro.views import PayPalPro
3c961c0 @johnboxall Updated README w/ a better description of putting in paypal pro.
johnboxall authored
284
285 def buy_my_item(request):
e528252 @johnboxall Updated the README with some common FAQ.
johnboxall authored
286 item = {"amt": "10.00", # amount to charge for item
287 "inv": "inventory", # unique tracking variable paypal
288 "custom": "tracking", # custom tracking variable for you
289 "cancelurl": "http://...", # Express checkout cancel url
290 "returnurl": "http://..."} # Express checkout return url
cce14ce @johnboxall condensed item/recurring into one parameter
johnboxall authored
291
e528252 @johnboxall Updated the README with some common FAQ.
johnboxall authored
292 kw = {"item": item, # what you're selling
293 "payment_template": "payment.html", # template name for payment
294 "confirm_template": "confirmation.html", # template name for confirmation
295 "success_url": "/success/"} # redirect location after success
296
297 ppp = PayPalPro(**kw)
298 return ppp(request)
299
300
24cb747 @dcramer Clean up docs a bit
authored
301 5. Create templates for payment and confirmation. By default both templates are
e528252 @johnboxall Updated the README with some common FAQ.
johnboxall authored
302 populated with the context variable `form` which contains either a
303 `PaymentForm` or a `Confirmation` form.
304
f96c58d @kevin1024 fix formatting for WPP template
kevin1024 authored
305 <!-- payment.html -->
306 <h1>Show me the money</h1>
307 <form method="post" action="">
308 {{ form }}
309 <input type="submit" value="Pay Up">
310 </form>
311
312 <!-- confirmation.html -->
313 <h1>Are you sure you want to buy this thing?</h1>
314 <form method="post" action="">
315 {{ form }}
316 <input type="submit" value="Yes I Yams">
317 </form>
e528252 @johnboxall Updated the README with some common FAQ.
johnboxall authored
318
f1452cf @kevin1024 updated README file to remove dead link and add missing step to WPP inst...
kevin1024 authored
319 6. Add your view to `urls.py`, and add the IPN endpoint to receive callbacks
e528252 @johnboxall Updated the README with some common FAQ.
johnboxall authored
320 from PayPal:
321
cce14ce @johnboxall condensed item/recurring into one parameter
johnboxall authored
322 # urls.py
e528252 @johnboxall Updated the README with some common FAQ.
johnboxall authored
323 ...
cce14ce @johnboxall condensed item/recurring into one parameter
johnboxall authored
324 urlpatterns = ('',
325 ...
e528252 @johnboxall Updated the README with some common FAQ.
johnboxall authored
326 (r'^payment-url/$', 'myproject.views.buy_my_item')
327 (r'^some/obscure/name/', include('paypal.standard.ipn.urls')),
cce14ce @johnboxall condensed item/recurring into one parameter
johnboxall authored
328 )
64a4c9c @johnboxall readme
johnboxall authored
329
f1452cf @kevin1024 updated README file to remove dead link and add missing step to WPP inst...
kevin1024 authored
330 7. Connect to the provided signals and have them do something useful:
331 - `payment_was_successful`
332 - `payment_was_flagged`
333
334
335 8. Profit.
64a4c9c @johnboxall readme
johnboxall authored
336
98460e0 @johnboxall Move `get_hexdigest` inside `make_secret` function to avoid `contrib.aut...
johnboxall authored
337
2e0ec2f @johnboxall + Formatting updates.
johnboxall authored
338 Links:
339 ------
340
341 1. [Set your IPN Endpoint on the PayPal Sandbox](https://www.sandbox.paypal.com/us/cgi-bin/webscr?cmd=_profile-ipn-notify)
342
e528252 @johnboxall Updated the README with some common FAQ.
johnboxall authored
343 2. [Django PayPal on Google Groups](http://groups.google.com/group/django-paypal)
344
15a3273 API ref link, as its extremely useful.
David Cramer authored
345 3. [PayPal API Reference](https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/howto_api_reference)
346
cce14ce @johnboxall condensed item/recurring into one parameter
johnboxall authored
347 License (MIT)
348 =============
349
350 Copyright (c) 2009 Handi Mobility Inc.
351
352 Permission is hereby granted, free of charge, to any person
353 obtaining a copy of this software and associated documentation
354 files (the "Software"), to deal in the Software without
355 restriction, including without limitation the rights to use,
356 copy, modify, merge, publish, distribute, sublicense, and/or sell
357 copies of the Software, and to permit persons to whom the
358 Software is furnished to do so, subject to the following
359 conditions:
360
361 The above copyright notice and this permission notice shall be
362 included in all copies or substantial portions of the Software.
363
364 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
365 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
366 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
367 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
368 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
369 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
370 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28c618e @wolever Adding instructions for getting PayPal API credentials.
wolever authored
371 OTHER DEALINGS IN THE SOFTWARE.
Something went wrong with that request. Please try again.