balanced / billy

Billy - The open source recurring billing system, powered by Balanced.
Other
172 stars 45 forks source link

Define invoicing usage scenario #62

Open fangpenlin opened 11 years ago

fangpenlin commented 11 years ago

With some inputs from a customer and the scenario of how invoice is processed in balanced, I have a better understanding of what invoicing should be. For now, I am staring to describe usage scenarios here, improve the design gradually, and implement it when it looks good.

Some wanted features here:

Unlike what the most obvious usage scenario we mentioned in the previous development cycle, common hosting service providers can use billy to charge or payout a fixed amount to specific customers. However, not all of hosting service providers, or cloud service providers are charging customers in a fixed rate. Such as Amazon Web Service, the price is basically determined by the usage. In this case, we want our customer to be able to charge customer based on metered usage. Another good example is Balanced itself, customer are charged based on transaction count. This is great, we can eat our own dog food at very first.

As we need to grab usage data from the user side, I think there are two obvious ways to do

Still thinking pros and cons of these two approaches, however, maybe we can implement them all. But anyway, the data should contain

I envision the data format of a invoice may look like this

amount: 100
customer: customer_guid
title: Foobar Super Cloud Service October Invoice
items:
    - name: Bandwidth usage
      count: 10
      unit: TB 
      amount: 20
    - name: Instance usage
      count: 200
      unit: hours
      amount: 80
notify_email:
    - foo@example.com
    - bar@example.com

On receiving these data from end of user, billy then generate corresponding invoice, send email to receivers and charge customers if it is necessary.

TODO:

mjallday commented 11 years ago

From a product development standpoint I think the email functionality should come after the basic Billy system is running. From a Balanced developer who wants to replace Balanced's invoicing billing with Billy's billing system and to speed up development of the system I would suggest that Billy generate events via webhooks rather than emails initially so that Balanced can listen for those events and take appropriate action (e.g. send an email).

The advantage to doing this is that we can leverage Balanced's existing emails (speeding up development time) and we can also hook into those events to generate Balanced internal events e.g. review customers who have invoices that cannot be charged.

Regarding if Billy should push or pull data, I think it's much simpler for people to integrate if they push data to Billy rather than setting up a webserver for Billy to pull data from.

I think the data format looks OK.

Does Billy take care of retrying failed charges? Balanced currently does not automatically retry billing failed invoices. Instead Balanced fires an email to the customer asking them to update their bank account details, once this is done then Balanced will retry debiting an invoice.

fangpenlin commented 11 years ago

@mjallday I agree that we can reuse existing email system. However, some customers want this feature, is it flexible enough to send invoice emails for our clients?

Does Billy take care of retrying failed charges? Balanced currently does not automatically retry billing failed invoices. Instead Balanced fires an email to the customer asking them to update their bank account details, once this is done then Balanced will retry debiting an invoice.

Retrying mechanism of invoicing is explained at #63

fangpenlin commented 11 years ago

billy_erd_rev2

The new ERD diagram, an Invoice table is added. And I divide original Transaction into SubscriptionTransaction and InvoiceTransaction, both of them share the same model and inherits from Transaction. By doing that, we can leverage retrying mechanism introduced before.

mahmoudimus commented 11 years ago

When I push data to Billy, I would like to be able to do roll-ups on events after a certain time interval and have my own custom logic in my app to control what Billy charges and what it doesn't.

It makes sense because I will be managing hundreds of customers, some of which I have specific pricing tiers for. That logic shouldn't live in Billy, but in my app.

I will also add my own couponing logic etc.

fangpenlin commented 11 years ago

Create an invoice, update payment method later

Thinking about invoicing API, let say, Steve is running a cloud service, he has a customer Tim, at the end of a month, Steve's routine script creates an invoice and send it to Tim.

guid = api.create_invoice(
    customer_guid='CUSTOMER_GUID',
    amount=15000,
)

By the time invoice is generated, payment method is still unknown and not set by Tim, so we don't have to set payment_uri at this moment. Upon receiving the bill notification from Steve, Tim decides to settle the invoice by his credit card. He then enters his credit card number on Steve's service backend. Steve's service web application then create the credit card token and update the payment_uri of the invoice like this

invoice = api.get_invoice(guid)
invoice.update(payment_uri='/v1/cards/xxx')

Billy will then start to charge against the credit card.

Create an invoice with payment method

Another case would be the payment_uri is already created (may be Steve's service requires customer to provide a valid payment method before they can use it), so the routine script can create an invoice with payment method right away

guid = api.create_invoice(
    customer_guid='CUSTOMER_GUID',
    amount=15000,
    payment_uri='/v1/cards/xxxx',
)

Update the payment method for invoice after some failures

Say, Tim made a typo when he is entering credit card number, and of course, Billy failed to process it then. Tim then correct the payment method like this

invoice = api.get_invoice(guid)
invoice.update(payment_uri='/v1/cards/xxx')

Pretty much just to update the payment_uri, easy.

Cancel an invoice

If somehow, during Billy is processing the invoice (failed few times), Steve want to cancel this invoice for some reason, he just call

invoice = api.get_invoice(guid)
invoice.cancel()

Refund an invoice

Steve's service had a big down time this month, so he wants to refund those settled invoices of this month. Here he calls

invoice = api.get_invoice(guid)
invoice.refund(amount=15000)

TODO: what about the refunding failed? e.g. a temporary connectivity issue between Billy and Balanced?

Create an invoice with more details

As Steve may want to provide an invoice page either in email page or web page to Tim, he may want to store some information for human, like the title and item details. So here he may call

guid = api.create_invoice(
    customer_guid='CUSTOMER_GUID',
    amount=15000,
    title='Awesome Steve Cloud Service Invoice for October, 2013',
    items=[
        'Bandwidth usage 100G $25 USD',
        'Instance usage 300 hours $75 USD',
        'Setup fee $50 USD',
    ],
)

hum.... are title and items good enough? or should we add more common fields of an invoice? what they would be?

And I am thinking should we provide more detail fields in an item rather than a simple string? May be we can provide description and price for each item... but what for? for rendering email and web page more clearly? what about unit field? hum... think this through later...

TODO: what about invoice notification when status changed (email or by other means)? web hook? a whole Company setup or a single Invoice setup?

mjallday commented 11 years ago

payment_uri doesn't quite seem to be the correct terminology here. in this case it's a source of money, in Balanced we'd call this a source_uri or a funding instrument specific term like card_uri.

Also, what does the payment_uri point to? If it's a funding instrument in Balanced then it should be an absolute URI as opposed to a relative one since it's not internal to Billy right?

fangpenlin commented 11 years ago

@mjallday

Yeah, you're right. As recurring transaction could be either charing or paying out, it could be source or destination, so I use payment_uri. But for invoicing, there should only be charging. It should be source_uri then.

So, you mean we should store the source_uri in Billy as a absolute URI like....

http://api.balancedpayments.com/v1/cards/xxxxx

is my understanding right?

mjallday commented 11 years ago

yeah, that way i think it's much more obvious that the uri refers to something outside of Billy.

fangpenlin commented 11 years ago

Yeah, it's correct and obvious to use a complete URL to a resource in Billy. But by using it, it makes the usage a little bit more verbose, as in the current context, there is only one possible source of external resource which is Balanced. :S

For example, if I want to copy the resource URI from browser URL field of dashboard, I will only get

/v1/cards/xxx
/v1/customers/xxx
...

and if it should be a complete URL, then I have to prepend a complete endpoint to these URI.

mahmoudimus commented 11 years ago

@victorlin can you make it part of the company configuration? This seems like a company configuration to me.

If you register a company, I would want to tell it where my api is, right?

mahmoudimus commented 11 years ago

@mjallday @victorlin actually, as long as the client library is being used, it will know what a source_uri is, wouldn't it? So why is this relevant?