Esempio n. 1
0
def main(argv=sys.argv, processor=None):
    logger = logging.getLogger(__name__)

    if len(argv) != 2:
        usage(argv)
    config_uri = argv[1]
    setup_logging(config_uri)
    settings = get_appsettings(config_uri)
    settings = setup_database({}, **settings)

    session = settings['session']
    subscription_model = SubscriptionModel(session)
    tx_model = TransactionModel(session)

    maximum_retry = int(settings.get(
        'billy.transaction.maximum_retry', 
        TransactionModel.DEFAULT_MAXIMUM_RETRY,
    ))

    resolver = DottedNameResolver()
    if processor is None:
        processor_factory = settings['billy.processor_factory']
        processor_factory = resolver.maybe_resolve(processor_factory)
        processor = processor_factory()

    # yield all transactions and commit before we process them, so that
    # we won't double process them. 
    with db_transaction.manager:
        logger.info('Yielding transaction ...')
        subscription_model.yield_transactions()

    with db_transaction.manager:
        logger.info('Processing transaction ...')
        tx_model.process_transactions(processor, maximum_retry=maximum_retry)
    logger.info('Done')
Esempio n. 2
0
def subscription_list_post(request):
    """Create a new subscription 

    """
    company = auth_api_key(request)
    form = validate_form(SubscriptionCreateForm, request)

    customer_guid = form.data['customer_guid']
    plan_guid = form.data['plan_guid']
    amount = form.data.get('amount')
    payment_uri = form.data.get('payment_uri')
    if not payment_uri:
        payment_uri = None
    started_at = form.data.get('started_at')
    maximum_retry = int(request.registry.settings.get(
        'billy.transaction.maximum_retry', 
        TransactionModel.DEFAULT_MAXIMUM_RETRY,
    ))

    model = SubscriptionModel(request.session)
    plan_model = PlanModel(request.session)
    customer_model = CustomerModel(request.session)
    tx_model = TransactionModel(request.session)

    customer = customer_model.get(customer_guid)
    if customer.company_guid != company.guid:
        return HTTPForbidden('Can only subscribe to your own customer')
    if customer.deleted:
        return HTTPBadRequest('Cannot subscript to a deleted customer')
    plan = plan_model.get(plan_guid)
    if plan.company_guid != company.guid:
        return HTTPForbidden('Can only subscribe to your own plan')
    if plan.deleted:
        return HTTPBadRequest('Cannot subscript to a deleted plan')

    # create subscription and yield transactions
    with db_transaction.manager:
        guid = model.create(
            customer_guid=customer_guid, 
            plan_guid=plan_guid, 
            amount=amount, 
            payment_uri=payment_uri,
            started_at=started_at, 
        )
        tx_guids = model.yield_transactions([guid])
    # this is not a deferred subscription, just process transactions right away
    if started_at is None:
        with db_transaction.manager:
            tx_model.process_transactions(
                processor=request.processor, 
                guids=tx_guids,
                maximum_retry=maximum_retry,
            )

    subscription = model.get(guid)
    return subscription
Esempio n. 3
0
def subscription_cancel(request):
    """Cancel a subscription

    """
    # TODO: it appears a DELETE request with body is not a good idea
    # for HTTP protocol as many server doesn't support this, this is why
    # we use another view with post method, maybe we should use a better 
    # approach later
    company = auth_api_key(request)
    form = validate_form(SubscriptionCancelForm, request)

    guid = request.matchdict['subscription_guid']
    prorated_refund = asbool(form.data.get('prorated_refund', False))
    refund_amount = form.data.get('refund_amount')
    maximum_retry = int(request.registry.settings.get(
        'billy.transaction.maximum_retry', 
        TransactionModel.DEFAULT_MAXIMUM_RETRY,
    ))

    model = SubscriptionModel(request.session)
    tx_model = TransactionModel(request.session)
    get_and_check_subscription(request, company, guid)
    subscription = model.get(guid)

    # TODO: maybe we can find a better way to integrate this with the 
    # form validation?
    if refund_amount is not None:
        if subscription.amount is not None:
            amount = subscription.amount
        else:
            amount = subscription.plan.amount
        if refund_amount > amount:
            return form_errors_to_bad_request(dict(
                refund_amount=['refund_amount cannot be greater than '
                               'subscription amount {}'.format(amount)]
            ))

    if subscription.canceled:
        return HTTPBadRequest('Cannot cancel a canceled subscription')

    with db_transaction.manager:
        tx_guid = model.cancel(
            guid, 
            prorated_refund=prorated_refund,
            refund_amount=refund_amount, 
        )
    if tx_guid is not None:
        with db_transaction.manager:
            tx_model.process_transactions(
                processor=request.processor, 
                guids=[tx_guid],
                maximum_retry=maximum_retry,
            )

    subscription = model.get(guid)
    return subscription 
Esempio n. 4
0
def subscription_cancel(request):
    """Cancel a subscription

    """
    # TODO: it appears a DELETE request with body is not a good idea
    # for HTTP protocol as many server doesn't support this, this is why
    # we use another view with post method, maybe we should use a better 
    # approach later
    company = auth_api_key(request)
    form = validate_form(SubscriptionCancelForm, request)

    guid = request.matchdict['subscription_guid']
    prorated_refund = asbool(form.data.get('prorated_refund', False))
    refund_amount = form.data.get('refund_amount')
    maximum_retry = int(request.registry.settings.get(
        'billy.transaction.maximum_retry', 
        TransactionModel.DEFAULT_MAXIMUM_RETRY,
    ))

    model = SubscriptionModel(request.session)
    tx_model = TransactionModel(request.session)
    get_and_check_subscription(request, company, guid)

    # TODO: maybe we can find a better way to integrate this with the 
    # form validation?
    if refund_amount is not None:
        subscription = model.get(guid)
        if subscription.amount is not None:
            amount = subscription.amount
        else:
            amount = subscription.plan.amount
        if refund_amount > amount:
            return form_errors_to_bad_request(dict(
                refund_amount=['refund_amount cannot be greater than '
                               'subscription amount {}'.format(amount)]
            ))

    # TODO: make sure the subscription is not already canceled

    with db_transaction.manager:
        tx_guid = model.cancel(
            guid, 
            prorated_refund=prorated_refund,
            refund_amount=refund_amount, 
        )
    if tx_guid is not None:
        with db_transaction.manager:
            tx_model.process_transactions(
                processor=request.processor, 
                guids=[tx_guid],
                maximum_retry=maximum_retry,
            )

    subscription = model.get(guid)
    return subscription 
Esempio n. 5
0
def subscription_list_post(request):
    """Create a new subscription 

    """
    company = auth_api_key(request)
    form = validate_form(SubscriptionCreateForm, request)

    customer_guid = form.data['customer_guid']
    plan_guid = form.data['plan_guid']
    amount = form.data.get('amount')
    payment_uri = form.data.get('payment_uri')
    started_at = form.data.get('started_at')
    maximum_retry = int(request.registry.settings.get(
        'billy.transaction.maximum_retry', 
        TransactionModel.DEFAULT_MAXIMUM_RETRY,
    ))

    model = SubscriptionModel(request.session)
    plan_model = PlanModel(request.session)
    customer_model = CustomerModel(request.session)
    tx_model = TransactionModel(request.session)

    customer = customer_model.get(customer_guid)
    if customer.company_guid != company.guid:
        return HTTPForbidden('Can only subscribe to your own customer')
    plan = plan_model.get(plan_guid)
    if plan.company_guid != company.guid:
        return HTTPForbidden('Can only subscribe to your own plan')
    # TODO: make sure user cannot subscribe to a deleted plan or customer

    # create subscription and yield transactions
    with db_transaction.manager:
        guid = model.create(
            customer_guid=customer_guid, 
            plan_guid=plan_guid, 
            amount=amount, 
            payment_uri=payment_uri,
            started_at=started_at, 
        )
        tx_guids = model.yield_transactions([guid])
    # this is not a deferred subscription, just process transactions right away
    if started_at is None:
        with db_transaction.manager:
            tx_model.process_transactions(
                processor=request.processor, 
                guids=tx_guids,
                maximum_retry=maximum_retry,
            )

    subscription = model.get(guid)
    return subscription
Esempio n. 6
0
def main(argv=sys.argv, processor=None):
    logger = logging.getLogger(__name__)

    if len(argv) != 2:
        usage(argv)
    config_uri = argv[1]
    setup_logging(config_uri)
    settings = get_appsettings(config_uri)
    settings = setup_database({}, **settings)

    session = settings['session']
    subscription_model = SubscriptionModel(session)
    tx_model = TransactionModel(session)

    maximum_retry = int(
        settings.get(
            'billy.transaction.maximum_retry',
            TransactionModel.DEFAULT_MAXIMUM_RETRY,
        ))

    resolver = DottedNameResolver()
    if processor is None:
        processor_factory = settings['billy.processor_factory']
        processor_factory = resolver.maybe_resolve(processor_factory)
        processor = processor_factory()

    # yield all transactions and commit before we process them, so that
    # we won't double process them.
    with db_transaction.manager:
        logger.info('Yielding transaction ...')
        subscription_model.yield_transactions()

    with db_transaction.manager:
        logger.info('Processing transaction ...')
        tx_model.process_transactions(processor, maximum_retry=maximum_retry)
    logger.info('Done')