File Coverage

lib/Business/CCProcessor.pm
Criterion Covered Total %
statement 95 110 86.3
branch 0 4 0.0
condition n/a
subroutine 9 10 90.0
pod 5 5 100.0
total 109 129 84.5


line stmt bran cond sub pod time code
1             package Business::CCProcessor;
2              
3 2     2   91220 use warnings;
  2         5  
  2         79  
4 2     2   11 use strict;
  2         4  
  2         165  
5 2     2   2328 use CGI::FormBuilder;
  2         78392  
  2         111  
6 2     2   26 use Carp;
  2         4  
  2         180  
7              
8 2     2   10 use vars qw($VERSION);
  2         3  
  2         3114  
9             $VERSION = '0.09';
10              
11             # Module implementation here
12              
13             1; # Magic true value required at end of module
14              
15             sub new {
16 1     1 1 29 my $self = shift;
17 1         4 my $cc = {};
18 1         3 bless $cc, $self;
19 1         5 return $cc;
20             }
21              
22             sub button_factory {
23 0     0 1 0 my $self = shift;
24 0         0 my $data = shift;
25 0         0 my $method = $data->{'processor_settings'}->{'processor'};
26              
27 0         0 my $fields = $self->$method(\%{$data});
  0         0  
28 0         0 my $form = CGI::FormBuilder->new(
29             method => 'POST',
30             action => $fields->{'action'},
31             # target => '_new',
32             name => "ProceedToCCProcessor",
33             keepextras => 1,
34             sticky => 1,
35             # js => 0,
36             submit => [ $fields->{'button_label'}->{'value'} ],
37             stylesheet => '/path/to/style.css',
38             );
39              
40 0         0 foreach my $key (keys %{$fields}){
  0         0  
41 0 0       0 if( $key eq 'action' ){ next; }
  0         0  
42 0 0       0 if( $key eq 'button_label' ){ next; }
  0         0  
43             $form->field(
44 0         0 name => $fields->{$key}->{'name'},
45             value => $fields->{$key}->{'value'},
46             type => $fields->{$key}->{'type'}
47             );
48             }
49 0         0 my $html = $form->render();
50 0         0 return $html;
51             }
52              
53             sub verisign {
54 1     1 1 12052 my $self = shift;
55 1         5 my $data = shift;
56              
57 1         3 my $fields = {};
58 1         5 $fields->{'action'} = "https://payments.verisign.com/payflowlink";
59 1         7 $fields->{'MFCIsapiCommand'} = { 'name' => 'MFCIsapiCommand', 'type' => 'hidden', 'value' => 'Orders' };
60 1         8 $fields->{'LOGIN'} = { 'name' => 'LOGIN', 'type' => 'hidden', 'value' => $data->{'processor_settings'}->{'login'} };
61 1         7 $fields->{'TYPE'} = { 'name' => 'TYPE', 'type' => 'hidden', 'value' => 'S' };
62 1         8 $fields->{'DESCRIPTION'} = { 'name' => 'DESCRIPTION', 'type' => 'hidden', 'value' => $data->{'processor_settings'}->{'description'} };
63 1         6 $fields->{'PARTNER'} = { 'name' => 'PARTNER', 'type' => 'hidden', 'value' => 'VeriSign' };
64 1         7 $fields->{'NAME'} = { 'name' => 'NAME', 'type' => 'hidden', 'value' => $data->{'credit_card_owner'}->{'name'} };
65 1         8 $fields->{'Street'} = { 'name' => 'NAME', 'type' => 'hidden', 'value' => $data->{'credit_card_owner'}->{'addr1'} };
66 1         7 $fields->{'COMMENT1'} = { 'name' => 'COMMENT1', 'type' => 'hidden', 'value' => $data->{'credit_card_owner'}->{'comments1'} };
67 1         7 $fields->{'COMMENT2'} = { 'name' => 'COMMENT2', 'type' => 'hidden', 'value' => $data->{'credit_card_owner'}->{'comments2'} };
68 1         7 $fields->{'AMOUNT'} = { 'name' => 'AMOUNT', 'type' => 'hidden', 'value' => $data->{'credit_card_owner'}->{'amount'} };
69 1         36 $fields->{'button_label'} = { 'name' => '_submit', 'type' => 'submit', 'value' => $data->{'processor_settings'}->{'button_label'} };
70             # print STDERR $fields->{'button_label'}->{'value'};
71             # print STDERR $data->{'processor_settings'}->{'button_label'};
72              
73 1         4 return $fields;
74             }
75              
76             sub paypal {
77 1     1 1 4089 my $self = shift;
78 1         3 my $data = shift;
79              
80 1         2 my $fields = {};
81 1         4 $fields->{'action'} = "https://www.paypal.com/cgi-bin/webscr";
82 1         7 $fields->{'cmd'} = { 'type' => 'hidden', 'name' => 'cmd', 'value' => '_ext-enter' };
83 1         5 $fields->{'redirect_cmd'} = { 'type' => 'hidden', 'name' => 'redirect_cmd', 'value' => '_xclick' };
84 1         9 $fields->{'business'} = { 'type' => 'hidden', 'name' => 'business', 'value' => $data->{'processor_settings'}->{'business'} };
85 1         6 $fields->{'item_name'} = { 'type' => 'hidden', 'name' => 'item_name', 'value' => $data->{'processor_settings'}->{'item_name'} };
86 1         6 $fields->{'page_style'} = { 'type' => 'hidden', 'name' => 'page_style', 'value' => 'vcol1' };
87 1         7 $fields->{'return'} = { 'type' => 'hidden', 'name' => 'return', 'value' => $data->{'processor_settings'}->{'return_url'} };
88 1         8 $fields->{'cancel_return'} = { 'type' => 'hidden', 'name' => 'cancel_return', 'value' => $data->{'processor_settings'}->{'cancel_return_url'} };
89 1         6 $fields->{'no_note'} = { 'type' => 'hidden', 'name' => 'no_note', 'value' => '1' };
90 1         9 $fields->{'currency_code'} = { 'type' => 'hidden', 'name' => 'currency_code', 'value' => $data->{'processor_settings'}->{'currency_code'} };
91 1         5 $fields->{'on0'} = { 'type' => 'hidden', 'name' => 'on0', 'value' => 'Your Employer' };
92 1         6 $fields->{'tax'} = { 'type' => 'hidden', 'name' => 'tax', 'value' => '0' };
93 1         6 $fields->{'amount'} = { 'type' => 'hidden', 'name' => 'amount', 'value' => $data->{'credit_card_owner'}->{'amount'} };
94 1         7 $fields->{'on1'} = { 'type' => 'hidden', 'name' => 'on1', 'value' => 'Your Occupation' };
95 1         6 $fields->{'on2'} = { 'type' => 'hidden', 'name' => 'on2', 'value' => 'Email' };
96 1         5 $fields->{'no_shipping'} = { 'type' => 'hidden', 'name' => 'no_shipping', 'value' => '1' };
97 1         6 $fields->{'country_code'} = { 'type' => 'hidden', 'name' => 'country_code', 'value' => '' };
98 1         4 $fields->{'process'} = { 'type' => 'hidden', 'name' => 'process', 'value' => '1' };
99 1         7 $fields->{'first_name'} = { 'type' => 'hidden', 'name' => 'first_name', 'value' => $data->{'credit_card_owner'}->{'fname'} };
100 1         6 $fields->{'last_name'} = { 'type' => 'hidden', 'name' => 'last_name', 'value' => $data->{'credit_card_owner'}->{'lname'} };
101 1         15 $fields->{'email'} = { 'type' => 'hidden', 'name' => 'email', 'value' => $data->{'credit_card_owner'}->{'email'} };
102 1         5 $fields->{'os1'} = { 'type' => 'hidden', 'name' => 'os1', 'value' => $data->{'credit_card_owner'}->{'occupation'} };
103 1         6 $fields->{'os0'} = { 'type' => 'hidden', 'name' => 'os0', 'value' => $data->{'credit_card_owner'}->{'employer'} };
104 1         5 $fields->{'address1'} = { 'type' => 'hidden', 'name' => 'address1', 'value' => $data->{'credit_card_owner'}->{'addr1'} };
105 1         4 $fields->{'address2'} = { 'type' => 'hidden', 'name' => 'address2', 'value' => $data->{'credit_card_owner'}->{'addr2'} };
106 1         4 $fields->{'city'} = { 'type' => 'hidden', 'name' => 'city', 'value' => $data->{'credit_card_owner'}->{'city'} };
107 1         5 $fields->{'state'} = { 'type' => 'hidden', 'name' => 'state', 'value' => $data->{'credit_card_owner'}->{'state'} };
108 1         4 $fields->{'zip'} = { 'type' => 'hidden', 'name' => 'zip', 'value' => $data->{'credit_card_owner'}->{'postal_code'} };
109 1         5 $fields->{'phn'} = { 'type' => 'hidden', 'name' => 'phn', 'value' => $data->{'credit_card_owner'}->{'phone'} };
110 1         4 $fields->{'amount'} = { 'type' => 'hidden', 'name' => 'amount', 'value' => $data->{'credit_card_owner'}->{'amount'} };
111 1         5 $fields->{'notes'} = { 'type' => 'hidden', 'name' => 'notes', 'value' => $data->{'credit_card_owner'}->{'notes'} };
112 1         4 $fields->{''} = { 'type' => 'hidden', 'name' => 'Continue', 'value' => 'Continue . . . ' };
113 1         5 $fields->{'button_label'} = { 'name' => '_submit', 'type' => 'submit', 'value' => $data->{'processor_settings'}->{'button_label'} };
114            
115 1         4 return $fields;
116             }
117              
118             sub dia {
119 1     1 1 3927 my $self = shift;
120 1         2 my $data = shift;
121              
122 1         3 my $fields = {};
123 1         3 $fields->{'action'} = "https://secure.democracyinaction.com/dia/shop/processDonate.jsp";
124             # "https://secure.democracyinaction.com/dia/organizations/Greens/shop/custom.jsp?donate_page_KEY=1239";
125 1         8 $fields->{'donate_page_KEY'} = { type => 'hidden', name =>
126             'donate_page_KEY', value => $data->{'preocessor_settings'}->{'donate_page_KEY'} };
127 1         5 $fields->{'amount'} = { 'name' => 'amount', 'type' => 'hidden', 'value' => 'checked' };
128 1         5 $fields->{'amountOther'} = { 'name' => 'amountOther', 'type' => 'hidden', 'value' => $data->{'credit_card_owner'}->{'amount'} };
129             # print STDERR $data->{'credit_card_owner'}->{'amount'}, '\n';
130             # print STDERR sprintf('$%.2f',$data->{'credit_card_owner'}->{'amount'});
131             # print STDERR '\n';
132 1         30 $fields->{'VARCHAR2'} = { 'name' => 'VARCHAR2', 'type' => 'hidden', 'value' => sprintf('$%.2f',$data->{'credit_card_owner'}->{'amount'}) };
133 1         6 $fields->{'First_Name'} = { 'name' => 'First_Name', 'type' => 'hidden', 'value' => $data->{'credit_card_owner'}->{'fname'} };
134 1         16 $fields->{'Last_Name'} = { 'name' => 'Last_Name', 'type' => 'hidden', 'value' => $data->{'credit_card_owner'}->{'lname'} };
135 1         7 $fields->{'Email'} = { 'name' => 'Email', 'type' => 'hidden', 'value' => $data->{'credit_card_owner'}->{'email'} };
136 1         7 $fields->{'Phone'} = { 'name' => 'Phone', 'type' => 'hidden', 'value' => $data->{'credit_card_owner'}->{'phone'} };
137 1         5 $fields->{'Street'} = { 'name' => 'Street', 'type' => 'hidden', 'value' => $data->{'credit_card_owner'}->{'addr1'} };
138 1         7 $fields->{'Street_2'} = { 'name' => 'Street_2', 'type' => 'hidden', 'value' => $data->{'credit_card_owner'}->{'addr2'} };
139 1         5 $fields->{'City'} = { 'name' => 'City', 'type' => 'hidden', 'value' => $data->{'credit_card_owner'}->{'city'} };
140 1         5 $fields->{'State'} = { 'name' => 'State', 'type' => 'hidden', 'value' => $data->{'credit_card_owner'}->{'state'} };
141 1         6 $fields->{'Zip'} = { 'name' => 'Zip', 'type' => 'hidden', 'value' => $data->{'credit_card_owner'}->{'postal_code'} };
142 1         6 $fields->{'VARCHAR0'} = { 'name' => 'VARCHAR0', 'type' => 'hidden', 'value' => $data->{'credit_card_owner'}->{'employer'} };
143 1         6 $fields->{'Occupation'} = { 'name' => 'Occupation', 'type' => 'hidden', 'value' => $data->{'credit_card_owner'}->{'occupation'} };
144 1         6 $fields->{'required'} = { type => 'hidden', name => 'required', value => '0,Phone,Occupation,0,VARCHAR0,VARCHAR2,First_Name,Last_Name,Street,City,State,Zip,' };
145 1         4 $fields->{'updateRowValues'} = { type => 'hidden', name => 'updateRowValues', 'value' => 'true' };
146 1         5 $fields->{'button_label'} = { 'name' => '_submit', 'type' => 'submit', 'value' => $data->{'processor_settings'}->{'button_label'} };
147              
148 1         4 return $fields;
149             }
150              
151             __END__
152              
153             =head1 NAME
154              
155             Business::CCProcessor - Pass transaction off to secure processor
156              
157              
158             =head1 VERSION
159              
160             This document describes Business::CCProcessor version 0.05
161              
162              
163             =head1 SYNOPSIS
164              
165             use Business::CCProcessor;
166             use CGI::FormBuilder; # this is optional
167              
168             my $cc = Business::CCProcessor->new();
169              
170             my %data = (
171             'processor_settings' => \%processor_settings,
172             'credit_card_owner' => \%credit_card_owner,
173             );
174              
175             See below for details about how those two hash references
176             ought to be structured.
177              
178             You may then create a button to include in a web page, like this:
179              
180             my $html_button = $cc->button_factory(\%data);
181              
182             or use any one of the following three methods to get a
183             hash of fields, you can use if you want some additional
184             control over how the button is rendered.
185              
186             my $fields = $cc->verisign(\%data);
187             my $fields = $cc->dia(\%data);
188             my $fields = $cc->paypal(\%data);
189              
190             The data in the $fields hashref can then be used to
191             construct a web form submission button which will take
192             a browser to the credit card forms for these providers.
193             You might consider using CGI::FormBuilder, CGI, CGI::Simple
194             or even hand rolled html fed to print statements, to then
195             render the form with that data.
196              
197             =head1 DESCRIPTION
198              
199             At present this module and its methods are trivially simple
200             in what they do, offering as its one service, the ability to
201             hide how to munge your web form's data into a post call to a
202             supported credit card processor.
203              
204             Business::CCProcessor will permit a script to collect
205             non-financial data locally and then using an http POST call,
206             hand that data off to a secure credit card processor which
207             then collects the credit card parameters, and processes
208             the transaction between the credit card owners account and
209             the script owners account. This is a poor man's variant on
210             Business::OnlinePayment for clients who cannot afford the video
211             camera watched locked cages around their dedicated server,
212             to collect credit card payments from their buyers or donors,
213             in a real-time interaction with the credit card owner.
214              
215             This module is for you if you need to accept online credit card
216             payments for your organization or services but are not prepared
217             to invest in an ssl certificate, a dedicated IP address, a
218             dedicated server and the monitored restricted access to your
219             server which the privacy of your customers or donors requires.
220              
221             Initially this module offers five public methods: a constructor,
222             a button_factory and methods for munging data for three (so
223             far) credit card processors, but additional methods to handle
224             additional credit card processors who permit this sort of
225             interaction should be straight forward to add.
226              
227             Each of the credit card processor methods takes a reference to
228             a hash of values which you will have to create before calling
229             the method. This data is generally of the form:
230              
231             my %data = (
232             'processor_settings' => \%processor_settings,
233             'credit_card_owner' => \%credit_card_owner,
234             );
235              
236             The second part of the hash is fairly consistent across the methods,
237             with some methods offering additional options for passing data than
238             others, but generally, this hash looks like this:
239              
240             %credit_card_owner = (
241             'fname' => '',
242             'lname' => '',
243             'addr1' => '',
244             'addr2' => '',
245             'city' => '',
246             'state' => '',
247             'postal_code' => '',
248             'comments' => '',
249             'comments1' => '',
250             'phone' => '',
251             'email' => '',
252             'employer' => '',
253             'occupation' => '',
254             'amount' => '',
255             'notes' => '',
256             'button_label' => '',
257             );
258              
259             The %processor_settings hash's structure is dependent on which
260             credit card processor method you are using.
261              
262             As this module develops, I anticipate also providing for a
263             mode, to permit this module to be switched from 'commercial',
264             to 'non-profit', to 'electoral_campaign' mode, to account
265             for variances in how what data is collected for each of these
266             types of users.
267              
268             =head2 my $cc = Business::CCProcessor->new();
269              
270             This method creates an object permitting access to the other
271             methods provided by this module.
272              
273             The three public methods listed below each take a reference
274             to a hash of data collected, cleaned and validated from a
275             preceeding web form interaction and returns a hash of fields
276             which can be used to construct a submission button which will
277             POST that data to a web accessible credit card processor.
278              
279             =head2 my $html_button = $cc->button_factory(\%data);
280              
281             By including the name of your credit card processor in the
282             %data hash, as $data->{'processor_settings'}->{'processor'},
283             you can use the ->button_factory() method to access the magic
284             of CGI::FormBuilder, and have returned to you a snippet of
285             html code defining a Proceed_to_CC_Processor form button, ready
286             for inclusion in a web page. The button will have encoded as
287             hidden values, the data given to the method in its invocation,
288             and that data should be handed off to the credit card processor,
289             at least that data the processor is designed to handle.
290              
291             If you need any more control than that over the final form of
292             your web form, you can use these following methods, which are
293             invoked by the ->button_factory() method when doing its work.
294              
295             =head2 my %fields = $cc->verisign(\%data);
296              
297             This method returns the fields necessary to process a payment
298             with Verisign.
299              
300             The %data hash must include the following:
301              
302             %processor_settings = (
303             'processor' => 'verisign',
304             'action' => '' # <-- url of web form posted to
305             'login' => '' # <-- account id
306             'description' => '' # <-- description of transaction
307             'button_label' => '' # <-- what to call the button
308             );
309              
310             =head2 my %fields = $cc->dia(\%data);
311              
312             This method returns the fields necessary to process a payment
313             with Democracy In Action.
314              
315             The %data hash must include the following:
316              
317             %processor_settings = (
318             'processor' => 'dia',
319             'action' => '' # <-- url of web form posted to
320             'button_label' => '' #<-- what to call the button
321             );
322              
323             =head2 my %fields = $cc->paypal(\%data);
324              
325             This method returns the fields necessary to process a payment
326             with Paypal.
327              
328             The %data hash must include the following:
329              
330             %processor_settings = (
331             'processor' => 'paypal',
332             'action' => '' # <-- url of web form posted to
333             'business' => '' # <-- email address registered with paypal
334             'item_name' => '' # <-- description of transaction
335             'return_url' => '' # <-- url on your site to return to
336             'cancel_return_url' => '' # <-- url on your site to error out to
337             'currency_code' => '' # <-- EUR, USD, CAD etc.
338             'button_label' => '' #<-- what to call the button
339             );
340              
341             =head1 INTERFACE
342              
343             =for author to fill in:
344             Write a separate section listing the public components of the modules
345             interface. These normally consist of either subroutines that may be
346             exported, or methods that may be called on objects belonging to the
347             classes provided by the module.
348              
349              
350             =head1 DIAGNOSTICS
351              
352             =for author to fill in:
353             List every single error and warning message that the module can
354             generate (even the ones that will "never happen"), with a full
355             explanation of each problem, one or more likely causes, and any
356             suggested remedies.
357              
358             =over
359              
360             =item C<< Error message here, perhaps with %s placeholders >>
361              
362             [Description of error here]
363              
364             =item C<< Another error message here >>
365              
366             [Description of error here]
367              
368             [Et cetera, et cetera]
369              
370             =back
371              
372              
373             =head1 CONFIGURATION AND ENVIRONMENT
374              
375             =for author to fill in:
376             A full explanation of any configuration system(s) used by the
377             module, including the names and locations of any configuration
378             files, and the meaning of any environment variables or properties
379             that can be set. These descriptions must also include details of any
380             configuration language used.
381            
382             Business::CCProcessor requires no configuration files or environment variables.
383              
384              
385             =head1 DEPENDENCIES
386              
387             =for author to fill in:
388             A list of all the other modules that this module relies upon,
389             including any restrictions on versions, and an indication whether
390             the module is part of the standard Perl distribution, part of the
391             module's distribution, or must be installed separately. ]
392              
393             None at the moment, though a future version will reguire
394             CGI::FormBuilder for some additional methods.
395              
396              
397             =head1 INCOMPATIBILITIES
398              
399             =for author to fill in:
400             A list of any modules that this module cannot be used in conjunction
401             with. This may be due to name conflicts in the interface, or
402             competition for system or program resources, or due to internal
403             limitations of Perl (for example, many modules that use source code
404             filters are mutually incompatible).
405              
406             None reported.
407              
408              
409             =head1 BUGS AND LIMITATIONS
410              
411             Apparently, some credit card processors require that their
412             clients register the domain and path of the scripts which
413             may refer a user and browser to them. Paypal did not reject
414             connections coming from my own development sandbox. But both
415             DiA and VeriSign seem to refuse to do business with the machine
416             under my desk, when I used keys for accounts who's forms are
417             hosted at real url's.
418              
419             If the Verisgn account is setup to screen posts to its forms by
420             referring url, it is possible to configure a new url by logging in to
421             the VeriSign account, and following the following links:
422              
423             Account Information ->
424             PayflowLink Info ->
425             Accepted URLs (look at bottom of configuration page)
426              
427             No bugs have been reported, but I'm sure this is riddled
428             with them.
429              
430             By no means should this module be mistaken for any sort of
431             informed distillation of the wisdom available in the API's
432             for the services these methods interface with. This is more
433             an attempt to hide some of the ugly details from myself for
434             some code I found myself rewriting on a regular basis.
435              
436             At this early stage of development, I am simply working to
437             write an interface which can be substituted into multiple
438             copies of similiar code I am hosting in various scripts,
439             which I'd like to refactor and simplify. If it serves your
440             needs, great. If not, perhaps over time, this can evolve into
441             a more generally useful tool to serve a broader audience.
442              
443             At this point, this is a "works-for-me" kind of project and
444             your input, questions, bug reports, patches and tests to create
445             a more robust, stable and useful tool are certainly welcome.
446              
447             I welcome bug reports and feature requests at both:
448             L<http://www.campaignfoundations.com/project/issues>
449             as well as through the cpan hosted channels at:
450             C<bug-business-ccprocessor@rt.cpan.org>, or through the web
451             interface at L<http://rt.cpan.org>.
452              
453             =head1 SEE ALSO
454              
455             Business::OnlinePayment allows a script to accept credit card
456             data from an end user or other source and process a transaction
457             of funds between the account represented by the credit card
458             data and the account owned by the merchant which deploys
459             the script. Its a fine tool for a client who can afford the
460             security it requires to appropriately handle credit card data.
461              
462              
463             =head1 AUTHOR
464              
465             Hugh Esco C<< <hesco@campaignfoundations.com> >>
466              
467              
468             =head1 LICENCE AND COPYRIGHT
469              
470             Copyright (c) 2007, Hugh Esco C<< <hesco@campaignfoundations.com> >>. All rights reserved.
471              
472             This module is free software; you can redistribute it and/or
473             modify it under the terms of the Gnu Public License. See L<gpl>.
474              
475              
476             =head1 DISCLAIMER OF WARRANTY
477              
478             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
479             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
480             OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
481             PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
482             EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
483             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
484             ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
485             YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
486             NECESSARY SERVICING, REPAIR, OR CORRECTION.
487              
488             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
489             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
490             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
491             LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
492             OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
493             THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
494             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
495             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
496             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
497             SUCH DAMAGES.