SubscriptionService Specification

From MacroDeckDevWiki

Jump to: navigation, search

This is a draft document. It may change at any time.

SubscriptionService will provide subscription features to controllers and possibly also widgets on MacroDeck. Subscriptions are things that actually cost money. A user could subscribe to a widget and then be able to get updates. Or, they could subscribe to a service (in our case Posima) and get varying levels of features. SubscriptionService will handle all of that.

Contents

Features Exposed To Ruby

  • Create/edit/delete subscriptions
  • Create/edit/delete subscription services (i.e. things that users can subscribe to)
  • Process billing (weekly, monthly, and yearly are the possible billing options)
  • Return enough data for an invoice (which would be handled by a remote application or a local controller)
  • Permissions on subscription services, so a subscription creator can limit people subscribing. For example, maybe only allow people who are part of a certain group to subscribe to something.
  • Subscriptions don't necessarily have to do stuff online. A person might have a subscription to a newspaper or something.
  • Include a method for seeing if a user is subscribed to a particular subscription service
  • Include check to see if a person has paid for a particular subscription service (i.e. they can be subscribed but they may be past due)
  • Integrate with Authorize.net and the payment rubygem
  • Allow for manual payment methods like cash, check, or money order
  • Pick which day of the month to bill on
  • Pick how many days before payment due date to e-mail out notices that money is due
  • Store credit card data encrypted in the database. See below for details.
  • Validate credit card numbers using check digits
  • There will be no automatic billing for security purposes -- each user will have to login when they want to pay
  • Support for one-time payments

Features Exposed Via XML-RPC/SOAP

  • Create/edit/delete subscriptions
  • Create/edit/delete subscription services
  • Return enough data for an invoice
  • Integrate with authorize.net/payment rubygem
  • All other processing done locally
  • Check to see if a person is subscribed to a particular subscription service
  • Check to see if a person has paid for a particular subscription service (i.e. they can be subscribed but they may be past due)

Widget-Specific Features

  • Check to see if a person is subscribed to a particular subscription service
  • Check to see if a person has paid for a particular subscription service

Application-Specific Features

  • View/print invoice
  • Check to see if a person is subscribed to a particular subscription service
  • Check to see if a person has paid for a particular subscription service
  • Add/delete/edit subscriptions

Synchronizable Data

  • Subscriptions
  • Subscription Services

Interaction With Other Services

  • UserService is used since users and groups are tracked here

Technical Data

UUIDs

Subscription Types

  • 8c730f9c-a95a-41e7-bd38-ada9277dc6ab - Online subscription (for something online)
  • 21a2ed06-f315-4941-9af8-a678dbed8858 - Offline subscription (for something offline)
  • 666ce30b-2a44-469b-ab29-42a65bfcc337 - Widget subscription
  • 6ea5818d-dbdf-4d96-95f7-f52a36234ecc - One-time payment

APIs

No APIs are currently defined.


Schema

This, like StorageService should be implemented without using DataService. There are several reasons for this decision, so here is a small list:

  • Subscriptions don't represent any data that is useful to the user. A widget or app developer generally only needs to know if the user is paid up or if they're subscribed.
  • Security. None of this data is kept in the common pool of data. Assume that someone finds a bug in MySQL and can get data from the next row and this row includes confidential data.
  • Performance increase

That said, there should be three tables. One table should be subscriptions, which contains a mapping of subscription services to users and all of their billing data. The second one should be subscription_payments, which contains a list of subscriptions (i.e. NOT subscription services) and when they were paid. The other should be subscription_services, which contains detailed information about each possible subscription service. They should look something like this (please do not limit yourself to these fields -- if you can think of any more to add, feel free to add them):

subscriptions

* id (int, primary key)
* uuid (string) /* i.e. the UUID of the subscription */
* user_uuid (string)
* sub_service_uuid (string) /* i.e. the UUID of the subscription service */
* billing_data (string, YAML)
* creation (int, UNIX time)
* updated (int, UNIX time)
* status (string/enum/int, needs to understand at least: "paid", "paymentdue", "suspended", "canceled", "partialpayment")

subscription_payments

* id (int, primary key)
* uuid (string)
* subscription_uuid (string) /* i.e. the UUID of the subscription */
* amount_paid (double)
* date_paid (int, UNIX time) /* NOT using a MySQL Date here because ints are easier ported to other databases */

subscription_services

* id (int, primary key)
* uuid (string)
* subscription_type (string)
* provider_uuid (string) /* i.e. the user/group that provides the subscription service */
* title (string)
* description (text)
* creator (uuid)
* creation (int, UNIX time)
* updated (int, UNIX time)
* amount (double)
* recurrence (string/enum/int, needs to understand at least: "monthly", "weekly", "yearly", "none")
* recurrence_day (int) /* specifies what day in the month/week/year that the money is due -- most people should pick "1" here, for the first day of the week/month/year -- should be 0 when recurrence == "none" */
* recurrence_notify (int) /* number of days before the money is due to let the people know via their user's email address that they owe money */
* notify_template (text) /* a template for the notification. contains variables like $USER_FULLNAME, $USER_USERNAME, etc. that are gsub'ed when sending out mail */

Credit Card Data Encryption

Credit card data needs to be encrypted so that even WE cannot access their credit card number. That said, we cannot use public key crypto, because we're essentially signing things to ourselves. After Googling, I discovered that OpenSSL supports passphrase-based encryption using DES, AES, etc. Look on this page. We can make a cryptographically strong passphrase quite simply. This is the formula for a cryptographically strong passphrase:

  SHA512(random_string_of_characters + ":" + last_four_of_card + ":" + user_password)

The random string of characters should be saved in the billing_data field of YAML data, probably as :security or :entropy. This way, the hash is impossible to crack, and it makes it impossible to pay for anything unless you own the credit card and know your password. Using this method, when a user tries to pay for a subscription, they'll be prompted for the last 4 digits of their credit card and their password. We'll assemble a hash based off of what they entered and attempt to use it to decrypt the AES-crypted data. If we get back a credit card number, that means everything was successful. If we don't, there's a problem.

There is one caveat: if a user changes their password, we must update all of their subscriptions. So, the change password dialog will be more complex than most. If you have subscriptions, we'll have to ask you to enter the last four digits of your credit card number used on that subscription for each subscription. We will only be able to change the password IF all of the credit card numbers come out okay. Because this operation will be fairly common, we need to have a function that lets you change your password for subscriptions. Here's how I propose it look:

  SubscriptionService.changeSubscriptionPassword(last4, oldpassword, newpassword)

This function should also randomly generate new random data and use it.

It's slightly less convenient than automatic billing, which we may be able to introduce in the future, but there is almost no chance of someone else billing something to their credit card, since you have to know your password and last four digits of your credit card.

Personal tools