GithubHelp home page GithubHelp logo

mozilla / subhub Goto Github PK

View Code? Open in Web Editor NEW
9.0 26.0 12.0 1.4 MB

DEPRECATED - payment subscription REST api for customers

License: Mozilla Public License 2.0

Python 92.33% Shell 1.40% Dockerfile 0.41% GDB 0.67% HCL 5.19%
abandoned unmaintained

subhub's Introduction

subhub's People

Contributors

bbangert avatar claudiouzelac avatar dependabot[bot] avatar gene1wood avatar jackiemunroe avatar marty331 avatar scottidler avatar siddharth1698 avatar sidler-mozilla avatar spiropulo avatar the-smooth-operator avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

subhub's Issues

Add more detailed properties to error responses for Stripe errors?

When I submit a known bad card to update payment for a customer, we get a response with a generic error like so:

{"message": "Request req_igSOxiiS3X3LWv: Your card has insufficient funds."}

We'd like to display some more tailored messages to the end-user and parsing this message for what happened may be difficult.

Could we maybe get some additional Stripe API error properties like type, code, decline_code, and message passed along in the subhub error response?

subscribe_to_plan should only respond with new subscription item

Here in subhub/api/payments.py the result of stripe.Subscription.create is discarded and the Customer is fetched to respond with the full current list of subscriptions:

    stripe.Subscription.create(customer=customer.id, items=[{"plan": data["plan_id"]}])
    updated_customer = stripe.Customer.retrieve(customer.id)
    return create_return_data(updated_customer["subscriptions"]), 201

But, in order to create a corresponding subscription record over on FxA, it might be better to return just the new subscription details returned by stripe.Subscription.create.

Otherwise, we'll need to do a comparison between the subhub & FxA subscription lists for the user and add/remove based on the difference. (Of course, now that I think about it, maybe a "sync" algorithm run at significant points wouldn't be a bad idea to keep FxA consistent with subhub & stripe.)

Need some additional specifications in the API YAML

I was working through writing some stub code in advance of subhub availability.

Could use some tweaks & additional specs in the API YAML. I was considering adding some suggestions to the YAML in a PR myself, but wanted to get this list out first:

  • Overall
    • What does auth look like? Simple Authorization: Bearer 39245792345 header? Hawk auth?
    • Would help to have specific codes or messages for every error condition, so we can thread that through to to the FxA UX in each case as necessary.
  • GET /customer/{uid}/subscriptions
    • Add subscription_id to the Subscriptions schema? Need that for referring to specific subscriptions from FxA
    • When would status 400 happen? (no parameters required)
    • 404 Not Found "No subscriptions for this customer." Is there a difference between a GET for a uid that's never had a customer created before - versus a user who once had subscriptions but cancelled them all? Are these cases both 404s?
  • POST /customer/{uid}/subscriptions
    • Schema for 201 Created response - ideally one that contains subscription_id? Maybe one of the elements from the Subscriptions schema with subscription_id added?
    • Error response for rejected / invalid pmt_token
    • Error response for invalid plan_id
    • Is it an error condition for a user to attempt to subscribe to the same plan twice?
  • GET /plans
    • Human-readable descriptions for each plan and product? We'll need that somehow in the frontend.
    • api_token parameter - is this meant to be the general Authorization header for all requests?
    • When would status 400 happen? (no parameters required)
    • Typo in appliation/json content type
  • DELETE ​/customer​/{uid}​/subscriptions​/{sub_id}
    • Schema for 201 Created response body? Or maybe status 204 No Content for success?
    • When would status 400 happen? (no parameters required)
  • GET /customer/{uid}
    • Schema for 200 OK response body with customer data?
  • POST /customer/{uid}
    • Error response for rejected / invalid pmt_token
    • Schema for 200 OK response body?

API: Add method to determine if source on file needs updating

There's no apparent API endpoint at the moment to determine if a customer's source needs to be updated. Either the existing API call to return customer information with payment status should be updated, or a new endpoint that can indicate if the payment source needs updating.

This is used by FxA Account Management to indicate to a user that they must update their payment data.

Exception thrown when fetching customer data after deleting last subscription

If all subscriptions are cancelled, payment sources are removed.

The customer record still exists, though - so, when fetching the customer data, it looks like an exception is thrown related to payment sources here.

127.0.0.1 - - [09/Jun/2019 21:28:44] "GET /v1/customer/93e870bff49b43acb34923bfc840fb55 HTTP/1.1" 500 -
Traceback (most recent call last):
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask/app.py", line 2309, in __call__
    return self.wsgi_app(environ, start_response)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask/app.py", line 2295, in wsgi_app
    response = self.handle_exception(e)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask_cors/extension.py", line 161, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask/app.py", line 1741, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask/_compat.py", line 35, in reraise
    raise value
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask/app.py", line 2292, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask/app.py", line 1815, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask_cors/extension.py", line 161, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask/app.py", line 1718, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask/_compat.py", line 35, in reraise
    raise value
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask/app.py", line 1813, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask/app.py", line 1799, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/connexion/decorators/decorator.py", line 48, in wrapper
    response = function(request)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/connexion/decorators/security.py", line 300, in wrapper
    return function(request)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/connexion/decorators/uri_parsing.py", line 143, in wrapper
    response = function(request)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/connexion/decorators/validation.py", line 347, in wrapper
    return function(request)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/connexion/decorators/parameter.py", line 126, in wrapper
    return function(**kwargs)
  File "/home/lmorc/devel-local/subhub/subhub/api/payments.py", line 246, in customer_update
    return_data = create_update_data(customer)
  File "/home/lmorc/devel-local/subhub/subhub/api/payments.py", line 262, in create_update_data
    return_data["payment_type"] = customer["sources"]["data"][0]["funding"]
IndexError: list index out of range

500 error in GET /v1/customer/{uid}

Edit: Looks like maybe some profiling annotations are causing trouble?

Trying to make a GET request to /v1/customer/{uid} and I'm getting a 500 error in our deployed FxA dev instance. I can reproduce locally and get this exception stack trace:

172.17.0.1 - - [15/Jul/2019 22:29:17] "GET /v1/customer/c4279736650649d6801bf291a2a40341 HTTP/1.1" 500 -
Traceback (most recent call last):
  File "/app/venv/lib/python3.7/site-packages/flask/app.py", line 2463, in __call__
    return self.wsgi_app(environ, start_response)
  File "/app/venv/lib/python3.7/site-packages/flask/app.py", line 2449, in wsgi_app
    response = self.handle_exception(e)
  File "/app/venv/lib/python3.7/site-packages/flask_cors/extension.py", line 161, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
  File "/app/venv/lib/python3.7/site-packages/flask/app.py", line 1866, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/app/venv/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/app/venv/lib/python3.7/site-packages/flask/app.py", line 2446, in wsgi_app
    response = self.full_dispatch_request()
  File "/app/venv/lib/python3.7/site-packages/flask/app.py", line 1951, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/app/venv/lib/python3.7/site-packages/flask_cors/extension.py", line 161, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
  File "/app/venv/lib/python3.7/site-packages/flask/app.py", line 1820, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/app/venv/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/app/venv/lib/python3.7/site-packages/flask/app.py", line 1949, in full_dispatch_request
    rv = self.dispatch_request()
  File "/app/venv/lib/python3.7/site-packages/flask/app.py", line 1935, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/app/venv/lib/python3.7/site-packages/connexion/decorators/decorator.py", line 48, in wrapper
    response = function(request)
  File "/app/venv/lib/python3.7/site-packages/connexion/decorators/security.py", line 299, in wrapper
    return function(request)
  File "/app/venv/lib/python3.7/site-packages/connexion/decorators/uri_parsing.py", line 143, in wrapper
    response = function(request)
  File "/app/venv/lib/python3.7/site-packages/connexion/decorators/validation.py", line 347, in wrapper
    return function(request)
  File "/app/venv/lib/python3.7/site-packages/connexion/decorators/parameter.py", line 126, in wrapper
    return function(**kwargs)
  File "/app/subhub/tracing.py", line 81, in timer
    return function(*args, **kwargs)
TypeError: customer_update() got an unexpected keyword argument 'user'

Use proper List typing

Multiple functions throughout subhub use list as the type for a parameter or result. They should however be using the typing modules List with the type of list it is:
https://docs.python.org/3/library/typing.html#type-aliases

For example, one function returns a list of subscriptions, which should be written as:

from typing import List

def somefunc(...args) -> List[stripe.Subscription]:
    ....

mypy and PyCharm will then work correctly with the type information.

Document format of subscription error responses

Please document the possible set of error codes returned when a subscription fails.

Based on #81, it appears that the stripe error info is directly passed through, but given that subhub is a payment abstraction layer, I could see this changing over time.

This could simply be added to the openAPI yaml as an enum of possible values, and we could generate documentation from there.

Exception thrown when card declined on attemping initial subscription for new customer

Starting to do some work around handling & displaying errors and ran into this:

When attempting to create the first subscription for a new customer, and the payment source results in a card declined error (e.g. using testing CC 4000 0000 0000 9979), the API responds with a 500 Server Error containing an exception stack trace.

Looks like when a customer is initially created, a card error is not expected?

127.0.0.1 - - [22/May/2019 17:11:11] "POST /v1/customer/2fbc85f877ca4364a5871136d557167d/subscriptions HTTP/1.1" 500 -
Traceback (most recent call last):
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask/app.py", line 1813, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask/app.py", line 1799, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/connexion/decorators/decorator.py", line 48, in wrapper
    response = function(request)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/connexion/decorators/security.py", line 300, in wrapper
    return function(request)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/connexion/decorators/uri_parsing.py", line 143, in wrapper
    response = function(request)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/connexion/decorators/validation.py", line 172, in wrapper
    response = function(request)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/connexion/decorators/validation.py", line 347, in wrapper
    return function(request)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/connexion/decorators/parameter.py", line 126, in wrapper
    return function(**kwargs)
  File "/home/lmorc/devel-local/subhub/subhub/api/payments.py", line 33, in subscribe_to_plan
    origin_system=data["orig_system"],
  File "/home/lmorc/devel-local/subhub/subhub/customer.py", line 69, in existing_or_new_customer
    subhub_accouunt, user_id, email, source_token, origin_system
  File "/home/lmorc/devel-local/subhub/subhub/customer.py", line 40, in create_customer
    metadata={"userid": user_id},
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/stripe/api_resources/abstract/createable_api_resource.py", line 22, in create
    response, api_key = requestor.request("post", url, params, headers)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/stripe/api_requestor.py", line 121, in request
    resp = self.interpret_response(rbody, rcode, rheaders)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/stripe/api_requestor.py", line 372, in interpret_response
    self.handle_error_response(rbody, rcode, resp.data, rheaders)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/stripe/api_requestor.py", line 151, in handle_error_response
    raise err
stripe.error.CardError: Request req_kN6nw8Sf9PsASl: Your card was declined.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask/app.py", line 1974, in make_response
    rv = self.response_class.force_type(rv, request.environ)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/werkzeug/wrappers/base_response.py", line 269, in force_type
    response = BaseResponse(*_run_wsgi_app(response, environ))
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/werkzeug/wrappers/base_response.py", line 26, in _run_wsgi_app
    return _run_wsgi_app(*args)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/werkzeug/test.py", line 1119, in run_wsgi_app
    app_rv = app(environ, start_response)
TypeError: 'dict' object is not callable

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask/app.py", line 2309, in __call__
    return self.wsgi_app(environ, start_response)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask/app.py", line 2295, in wsgi_app
    response = self.handle_exception(e)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask_cors/extension.py", line 161, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask/app.py", line 1741, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask/_compat.py", line 35, in reraise
    raise value
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask/app.py", line 2292, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask/app.py", line 1815, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask_cors/extension.py", line 161, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask/app.py", line 1982, in make_response
    reraise(TypeError, new_error, sys.exc_info()[2])
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask/_compat.py", line 34, in reraise
    raise value.with_traceback(tb)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/flask/app.py", line 1974, in make_response
    rv = self.response_class.force_type(rv, request.environ)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/werkzeug/wrappers/base_response.py", line 269, in force_type
    response = BaseResponse(*_run_wsgi_app(response, environ))
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/werkzeug/wrappers/base_response.py", line 26, in _run_wsgi_app
    return _run_wsgi_app(*args)
  File "/home/lmorc/devel-local/subhub/subhub/.venv/lib/python3.7/site-packages/werkzeug/test.py", line 1119, in run_wsgi_app
    app_rv = app(environ, start_response)
TypeError: 'dict' object is not callable
The view function did not return a valid response. The return type must be a string, tuple, Response instance, or WSGI callable, but it was a dict.

Reactivation of cancelled subscription email is not sent

Affected versions:

  • Firefox Release 70.0
  • Firefox Beta 71.0b4
  • Firefox Nightly 72.0a1

Affected Platforms:

  • Windows 10 (64-bit)
  • MacOS 10.15
  • Linux Ubuntu 16.04 (64-bit)

Prerequisites:

Have an active subscription.

Steps to reproduce

  1. Go to Subscription settings and Cancel your subscription.
  2. Reactivate the cancelled subscription.

Expected result:

A mail is sent, informing the user about the resubscription.

Actual result:

The mail is not sent.

Notes:

I used this email for subscribing: [email protected].

Need more details about card_declined errors

Since Issue #70 and then PR #81, I'm seeing code and message added to errors relayed from Stripe.

I think we'll also need more things like type and decline_code if available. We can display the message for now, but we will need the more abstract codes to select localized messages when we eventually add more locales than en-US.

`201` should probably be a `200`

Looks like this property is appearing now! But, it seems strange that it's giving a 201 Created response for GET /customer/{uid}/subscriptions - maybe that's a separate issue? (Edit: Yeah, looks like this 201 should probably be a 200?)

➜  subhub git:(master) ✗ curl -sD - -X GET --header 'Accept: application/json' --header 'Authorization: abcde' 'http://127.0.0.1:8012/v1/customer/9f960ea24a194d67ad62ee57faeb1308/subscriptions'
HTTP/1.0 201 CREATED
Content-Type: application/json
Content-Length: 337
Access-Control-Allow-Origin: *
Server: Werkzeug/0.15.2 Python/3.7.1
Date: Tue, 11 Jun 2019 22:09:21 GMT

{
  "subscriptions": [
    {
      "cancel_at_period_end": true,
      "current_period_end": 1562882826,
      "current_period_start": 1560290826,
      "ended_at": null,
      "plan_id": "plan_F4bof27uz71Vk7",
      "plan_name": "123Done Pro Monthly",
      "status": "active",
      "subscription_id": "sub_FEleCnZNUg1ZDb"
    }
  ]
}

Originally posted by @lmorchard in #67 (comment)

Error while attempting to retrieve_stripe_customer using a charge ID

While working to troubleshoot an issue adjacent to zip code validation, I noticed that subhub was logging this error while attempting to fetch customer data:

{"message": "RETRIEVE STRIPE CUSTOMER ERROR", "lineno": 168, "pathname": "/subhub/sub/shared/vendor.py", "levelname": "ERROR", "threadName": "Thread-31", "error": "Request req_fF3sb9JkiNGkbd: No such customer: ch_1FYh1KEOSeHhIAfQY32wfWhV", "logger": "__main__", "timestamp": "2019-10-28T23:26:25.579941Z"}

The ch_ prefix on ch_1FYh1KEOSeHhIAfQY32wfWhV looks like it's a Charge ID and not a Customer ID.

Digging around, I noticed this line in create_update_data in src/sub/payments.py:

intents = vendor.retrieve_stripe_customer(invoice["charge"])

Seems like this should be more like this line?

intents = vendor.retrieve_stripe_charge(invoice["charge"])

(Subscriptions Payments) Email: Thank you went into Spam

This was filed over in the guardian website repo by @rbillings.

After signing up for Guardian, the Thank You for Subscribing email went directly into my Spam folder [although the payment one went through]. Not sure if there's a way to avoid this, but I wouldn't have seen it as a user.

Email: Suggest removing duplicate "monthly" from title

This was filed over in the guardian website repo by @rbillings. I'm guessing this is a simple configuration update on the product name?

The email for payment received has this title:
Sandbox: Firefox Guardian (Monthly) monthly payment received

Suggest removing the second "monthly" or changing the title of the product to remove "Monthly".

The user is billed again when he resubscribes after canceling a subscription

Affected versions:

  • Firefox Release 69.0.1
  • Firefox Beta 70.0b9
  • Firefox Nightly 71.0a1

Affected Platforms:

  • Windows 10 (64-bit)
  • MacOS 10.14.5
  • Linux Ubuntu 16.04 (64-bit)

Precondition

User is logged in in the Guardian VPN homepage and he does have an active subscription.
https://stage.guardian.nonprod.cloudops.mozgcp.net/vpn

Steps to reproduce

  1. Navigate to subscription page and cancel subscription for Firefox Guardian
  2. From the same page Resubscribe to Firefox Guardian

Expected result:

The user is not billed again so no Payment Received email is sent to the user.

Actual result:

When the user resubscribes a payment received email is sent
userBilledAgainAfterResubscribe

Download email, new subscription and payment receipt emails are not sent after the user completes the subscription process

Affected versions:

  • Firefox Release 69.0.1
  • Firefox Beta 70.0b9
  • Firefox Nightly 71.0a1

Affected Platforms:

  • Windows 10 (64-bit)
  • MacOS 10.14.5
  • Linux Ubuntu 16.04 (64-bit)

Precondition

User is logged in in the Guardian VPN homepage and he does not have an active subscription.
https://stage.guardian.nonprod.cloudops.mozgcp.net/vpn

Steps to reproduce

  1. Click on the "Subscribe for just $9.99/mo" blue button
  2. Enter valid valid info and complete the subscription process

Expected result:

  • The Download email is sent to the user
  • The New subscription email is sent to the user
  • The Payment receipt email is sent to the user

Actual result:

  • None of the above 3 emails are sent to the user

Thank you for subscribing mail is not formatted/displayed correctly

Affected versions:

  • All Firefox

Affected Platforms:

  • All Windows
  • All Mac
  • All Linux

Steps to reproduce

  1. Make a subscription for Guardian VPN.
  2. Open your inbox and check the invoice.

Expected result:

  • The invoice is correctly formatted and displayed.

Actual result:

  • The overall format for the mail is incorrect.
  • The "Invoice Number: ###" is not bolded.
  • The formatting for the product payment and total payment are incorrect as they are displayed to the center/left side.
  • The charge information is incomplete reading "Charged to visa #" instead of "Charged to Visa card ending ##"
  • Various other options are not available in the bottom of the mail.

img

Need a DELETE /customer/{uid} API resource?

This might be an edge case, but:

Over in FxA, we cancel all subscriptions when a user is deleted. But, we can't actually delete the customer record in subhub.

So, if someone signs up again on FxA with the same email address, subhub & Stripe still retain the old customer UID and attempts to subscribe from the new account will fail.

I think we'd need to delete the customer upon deleting the FxA account. (Also guessing if we did that, we wouldn't need to cancel the subscriptions separately.)

Subscription cancelation email is not sent

Affected versions:

  • Firefox Release 70.0

Affected Platforms:

  • Windows 10 (64-bit)
  • MacOS 10.14.2

Precondition

User has an active subscription

Steps to reproduce

  1. Open the manage subscription page and cancel the subscription
  2. Observe the received emails

Expected result:

  • Subscription cancelation email is sent

Actual result:

  • Subscription cancelation email is not sent

Notes:

Remove extraneous data from StripeCustomerSubscriptionCreated payload for FxA

The following fields must be removed from the payload sent to FxA for a new subscrption:
brand, cancel_at, cancel_at_period_end, canceled_at, charge, created, currency, current_period_end, current_period_start, customer_id, event_id, event_type, invoice_id, invoice_number, last4, nickname, plan_amount, subscription_id

File:
src/hub/vendor/customer.py 78-157

FxA payload - https://github.com/mozilla/fxa/blob/993644c06c8282611193f1b90cc68311b9a553fd/packages/fxa-auth-server/lib/subhub/updates.js#L11-L17

Subscription cancellation mail is not sent

Affected versions:

  • Firefox Release 69.0.1
  • Firefox Beta 70.0b9
  • Firefox Nightly 71.0a1

Affected Platforms:

  • Windows 10 (64-bit)
  • MacOS 10.14.5
  • Linux Ubuntu 16.04 (64-bit)

Prerequisites:

  • You have an active Guardian VPN subscription.

Steps to reproduce

  1. Go to Subscriptions page.
  2. Click on the "Cancel" button
  3. Check the "Cancel my access and my saved information within Firefox Guardian (Monthly) on month day, year" box.
  4. Click on the "Cancel Subscription" button.

Expected result:

  • A modal is displayed in the middle of the page.
  • An email is sent with the Subject: "Your Guardian VPN subscription has been cancelled"

Actual result:

  • The email is not sent.

Clean up and fix code around find newest subscription

The find_newest_subscription function in payments.py is missing a return type annotation. It would've helped in this case to catch a failed expectation. The function on its own is capable of returning None (if subscriptions is empty), or a dict with a data key.

At the moment, there's only a single calling context, that typically guarantees there will be at least one subscription (as it creates one), but if it is not created, then the function would return None. This is a problem because the caller of the function proceeds with the assumption that the call will always return a dict with a data key which is used by create_return_data. create_return_data requires a dict to be passed in with a data key or it will throw a KeyError.

In the future, to make some of these errors less likely, there's a TypedDict PEP which would be used to ensure this isn't done.

Alternatively, the find_newest_subscription function could just return a list of subscriptions, and it would be empty in the event there is none. The create_return_data could then be updated to not use a data key in a dict.

Add stripe-mock option for local testing

It would be useful to use stripe-mock as an optional backend for local subhub testing. This would make it easier for devs to test, enable contributors to get involved, and shorten local test running time.

Remove unnecessary source file headers

All of the Python files have had headers inserted that are unnecessary or odd to include. The first one:
#!/usr/bin/env python3

Is used for running a Python file as a script, and Python library source files shouldn't have this added as they're not directly executed as a script.

The second:
# -*- coding: utf-8 -*-

is used when including unicode characters in a Python 2 source file. SubHub is entirely Python 3, which is unicode by default so this shouldn't be included.

Support tickets are submitted under the wrong branding

Affected versions:

  • Firefox 68.0.2

Affected Platforms:

  • Windows 10 (64-bit)

Preconditions

Have a account with an active subscription.

Steps to reproduce

  1. Go to Firefox Accounts.
  2. In the "Subscriptions" section click on "Manage" and then on "Contact Support" from the next page.
  3. Complete all of the fields and file a support ticket.
  4. Open the previously filled ticket in Zendesk and check the brand in the left side panel.

Expected result:

The brand is displayed as "Premium Services".

Actual result:

The brand is displayed as "Mozilla Enterprise".

Notes:

The issue is reproducible for any of the support categories.

1

Inconsistent invoice dates in the New subscription email

Affected versions:

  • Firefox Release 69.0.1
  • Firefox Beta 70.0b9
  • Firefox Nightly 71.0a1

Affected Platforms:

  • Windows 10 (64-bit)
  • MacOS 10.14.5
  • Linux Ubuntu 16.04 (64-bit)

Precondition

User is logged in in the Guardian VPN homepage and he does not have an active subscription.
https://stage.guardian.nonprod.cloudops.mozgcp.net/vpn

Steps to reproduce

  1. Click on the "Subscribe for just $9.99/mo" blue button
  2. Enter valid valid info and complete the subscription process
  3. Observe the New Subscription email that was sent to the user

Expected result:

In the email the next invoice date is tomorrow (10/10.2019)

Actual result:

In the email the current invoice is having the present day date (10/9/2019 ) and the next invoice date is 2 days from now (10/11/2019)
Please view this screenshot containing the new subscription email:
newSubEmail

Also in the Subscriptions page the date for the next payment is tommrow (10/10/2019) so I would expect the invoice to be sent in the same date.
Please view this screenshot from the Subscription page:
image

Incorrect close date on Sub Services Opportunities

We're finding incorrect close dates on subscription records in Salesforce. When troubleshooting email sends, we noticed that the close date on the following subscriptions was incorrectly set. Can we verify the dates we're sending for the subscriptions and/or involve basket if necessary?

Charge processed on 10/9/19, close date is 10/7/19
Invoice id: in_1FRnRAKb9q6OnNsLXKZLSYvo
charge id: ch_1FRoQfKb9q6OnNsLoLoVycWL
subscription id: sub_FwyQnMNwiIqpPQ

Charge processed on 10/8/19, close date is 10/7/19
Invoice id: in_1FRQxYKb9q6OnNsLvscz8GyX
charge id: ch_1FRRw3Kb9q6OnNsLkfZNRdN0
subscription id: sub_FwyQnMNwiIqpPQ

No email notification sent to customer when zendesk ticket is solved

Description

After a zendesk agent marks a support ticket as "Solved", I would expect to send an email notification to the person who filed the support request. That is currently not happening, the support requester receives notifications only when the ticket was filed and when there are comments added to it.

Steps to reproduce

  1. Login into guardian with an fxa account that has a subscription enabled
  2. Navigate to subscription page
  3. Click "Contact Support"
  4. Fill in all the fields and click "Send"
  5. Login into zendesk with an agent account
  6. Find the ticket created for the request in step 4
  7. Mark the ticket as Solved
  8. Login into you email server of the email address used in step 1
  9. Search after recent emails => There is a notification saying a ticket was filed for my request

Expected result

User should receive an email notification saying the status of the ticket was changed to "Solved"

Actual result

No email notification for the ticket status change is received

Environment

Ubuntu 16.04, Firefox 69.0.3

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.