-
-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Stripe integration #595
Stripe integration #595
Conversation
This reverts commit 4c14fdf.
Terraform plan in terraform/dev in the hushline-dev-paid-features2 workspace With variablesbranch = "paid-features2"
name = "dev-paid-features2" Plan: 3 to add, 0 to change, 0 to destroy. Changes to Outputs.Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# digitalocean_project.hush_line_dev will be created
+ resource "digitalocean_project" "hush_line_dev" {
+ created_at = (known after apply)
+ description = "Development instance based on the paid-features2 branch"
+ environment = "Development"
+ id = (known after apply)
+ is_default = false
+ name = "dev-paid-features2"
+ owner_id = (known after apply)
+ owner_uuid = (known after apply)
+ purpose = "Web Application"
+ resources = (known after apply)
+ updated_at = (known after apply)
}
# module.app.digitalocean_app.app will be created
+ resource "digitalocean_app" "app" {
+ active_deployment_id = (known after apply)
+ created_at = (known after apply)
+ default_ingress = (known after apply)
+ id = (known after apply)
+ live_url = (known after apply)
+ project_id = (known after apply)
+ updated_at = (known after apply)
+ urn = (known after apply)
+ dedicated_ips (known after apply)
+ spec {
+ domains = (known after apply)
+ features = [
+ "buildpack-stack=ubuntu-22",
]
+ name = "dev-paid-features2"
+ region = "sfo"
+ alert {
+ disabled = false
+ rule = "DEPLOYMENT_FAILED"
}
+ domain (known after apply)
+ ingress (known after apply)
+ service {
+ dockerfile_path = "Dockerfile"
+ http_port = 8080
+ instance_count = 1
+ instance_size_slug = "apps-s-1vcpu-0.5gb"
+ internal_ports = (known after apply)
+ name = "app"
+ run_command = (known after apply)
+ github {
+ branch = "paid-features2"
+ deploy_on_push = true
+ repo = "scidsg/hushline"
}
+ health_check {
+ http_path = "/health.json"
}
+ routes (known after apply)
}
+ service {
+ http_port = (known after apply)
+ instance_count = 1
+ instance_size_slug = "apps-s-1vcpu-0.5gb"
+ internal_ports = [
+ 5432,
]
+ name = "db"
+ run_command = (known after apply)
+ image {
+ registry = "library"
+ registry_type = "DOCKER_HUB"
+ repository = "postgres"
+ tag = "16.4-alpine3.20"
+ deploy_on_push (known after apply)
}
+ routes (known after apply)
}
}
}
# module.app.random_password.local_db_password will be created
+ resource "random_password" "local_db_password" {
+ bcrypt_hash = (sensitive value)
+ id = (known after apply)
+ length = 16
+ lower = true
+ min_lower = 0
+ min_numeric = 0
+ min_special = 0
+ min_upper = 0
+ number = true
+ numeric = true
+ override_special = "!#$%&*()-_=+[]{}<>:?"
+ result = (sensitive value)
+ special = true
+ upper = true
}
Plan: 3 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ app_live_url = (known after apply) ✅ Plan applied in Deploy/Destroy Branch Dev Environment #282 Outputsapp_live_url = "https://dev-paid-features2-oltb6.ondigitalocean.app" |
Terraform plan in terraform/dev in the hushline-dev-paid-features2 workspace With variablesbranch = "paid-features2"
name = "dev-paid-features2" Plan: 3 to add, 0 to change, 0 to destroy. Changes to Outputs.Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# digitalocean_project.hush_line_dev will be created
+ resource "digitalocean_project" "hush_line_dev" {
+ created_at = (known after apply)
+ description = "Development instance based on the paid-features2 branch"
+ environment = "Development"
+ id = (known after apply)
+ is_default = false
+ name = "dev-paid-features2"
+ owner_id = (known after apply)
+ owner_uuid = (known after apply)
+ purpose = "Web Application"
+ resources = (known after apply)
+ updated_at = (known after apply)
}
# module.app.digitalocean_app.app will be created
+ resource "digitalocean_app" "app" {
+ active_deployment_id = (known after apply)
+ created_at = (known after apply)
+ default_ingress = (known after apply)
+ id = (known after apply)
+ live_url = (known after apply)
+ project_id = (known after apply)
+ updated_at = (known after apply)
+ urn = (known after apply)
+ dedicated_ips (known after apply)
+ spec {
+ domains = (known after apply)
+ features = [
+ "buildpack-stack=ubuntu-22",
]
+ name = "dev-paid-features2"
+ region = "sfo"
+ alert {
+ disabled = false
+ rule = "DEPLOYMENT_FAILED"
}
+ domain (known after apply)
+ ingress (known after apply)
+ service {
+ dockerfile_path = "Dockerfile"
+ http_port = 8080
+ instance_count = 1
+ instance_size_slug = "apps-s-1vcpu-0.5gb"
+ internal_ports = (known after apply)
+ name = "app"
+ run_command = (known after apply)
+ github {
+ branch = "paid-features2"
+ deploy_on_push = true
+ repo = "scidsg/hushline"
}
+ health_check {
+ http_path = "/health.json"
}
+ routes (known after apply)
}
+ service {
+ http_port = (known after apply)
+ instance_count = 1
+ instance_size_slug = "apps-s-1vcpu-0.5gb"
+ internal_ports = [
+ 5432,
]
+ name = "db"
+ run_command = (known after apply)
+ image {
+ registry = "library"
+ registry_type = "DOCKER_HUB"
+ repository = "postgres"
+ tag = "16.4-alpine3.20"
+ deploy_on_push (known after apply)
}
+ routes (known after apply)
}
}
}
# module.app.random_password.local_db_password will be created
+ resource "random_password" "local_db_password" {
+ bcrypt_hash = (sensitive value)
+ id = (known after apply)
+ length = 16
+ lower = true
+ min_lower = 0
+ min_numeric = 0
+ min_special = 0
+ min_upper = 0
+ number = true
+ numeric = true
+ override_special = "!#$%&*()-_=+[]{}<>:?"
+ result = (sensitive value)
+ special = true
+ upper = true
}
Plan: 3 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ app_live_url = (known after apply) ❌ Error applying plan in Deploy/Destroy Branch Dev Environment #281 |
🚀 App successfully deployed to https://dev-paid-features2-oltb6.ondigitalocean.app! |
…if premium is disabled, which makes tests mass
…ving the waiting page to another route
EDIT: moving the to do list to the PR description |
️✅ There are no secrets present in this pull request anymore.If these secrets were true positive and are still valid, we highly recommend you to revoke them. 🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request. |
…tripe event object
This is just about ready for a final review, as well as for some design work! Let me show how it all works. When new users register accounts, the first time they login they are prompted to select a tier: This displays the Once you select a plan (I selected free) it no longer displays that on login. If you're logged into the free tier, there's now an "Upgrade" button in the nav bar. You may want to change how it looks: Clicking it brings you to Here's the premium index page: I've switched it to use Stripe Checkout. If you click "Upgrade Now", it now creates a new checkout session and redirects to a Stripe-hosted page to collect payment info: After filling out the payment info, it briefly shows a waiting page ( This time, now that the user is signed in and using a business account, it no longer has the Upgrade button in the nav bar. It also displays a table of invoices at the bottom (this part needs design work). And instead of just canceling the subscription, there's a whole workflow for disabling and re-enabling auto-renew. Here's a short video showing it: Screencast.from.2024-10-01.10-21-14.webmThe buttons need some design work too, but it works! If you're on a business plan, and you load the profile page of Settings, it should no longer show the Upgrade button there: And if you go to the Advanced tab, it should now have a link to manage your plan (this definitely needs some design work): That just links back to the premium page, where you can choose to not renew/cancel your subscription, or access your invoices. === All of these premium features should only happen if the @app.context_processor
def inject_is_premium_enabled() -> dict[str, Any]:
return {"is_premium_enabled": bool(app.config.get("STRIPE_SECRET_KEY", False))} So an important thing to check for while reviewing is that if premium mode is disabled (no This PR also adds a Tier model with two tiers, free and business. It adds a bunch of new fields to the User model:
And it adds models for StripeInvoice and StripeEvent. === Stripe webhooks work like this: The webhook URL is The premium features will require an extra worker container, so before we can deploy this to prod, there needs to be some infra work to spin up an extra container. The worker container is just the app contain but it starts with this command: The worker container runs an infinite loop looking for pending Stripe events and processing them. Right now it only handles the following events (which are all that's needed for the functionality we're using):
So when creating the webhook in the Stripe dashboard, we can limit it to these events. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Went through the manual test plan. Everything works as advertised. I have some open remarks that might be good to address, but those can be in follows to. Approved.
Glenn's Stripe Visual Updates Try 2
…er.is_free_tier and User.is_business_tier properties, and overall clean everything up
…ble. - Lets you pass in kwargs to StripeEvent.__init__. - Removes unnecessary methods=["GET"] from routes. - Tightens a try/except block in premium.waiting(). - Fixes some HTML indentation. - Renames a foreign key in the migration to match the pattern.
I'm closing the #534 PR and using this one instead. I've also merged #593 into it. This will address #364.
How to test this
Install and configure stripe-cli, and then run this command to forward webhook events. The output will show you a webhook secret:
Copy
.env.stripe-sample
into.env-stripe
and edit it to setSTRIPE_SECRET_KEY
(you can find this in the Stripe dashboard at https://dashboard.stripe.com/test/apikeys) andSTRIPE_WEBHOOK_SECRET
(the stripe-cli command showed you that).Then, run the docker-compose.stripe.yaml containers:
Things to do
Done
current_period_end
andcurrent_period_start
, so it should be simple enough)create_products_and_prices
to intelligently make sure the db and Stripe are synced up, without creating lots of extra dev products