5 use FS::Record qw( qsearch qsearchs );
14 FS::API - Freeside backend API
22 This module implements a backend API for advanced back-office integration.
24 In contrast to the self-service API, which authenticates an end-user and offers
25 functionality to that end user, the backend API performs a simple shared-secret
26 authentication and offers full, administrator functionality, enabling
27 integration with other back-office systems. Only access this API from a secure
28 network from other backoffice machines. DON'T use this API to create customer
31 If accessing this API remotely with XML-RPC or JSON-RPC, be careful to block
32 the port by default, only allow access from back-office servers with the same
33 security precations as the Freeside server, and encrypt the communication
34 channel (for example, with an SSH tunnel or VPN) rather than accessing it
41 =item insert_payment OPTION => VALUE, ...
43 Adds a new payment to a customers account. Takes a list of keys and values as
44 paramters with the following keys:
66 Option date for payment
72 my $result = FS::API->insert_payment(
73 'secret' => 'sharingiscaring',
79 '_date' => 1397977200, #UNIX timestamp
82 if ( $result->{'error'} ) {
83 die $result->{'error'};
86 print "paynum ". $result->{'paynum'};
93 my($class, %opt) = @_;
94 my $conf = new FS::Conf;
95 return { 'error' => 'Incorrect shared secret' }
96 unless $opt{secret} eq $conf->config('api_shared_secret');
98 #less "raw" than this? we are the backoffice API, and aren't worried
99 # about version migration ala cust_main/cust_location here
100 my $cust_pay = new FS::cust_pay { %opt };
101 my $error = $cust_pay->insert( 'manual'=>1 );
102 return { 'error' => $error,
103 'paynum' => $cust_pay->paynum,
107 # pass the phone number ( from svc_phone )
108 sub insert_payment_phonenum {
109 my($class, %opt) = @_;
110 my $conf = new FS::Conf;
111 return { 'error' => 'Incorrect shared secret' }
112 unless $opt{secret} eq $conf->config('api_shared_secret');
114 $class->_by_phonenum('insert_payment', %opt);
119 my($class, $method, %opt) = @_;
120 my $conf = new FS::Conf;
121 return { 'error' => 'Incorrect shared secret' }
122 unless $opt{secret} eq $conf->config('api_shared_secret');
124 my $phonenum = delete $opt{'phonenum'};
126 my $svc_phone = qsearchs('svc_phone', { 'phonenum' => $phonenum } )
127 or return { 'error' => 'Unknown phonenum' };
129 my $cust_pkg = $svc_phone->cust_svc->cust_pkg
130 or return { 'error' => 'Unlinked phonenum' };
132 $opt{'custnum'} = $cust_pkg->custnum;
134 $class->$method(%opt);
138 =item insert_credit OPTION => VALUE, ...
140 Adds a a credit to a customers account. Takes a list of keys and values as
141 parameters with the following keys
159 The date the credit will be posted
165 my $result = FS::API->insert_credit(
166 'secret' => 'sharingiscaring',
171 '_date' => 1397977200, #UNIX timestamp
174 if ( $result->{'error'} ) {
175 die $result->{'error'};
178 print "crednum ". $result->{'crednum'};
185 my($class, %opt) = @_;
186 my $conf = new FS::Conf;
187 return { 'error' => 'Incorrect shared secret' }
188 unless $opt{secret} eq $conf->config('api_shared_secret');
190 $opt{'reasonnum'} ||= $conf->config('api_credit_reason');
192 #less "raw" than this? we are the backoffice API, and aren't worried
193 # about version migration ala cust_main/cust_location here
194 my $cust_credit = new FS::cust_credit { %opt };
195 my $error = $cust_credit->insert;
196 return { 'error' => $error,
197 'crednum' => $cust_credit->crednum,
201 # pass the phone number ( from svc_phone )
202 sub insert_credit_phonenum {
203 my($class, %opt) = @_;
204 my $conf = new FS::Conf;
205 return { 'error' => 'Incorrect shared secret' }
206 unless $opt{secret} eq $conf->config('api_shared_secret');
208 $class->_by_phonenum('insert_credit', %opt);
212 =item insert_refund OPTION => VALUE, ...
214 Adds a a credit to a customers account. Takes a list of keys and values as
215 parmeters with the following keys: custnum, payby, refund
219 my $result = FS::API->insert_refund(
220 'secret' => 'sharingiscaring',
226 '_date' => 1397977200, #UNIX timestamp
229 if ( $result->{'error'} ) {
230 die $result->{'error'};
233 print "refundnum ". $result->{'crednum'};
240 my($class, %opt) = @_;
241 my $conf = new FS::Conf;
242 return { 'error' => 'Incorrect shared secret' }
243 unless $opt{secret} eq $conf->config('api_shared_secret');
245 # when github pull request #24 is merged,
246 # will have to change over to default reasonnum like credit
247 # but until then, this will do
248 $opt{'reason'} ||= 'API refund';
250 #less "raw" than this? we are the backoffice API, and aren't worried
251 # about version migration ala cust_main/cust_location here
252 my $cust_refund = new FS::cust_refund { %opt };
253 my $error = $cust_refund->insert;
254 return { 'error' => $error,
255 'refundnum' => $cust_refund->refundnum,
259 # pass the phone number ( from svc_phone )
260 sub insert_refund_phonenum {
261 my($class, %opt) = @_;
262 my $conf = new FS::Conf;
263 return { 'error' => 'Incorrect shared secret' }
264 unless $opt{secret} eq $conf->config('api_shared_secret');
266 $class->_by_phonenum('insert_refund', %opt);
272 # "2 way syncing" ? start with non-sync pulling info here, then if necessary
273 # figure out how to trigger something when those things change
275 # long-term: package changes?
277 =item new_customer OPTION => VALUE, ...
279 Creates a new customer. Takes a list of keys and values as parameters with the
290 first name (required)
298 (not typically collected; mostly used for ACH transactions)
304 =item address1 (required)
308 =item city (required)
316 =item state (required)
338 Currently used for third party tax vendor lookups
342 Used for determining FCC 477 reporting
346 Used for determining FCC 477 reporting
366 comma-separated list of email addresses for email invoices. The special value 'POST' is used to designate postal invoicing (it may be specified alone or in addition to email addresses),
368 Set to 1 to enable postal invoicing
370 =item referral_custnum
372 Referring customer number
384 Agent specific customer number
386 =item referral_custnum
388 Referring customer number
394 #certainly false laziness w/ClientAPI::Signup new_customer/new_customer_minimal
395 # but approaching this from a clean start / back-office perspective
396 # i.e. no package/service, no immediate credit card run, etc.
399 my( $class, %opt ) = @_;
401 my $conf = new FS::Conf;
402 return { 'error' => 'Incorrect shared secret' }
403 unless $opt{secret} eq $conf->config('api_shared_secret');
405 #default agentnum like signup_server-default_agentnum?
406 #$opt{agentnum} ||= $conf->config('signup_server-default_agentnum');
408 #same for refnum like signup_server-default_refnum
409 $opt{refnum} ||= $conf->config('signup_server-default_refnum');
411 $class->API_insert( %opt );
414 =item update_customer
416 Updates an existing customer. Passing an empty value clears that field, while
417 NOT passing that key/value at all leaves it alone. Takes a list of keys and
418 values as parameters with the following keys:
424 API Secret (required)
428 Customer number (required)
484 Comma-separated list of email addresses for email invoices. The special value
485 'POST' is used to designate postal invoicing (it may be specified alone or in
486 addition to email addresses),
488 Set to 1 to enable postal invoicing
490 =item referral_custnum
492 Referring customer number
506 sub update_customer {
507 my( $class, %opt ) = @_;
509 my $conf = new FS::Conf;
510 return { 'error' => 'Incorrect shared secret' }
511 unless $opt{secret} eq $conf->config('api_shared_secret');
513 FS::cust_main->API_update( %opt );
516 =item customer_info OPTION => VALUE, ...
518 Returns general customer information. Takes a list of keys and values as
519 parameters with the following keys: custnum, secret
524 my( $class, %opt ) = @_;
525 my $conf = new FS::Conf;
526 return { 'error' => 'Incorrect shared secret' }
527 unless $opt{secret} eq $conf->config('api_shared_secret');
529 my $cust_main = qsearchs('cust_main', { 'custnum' => $opt{custnum} })
530 or return { 'error' => 'Unknown custnum' };
532 $cust_main->API_getinfo;
537 Returns location specific information for the customer. Takes a list of keys
538 and values as paramters with the following keys: custnum, secret
542 #I also monitor for changes to the additional locations that are applied to
543 # packages, and would like for those to be exportable as well. basically the
544 # location data passed with the custnum.
547 my( $class, %opt ) = @_;
548 my $conf = new FS::Conf;
549 return { 'error' => 'Incorrect shared secret' }
550 unless $opt{secret} eq $conf->config('api_shared_secret');
552 my @cust_location = qsearch('cust_location', { 'custnum' => $opt{custnum} });
556 'locations' => [ map $_->hashref, @cust_location ],
562 =item bill_now OPTION => VALUE, ...
564 Bills a single customer now, in the same fashion as the "Bill now" link in the
567 Returns a hash reference with a single key, 'error'. If there is an error,
568 the value contains the error, otherwise it is empty. Takes a list of keys and
569 values as parameters with the following keys:
575 API Secret (required)
579 Customer number (required)
586 my( $class, %opt ) = @_;
587 my $conf = new FS::Conf;
588 return { 'error' => 'Incorrect shared secret' }
589 unless $opt{secret} eq $conf->config('api_shared_secret');
591 my $cust_main = qsearchs('cust_main', { 'custnum' => $opt{custnum} })
592 or return { 'error' => 'Unknown custnum' };
594 my $error = $cust_main->bill_and_collect( 'fatal' => 'return',
599 return { 'error' => $error,
605 #Advertising sources?