1 package FS::part_export::ez_prepaid;
3 use base qw( FS::part_export );
6 use vars qw(@ISA %info $version $replace_ok_kludge $product_info);
8 use FS::Record qw( qsearchs );
11 use XML::Simple qw( xml_in );
17 my %language_id = ( English => 1, Spanish => 2 );
19 tie my %options, 'Tie::IxHash',
20 'site_id' => { label => 'Site ID' },
21 'clerk_id' => { label => 'Clerk ID' },
22 # 'product_id' => { label => 'Product ID' }, use the 'title' field
23 # 'amount' => { label => 'Purchase amount' },
24 'language' => { label => 'Language',
26 options => [ 'English', 'Spanish' ],
29 'debug' => { label => 'Debug level',
30 type => 'select', options => [0, 1, 2 ] },
34 'svc' => 'svc_external',
35 'desc' => 'Purchase EZ-Prepaid PIN',
36 'options' => \%options,
38 <P>Export to the EZ-Prepaid PIN purchase service. If the purchase is allowed,
39 the PIN will be stored as svc_external.id.</P>
40 <P>svc_external.title must contain the product ID, and should be set as a fixed
41 field in the service definition. For a list of product IDs, see the
42 "Merchant Info" tab in the EZ Prepaid reseller portal.</P>
46 $replace_ok_kludge = 0;
49 my ($self, $svc_external) = @_;
51 # the name on the certificate is 'debisys.com', for some reason
52 local $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME}=0;
54 my $pin = eval { $self->ez_prepaid_PinDistSale( $svc_external->title ) };
57 local($replace_ok_kludge) = 1;
58 $svc_external->set('id', $pin);
59 $svc_external->replace;
63 $replace_ok_kludge ? '' : "can't change PIN after purchase";
67 "can't delete PIN after purchase";
70 # possibly options at some point to relate these to agentnum/usernum
71 sub site_id { $_[0]->option('site_id') }
73 sub clerk_id { $_[0]->option('clerk_id') }
75 sub ez_prepaid_PinDistSale {
77 my $product_id = shift;
78 $self->ez_prepaid_init; # populate product ID cache
79 my $info = $product_info->{$product_id};
81 if ( $self->option('debug') ) {
82 warn "Purchasing PIN product #$product_id:\n" .
83 $info->{Description}."\n".
84 $info->{CurrencyCode} . ' ' .$info->{Amount}."\n";
87 die "Unknown PIN product #$product_id.\n";
90 my $response = $self->ez_prepaid_request(
96 '', # AccountID, not used for PIN sale
97 $product_info->{$product_id}->{Amount},
99 ($language_id{ $self->option('language') } || 1),
101 if ( $self->option('debug') ) {
102 warn Dumper($response);
103 # includes serial number and transaction ID, possibly useful
104 # (but we don't have a structured place to store it--maybe in
110 sub ez_prepaid_init {
111 # returns the SOAP client object
113 my $wsdl = 'https://webservice.ez-prepaid.com/soap/webServices.wsdl';
115 if ( $self->option('debug') >= 2 ) {
116 SOAP::Lite->import(+trace => [transport => \&log_transport ]);
119 if ( !$self->client ) {
120 $self->set(client => SOAP::Lite->new->service($wsdl));
121 # I don't know if this can happen, but better to bail out here
122 # than go into recursion.
123 die "Error creating SOAP client\n" if !$self->client;
126 if ( !defined($product_info) ) {
127 # for now we only support the 'PIN' type
128 my $response = $self->ez_prepaid_request(
129 'GetTransTypeList', $version, $self->site_id, '', '', '', ''
131 my %transtype = map { $_->{Description} => $_->{TransTypeId} }
132 @{ $response->{TransType} };
134 if ( !exists $transtype{PIN} ) {
135 warn "'PIN' transaction type not available.\n";
136 # or else your site ID is wrong
140 $response = $self->ez_prepaid_request(
143 $self->option('site_id'),
150 map { $_->{ProductId} => $_ }
151 @{ $response->{Product} }
153 } #!defined $product_info
158 if ( UNIVERSAL::can($in, 'content') ) {
159 warn $in->content."\n";
163 my @ForceArray = qw(TransType Product); # add others as needed
164 sub ez_prepaid_request {
166 # takes a method name and param list,
167 # returns a hashref containing the unpacked response
170 $self->ez_prepaid_init if !$self->client;
173 my $xml = $self->client->$method(@_);
174 # All of their response data types are one part, a string, containing
175 # an encoded XML structure, containing the fields described in the docs.
176 my $response = xml_in($xml, ForceArray => \@ForceArray);
177 if ( exists($response->{ResponseCode}) && $response->{ResponseCode} > 0 ) {
178 die "[$method] ".$response->{ResponseMessage};