Ticket #33252 API customer update
[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.
28
29 If accessing this API remotely with XML-RPC or JSON-RPC, be careful to block
30 the port by default, only allow access from back-office servers with the same
31 security precations as the Freeside server, and encrypt the communication
32 channel (for example, with an SSH tunnel or VPN) rather than accessing it
33 in plaintext.
34
35 =head1 METHODS
36
37 =over 4
38
39 =item insert_payment
40
41 Adds a new payment to a customers account. Takes a hash reference as parameter with the following keys:
42
43 =over 5
44
45 =item secret
46
47 API Secret
48
49 =item custnum
50
51 Customer number
52
53 =item payby
54
55 Payment type
56
57 =item paid
58
59 Amount paid
60
61 =item _date
62
63
64 Option date for payment
65
66 Example:
67
68   my $result = FS::API->insert_payment(
69     'secret'  => 'sharingiscaring',
70     'custnum' => 181318,
71     'payby'   => 'CASH',
72     'paid'    => '54.32',
73
74     #optional
75     '_date'   => 1397977200, #UNIX timestamp
76   );
77
78   if ( $result->{'error'} ) {
79     die $result->{'error'};
80   } else {
81     #payment was inserted
82     print "paynum ". $result->{'paynum'};
83   }
84
85 =back
86
87 =cut
88
89 #enter cash payment
90 sub insert_payment {
91   my($class, %opt) = @_;
92   my $conf = new FS::Conf;
93   return { 'error' => 'Incorrect shared secret' }
94     unless $opt{secret} eq $conf->config('api_shared_secret');
95
96   #less "raw" than this?  we are the backoffice API, and aren't worried
97   # about version migration ala cust_main/cust_location here
98   my $cust_pay = new FS::cust_pay { %opt };
99   my $error = $cust_pay->insert( 'manual'=>1 );
100   return { 'error'  => $error,
101            'paynum' => $cust_pay->paynum,
102          };
103 }
104
105 # pass the phone number ( from svc_phone ) 
106 sub insert_payment_phonenum {
107   my($class, %opt) = @_;
108   my $conf = new FS::Conf;
109   return { 'error' => 'Incorrect shared secret' }
110     unless $opt{secret} eq $conf->config('api_shared_secret');
111
112   $class->_by_phonenum('insert_payment', %opt);
113
114 }
115
116 sub _by_phonenum {
117   my($class, $method, %opt) = @_;
118   my $conf = new FS::Conf;
119   return { 'error' => 'Incorrect shared secret' }
120     unless $opt{secret} eq $conf->config('api_shared_secret');
121
122   my $phonenum = delete $opt{'phonenum'};
123
124   my $svc_phone = qsearchs('svc_phone', { 'phonenum' => $phonenum } )
125     or return { 'error' => 'Unknown phonenum' };
126
127   my $cust_pkg = $svc_phone->cust_svc->cust_pkg
128     or return { 'error' => 'Unlinked phonenum' };
129
130   $opt{'custnum'} = $cust_pkg->custnum;
131
132   $class->$method(%opt);
133
134 }
135
136 =item insert_credit
137
138 Adds a a credit to a customers account. Takes a hash reference as parameter with the following keys
139
140 =over 
141
142 =item secret
143
144 API Secret
145
146 =item custnum
147
148 customer number
149
150 =item amount
151
152 Amount of the credit
153
154 =item _date
155
156 The date the credit will be posted
157
158 Example:
159
160   my $result = FS::API->insert_credit(
161     'secret'  => 'sharingiscaring',
162     'custnum' => 181318,
163     'amount'  => '54.32',
164
165     #optional
166     '_date'   => 1397977200, #UNIX timestamp
167   );
168
169   if ( $result->{'error'} ) {
170     die $result->{'error'};
171   } else {
172     #credit was inserted
173     print "crednum ". $result->{'crednum'};
174   }
175
176 =back
177
178 =cut
179
180 #Enter credit
181 sub insert_credit {
182   my($class, %opt) = @_;
183   my $conf = new FS::Conf;
184   return { 'error' => 'Incorrect shared secret' }
185     unless $opt{secret} eq $conf->config('api_shared_secret');
186
187   $opt{'reasonnum'} ||= $conf->config('api_credit_reason');
188
189   #less "raw" than this?  we are the backoffice API, and aren't worried
190   # about version migration ala cust_main/cust_location here
191   my $cust_credit = new FS::cust_credit { %opt };
192   my $error = $cust_credit->insert;
193   return { 'error'  => $error,
194            'crednum' => $cust_credit->crednum,
195          };
196 }
197
198 # pass the phone number ( from svc_phone ) 
199 sub insert_credit_phonenum {
200   my($class, %opt) = @_;
201   my $conf = new FS::Conf;
202   return { 'error' => 'Incorrect shared secret' }
203     unless $opt{secret} eq $conf->config('api_shared_secret');
204
205   $class->_by_phonenum('insert_credit', %opt);
206
207 }
208
209 =item insert_refund
210
211 Adds a a credit to a customers account. Takes a hash reference as parameter with the following keys: custnum,payby,refund
212
213 Example:
214
215   my $result = FS::API->insert_refund(
216     'secret'  => 'sharingiscaring',
217     'custnum' => 181318,
218     'payby'   => 'CASH',
219     'refund'  => '54.32',
220
221     #optional
222     '_date'   => 1397977200, #UNIX timestamp
223   );
224
225   if ( $result->{'error'} ) {
226     die $result->{'error'};
227   } else {
228     #refund was inserted
229     print "refundnum ". $result->{'crednum'};
230   }
231
232 =cut
233
234 #Enter cash refund.
235 sub insert_refund {
236   my($class, %opt) = @_;
237   my $conf = new FS::Conf;
238   return { 'error' => 'Incorrect shared secret' }
239     unless $opt{secret} eq $conf->config('api_shared_secret');
240
241   # when github pull request #24 is merged,
242   #  will have to change over to default reasonnum like credit
243   # but until then, this will do
244   $opt{'reason'} ||= 'API refund';
245
246   #less "raw" than this?  we are the backoffice API, and aren't worried
247   # about version migration ala cust_main/cust_location here
248   my $cust_refund = new FS::cust_refund { %opt };
249   my $error = $cust_refund->insert;
250   return { 'error'     => $error,
251            'refundnum' => $cust_refund->refundnum,
252          };
253 }
254
255 # pass the phone number ( from svc_phone ) 
256 sub insert_refund_phonenum {
257   my($class, %opt) = @_;
258   my $conf = new FS::Conf;
259   return { 'error' => 'Incorrect shared secret' }
260     unless $opt{secret} eq $conf->config('api_shared_secret');
261
262   $class->_by_phonenum('insert_refund', %opt);
263
264 }
265
266 #---
267
268 # "2 way syncing" ?  start with non-sync pulling info here, then if necessary
269 # figure out how to trigger something when those things change
270
271 # long-term: package changes?
272
273 =item new_customer
274
275 Creates a new customer. Takes a hash reference as parameter with the following keys:
276
277 =over 4
278
279 =item secret
280
281 API Secret
282
283 =item first
284
285 first name (required)
286
287 =item last
288
289 last name (required)
290
291 =item ss
292
293 (not typically collected; mostly used for ACH transactions)
294
295 =item company
296
297 Company name
298
299 =item address1 (required)
300
301 Address line one
302
303 =item city (required)
304
305 City
306
307 =item county
308
309 County
310
311 =item state (required)
312
313 State
314
315 =item zip (required)
316
317 Zip or postal code
318
319 =item country
320
321 2 Digit Country Code
322
323 =item latitude
324
325 latitude
326
327 =item Longitude
328
329 longitude
330
331 =item geocode
332
333 Currently used for third party tax vendor lookups
334
335 =item censustract
336
337 Used for determining FCC 477 reporting
338
339 =item censusyear
340
341 Used for determining FCC 477 reporting
342
343 =item daytime
344
345 Daytime phone number
346
347 =item night
348
349 Evening phone number
350
351 =item fax
352
353 Fax number
354
355 =item mobile
356
357 Mobile number
358
359 =item invoicing_list
360
361 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),
362 postal_invoicing
363 Set to 1 to enable postal invoicing
364
365 =item payby
366
367 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY
368
369 =item payinfo
370
371 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
372
373 =item paycvv
374
375 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
376
377 =item paydate
378
379 Expiration date for CARD/DCRD
380
381 =item payname
382
383 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
384
385 =item referral_custnum
386
387 Referring customer number
388
389 =item salesnum
390
391 Sales person number
392
393 =item agentnum
394
395 Agent number
396
397 =item agent_custid
398
399 Agent specific customer number
400
401 =item referral_custnum
402
403 Referring customer number
404
405
406 =cut
407
408 #certainly false laziness w/ClientAPI::Signup new_customer/new_customer_minimal
409 # but approaching this from a clean start / back-office perspective
410 #  i.e. no package/service, no immediate credit card run, etc.
411
412 sub new_customer {
413   my( $class, %opt ) = @_;
414
415   my $conf = new FS::Conf;
416   return { 'error' => 'Incorrect shared secret' }
417     unless $opt{secret} eq $conf->config('api_shared_secret');
418
419   #default agentnum like signup_server-default_agentnum?
420   #$opt{agentnum} ||= $conf->config('signup_server-default_agentnum');
421  
422   #same for refnum like signup_server-default_refnum
423   $opt{refnum} ||= $conf->config('signup_server-default_refnum');
424
425   $class->API_insert( %opt );
426 }
427
428 =back 
429
430 =item update_customer
431
432 Updates an existing customer. Takes a hash reference as parameter with the foll$
433
434 =over 4
435
436 =item secret
437
438 API Secret
439
440 =item first
441
442 first name (required)
443
444 =item last
445
446 last name (required)
447
448 =item company
449
450 Company name
451
452 =item address1 (required)
453
454 Address line one
455
456 =item city (required)
457
458 City
459
460 =item county
461
462 County
463
464 =item state (required)
465
466 State
467
468 =item zip (required)
469
470 Zip or postal code
471
472 =item country
473
474 2 Digit Country Code
475
476 =item daytime
477
478 Daytime phone number
479
480 =item night
481
482 Evening phone number
483
484 =item fax
485
486 Fax number
487
488 =item mobile
489
490 Mobile number
491
492 =item invoicing_list
493
494 comma-separated list of email addresses for email invoices. The special value '$
495 postal_invoicing
496 Set to 1 to enable postal invoicing
497
498 =item payby
499
500 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY
501
502 =item payinfo
503
504 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pi$
505
506 =item paycvv
507
508 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
509
510 =item paydate
511
512 Expiration date for CARD/DCRD
513
514 =item payname
515
516 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
517
518 =item referral_custnum
519
520 Referring customer number
521
522 =item salesnum
523 Sales person number
524
525 =item agentnum
526
527 Agent number
528
529 =cut
530 sub update_customer {
531   my( $class, %opt ) = @_;
532
533   my $conf = new FS::Conf;
534   return { 'error' => 'Incorrect shared secret' }
535     unless $opt{secret} eq $conf->config('api_shared_secret');
536
537   FS::cust_main->API_update( %opt );
538 }
539
540 =back
541
542
543 =item customer_info
544
545 Returns general customer information. Takes a hash reference as parameter with the following keys: custnum and API secret 
546
547 =cut
548
549 sub customer_info {
550   my( $class, %opt ) = @_;
551   my $conf = new FS::Conf;
552   return { 'error' => 'Incorrect shared secret' }
553     unless $opt{secret} eq $conf->config('api_shared_secret');
554
555   my $cust_main = qsearchs('cust_main', { 'custnum' => $opt{custnum} })
556     or return { 'error' => 'Unknown custnum' };
557
558   $cust_main->API_getinfo;
559 }
560
561 =item location_info
562
563 Returns location specific information for the customer. Takes a hash reference as parameter with the following keys: custnum,secret
564
565 =back
566
567 =cut
568
569 #I also monitor for changes to the additional locations that are applied to
570 # packages, and would like for those to be exportable as well.  basically the
571 # location data passed with the custnum.
572
573 sub location_info {
574   my( $class, %opt ) = @_;
575   my $conf = new FS::Conf;
576   return { 'error' => 'Incorrect shared secret' }
577     unless $opt{secret} eq $conf->config('api_shared_secret');
578
579   my @cust_location = qsearch('cust_location', { 'custnum' => $opt{custnum} });
580
581   my %return = (
582     'error'           => '',
583     'locations'       => [ map $_->hashref, @cust_location ],
584   );
585
586   return \%return;
587 }
588
589 #Advertising sources?
590
591
592 1;