File Coverage

lib/Payment/Sisow.pm
Criterion Covered Total %
statement 18 87 20.6
branch 0 40 0.0
condition 0 22 0.0
subroutine 6 18 33.3
pod 11 12 91.6
total 35 179 19.5


line stmt bran cond sub pod time code
1             # Copyrights 2013-2014 by [Mark Overmeer].
2             # For other contributors see ChangeLog.
3             # See the manual pages for details on the licensing terms.
4             # Pod stripped from pm file by OODoc 2.01.
5 1     1   149795 use warnings;
  1         3  
  1         44  
6 1     1   8 use strict;
  1         2  
  1         64  
7 1     1   22 use utf8;
  1         2  
  1         11  
8              
9             package Payment::Sisow;
10 1     1   54 use vars '$VERSION';
  1         2  
  1         73  
11             $VERSION = '0.13';
12              
13              
14 1     1   7 use Log::Report 'sisow';
  1         1  
  1         31  
15              
16 1     1   1605 use Digest::SHA1 qw(sha1_hex);
  1         1030  
  1         1467  
17              
18             # documentation calls this "alfanumerical characters"
19             my $valid_purchase_chars = q{ A-Za-z0-9=%*+,./&@"':;?()$-};
20             my $valid_descr_chars = q{ A-Za-z0-9=%*+,./&@"':;?()$-};
21             my $purchase_become_star = q{"':;?()$}; # accepted but replaced by Sisow
22              
23             # documentation calls this "strict alfanumerical characters"
24             my $valid_entrance_chars = q{A-Za-z0-9};
25              
26              
27             sub new(%)
28 0     0 1   { my $class = shift;
29 0 0         $class ne __PACKAGE__ or panic "instantiate an extension of ".__PACKAGE__;
30 0           (bless {}, $class)->init( {@_} );
31             }
32              
33             sub init($)
34 0     0 0   { my ($self, $args) = @_;
35 0 0         $self->{PS_m_id} = $args->{merchant_id} or panic "merchant_id required";
36 0 0         $self->{PS_m_key} = $args->{merchant_key} or panic "merchant_key required";
37 0   0       $self->{PS_test} = $args->{test} || 0;
38 0           $self;
39             }
40              
41             #--------------
42              
43 0     0 1   sub merchantId() {shift->{PS_m_id}}
44 0     0 1   sub merchantKey() {shift->{PS_m_key}}
45 0     0 1   sub isTest() {shift->{PS_test}}
46              
47             #--------------
48              
49              
50             sub listIdealBanks(%)
51 0     0 1   { my ($self, %args) = @_;
52 0           my $b = $self->_list_ideal_banks(%args);
53 0 0         $b ? @$b : ();
54             }
55              
56              
57             sub transactionStatus($)
58 0     0 1   { my ($self, $tid) = @_;
59              
60 0 0         my $p = $self->_transaction_status
61             ( transaction => $tid
62             , merchantid => $self->merchantId
63             , merchantkey => $self->merchantKey
64             ) or return undef;
65              
66 0           $p->{status};
67             }
68              
69              
70              
71             sub transactionInfo($)
72 0     0 1   { my ($self, $tid) = @_;
73              
74 0 0         my $p = $self->_transaction_info
75             ( transaction => $tid
76             , merchantid => $self->merchantId
77             , merchantkey => $self->merchantKey
78             ) or return undef;
79              
80 0           $p->{stamp} =~ s/ /T/; # timestamp lacks 'T' between date and time
81 0           $p;
82             }
83              
84              
85             sub startTransaction(%)
86 0     0 1   { my ($self, %args) = @_;
87 0           my $bank_id = $args{bank_id};
88 0   0       my $amount_euro = $args{amount} // panic;
89 0           my $amount_cent = int($amount_euro*100 + 0.5); # float euro -> int cents
90              
91 0 0         my $purchase_id = $args{purchase_id} or panic;
92 0 0         if(length $purchase_id > 16)
93             { # max 16 chars alphanum
94 0           $purchase_id =~ s/[^$valid_purchase_chars]/ /g;
95 0           warning __x"purchase_id shortened: {id}", id => $purchase_id;
96 0           $purchase_id = substr $purchase_id, 0, 16;
97             }
98              
99 0           my $description;
100 0 0         if(my $d = $args{description})
101             { # max 32 alphanumerical. '_' allowed?
102 0           for($d)
103 0           { s/[^$valid_descr_chars]/ /g;
104 0           s/\s+/ /gs;
105 0           s/\s+$//s;
106             }
107 0 0         if(length $d > 32)
108 0           { warning __x"description shortened for {id}: {descr}"
109             , id => $purchase_id, descr => $d;
110             }
111 0           $description = $d;
112             }
113              
114 0   0       my $entrance = $args{entrance_code} || $purchase_id;
115 0           $entrance =~ s/[^$valid_entrance_chars]//g;
116 0 0         if(length $entrance > 40)
117             { # max 40 chars, defaults to purchaseid
118 0           warning __x"entrance code shortened for {id}: {code}"
119             , id => $purchase_id, code => $entrance;
120 0           $entrance = substr $entrance, 0, 40;
121             }
122 0 0         $entrance = ''
123             if $entrance eq $purchase_id;
124              
125 0   0       my $payment = $args{payment} || 'ideal';
126 0 0 0       error __x"payment via iDEAL requires bank id"
127             if $payment eq 'ideal' && !$bank_id;
128              
129 0 0         my $return = $args{return_url} or panic;
130 0           my $cancel = $args{cancel_url};
131 0           my $callback = $args{callback_url};
132 0   0       my $notify = $args{notify_url} || $return;
133 0 0 0       undef $cancel if defined $cancel && $cancel eq $return;
134 0 0 0       undef $callback if defined $callback && $callback eq $return;
135              
136 0 0         my $p = $self->_start_transaction
    0          
137             ( merchantid => $self->merchantId
138             , merchantkey => $self->merchantKey
139             , payment => ($payment eq 'ideal' ? '' : $payment)
140             , issuerid => $bank_id
141             , amount => $amount_cent
142             , purchaseid => $purchase_id
143             , description => $description
144             , entrancecode=> $entrance
145             , returnurl => $return
146             , cancelurl => $cancel
147             , callbackurl => $callback
148             , notifyurl => $notify
149             ) or return;
150              
151 0           my $bank_page = $p->{issuerurl};
152 0           my $tid = $p->{trxid};
153 0           info __x"redirecting user for purchase {id} to {url}, transaction {tid}"
154             , id => $purchase_id, url => $bank_page, tid => $tid;
155              
156 0           ($tid, $bank_page);
157             }
158              
159             #----------------
160              
161             sub securedPayment(@)
162 0     0 1   { my $self = shift;
163 0 0         my $qs = @_ > 1 ? {@_} : shift;
164 0           my $ec = $qs->{ec};
165 0           my $trxid = $qs->{trxid};
166 0           my $status = $qs->{status};
167             # docs say separated by '/', but isn't in practice
168 0           my $checksum = sha1_hex
169             (join '', $trxid, $ec, $status, $self->merchantId, $self->merchantKey);
170              
171 0 0         return 1
172             if $checksum eq $qs->{sha1};
173              
174 0           alert "checksum of reply failed: $ec/$trxid/$status sum is $checksum";
175 0           0;
176             }
177              
178              
179 0     0 1   sub isValidPurchaseId($) { $_[1] =~ /^[$valid_purchase_chars]{1,16}$/o }
180 0     0 1   sub isValidDescription($) { $_[1] =~ /^[$valid_descr_chars]{0,32}$/o }
181              
182              
183             #--------------
184              
185             1;