37669 Additional back-office disclaimers
[freeside.git] / FS / FS / API.pm
1 package FS::API;
2
3 use strict;
4 use FS::Conf;
5 use FS::Record qw( qsearch qsearchs );
6 use FS::cust_main;
7 use FS::cust_location;
8 use FS::cust_pay;
9 use FS::cust_credit;
10 use FS::cust_refund;
11
12 =head1 NAME
13
14 FS::API - Freeside backend API
15
16 =head1 SYNOPSIS
17
18   use FS::API;
19
20 =head1 DESCRIPTION
21
22 This module implements a backend API for advanced back-office integration.
23
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 ccess this API from a secure 
28 network from other backoffice machines. DON'T use this API to create customer 
29 portal functionality.
30
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
35 in plaintext.
36
37 =head1 METHODS
38
39 =over 4
40
41 =item insert_payment OPTION => VALUE, ...
42
43 Adds a new payment to a customers account. Takes a list of keys and values as
44 paramters with the following keys:
45
46 =over 5
47
48 =item secret
49
50 API Secret
51
52 =item custnum
53
54 Customer number
55
56 =item payby
57
58 Payment type
59
60 =item paid
61
62 Amount paid
63
64 =item _date
65
66 Option date for payment
67
68 =back
69
70 Example:
71
72   my $result = FS::API->insert_payment(
73     'secret'  => 'sharingiscaring',
74     'custnum' => 181318,
75     'payby'   => 'CASH',
76     'paid'    => '54.32',
77
78     #optional
79     '_date'   => 1397977200, #UNIX timestamp
80   );
81
82   if ( $result->{'error'} ) {
83     die $result->{'error'};
84   } else {
85     #payment was inserted
86     print "paynum ". $result->{'paynum'};
87   }
88
89 =cut
90
91 #enter cash payment
92 sub insert_payment {
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');
97
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,
104          };
105 }
106
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');
113
114   $class->_by_phonenum('insert_payment', %opt);
115
116 }
117
118 sub _by_phonenum {
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');
123
124   my $phonenum = delete $opt{'phonenum'};
125
126   my $svc_phone = qsearchs('svc_phone', { 'phonenum' => $phonenum } )
127     or return { 'error' => 'Unknown phonenum' };
128
129   my $cust_pkg = $svc_phone->cust_svc->cust_pkg
130     or return { 'error' => 'Unlinked phonenum' };
131
132   $opt{'custnum'} = $cust_pkg->custnum;
133
134   $class->$method(%opt);
135
136 }
137
138 =item insert_credit OPTION => VALUE, ...
139
140 Adds a a credit to a customers account.  Takes a list of keys and values as
141 parameters with the following keys
142
143 =over 
144
145 =item secret
146
147 API Secret
148
149 =item custnum
150
151 customer number
152
153 =item amount
154
155 Amount of the credit
156
157 =item _date
158
159 The date the credit will be posted
160
161 =back
162
163 Example:
164
165   my $result = FS::API->insert_credit(
166     'secret'  => 'sharingiscaring',
167     'custnum' => 181318,
168     'amount'  => '54.32',
169
170     #optional
171     '_date'   => 1397977200, #UNIX timestamp
172   );
173
174   if ( $result->{'error'} ) {
175     die $result->{'error'};
176   } else {
177     #credit was inserted
178     print "crednum ". $result->{'crednum'};
179   }
180
181 =cut
182
183 #Enter credit
184 sub insert_credit {
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');
189
190   $opt{'reasonnum'} ||= $conf->config('api_credit_reason');
191
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,
198          };
199 }
200
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');
207
208   $class->_by_phonenum('insert_credit', %opt);
209
210 }
211
212 =item insert_refund OPTION => VALUE, ...
213
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
216
217 Example:
218
219   my $result = FS::API->insert_refund(
220     'secret'  => 'sharingiscaring',
221     'custnum' => 181318,
222     'payby'   => 'CASH',
223     'refund'  => '54.32',
224
225     #optional
226     '_date'   => 1397977200, #UNIX timestamp
227   );
228
229   if ( $result->{'error'} ) {
230     die $result->{'error'};
231   } else {
232     #refund was inserted
233     print "refundnum ". $result->{'crednum'};
234   }
235
236 =cut
237
238 #Enter cash refund.
239 sub insert_refund {
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');
244
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';
249
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,
256          };
257 }
258
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');
265
266   $class->_by_phonenum('insert_refund', %opt);
267
268 }
269
270 #---
271
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
274
275 # long-term: package changes?
276
277 =item new_customer OPTION => VALUE, ...
278
279 Creates a new customer. Takes a list of keys and values as parameters with the
280 following keys:
281
282 =over 4
283
284 =item secret
285
286 API Secret
287
288 =item first
289
290 first name (required)
291
292 =item last
293
294 last name (required)
295
296 =item ss
297
298 (not typically collected; mostly used for ACH transactions)
299
300 =item company
301
302 Company name
303
304 =item address1 (required)
305
306 Address line one
307
308 =item city (required)
309
310 City
311
312 =item county
313
314 County
315
316 =item state (required)
317
318 State
319
320 =item zip (required)
321
322 Zip or postal code
323
324 =item country
325
326 2 Digit Country Code
327
328 =item latitude
329
330 latitude
331
332 =item Longitude
333
334 longitude
335
336 =item geocode
337
338 Currently used for third party tax vendor lookups
339
340 =item censustract
341
342 Used for determining FCC 477 reporting
343
344 =item censusyear
345
346 Used for determining FCC 477 reporting
347
348 =item daytime
349
350 Daytime phone number
351
352 =item night
353
354 Evening phone number
355
356 =item fax
357
358 Fax number
359
360 =item mobile
361
362 Mobile number
363
364 =item invoicing_list
365
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),
367 postal_invoicing
368 Set to 1 to enable postal invoicing
369
370 =item referral_custnum
371
372 Referring customer number
373
374 =item salesnum
375
376 Sales person number
377
378 =item agentnum
379
380 Agent number
381
382 =item agent_custid
383
384 Agent specific customer number
385
386 =item referral_custnum
387
388 Referring customer number
389
390 =back
391
392 =cut
393
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.
397
398 sub new_customer {
399   my( $class, %opt ) = @_;
400
401   my $conf = new FS::Conf;
402   return { 'error' => 'Incorrect shared secret' }
403     unless $opt{secret} eq $conf->config('api_shared_secret');
404
405   #default agentnum like signup_server-default_agentnum?
406   #$opt{agentnum} ||= $conf->config('signup_server-default_agentnum');
407  
408   #same for refnum like signup_server-default_refnum
409   $opt{refnum} ||= $conf->config('signup_server-default_refnum');
410
411   $class->API_insert( %opt );
412 }
413
414 =item update_customer
415
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:
419
420 =over 4
421
422 =item secret
423
424 API Secret (required)
425
426 =item custnum
427
428 Customer number (required)
429
430 =item first
431
432 first name 
433
434 =item last
435
436 last name 
437
438 =item company
439
440 Company name
441
442 =item address1 
443
444 Address line one
445
446 =item city 
447
448 City
449
450 =item county
451
452 County
453
454 =item state 
455
456 State
457
458 =item zip 
459
460 Zip or postal code
461
462 =item country
463
464 2 Digit Country Code
465
466 =item daytime
467
468 Daytime phone number
469
470 =item night
471
472 Evening phone number
473
474 =item fax
475
476 Fax number
477
478 =item mobile
479
480 Mobile number
481
482 =item invoicing_list
483
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),
487 postal_invoicing
488 Set to 1 to enable postal invoicing
489
490 =item referral_custnum
491
492 Referring customer number
493
494 =item salesnum
495
496 Sales person number
497
498 =item agentnum
499
500 Agent number
501
502 =back
503
504 =cut
505
506 sub update_customer {
507   my( $class, %opt ) = @_;
508
509   my $conf = new FS::Conf;
510   return { 'error' => 'Incorrect shared secret' }
511     unless $opt{secret} eq $conf->config('api_shared_secret');
512
513   FS::cust_main->API_update( %opt );
514 }
515
516 =item customer_info OPTION => VALUE, ...
517
518 Returns general customer information. Takes a list of keys and values as
519 parameters with the following keys: custnum, secret 
520
521 =cut
522
523 sub customer_info {
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');
528
529   my $cust_main = qsearchs('cust_main', { 'custnum' => $opt{custnum} })
530     or return { 'error' => 'Unknown custnum' };
531
532   $cust_main->API_getinfo;
533 }
534
535 =item location_info
536
537 Returns location specific information for the customer. Takes a list of keys
538 and values as paramters with the following keys: custnum, secret
539
540 =cut
541
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.
545
546 sub location_info {
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');
551
552   my @cust_location = qsearch('cust_location', { 'custnum' => $opt{custnum} });
553
554   my %return = (
555     'error'           => '',
556     'locations'       => [ map $_->hashref, @cust_location ],
557   );
558
559   return \%return;
560 }
561
562 =item bill_now OPTION => VALUE, ...
563
564 Bills a single customer now, in the same fashion as the "Bill now" link in the
565 UI.
566
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:
570
571 =over 4
572
573 =item secret
574
575 API Secret (required)
576
577 =item custnum
578
579 Customer number (required)
580
581 =back
582
583 =cut
584
585 sub bill_now {
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');
590
591   my $cust_main = qsearchs('cust_main', { 'custnum' => $opt{custnum} })
592     or return { 'error' => 'Unknown custnum' };
593
594   my $error = $cust_main->bill_and_collect( 'fatal'      => 'return',
595                                             'retry'      => 1,
596                                             'check_freq' =>'1d',
597                                           );
598
599    return { 'error' => $error,
600           };
601
602 }
603
604
605 #Advertising sources?
606
607
608 1;