File Coverage

blib/lib/Business/OnlinePayment/IPayment.pm
Criterion Covered Total %
statement 9 11 81.8
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 13 15 86.6


line stmt bran cond sub pod time code
1             package Business::OnlinePayment::IPayment;
2              
3 1     1   22787 use 5.010001;
  1         5  
4 1     1   6 use strict;
  1         2  
  1         23  
5 1     1   5 use warnings;
  1         6  
  1         30  
6              
7             # preparation
8 1     1   980 use XML::Compile::WSDL11;
  0            
  0            
9             use XML::Compile::SOAP11;
10             use XML::Compile::Transport::SOAPHTTP;
11             use Business::OnlinePayment::IPayment::Response;
12             use Business::OnlinePayment::IPayment::Transaction;
13             use Business::OnlinePayment::IPayment::Return;
14             use Digest::MD5 qw/md5_hex/;
15             use URI;
16              
17             use Business::OnlinePayment 3;
18             use Moo;
19              
20             use base 'Business::OnlinePayment';
21              
22             # use Log::Report mode => 'DEBUG';
23              
24             =head1 NAME
25              
26             Business::OnlinePayment::IPayment - Checkout via Ipayment Silent Mode
27              
28             =head1 VERSION
29              
30             Version 0.08
31              
32             =cut
33              
34             our $VERSION = '0.08';
35              
36             =head1 DESCRIPTION
37              
38             This module provides an interface for online payments via gateway, using the
39             IPayment silent mode (L).
40              
41             It supports payments, capture and reverse operations, and vault-related
42             functions.
43              
44             =head1 SYNOPSIS
45              
46             use Business::OnlinePayment::IPayment;
47             my %account = (
48             accountid => 99999,
49             trxuserid => 99998,
50             trxpassword => 0,
51             adminactionpassword => '5cfgRT34xsdedtFLdfHxj7tfwx24fe',
52             app_security_key => 'testtest',
53             wsdl_file => $wsdl_file,
54             success_url => 'http://example.net/checkout-payment',
55             failure_url => 'http://example.net/checkout-success',
56             hidden_trigger_rul => 'http://example.net/trigger',
57             );
58            
59            
60             my $secbopi = Business::OnlinePayment::IPayment->new(%account);
61             $secbopi->transaction(transactionType => 'preauth',
62             trxAmount => 5000);
63             # see Business::OnlinePayment::IPayment::Transaction for available options
64              
65             $response = $ua->post('https://ipayment.de/merchant/99999/processor/2.0/',
66             { ipayment_session_id => $secbopi->session_id,
67             addr_name => "Mario Pegula",
68             silent => 1,
69             cc_number => "4111111111111111",
70             cc_checkcode => "",
71             cc_expdate_month => "02",
72             trx_securityhash => $secbopi->trx_securityhash,
73             cc_expdate_year => "2014" });
74            
75            
76             =head2 ACCESSORS
77              
78             =head3 Fixed values (accountData and processorUrls)
79              
80             The following attributes should and can be set only in the
81             constructor, as they are pretty much fixed values.
82              
83             =over 4
84              
85             =item wsdl_file
86              
87             The name of th WSDL file. It should be a local file.
88              
89             =cut
90              
91             has wsdl_file => (is => 'rw');
92              
93             =item accountid
94              
95             The Ipayment account id (the one put into the CGI url). Integer.
96              
97             =cut
98              
99             has accountid => (is => 'rw',
100             isa => sub {
101             die "Not an integer" unless $_[0] =~ m/^[0-9]+$/s
102             });
103              
104             =item trxuserid
105              
106             The application ID, you can in your ipayment configuration menu read
107             using Anwendung > Details. Integer
108              
109             =cut
110              
111             has trxuserid => (is => 'rw',
112             isa => sub {
113             die "Not an integer" unless $_[0] =~ m/^[0-9]+$/s
114             });
115              
116             =item trxpassword
117              
118             For each application, there is an application password which
119             automatically ipayment System is presented. The password consists of
120             numbers. You will find the application password in your ipayment
121             Anwendungen > Details
122              
123             B
124              
125             =cut
126              
127             has trxpassword => (is => 'rw');
128              
129             =item adminactionpassword
130              
131             The admin password.
132              
133             B
134              
135             =cut
136              
137             has adminactionpassword => (is => 'rw');
138              
139              
140             =item app_security_key
141              
142             If this attribute is set, we will (and shall) send a checksum for the
143             parameters.
144              
145             B
146              
147             =cut
148              
149             has app_security_key => (is => 'rw');
150              
151              
152             =item accountData
153              
154             Accessor to retrieve the hash with the account data details. The
155             output will look like this:
156              
157             accountData => {
158             accountid => 99999,
159             trxuserid => 99999,
160             trxpassword =>0,
161             adminactionpassword => '5cfgRT34xsdedtFLdfHxj7tfwx24fe'}
162              
163              
164             =cut
165              
166             sub accountData {
167             my $self = shift;
168             my %account_data = ( # mandatory
169             accountId => $self->accountid,
170             trxuserId => $self->trxuserid,
171             trxpassword => $self->trxpassword
172             );
173             my $adminpass = $self->adminactionpassword;
174             if (defined $adminpass) {
175             $account_data{adminactionpassword} = $adminpass;
176             }
177             return \%account_data;
178             }
179              
180             =item success_url
181              
182             Mandatory (for us) field, where to redirect the user in case of success.
183              
184             CGI-Name: C
185              
186             I
187             script.> (no need to C)
188              
189             =cut
190              
191             has success_url => (is => 'rw',
192             isa => sub { die "Missing success url" unless $_[0] },
193             default => sub { die "Missing success url" },
194             );
195              
196             =item failure_url
197              
198             Mandatory (for us) field, where to redirect the user in case of failure.
199              
200             CGI Name: C
201             Data type: String
202              
203             This URL is more in case of failure of ipayment system with the error information and parameters B. This URL must point to a CGI script that can handle the paramaters.
204              
205             =cut
206              
207             has failure_url => (is => 'rw',
208             isa => sub { die "Missing failure url" unless $_[0] },
209             default => sub { die "Missing success url" },
210             );
211              
212              
213             =item hidden_trigger_url
214              
215             Optional url for the hidden trigger.
216              
217             =cut
218              
219             has hidden_trigger_url => (is => 'rw');
220              
221              
222             =item processorUrls
223              
224             Return the hashref with the defined urls
225              
226             =back
227              
228             =cut
229              
230             sub processorUrls {
231             my $self = shift;
232             return {
233             redirectUrl => $self->success_url,
234             silentErrorUrl => $self->failure_url,
235             hiddenTriggerUrl => $self->hidden_trigger_url
236             };
237             }
238              
239              
240              
241              
242              
243             =head3 error
244              
245             This accessors point to a XML::Compile::SOAP backtrace. The object is
246             quite large and deeply nested, but it's there just in case we need it.
247              
248             =cut
249              
250             has error => (is => 'rwp');
251              
252             =head3 debug
253              
254             Every call to session id stores the trace into this attribute.
255              
256             =cut
257              
258             has debug => (is => 'rwp');
259              
260             =head3 trx_obj
261              
262             Attribute to hold a L object
263              
264             =cut
265              
266             has trx_obj => (is => 'rwp');
267              
268             =head3 transaction
269              
270             Constructor for the object above. All the argument are passed verbatim
271             to the L constructor,
272             then the object is stored.
273              
274             =cut
275              
276             sub transaction {
277             my $self = shift;
278             my %trx = @_;
279             my $trxdata = Business::OnlinePayment::IPayment::Transaction->new(%trx);
280             $self->_set_trx_obj($trxdata);
281             }
282              
283              
284             =head2 METHODS
285              
286             =over 4
287              
288             =item session_id
289              
290             This is the main method to call. The session is not stored in the object, because it can used only B. So calling session_id will send the data to the SOAP service and retrieve the session key.
291              
292             =cut
293              
294             sub session_id {
295             my $self = shift;
296             # clean eventually stale data
297             $self->_set_error(undef);
298              
299             my %args = (
300             # fixed values
301             accountData => $self->accountData,
302             processorUrls => $self->processorUrls,
303             # then the transaction
304             transactionType => $self->trx_obj->transactionType,
305             paymentType => $self->trx_obj->paymentType,
306             transactionData => $self->trx_obj->transactionData,
307             );
308             # and the options, if needed
309             if ($self->trx_obj->options) {
310             $args{options} = $self->trx_obj->options;
311             }
312              
313             # do the request passing the accountData
314             my ($res, $trace) = $self->_get_soap_object('createSession')->(%args);
315             $self->_set_debug($trace);
316              
317             # check if we got something valuable
318             unless ($res and
319             ref($res) eq 'HASH' and
320             exists $res->{createSessionResponse}->{sessionId}) {
321             # ok, we got an error. Save the trace to the error and return
322             $self->_set_error($trace);
323             return undef;
324             }
325              
326             return $res->{createSessionResponse}->{sessionId};
327             # please note that we don't store the sessionId. It's a fire and forget.
328             }
329              
330              
331             =item raw_response_hash
332              
333             Debug for the arguments passed to IPayment::Return;
334              
335             =cut
336              
337             has raw_response_hash => (is => 'rwp');
338              
339              
340              
341             =item capture($ret_trx_number, $amount, $currency, $opts)
342              
343             Charge an amount previously preauth'ed. C<$amount> and C<$currency>
344             are optional and may be used to charge partial amounts. C<$amount> and
345             C<$currency> follow the same rules of C and C
346             of L (no decimal,
347             usually multiply by 100).
348              
349             The last optional argument should be a hashref with additional
350             parameters to pass to transactionData (notably shopperId).
351              
352             =cut
353              
354             sub _do_post_payment_op {
355             my ($self, $op, $number, $amount, $currency, $trxdetails) = @_;
356             unless (defined $number) {
357             $self->_set_error("Missing transaction number");
358             return undef;
359             }
360             die "Wrong usage, missing operation" unless $op;
361             my %args = (
362             accountData => $self->accountData,
363             origTrxNumber => $number,
364             );
365             # amount is always mandatory for transactionData
366             if ($amount) {
367             my %trxdata;
368             die "Wrong amount $amount!\n" unless ($amount =~ m/^[1-9][0-9]*$/s);
369              
370             unless ($currency) {
371             $currency = 'EUR'
372             }
373             unless ($currency =~ m/^[A-Z]{3}$/s) {
374             die "Wrong currency name!\n";
375             }
376            
377             %trxdata = (
378             trxAmount => $amount,
379             trxCurrency => $currency,
380             );
381             if ($trxdetails and
382             (ref($trxdetails) eq 'HASH')
383             and %$trxdetails) {
384             foreach my $k (keys %$trxdetails) {
385             unless ($trxdata{$k}) {
386             $trxdata{$k} = $trxdetails->{$k}
387             }
388             }
389             }
390             $args{transactionData} = \%trxdata;
391             }
392              
393             die "Wrong operation" unless ($op eq 'capture' or
394             $op eq 'refund' or
395             $op eq 'reverse');
396              
397             my ($res, $trace) = $self->_get_soap_object($op)->(%args);
398             $self->_set_debug($trace);
399             $self->_set_raw_response_hash($res);
400             if ($res and ref($res) eq 'HASH' and
401             exists $res->{"${op}Response"}->{ipaymentReturn}) {
402             return Business::OnlinePayment::IPayment::Return
403             ->new($res->{"${op}Response"}->{ipaymentReturn});
404             }
405             else {
406             $self->_set_error($trace);
407             return undef;
408             }
409             }
410              
411             =item datastorage_op($datastorage_id)
412              
413             After calling C, if you have a valid datastorage id, you
414             may want to use that instead of creating a session and use the form.
415              
416             This method will do a SOAP request to the Ipayment server, using the
417             transaction details provided in the call to C, and do the
418             requested operation. So far it's supported preauth and auth. The
419             capture and other operations should be done via its own method (which
420             don't require the datastorage, but simply the previous transaction's
421             id).
422              
423             =cut
424              
425             sub datastorage_op {
426             my ($self, $id) = @_;
427             return unless $id;
428            
429             $self->_set_error(undef);
430             # this should be fully populated by now
431             my %args = (
432             accountData => $self->accountData,
433             paymentData => {
434             storageData => {
435             fromDatastorageId => $id,
436             },
437             },
438             transactionData => $self->trx_obj->transactionData,
439             );
440             my $operation = $self->trx_obj->transactionType;
441             # append the options if needed
442             if ($self->trx_obj->options) {
443             $args{options} = $self->trx_obj->options;
444             }
445             my ($res, $trace) = $self->_get_soap_object($operation)->(%args);
446             $self->_set_debug($trace);
447             $self->_set_raw_response_hash($res);
448              
449             # in the trasaction object the call is defined as in CGI, but we
450             # need the SOAP one
451             my $op = $self->_translate_to_soap_call($operation);
452              
453             if ($res and ref($res) eq 'HASH' and
454             exists $res->{"${op}Response"}->{ipaymentReturn}) {
455             return Business::OnlinePayment::IPayment::Return
456             ->new($res->{"${op}Response"}->{ipaymentReturn});
457             }
458             else {
459             $self->_set_error($trace);
460             return undef;
461             }
462             }
463              
464             =item expire_datastorage($id)
465              
466             Given the storage id passed as argument, expire it. Keep in mind that
467             expiring it multiple times returns always true, so the return code is
468             not really interesting.
469              
470             It returns 0 if the storage didn't exist.
471              
472             =cut
473              
474             sub expire_datastorage {
475             my ($self, $id) = @_;
476             return unless $id;
477             my $op = 'expireDatastorage';
478             my %args = (
479             accountData => $self->accountData,
480             datastorageId => $id,
481             );
482             my ($res, $trace) = $self->_get_soap_object($op)->(%args);
483             $self->_set_debug($trace);
484             $self->_set_raw_response_hash($res);
485             if ($res and ref($res) eq 'HASH' and
486             exists $res->{"${op}Response"}->{expireDatastorageReturn}) {
487             return $res->{"${op}Response"}->{expireDatastorageReturn};
488             }
489             return;
490             }
491              
492              
493             sub capture {
494             my ($self, $number, $amount, $currency, $opts) = @_;
495             # init the soap, if not already
496             return $self->_do_post_payment_op(capture => $number,
497             $amount, $currency, $opts);
498             }
499              
500             =item reverse($ret_trx_number)
501              
502             Release the amount previously preauth'ed, passing the original
503             transaction number. No partial amount can be released, and will
504             succeed only if no charging has been done.
505              
506             =cut
507              
508              
509             sub reverse {
510             my ($self, $number) = @_;
511             # we don't pass $amount and $currency
512             return $self->_do_post_payment_op(reverse => $number);
513             }
514              
515             =item refund($ret_trx_number, $amount, $currency, $opts)
516              
517             Refund the given amount. Please note that we have to pass the
518             transaction number B, not the C one.
519              
520             The last optional argument should be a hashref with additional
521             parameters to pass to transactionData (notably shopperId).
522              
523             =cut
524              
525             sub refund {
526             my ($self, $number, $amount, $currency, $opts) = @_;
527             return $self->_do_post_payment_op(refund => $number,
528             $amount, $currency, $opts);
529             }
530              
531             # accessors to soap objects
532              
533             has _soap_createSession => (is => 'rw');
534             has _soap_capture => (is => 'rw');
535             has _soap_reverse => (is => 'rw');
536             has _soap_refund => (is => 'rw');
537             has _soap_preAuthorize => (is => 'rw');
538             has _soap_authorize => (is => 'rw');
539             has _soap_expireDatastorage => (is => 'rw');
540              
541             sub _get_soap_object {
542             my ($self, $op) = @_;
543             my $call = $self->_translate_to_soap_call($op);
544             my $accessor = "_soap_" . $call;
545             my $obj = $self->$accessor;
546             return $obj if $obj;
547             my $wsdl = XML::Compile::WSDL11->new($self->wsdl_file);
548             my $client = $wsdl->compileClient($call);
549             # set the object
550             $self->$accessor($client);
551             return $self->$accessor;
552             }
553              
554             # this method may be used for to do a sanity check, as it will die on
555             # undef/wrong values.
556              
557             sub _translate_to_soap_call {
558             my ($self, $op) = @_;
559             die "No operation provided!" unless $op;
560             my %hash = (capture => 'capture',
561             reverse => 'reverse',
562             refund => 'refund',
563             preauth => 'preAuthorize',
564             auth => 'authorize',
565             authorize => 'authorize',
566             preAuthorize => 'preAuthorize',
567             createSession => 'createSession',
568             expireDatastorage => 'expireDatastorage',
569             );
570             die "Wrong call $op!" unless $hash{$op};
571             return $hash{$op};
572             }
573              
574             =back
575              
576             =head2 SOAP specification
577              
578             Name: createSession
579             Binding: ipaymentBinding
580             Endpoint: https://ipayment.de/service/3.0/
581             SoapAction: createSession
582             Input:
583             use: literal
584             namespace: https://ipayment.de/service_v3/binding
585             message: createSessionRequest
586             parts:
587             accountData: https://ipayment.de/service_v3/extern:AccountData
588             transactionData: https://ipayment.de/service_v3/extern:TransactionData
589             transactionType: https://ipayment.de/service_v3/extern:TransactionType
590             paymentType: https://ipayment.de/service_v3/extern:PaymentType
591             options: https://ipayment.de/service_v3/extern:OptionData
592             processorUrls: https://ipayment.de/service_v3/extern:ProcessorUrlData
593             Output:
594             use: literal
595             namespace: https://ipayment.de/service_v3/binding
596             message: createSessionResponse
597             parts:
598             sessionId: http://www.w3.org/2001/XMLSchema:string
599             Style: rpc
600             Transport: http://schemas.xmlsoap.org/soap/http
601            
602              
603             =head2 SECURITY
604              
605             =over 4
606              
607             =item trx_securityhash
608              
609             If we have a security key, we trigger the hash generation, so we can
610             double check the result.
611              
612             CGI Name: C
613             Data type: string, maximum 32 characters
614              
615             Security hash of CGI command concatenating Id, amount, currency,
616             password, Transaction Security Key (should be set in the configuration
617             menu using ipayment). The hash is C, C,
618             C, C and the I.
619              
620             md5_hex($trxuser_id . $trx_amount . $trx_currency . $trxpassword . $sec_key);
621              
622             perl -e 'use Digest::MD5 qw/md5_hex/;
623             print md5_hex("99998" . 5000 . "EUR" . 0 . "testtest"), "\n";'
624             # => then in the form
625            
626             value="db4812171baef817dec0cd56c0f5c8cd">
627              
628             =cut
629              
630             sub trx_securityhash {
631             my $self = shift;
632             unless ($self->app_security_key) {
633             warn "hash requested, but app_security_key wasn't provided!\n";
634             return;
635             }
636             return md5_hex($self->trxuserid .
637             $self->trx_obj->trxAmount .
638             $self->trx_obj->trxCurrency .
639             $self->trxpassword .
640             $self->app_security_key);
641             }
642              
643             =back
644              
645             =head2 UTILITIES
646              
647             =head3 get_response_obj($rawuri) or get_response_obj(%params)
648              
649             To be sure the transaction happened as aspected, we have to check this back.
650             Expected hash:
651              
652             Success:
653              
654             'ret_transtime' => '08:42:05', 'ret_transtime' => '08:42:03',
655             'ret_errorcode' => '0', 'ret_errorcode' => '0',
656             'redirect_needed' => '0', 'redirect_needed' => '0',
657             'ret_transdate' => '14.03.13', 'ret_transdate' => '14.03.13',
658             'addr_name' => 'Mario Pegula', 'addr_name' => 'Mario Rossi',
659             'trx_paymentmethod' => 'VisaCard', 'trx_paymentmethod' => 'AmexCard',
660             'ret_authcode' => '', 'ret_authcode' => '',
661             'trx_currency' => 'EUR', 'trx_currency' => 'EUR',
662             'ret_url_checksum' => 'md5sum',
663             'ret_param_checksum' => 'md5sum',
664             'ret_ip' => '88.198.37.147', 'ret_ip' => '88.198.37.147',
665             'trx_typ' => 'preauth', 'trx_typ' => 'preauth',
666             'ret_trx_number' => '1-83443831', 'ret_trx_number' => '1-83443830',
667             'ret_status' => 'SUCCESS', 'ret_status' => 'SUCCESS',
668             'trx_paymenttyp' => 'cc', 'trx_paymenttyp' => 'cc',
669             'trx_paymentdata_country' => 'US',
670             'trx_amount' => '5000', 'trx_amount' => '1000',
671             'ret_booknr' => '1-83443831', 'ret_booknr' => '1-83443830',
672             'trxuser_id' => '99998', 'trxuser_id' => '99999',
673             'trx_remoteip_country' => 'DE' 'trx_remoteip_country' => 'DE'
674              
675             Returns a L object, so you
676             can call ->is_success on it.
677              
678             This is just a shortcut for
679              
680             Business::OnlinePayment::IPayment::Response->new(%params);
681              
682             with C and C inherited from the fixed
683             values of this class.
684              
685             =cut
686              
687             sub get_response_obj {
688             my ($self, @args) = @_;
689             my %details;
690             # only one argument: we have an URI
691             if (@args == 1) {
692             my $raw_url = shift(@args);
693             my $uri = URI->new($raw_url);
694             %details = $uri->query_form;
695             $details{raw_url} = $raw_url;
696             }
697             elsif ((@args % 2) == 0) {
698             %details = @args;
699             }
700             else {
701             die "Arguments to validate the response not provided "
702             . "(paramaters or raw url";
703             }
704             unless (exists $details{my_security_key}) {
705             $details{my_security_key} = $self->app_security_key;
706             }
707             unless (exists $details{my_userid}) {
708             $details{my_userid} = $self->trxuserid;
709             }
710             return Business::OnlinePayment::IPayment::Response->new(%details);
711             }
712              
713             =head3 ipayment_cgi_location
714              
715             Returns the correct url where the customer posts the CC data, which is simply:
716             L/processor/2.0/>
717              
718             =cut
719              
720             sub ipayment_cgi_location {
721             my $self = shift;
722             return 'https://ipayment.de/merchant/' . $self->accountid
723             . '/processor/2.0/';
724             }
725              
726              
727             =head2 Additional information
728              
729             =head3 country
730              
731             Country code of the cardholder of the current
732             L object
733              
734             Being these information transaction specific, if a transaction has not
735             been initiated, the method will not do anything nor will return
736             anything.
737              
738             UK will be translated to GB, and EI to IE.
739              
740              
741             =cut
742              
743             sub country {
744             my $self = shift;
745             #
746             return unless $self->trx_obj;
747             if (@_ == 1) {
748             $self->trx_obj->addr_info->{country} = shift;
749             }
750             my $country = uc($self->trx_obj->addr_info->{country});
751             return unless $country =~ m/^[A-Z]{2,3}$/s;
752             if ($country eq 'UK') {
753             return 'GB';
754             }
755             elsif ($country eq 'EI') {
756             return 'IE';
757             }
758             else {
759             return $country;
760             }
761             }
762              
763             =head1 TESTING
764              
765             Test credit card numbers can be found here: L.
766              
767             =head1 AUTHOR
768              
769             Marco Pessotto, C<< >>
770              
771             =head1 BUGS
772              
773             Please report any bugs or feature requests to C, or through
774             the web interface at L. I will be notified, and then you'll
775             automatically be notified of progress on your bug as I make changes.
776              
777              
778              
779              
780             =head1 SUPPORT
781              
782             You can find documentation for this module with the perldoc command.
783              
784             perldoc Business::OnlinePayment::IPayment
785              
786              
787             You can also look for information at:
788              
789             =over 4
790              
791             =item * RT: CPAN's request tracker (report bugs here)
792              
793             L
794              
795             =item * AnnoCPAN: Annotated CPAN documentation
796              
797             L
798              
799             =item * CPAN Ratings
800              
801             L
802              
803             =item * Search CPAN
804              
805             L
806              
807             =back
808              
809              
810             =head1 ACKNOWLEDGEMENTS
811              
812             Thanks to Stefan Hornburg (Racke) C for the initial
813             code, ideas and support.
814              
815             =head1 LICENSE AND COPYRIGHT
816              
817             Copyright 2013-2014 Marco Pessotto.
818              
819             This program is free software; you can redistribute it and/or modify it
820             under the terms of the the Artistic License (2.0). You may obtain a
821             copy of the full license at:
822              
823             L
824              
825             Any use, modification, and distribution of the Standard or Modified
826             Versions is governed by this Artistic License. By using, modifying or
827             distributing the Package, you accept this license. Do not use, modify,
828             or distribute the Package, if you do not accept this license.
829              
830             If your Modified Version has been derived from a Modified Version made
831             by someone other than you, you are nevertheless required to ensure that
832             your Modified Version complies with the requirements of this license.
833              
834             This license does not grant you the right to use any trademark, service
835             mark, tradename, or logo of the Copyright Holder.
836              
837             This license includes the non-exclusive, worldwide, free-of-charge
838             patent license to make, have made, use, offer to sell, sell, import and
839             otherwise transfer the Package with respect to any patent claims
840             licensable by the Copyright Holder that are necessarily infringed by the
841             Package. If you institute patent litigation (including a cross-claim or
842             counterclaim) against any party alleging that the Package constitutes
843             direct or contributory patent infringement, then this Artistic License
844             to you shall terminate on the date that such litigation is filed.
845              
846             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
847             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
848             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
849             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
850             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
851             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
852             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
853             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
854              
855              
856             =cut
857              
858             1; # End of Business::OnlinePayment::IPayment