From 66260a1fb7067122b7ad58e8f88c69408f634ac5 Mon Sep 17 00:00:00 2001 From: Endre Karlson Date: Sun, 31 Mar 2013 20:48:49 +0000 Subject: [PATCH] Payments work --- billingstack/payment_gateway/__init__.py | 12 +++ billingstack/payment_gateway/rpcapi.py | 44 +++++++++++ billingstack/payment_gateway/service.py | 69 +++++++++++------- .../storage/impl_sqlalchemy/models.py | 4 +- ...stack-pg-gateway => billingstack-payments} | 6 +- doc/source/resources/index.rst | 1 + doc/source/resources/payments.rst | 73 +++++++++++++++++++ 7 files changed, 177 insertions(+), 32 deletions(-) create mode 100644 billingstack/payment_gateway/rpcapi.py rename bin/{billingstack-pg-gateway => billingstack-payments} (83%) create mode 100644 doc/source/resources/payments.rst diff --git a/billingstack/payment_gateway/__init__.py b/billingstack/payment_gateway/__init__.py index 509687b..6996143 100644 --- a/billingstack/payment_gateway/__init__.py +++ b/billingstack/payment_gateway/__init__.py @@ -9,6 +9,18 @@ LOG = log.getLogger(__name__) +from oslo.config import cfg + +cfg.CONF.register_group(cfg.OptGroup( + name='service:payments', title="Configuration for Payments Service" +)) + +cfg.CONF.register_opts([ + cfg.IntOpt('workers', default=None, + help='Number of worker processes to spawn') +], group='service:payments') + + def _register(ep, context, conn): provider = ep.plugin diff --git a/billingstack/payment_gateway/rpcapi.py b/billingstack/payment_gateway/rpcapi.py new file mode 100644 index 0000000..3ec4bd2 --- /dev/null +++ b/billingstack/payment_gateway/rpcapi.py @@ -0,0 +1,44 @@ +from oslo.config import cfg + +from billingstack.openstack.common.rpc import proxy + +rpcapi_opts = [ + cfg.StrOpt('payment_topic', default='payment', + help='the topic payments nodes listen on') +] + +cfg.CONF.register_opts(rpcapi_opts) + + +class PaymentAPI(proxy.RpcProxy): + BASE_RPC_VERSION = '1.0' + + def __init__(self): + super(PaymentAPI, self).__init__( + topic=cfg.CONF.payment_topic, + default_version=self.BASE_RPC_VERSION) + + def _do_call(self, ctxt, msg, async=True): + """ + Helper for doing RPC calls. + """ + rpc_func = self.cast if async else self.call + return rpc_func(ctxt, msg) + + def create_account(self, ctxt, pg_config, values, async=False): + msg = self.make_msg('create_account', pg_config=pg_config, + values=values) + return self._do_call(ctxt, msg, async=async) + + def create_payment_method(self, ctxt, pg_config, values, async=True): + msg = self.make_msg('create_payment_method', pg_config=pg_config, + values=values) + return self._do_call(ctxt, msg, async=async) + + def create_tranaction(self, ctxt, pg_config, values, async=True): + msg = self.make_msg('create_tranaction', pg_config=pg_config, + values=values) + return self._do_call(ctxt, msg, async=async) + + +payment_api = PaymentAPI() diff --git a/billingstack/payment_gateway/service.py b/billingstack/payment_gateway/service.py index 9ac3c20..be3aa2e 100644 --- a/billingstack/payment_gateway/service.py +++ b/billingstack/payment_gateway/service.py @@ -7,60 +7,73 @@ from billingstack.openstack.common import log as logging from billingstack.openstack.common.rpc import service as rpc_service from billingstack.central.rpcapi import CentralAPI +from billingstack.payment_gateway import get_provider +from billingstack import exceptions cfg.CONF.import_opt('host', 'billingstack.netconf') -cfg.CONF.import_opt('pg_topic', 'billingstack.payment_gateway.rpcapi') +cfg.CONF.import_opt('payment_topic', 'billingstack.payment_gateway.rpcapi') cfg.CONF.import_opt('state_path', 'billingstack.paths') LOG = logging.getLogger(__name__) +central_api = CentralAPI() + + class Service(rpc_service.Service): def __init__(self, *args, **kwargs): kwargs.update( host=cfg.CONF.host, - topic=cfg.CONF.central_topic, + topic=cfg.CONF.payment_topic ) super(Service, self).__init__(*args, **kwargs) - # Get a storage connection - self.central_api = CentralAPI() - - def get_pg_provider(self, ctxt, pg_info): + def get_pg_provider(self, ctxt, pg_config): """ - Work out a PGC config either from pg_info or via ctxt fetching it - from central. - Return the appropriate PGP for this info. + Work out a PGP from PGC. - :param ctxt: Request context + :param ctxt: Request context. :param pg_info: Payment Gateway Config... """ + try: + name = pg_config['name'] + except KeyError: + msg = 'Missing name in config' + LOG.error(msg) + raise exceptions.ConfigurationError(msg) + + provider = get_provider(name) + return provider(pg_config) + + def create_account(self, ctxt, pg_config, values): + """ + Create an Account on the underlying Provider. (Typically a Customer) + + :param ctxt: Request context. + :param values: The account values. + """ + provider = self.get_pg_provider(pg_config) + account = provider.create_account(values) - def create_account(self, ctxt, values, pg_config=None): + def create_payment_method(self, ctxt, pg_config, values): """ - Create an Account on the underlying provider + Create a PaymentMethod. - :param values: The account values + :param ctxt: Request context. + :param values: The account values. """ + provider = self.get_pg_provider(pg_config) + method = provider.create_payment_method(values) - def __getattr__(self, name): + def create_transaction(self, ctxt, pg_config, values): """ - Proxy onto the storage api if there is no local method present.. + Create a Transaction. - For now to avoid to have to write up every method once more here... + :param ctxt: Request context. + :param values: The Transaction values. """ - if hasattr(self, name): - return getattr(self, name) - - f = getattr(self.provider, name) - if not f: - raise AttributeError - - @functools.wraps(f) - def _wrapper(*args, **kw): - return f(*args, **kw) - setattr(self, name, _wrapper) - return _wrapper + provider = self.get_pg_provider(pg_config) + transaction = provider.create_transaction(values) \ No newline at end of file diff --git a/billingstack/storage/impl_sqlalchemy/models.py b/billingstack/storage/impl_sqlalchemy/models.py index 246fb36..9e9f8c6 100644 --- a/billingstack/storage/impl_sqlalchemy/models.py +++ b/billingstack/storage/impl_sqlalchemy/models.py @@ -12,7 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. from sqlalchemy import Column, ForeignKey, UniqueConstraint -from sqlalchemy import Integer, Float +from sqlalchemy import Integer, Float, Boolean from sqlalchemy import DateTime, Unicode from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declarative_base, declared_attr @@ -169,6 +169,7 @@ class PGConfig(BASE, BaseMixin): title = Column(Unicode(100)) properties = Column(JSON) + status = Column(Boolean, default=False) # Link to the Merchant merchant_id = Column(UUID, ForeignKey('merchant.id'), nullable=False) @@ -223,6 +224,7 @@ class PaymentMethod(BASE, BaseMixin): expires = Column(Unicode(255)) properties = Column(JSON) + status = Column(Boolean, default=False) customer_id = Column(UUID, ForeignKey('customer.id', onupdate='CASCADE'), nullable=False) diff --git a/bin/billingstack-pg-gateway b/bin/billingstack-payments similarity index 83% rename from bin/billingstack-pg-gateway rename to bin/billingstack-payments index 6ccac43..344a258 100644 --- a/bin/billingstack-pg-gateway +++ b/bin/billingstack-payments @@ -20,7 +20,7 @@ from oslo.config import cfg from billingstack.openstack.common import log as logging from billingstack.openstack.common import service from billingstack import utils -from billingstack.central import service as central_service +from billingstack.payment_gateway import service as pg_service eventlet.monkey_patch() @@ -28,6 +28,6 @@ utils.read_config('billingstack', sys.argv) logging.setup('billingstack') -launcher = service.launch(central_service.Service(), - cfg.CONF['service:payment_gateway'].workers) +launcher = service.launch(pg_service.Service(), + cfg.CONF['service:payments'].workers) launcher.wait() diff --git a/doc/source/resources/index.rst b/doc/source/resources/index.rst index e9bcc0a..b25cf23 100644 --- a/doc/source/resources/index.rst +++ b/doc/source/resources/index.rst @@ -23,4 +23,5 @@ Resources in Billingstack :maxdepth: 2 api_filtering + payments subscriptions \ No newline at end of file diff --git a/doc/source/resources/payments.rst b/doc/source/resources/payments.rst new file mode 100644 index 0000000..ebe5df7 --- /dev/null +++ b/doc/source/resources/payments.rst @@ -0,0 +1,73 @@ +.. + Copyright 2013 Endre Karlson + + Licensed 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. + +.. _payments: + + +===================== +Payments Architecture +===================== + +.. index:: + double: payments; brief + + +Payment Gateway ++++++++++++++++ + +A PaymentGateway is a service running on the outside of BS. It's typically +something like Braintree, PayPal, eNets, DIBS, Authorize or any other service +that you want to use. + +Inside of BillingStack: +BillingStack refers to a implementation of a PaymentGateway as a PGP which is a +plugin that provides a set of methods that BillingStack needs in order to do +it's operations towards the PaymentGateway service. + + +Payment Gateway Provider +++++++++++++++++++++++++ + +The Payment Gateway Provider (PGP) module is installed standalone of the +core service. This is done to not having to install whatever dependencies +the underlying modules the libraries it builds on needs if you only need a +specific provider. + +A PGP is "registered" into the database after installing it. +See :doc:`pgp` for information on this. + +Reasoning behind PGP architecture: + +* Systems that doesn't have the PGP modules installed won't know about PGP + information without this. + +* Implementation of a in-house system becomes very easy, write a new PGP that + does what you need, install, register and you are good to go. BS doesn't care + on what technology you use. + + +Payment Gateway Method +++++++++++++++++++++++ + +The PGM is just a string or something to tell what methods are supported by +the PGP. Like 'visa' or any other string, piece of data or similar. + + +Payment Gateway Configuration ++++++++++++++++++++++++++++++ + +A Payment Gateway Configuration refers to either a piece of configuration stored +in the database or a configuration section in a config file (Useful for things +that shouldn't be stored in the DB because of law or similar).