1: <?php
2: declare(strict_types=1);
3: /**
4: * +------------------------------------------------------------+
5: * | apnscp |
6: * +------------------------------------------------------------+
7: * | Copyright (c) Apis Networks |
8: * +------------------------------------------------------------+
9: * | Licensed under Artistic License 2.0 |
10: * +------------------------------------------------------------+
11: * | Author: Matt Saladna (msaladna@apisnetworks.com) |
12: * +------------------------------------------------------------+
13: */
14:
15: /**
16: * Billing and referral module
17: *
18: * @package core
19: */
20: class Billing_Module extends Module_Skeleton implements \Module\Skeleton\Contracts\Hookable
21: {
22: // @ignore
23: private static $BILLING_SERVER_HOST = BILLING_HOST;
24: private static $BILLING_SERVER_USER = BILLING_USER;
25: private static $BILLING_SERVER_PASSWORD = BILLING_PASSWORD;
26: private static $BILLING_SERVER_DATABASE = BILLING_DB;
27:
28: /**
29: * Recognized preferences in [change,get]_payout_preferences
30: * tender: tender method
31: * freeze: account frozen
32: * balance: credit hosting balance
33: *
34: * @var array
35: */
36: private static $_PAYOUT_OPTIONS = array('tender', 'freeze', 'balance');
37:
38: /**
39: * void __construct(void)
40: *
41: * @ignore
42: */
43: public function __construct()
44: {
45: parent::__construct();
46:
47: $this->exportedFunctions = array(
48: '*' => PRIVILEGE_SITE,
49: 'add_referral' => PRIVILEGE_ADMIN,
50: 'get_invoice_from_domain' => PRIVILEGE_ADMIN,
51: 'get_package_type' => PRIVILEGE_SITE|PRIVILEGE_USER,
52: 'get_package_by_invoice' => PRIVILEGE_ADMIN,
53:
54: /** necessary for sanity checks */
55: 'get_invoice' => PRIVILEGE_ALL
56: );
57: }
58:
59: /**
60: * Get current payment method
61: *
62: * @return null|string current payment method, enum: [credit, check, paypal, cash, other]
63: */
64: public function get_payment_method(): ?string
65: {
66: return 'other';
67:
68: }
69:
70: /**
71: * Next payment date and amount for the account
72: *
73: * Array fields-
74: * date: date of next payment for account. In the event the
75: * information cannot be found (reseller account for example),
76: * -1 is returned.
77: * amount: amount for next payment
78: *
79: * @return array
80: */
81: public function get_next_payment(): array
82: {
83: return [
84: 'date' => -1,
85: 'amount' => 0
86: ];
87: }
88:
89: /**
90: * array list_payments
91: *
92: * Sample response:
93: * Array
94: * (
95: * [0] => Array
96: * (
97: * [date] => 1469937612
98: * [amount] => 65.00
99: * [service] => Webhosting->Developer
100: * [number] => C-2ALQUJ67SSQXIDJEESZFRMZ
101: * [note] =>
102: * [type] => credit
103: * )
104: * )
105: *
106: * @return array
107: */
108: public function list_payments(): array
109: {
110: $recs = [];
111:
112: return $recs;
113:
114: }
115:
116: /**
117: * Fetch billing status from backend billing server
118: *
119: * @param string $invoice
120: * @return int
121: */
122: public function get_billing_status(string $invoice = ''): int
123: {
124: return $this->get_status($invoice);
125: }
126:
127: /**
128: * int get_standing_status()
129: *
130: * Fetch recurring subscription status from billing server
131: *
132: * 1 - subscription in good standing
133: * -1 - subscription cancelled, account within 90 days of expiring
134: * 0 - subscription cancelled, account outside 90 days of expiring
135: * null - cannot find subscription
136: *
137: * @param string $invoice
138: * @return int
139: */
140: public function get_status(string $invoice = ''): ?int
141: {
142: return 1;
143: }
144:
145: /**
146: * bool is_billed()
147: *
148: * @return bool billing record exists
149: */
150: public function is_billed(): bool
151: {
152: return (bool)$this->get_hosting_subscription();
153: }
154:
155: /**
156: * Currently active subscription number attached to invoice
157: *
158: * @return mixed|NULL
159: */
160: public function get_hosting_subscription(): ?string
161: {
162: return $this->getConfig('billing', 'invoice');
163: }
164:
165: /**
166: * Account was referred by another
167: *
168: * @param $invoice
169: * @return bool
170: */
171: public function is_referred(string $invoice): bool
172: {
173: return false;
174: }
175:
176: /**
177: * array get_payment_information(string)
178: *
179: * Sample response:
180: * Array
181: * (
182: * [date] => 1469937612
183: * [amount] => 65
184: * [domain] => apnscp.com
185: * [service_level] => Developer
186: * [name] => Tark Sammons
187: * [email] => tark.sammons@apnscp.com
188: * [note] =>
189: * [service_type] => Webhosting
190: * [method] => recurring
191: * [payment_method] => credit
192: * [reference] => C-2ALQUJ67SSQXIDJEESZFRMZ
193: * [txn_id] => 11111
194: * [invoice] => APNSCP-CJKFHECZO35
195: * [cc_type] => visa
196: * [cc_number] => 1111
197: * [cc_exp] => 03/20
198: * )
199: *
200: * @param string $transnum transaction number
201: * @param int $date unix timestamp of date of transaction
202: * @param string $type payment method type
203: * @return array
204: */
205: public function get_payment_information(string $transnum, int $date, string $type = 'paypal'): array
206: {
207: return [];
208: }
209:
210: /**
211: * Update profile information
212: *
213: * @param array $credentials
214: * @return bool
215: */
216: public function edit_referral_profile(array $credentials): bool {
217: return error('not implemented');
218: }
219:
220: /**
221: * Check if referral profile exists
222: *
223: * @return bool
224: */
225: public function referral_profile_exists(): bool
226: {
227: return false;
228: }
229:
230: /**
231: * Get referral username
232: *
233: * @return string|null
234: */
235: public function get_referral_username(): ?string
236: {
237: return null;
238: }
239:
240: /**
241: * Set minimum balance for a payout
242: *
243: * @param float $amount
244: * @return bool
245: */
246: public function edit_payout_amount(float $amount): bool
247: {
248: return error('not implemented');
249: }
250:
251: /**
252: * float get_payout_amount()
253: *
254: * @return float|null
255: */
256: public function get_payout_amount(): ?float
257: {
258: return null;
259: }
260:
261: /**
262: * float get_minimum_payout_amount
263: *
264: * @return float
265: */
266: public function get_minimum_payout_amount(): float
267: {
268: return (float)self::PAYOUT_MINBAL;
269: }
270:
271: /**
272: * Get current referral balance
273: *
274: * @return float
275: */
276: public function referral_balance(): float
277: {
278: return 0;
279: }
280:
281: /**
282: * Get multiplier used in commission credit
283: *
284: * @return float
285: */
286: public function referral_multiplier(): float
287: {
288: return 1;
289: }
290:
291: /**
292: * Minimum requirement for next referral level
293: * array(2) {
294: * ["method"] => 254
295: * ["client"] => 2
296: * }
297: *
298: * @return array
299: */
300: public function referral_upgrade_needed(): array
301: {
302: $next = array('method' => 'client', 'next' => 2);
303:
304: return $next;
305: }
306:
307: /**
308: * Get earned commissions
309: *
310: * Sample response
311: * array(1) {
312: * [0]=>
313: * array(5) {
314: * ["domain"]=>
315: * string(8) "apnscp.com"
316: * ["accrued"]=>
317: * string(5) "30.00"
318: * ["level"]=>
319: * string(5) "Basic"
320: * ["maturity"]=>
321: * int(1172293200)
322: * ["status"]=>
323: * NULL
324: * }
325: *
326: * domain (string):
327: * accrued (float)
328: *
329: * @return array
330: *
331: */
332:
333: public function get_referrals(): array
334: {
335: return [];
336: }
337:
338: /**
339: * array get_customer_referral_information()
340: *
341: * @return null|array
342: */
343: public function get_customer_referral_information(): ?array
344: {
345: return null;
346: }
347:
348: /**
349: * Create initial referral profile
350: *
351: * @param string $username
352: * @param array $personal credentials
353: *
354: * @return bool
355: */
356: public function create_referral_profile(string $username, array $personal = []): bool {
357: return error('not implemented');
358: }
359:
360: /**
361: * Change referral payout options
362: *
363: * @param string $pref preference ['frozen', 'tender']
364: * @param mixed $val payment preference ['paypal', 'check']
365: * @return bool
366: */
367: public function change_payout_preference(string $pref, string $val): bool
368: {
369: return error('not implemented');
370: }
371:
372: /**
373: * Fetch payout option
374: *
375: * @param string $pref preference
376: * @return string|null
377: */
378: public function get_payout_preference(string $pref): ?string
379: {
380: return null;
381: }
382:
383: /**
384: * Record a new referral
385: *
386: * @param string $invoice new account invoice
387: * @param string $pinvoice parent invoice (referral profile)
388: * @return bool
389: */
390: public function add_referral(string $invoice, string $pinvoice): bool
391: {
392: return error('not implemented');
393: }
394:
395: /**
396: * Get referral credit by invoice
397: *
398: * @param string $invoice
399: * @return float|null
400: */
401: public function get_credit_from_invoice(string $invoice): ?float
402: {
403: return 0;
404: }
405:
406: /**
407: * Get referral credit by package type
408: *
409: * @param string $package package type
410: * @return float
411: */
412: public function get_credit_from_package_type(string $package): float
413: {
414: return 1;
415: }
416:
417: /**
418: * Get hosting invoice by domain
419: *
420: * @param string $domain domain name
421: * @return string|null
422: */
423: public function get_invoice_from_domain(string $domain): ?string
424: {
425: return null;
426: }
427:
428: /**
429: * Retrieve latest billing renewal hash for service
430: *
431: * @return string|null
432: */
433: public function get_renewal_hash(string $invoice = null): ?string
434: {
435: return null;
436: }
437:
438: /**
439: * Get billing renewal link
440: *
441: * Link must provide direct access to billing portal
442: *
443: * @param string|null $invoice leave blank for current outstanding invoice
444: * @return string
445: */
446: public function get_renewal_link(string $invoice = null): ?string
447: {
448: return null;
449: }
450:
451: /**
452: * Get customer since
453: *
454: * @return int unix timestamp or -1 for connectivity issues
455: */
456: public function get_customer_since(): int
457: {
458: return (int)($this->getConfig('billing', 'ctime') ??
459: filectime($this->domain_fs_path()));
460: }
461:
462: /**
463: * Get billing information attached to account
464: *
465: * Array
466: * (
467: * [first_name] => Tark
468: * [last_name] => Sammons
469: * [city] => Atlanta
470: * [state] => Georgia
471: * [zip_code] => 30308
472: * [country] => US
473: * [company] =>
474: * [address] => 123 Anywhere St
475: * [phone] => 867-5309
476: * [email] => tark.sammons@apnscp.com
477: * )
478: *
479: * @return array
480: */
481: public function get_billing_information(): array
482: {
483: return array();
484: }
485:
486: /**
487: * Get billing details
488: *
489: * @return array|false
490: */
491: public function get_credit_card_information()
492: {
493: return [];
494: }
495:
496: /**
497: * Update billing details
498: *
499: * @param int $expyear
500: * @param int $expmonth
501: * @param string|null $cvm cvm number, with or without leading zeroes
502: * @param int|null $number
503: * @param string|null $type
504: * @return bool
505: */
506: public function change_credit_card_information(
507: int $expyear,
508: int $expmonth,
509: ?string $cvm = null,
510: ?string $number = null,
511: string $type = null
512: ): bool {
513: return error('not implemented');
514: }
515:
516: /**
517: * Update billing information on account
518: *
519: * @param string $firstname
520: * @param string $lastname
521: * @param string|null $company
522: * @param string $address
523: * @param string $city
524: * @param string $state
525: * @param string $zip
526: * @param string $country
527: * @param string|null $phone
528: * @param string|null $email
529: * @return bool
530: */
531: public function change_billing_information(array $details): bool {
532: return error('not implemented');
533: }
534:
535: /**
536: * Get current package
537: *
538: * @return string|null
539: */
540: public function get_package_type(): ?string
541: {
542: $invoice = $this->get_invoice();
543:
544: return $this->get_package_by_invoice($invoice);
545: }
546:
547: /**
548: * Get package name from invoice
549: *
550: * @param $invoice
551: * @return mixed|null
552: */
553: public function get_package_by_invoice(string $invoice): ?string
554: {
555: if ($invoice === $this->get_invoice()) {
556: return $this->getServiceValue('siteinfo', 'plan');
557: }
558:
559: $ids = \Auth::get_site_id_from_invoice($invoice);
560: if (null === $ids) {
561: return null;
562: }
563:
564: foreach ($ids as $siteid) {
565: $ctx = \Auth::context(null, "site{$siteid}");
566: if ($ctx->getAccount()->cur['billing']['invoice'] === $invoice) {
567: return $ctx->getAccount()->cur['siteinfo']['plan'] ?? null;
568: }
569: }
570:
571: return null;
572: }
573:
574: /**
575: * Claim referral from token
576: *
577: * @param $token string 40 character hash
578: * @return bool
579: */
580: public function claim_referral(string $token): bool
581: {
582: return error('not implemented');
583: }
584:
585: /**
586: * Get data about a referral
587: * Sample return:
588: * Array(
589: * [domain] => apnscp.com
590: * [revenue] => 20.00
591: * [maturity] => 1469937612
592: * )
593: *
594: * @param string $token
595: * @return array|bool
596: */
597: public function claim_metadata(string $token)
598: {
599: return error('not implemented');
600: }
601:
602: /**
603: * Used by TemplateEngine to confirm module is setup
604: *
605: * @return bool
606: */
607: public function configured(): bool
608: {
609: return static::class !== self::class;
610: }
611:
612: public function _verify_conf(\Opcenter\Service\ConfigurationContext $ctx): bool
613: {
614: return true;
615: }
616:
617: public function _create()
618: {
619: // TODO: Implement _create() method.
620: }
621:
622: public function _delete()
623: {
624: // TODO: Implement _delete() method.
625: }
626:
627: public function _edit()
628: {
629: // TODO: Implement _edit() method.
630: }
631:
632: public function _create_user(string $user)
633: {
634: // TODO: Implement _create_user() method.
635: }
636:
637: public function _delete_user(string $user)
638: {
639: // TODO: Implement _delete_user() method.
640: }
641:
642: public function _edit_user(string $userold, string $usernew, array $oldpwd)
643: {
644: // TODO: Implement _edit_user() method.
645: }
646:
647: /**
648: * Get all invoices
649: *
650: * @return array
651: */
652: private function get_all_invoices(): array
653: {
654: $invoice = (array)$this->get_invoice();
655: if (!$invoice) {
656: return [];
657: }
658: $addons = (array)$this->getConfig('billing', 'addons');
659: if (!$addons) {
660: return $invoice;
661: }
662:
663: return array_merge($invoice, $addons);
664: }
665:
666: /**
667: * Invariant invoice tied to an account
668: *
669: * @return null|string
670: */
671: public function get_invoice(): ?string
672: {
673: if ($this->permission_level & (PRIVILEGE_ADMIN | PRIVILEGE_RESELLER)) {
674: return null;
675: }
676: $invoice = (string)$this->getConfig('billing', 'invoice');
677: if ($invoice) {
678: return $invoice;
679: }
680: if ($this->getConfig('billing', 'parent_invoice')) {
681: return (string)$this->getConfig('billing', 'parent_invoice');
682: }
683:
684: return null;
685: }
686:
687:
688: }