diff --git a/killbill/__init__.py b/killbill/__init__.py index c0804a4..a93370a 100644 --- a/killbill/__init__.py +++ b/killbill/__init__.py @@ -16,6 +16,7 @@ # under the License. # +user = 'KillBill Python Client' base_uri = 'http://localhost:8080' username = 'admin' password = 'password' @@ -39,4 +40,10 @@ from killbill.resource import Resource from killbill.account import Account +from killbill.bundle import Bundle +from killbill.catalog import Catalog +from killbill.custom_field import CustomField +from killbill.invoice import Invoice +from killbill.payment_method import PaymentMethod +from killbill.plan_detail import PlanDetail from killbill.subscription import Subscription diff --git a/killbill/account.py b/killbill/account.py index fdfbe9c..030b7b1 100644 --- a/killbill/account.py +++ b/killbill/account.py @@ -24,7 +24,7 @@ class Account(killbill.Resource): def __init__(self, **d): super(Account, self).__init__(d) - def create(self, user, reason=None, comment=None, **options): + def create(self, user=killbill.user, reason=None, comment=None, **options): created_account = self.post(self.KILLBILL_API_ACCOUNTS_PREFIX, self.to_json(), {}, @@ -36,12 +36,20 @@ def create(self, user, reason=None, comment=None, **options): )) return self.refresh(created_account, **options) + def update(self, **options): + updated_account = self.put("%s/%s" % (self.KILLBILL_API_ACCOUNTS_PREFIX, self.accountId), + self.to_json(), + {}, + self.build_options(**options)) + return self.fromJson(updated_account['body']) + def bundles(self, **options): - return self.get("%s/%s/bundles" % (self.KILLBILL_API_ACCOUNTS_PREFIX, self.accountId), - {}, - self.build_options(**options)) + return killbill.Bundle.get("%s/%s/bundles" % (self.KILLBILL_API_ACCOUNTS_PREFIX, self.accountId), + {}, + killbill.Bundle.build_options(**options)) - def close(self, cancel_all_subscriptions, write_off_unpaid_invoices, item_adjust_unpaid_invoices, user, reason=None, comment=None, **options): + def close(self, cancel_all_subscriptions, write_off_unpaid_invoices, item_adjust_unpaid_invoices, user, reason=None, + comment=None, **options): return self.delete("%s/%s" % (self.KILLBILL_API_ACCOUNTS_PREFIX, self.accountId), "{}", { @@ -56,9 +64,31 @@ def close(self, cancel_all_subscriptions, write_off_unpaid_invoices, item_adjust **options )) + def invoices(self, with_items=False, with_migration_invoices=False, unpaid_invoices_only=False, audit='NONE', + **options): + return killbill.Invoice.get("%s/%s/invoices" % (self.KILLBILL_API_ACCOUNTS_PREFIX, self.accountId), + { + 'withItems': with_items, + 'withMigrationInvoices': with_migration_invoices, + 'unpaidInvoicesOnly': unpaid_invoices_only, + 'audit': audit + }, + killbill.Invoice.build_options(**options)) + @classmethod def find_by_external_key(cls, external_key, **options): query_params = { 'externalKey': external_key } return cls.get(cls.KILLBILL_API_ACCOUNTS_PREFIX, query_params, cls.build_options(**options)) + + @classmethod + def find_by_id(cls, account_id, account_with_balance=False, account_with_balance_and_cba=False, audit='NONE', + **options): + relative_url = "%s/%s" % (cls.KILLBILL_API_ACCOUNTS_PREFIX, account_id) + query_params = { + 'accountWithBalance': account_with_balance, + 'accountWithBalanceAndCBA': account_with_balance_and_cba, + 'audit': audit + } + return cls.get(relative_url, query_params, cls.build_options(**options)) diff --git a/killbill/bundle.py b/killbill/bundle.py new file mode 100644 index 0000000..aa7a770 --- /dev/null +++ b/killbill/bundle.py @@ -0,0 +1,31 @@ +# +# Copyright 2014-2017 The Billing Project, LLC +# +# The Billing Project, LLC licenses this file to you under the Apache License, version 2.0 +# (the "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +import killbill + + +class Bundle(killbill.Resource): + KILLBILL_API_BUNDLE_PREFIX = killbill.Resource.KILLBILL_API_PREFIX + '/bundles' + + def __init__(self, **d): + super(Bundle, self).__init__(d) + + @classmethod + def find_all_by_account_id_and_external_key(cls, account_id, external_key=None, **options): + relative_url = "%s/%s/bundles" % (killbill.Account.KILLBILL_API_ACCOUNTS_PREFIX, account_id) + query_params = { + 'externalKey': external_key + } + return cls.get(relative_url, query_params, cls.build_options(**options)) diff --git a/killbill/catalog.py b/killbill/catalog.py new file mode 100644 index 0000000..fd93a60 --- /dev/null +++ b/killbill/catalog.py @@ -0,0 +1,46 @@ +# +# Copyright 2014-2017 The Billing Project, LLC +# +# The Billing Project, LLC licenses this file to you under the Apache License, version 2.0 +# (the "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +import killbill + + +class Catalog(killbill.Resource): + KILLBILL_API_CATALOG_PREFIX = killbill.Resource.KILLBILL_API_PREFIX + '/catalog' + + def __init__(self, **d): + super(Catalog, self).__init__(d) + + @classmethod + def available_addons(cls, base_product_name, **options): + relative_url = "%s/availableAddons" % cls.KILLBILL_API_CATALOG_PREFIX + query_params = { + 'baseProductName': base_product_name + } + return killbill.PlanDetail.get(relative_url, query_params, killbill.PlanDetail.build_options(**options)) + + @classmethod + def available_base_plans(cls, **options): + relative_url = "%s/availableBasePlans" % cls.KILLBILL_API_CATALOG_PREFIX + query_params = {} + return killbill.PlanDetail.get(relative_url, query_params, killbill.PlanDetail.build_options(**options)) + + @classmethod + def catalog(cls, requested_date=None, **options): + relative_url = cls.KILLBILL_API_CATALOG_PREFIX + options['accept'] = 'application/json' + query_params = { + 'requestedDate': requested_date + } + return cls.get(relative_url, query_params, cls.build_options(**options)) diff --git a/killbill/custom_field.py b/killbill/custom_field.py new file mode 100644 index 0000000..b03aa11 --- /dev/null +++ b/killbill/custom_field.py @@ -0,0 +1,22 @@ +# +# Copyright 2014-2017 The Billing Project, LLC +# +# The Billing Project, LLC licenses this file to you under the Apache License, version 2.0 +# (the "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +import killbill + + +class CustomField(killbill.Resource): + + def __init__(self, **d): + super(CustomField, self).__init__(d) \ No newline at end of file diff --git a/killbill/invoice.py b/killbill/invoice.py new file mode 100644 index 0000000..f6520d5 --- /dev/null +++ b/killbill/invoice.py @@ -0,0 +1,42 @@ +# +# Copyright 2014-2017 The Billing Project, LLC +# +# The Billing Project, LLC licenses this file to you under the Apache License, version 2.0 +# (the "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +import killbill + + +class Invoice(killbill.Resource): + KILLBILL_API_INVOICE_PREFIX = killbill.Resource.KILLBILL_API_PREFIX + '/invoices' + + def __init__(self, **d): + super(Invoice, self).__init__(d) + + @classmethod + def find_by_id_or_number(cls, id_or_number, with_items=True, audit="NONE", **options): + relative_url = "%s/%s" % (cls.KILLBILL_API_INVOICE_PREFIX, id_or_number) + query_params = { + 'withItems': with_items, + 'audit': audit + } + return cls.get(relative_url, query_params, cls.build_options(**options)) + + @classmethod + def as_html(cls, invoice_id, **options): + relative_url = "%s/%s/html" % (cls.KILLBILL_API_INVOICE_PREFIX, invoice_id) + options['accept'] = 'text/html' + options = cls.build_options(**options) + options['method'] = 'GET' + options['queryParams'] = {} + raw_get_response = cls.send_request(relative_url, options) + return raw_get_response['body'] diff --git a/killbill/payment_method.py b/killbill/payment_method.py new file mode 100644 index 0000000..aaf7e2f --- /dev/null +++ b/killbill/payment_method.py @@ -0,0 +1,86 @@ +# +# Copyright 2014-2018 The Billing Project, LLC +# +# The Billing Project, LLC licenses this file to you under the Apache License, version 2.0 +# (the "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +import killbill + + +class PaymentMethod(killbill.Resource): + KILLBILL_API_PAYMENT_METHODS_PREFIX = killbill.Resource.KILLBILL_API_PREFIX + '/paymentMethods' + + def __init__(self, **d): + super(PaymentMethod, self).__init__(d) + + @classmethod + def get_refresh_query(cls): + return {'withPluginInfo': True} + + def create(self, is_default=True, user=killbill.user, reason=None, comment=None, **options): + created_payment_method = self.post("%s/%s/paymentMethods" % + (killbill.Account.KILLBILL_API_ACCOUNTS_PREFIX, self.accountId), + self.to_json(), + {'isDefault': is_default}, + self.build_options( + user=user, + reason=reason, + comment=comment, + **options + )) + return self.refresh(created_payment_method, **options) + + def set_default(self, user=killbill.user, reason=None, comment=None, **options): + self.put("%s/%s/paymentMethods/%s/setDefault" % + (killbill.Account.KILLBILL_API_ACCOUNTS_PREFIX, + self.accountId, self.paymentMethodId), + "{}", + {}, + self.build_options( + user=user, + reason=reason, + comment=comment, + **options + )) + return self.find_by_id(self.paymentMethodId, **options) + + def destroy(self, delete_default_pm_with_auto_pay_off=False, force_default_pm_deletion=False, user=killbill.user, + reason=None, comment=None, **options): + return self.delete("%s/%s" % (self.KILLBILL_API_PAYMENT_METHODS_PREFIX, self.paymentMethodId), + "{}", + {'deleteDefaultPmWithAutoPayOff': delete_default_pm_with_auto_pay_off, + 'forceDefaultPmDeletion': force_default_pm_deletion}, + self.build_options( + user=user, + reason=reason, + comment=comment, + **options + )) + + @classmethod + def find_all_by_account_id(cls, account_id, with_plugin_info=False, **options): + relative_url = "%s/%s/paymentMethods" % (killbill.Account.KILLBILL_API_ACCOUNTS_PREFIX, account_id) + query_params = { + 'withPluginInfo': with_plugin_info + } + return cls.get(relative_url, query_params, cls.build_options(**options)) + + @classmethod + def find_by_id(cls, payment_method_id, plugin_property=None, included_deleted=False, audit='NONE', with_plugin_info=False, **options): + relative_url = "%s/%s" % (cls.KILLBILL_API_PAYMENT_METHODS_PREFIX, payment_method_id) + query_params = { + 'pluginProperty': plugin_property, + 'includedDeleted': included_deleted, + 'audit': audit, + 'withPluginInfo': with_plugin_info + } + return cls.get(relative_url, query_params, cls.build_options(**options)) diff --git a/killbill/plan_detail.py b/killbill/plan_detail.py new file mode 100644 index 0000000..e164fb8 --- /dev/null +++ b/killbill/plan_detail.py @@ -0,0 +1,28 @@ +# +# Copyright 2014-2017 The Billing Project, LLC +# +# The Billing Project, LLC licenses this file to you under the Apache License, version 2.0 +# (the "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +import killbill + + +class PlanDetail(killbill.Resource): + + def __init__(self, **d): + super(PlanDetail, self).__init__(d) + + @classmethod + def dict_to_object(cls, d): + if 'plan' in d: + return cls(**d) + return d \ No newline at end of file diff --git a/killbill/resource.py b/killbill/resource.py index 5146a61..a246c0a 100644 --- a/killbill/resource.py +++ b/killbill/resource.py @@ -45,7 +45,7 @@ def __init__(self, d): setattr(self, key, value) def __repr__(self): - return '%s %s' % (self.__class__, self.to_json) + return '%s %s' % (self.__class__, self.to_json()) @staticmethod def build_options(**options): @@ -63,9 +63,13 @@ def build_options(**options): def to_json(self): return killbill.json.dumps(self, default=lambda o: o.__dict__) + @classmethod + def get_refresh_query(cls): + return {} + def refresh(self, raw_response, **options): url = raw_response['response'].headers['Location'] - return self.get(url, {}, self.build_options(**options)) + return self.get(url, self.get_refresh_query(), self.build_options(**options)) @classmethod def fromJson(cls, jsonString): @@ -96,6 +100,13 @@ def delete(cls, relative_uri, body, query_params, options): options['queryParams'] = query_params return cls.send_request(relative_uri, options) + @classmethod + def put(cls, relative_uri, body, query_params, options): + options['method'] = 'PUT' + options['body'] = body + options['queryParams'] = query_params + return cls.send_request(relative_uri, options) + @classmethod def send_request(cls, relative_uri, options): headers = {} diff --git a/killbill/subscription.py b/killbill/subscription.py index e89e41a..5e36336 100644 --- a/killbill/subscription.py +++ b/killbill/subscription.py @@ -23,7 +23,8 @@ class Subscription(killbill.Resource): def __init__(self, **d): super(Subscription, self).__init__(d) - def create(self, user, reason=None, comment=None, requested_date=None, call_completion=False, **options): + def create(self, user=killbill.user, reason=None, comment=None, requested_date=None, call_completion=False, + **options): query_params = {} if call_completion: @@ -43,3 +44,108 @@ def create(self, user, reason=None, comment=None, requested_date=None, call_comp **options )) return self.refresh(created_subscription, **options) + + def change_plan(self, update, user=killbill.user, reason=None, comment=None, requested_date=None, + billing_policy=None, call_completion=False, **options): + query_params = {} + + if call_completion: + query_params['callCompletion'] = call_completion + + if requested_date: + query_params['entitlementDate'] = requested_date + query_params['billingDate'] = requested_date + + for key, value in update.items(): + setattr(self, key, value) + + updated_subscription = self.put("%s/%s" % (self.KILLBILL_API_ENTITLEMENT_PREFIX, self.subscriptionId), + self.to_json(), + query_params, + self.build_options( + user=user, + reason=reason, + comment=comment, + **options + )) + + return self.fromJson(updated_subscription['body']) + + def cancel(self, requested_date=None, call_completion=False, call_timeout_sec=5, entitlement_policy=None, + billing_policy=None, use_requested_date_for_billing=False, plugin_property=None, + user=killbill.user, reason=None, comment=None, **options): + + query_params = {} + + if requested_date: + query_params['requestedDate'] = requested_date + if call_completion: + query_params['callCompletion'] = call_completion + if call_timeout_sec: + query_params['callTimeoutSec'] = call_timeout_sec + if entitlement_policy: + query_params['entitlementPolicy'] = entitlement_policy + if billing_policy: + query_params['billingPolicy'] = billing_policy + if use_requested_date_for_billing: + query_params['useRequestedDateForBilling'] = use_requested_date_for_billing + if plugin_property: + query_params['pluginProperty'] = plugin_property + + return self.delete("%s/%s" % (self.KILLBILL_API_ENTITLEMENT_PREFIX, self.subscriptionId), + "{}", + query_params, + self.build_options( + user=user, + reason=reason, + comment=comment, + **options + )) + + def add_custom_fields(self, custom_fields, user=killbill.user, reason=None, comment=None, **options): + query_params = {} + custom_fields_response = killbill.CustomField.post( + "%s/%s/customFields" % (self.KILLBILL_API_ENTITLEMENT_PREFIX, self.subscriptionId), + killbill.json.dumps(custom_fields), + query_params, + self.build_options( + user=user, + reason=reason, + comment=comment, + **options + )) + return killbill.CustomField().refresh(custom_fields_response, **options) + + def remove_custom_fields(self, custom_field_list=None, user=killbill.user, reason=None, comment=None, **options): + query_params = {} + if custom_field_list and len(custom_field_list) > 0: + query_params['customFieldList'] = ','.join(custom_field_list) + + killbill.CustomField.delete("%s/%s/customFields" % (self.KILLBILL_API_ENTITLEMENT_PREFIX, self.subscriptionId), + "{}", + query_params, + self.build_options( + user=user, + reason=reason, + comment=comment, + **options + )) + + def custom_fields(self, user=killbill.user, reason=None, comment=None, **options): + query_params = {} + return killbill.CustomField.get("%s/%s/customFields" % (self.KILLBILL_API_ENTITLEMENT_PREFIX, self.subscriptionId), + query_params, + self.build_options( + user=user, + reason=reason, + comment=comment, + **options + )) + + @classmethod + def find_by_id(cls, subscription_id, audit='NONE', **options): + relative_url = "%s/%s" % (cls.KILLBILL_API_ENTITLEMENT_PREFIX, subscription_id) + query_params = { + 'audit': audit + } + return cls.get(relative_url, query_params, cls.build_options(**options))