File Coverage

blib/lib/Business/GoCardless/Utils.pm
Criterion Covered Total %
statement 53 53 100.0
branch 11 12 91.6
condition 1 2 50.0
subroutine 12 12 100.0
pod 5 5 100.0
total 82 84 97.6


line stmt bran cond sub pod time code
1             package Business::GoCardless::Utils;
2              
3             =head1 NAME
4              
5             Business::GoCardless::Utils
6              
7             =head1 DESCRIPTION
8              
9             A role containing gocardless utilities.
10              
11             =cut
12              
13 20     20   152039 use strict;
  20         59  
  20         630  
14 20     20   1457 use warnings;
  20         55  
  20         4570  
15              
16 20     20   562 use Moo::Role;
  20         9980  
  20         1675  
17              
18 20     20   11755 use MIME::Base64 qw/ encode_base64 /;
  20         53  
  20         1088  
19 20     20   11287 use Digest::SHA qw/ hmac_sha256_hex /;
  20         54400  
  20         15143  
20              
21             =head1 METHODS
22              
23             =head2 sign_params
24              
25             Signs the passed params hash using the app secret
26              
27             my $signature = $self->sign_params( \%params,$app_secret );
28              
29             =cut
30              
31             sub sign_params {
32 12     12 1 59 my ( $self,$params,$app_secret ) = @_;
33              
34 12         44 return hmac_sha256_hex(
35             $self->normalize_params( $params ),
36             $app_secret
37             );
38             }
39              
40             =head2 signature_valid
41              
42             Checks the signature is valid for the given params hash with the app secret
43              
44             if ( ! $self->signature_valid( \%params,$app_secret ) ) {
45             # throw an error
46             }
47              
48             =cut
49              
50             sub signature_valid {
51 14     14 1 175 my ( $self,$params,$app_secret,$sig ) = @_;
52              
53             # for testing, use live at your own risk
54 14 100       65 return 1 if $ENV{GOCARDLESS_SKIP_SIG_CHECK};
55              
56 11 100       39 if ( $sig ) { # version > 1 (since we don't have access to ->client here)
57 4         119 return $sig eq hmac_sha256_hex( $params,$app_secret );
58             } else {
59             # delete local is 5.12+ only so need to copy hash here
60 7         20 my $params_copy = { %{ $params } };
  7         36  
61 7         36 $sig = delete( $params_copy->{signature} );
62 7         34 return $sig eq $self->sign_params( $params_copy,$app_secret );
63             }
64             }
65              
66             =head2 generate_nonce
67              
68             Generates a random nonce for use with a gocardless request, it being a base64
69             encoded concatination of the current seconds since epoch + | + rand(256)
70              
71             my $nonce = $self->generate_nonce;
72              
73             =cut
74              
75             sub generate_nonce {
76 4     4 1 12 my ( $self ) = @_;
77              
78 4         241 chomp( my $nonce = encode_base64( time . '|' . rand(256) ) );
79 4         303 return $nonce;
80             }
81              
82             =head2 flatten_params
83              
84             Flattens a hash as specified by the gocardless API. see
85             https://developer.gocardless.com/#constructing-the-parameter-array
86              
87             my $flat_params = $self->flatten_params( \%params );
88              
89             =cut
90              
91             sub flatten_params {
92 30     30 1 30734 my ( $self,$params ) = @_;
93              
94             return [
95 90         193 map { _flatten_param( $_,$params->{$_} ) }
96 30         55 sort keys( %{ $params } )
  30         151  
97             ];
98             }
99              
100             =head2 normalize_params
101              
102             Normalizes the passed params hash into a string for use in queries to the
103             gocardless API. Includes param flattening and RFC5849 encoding
104              
105             my $query_string = $self->normalize_params( \%params );
106              
107             =cut
108              
109             sub normalize_params {
110 32     32 1 4319 my ( $self,$params ) = @_;
111              
112             return join( '&',
113 229         1073 map { $_->[0] . '=' . $_->[1] }
114 229         423 map { [ _rfc5849_encode( $_->[0] ),_rfc5849_encode( $_->[1] ) ] }
115 468 50       882 sort { $a->[0] cmp $b->[0] || $a->[1] cmp $b->[1] }
116 32 100 50     65 @{ ref( $params ) eq 'HASH'
  32         218  
117             ? $self->flatten_params( $params )
118             : ( $params // [] )
119             }
120             );
121             }
122              
123             sub _flatten_param {
124 275     275   497 my( $key,$value ) = @_;
125              
126 275         358 my @r;
127              
128 275 100       583 if ( ref( $value ) eq 'HASH' ) {
    100          
129 30         49 foreach my $sub_key ( sort keys( %{ $value } ) ) {
  30         135  
130 168         434 push( @r,_flatten_param( "$key\[$sub_key\]",$value->{$sub_key} ) );
131             }
132             } elsif ( ref( $value ) eq 'ARRAY' ) {
133 8         18 foreach my $sub_key ( @{ $value } ) {
  8         22  
134 17         60 push( @r,_flatten_param( "$key\[\]",$sub_key ) );
135             }
136             } else {
137 237         524 push( @r,[ $key,$value ] );
138             }
139              
140 275         731 return @r;
141             }
142              
143             sub _rfc5849_encode {
144 458     458   777 my ( $str ) = @_;
145              
146 458         1012 $str =~ s#([^-.~_a-z0-9])#sprintf('%%%02X', ord($1))#gei;
  715         2202  
147 458         1225 return $str;
148             }
149              
150             =head1 AUTHOR
151              
152             Lee Johnson - C
153              
154             This library is free software; you can redistribute it and/or modify it under
155             the same terms as Perl itself. If you would like to contribute documentation,
156             features, bug fixes, or anything else then please raise an issue / pull request:
157              
158             https://github.com/Humanstate/business-gocardless
159              
160             =cut
161              
162             1;
163              
164             # vim: ts=4:sw=4:et