-
-
Notifications
You must be signed in to change notification settings - Fork 5
Initial draft of paid content #1028
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
base: dev
Are you sure you want to change the base?
Conversation
tibetsprague
left a comment
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.
looks good so far
| table.bigInteger('group_id').unsigned().notNullable().references('id').inTable('groups') | ||
| table.bigInteger('product_id').unsigned().references('id').inTable('stripe_products') | ||
| table.bigInteger('track_id').unsigned().references('id').inTable('tracks') | ||
| table.integer('role_id').unsigned().references('id').inTable('groups_roles') |
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.
what is the role for here?
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.
A group has three membership options. The basic option just gets you access to the group. The next two options get to access to the group and a specific role. The role is either a cosmetic buff or it actually 'unlocks' some sort of content.
| table.string('stripe_session_id', 255) | ||
| table.string('stripe_payment_intent_id', 255) | ||
| table.integer('amount_paid').defaultTo(0) // 0 for free grants | ||
| table.string('currency', 3).defaultTo('usd') |
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.
all currencies are 3 characters for sure? what about cryptos?
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.
Looks like it;
https://docs.stripe.com/currencies
I have seen some reference to users being able to pay with crypto but its not on the currencies page (?)
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.
As far as I understand at the moment, these aspects of the transaciton info in our database will not the source of truth; that will be the transaction on stripe's side.
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.
And to this end, I have actually removed them. We will list transactions in our interface but basically all details for them will stay with stripe and we would just link to them from our UI
|
|
||
| This document provides a comprehensive walkthrough of the entire paid content workflow, from admin setup to user purchase completion. | ||
|
|
||
| ## Table of Contents |
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.
@tibetsprague give this a look and see if it makes sense to you as a workflow
|
@tibetsprague ok, so here is another round of changes. A few updates to the migration:
Also added a workflow explainer markdown file, that you should check to make sure the workflow sounds sensible |
tibetsprague
left a comment
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.
looking good so far!
| } | ||
|
|
||
| // Find the group with this Stripe account ID | ||
| const group = await Group.where({ stripe_account_id: account.id }).fetch() |
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.
can multiple groups use the same stripe account? probably. i wonder if there is another identifier for like a sub-account or something like that? or maybe they just give separate account_ids for each one.
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.
ooo right that is an interesting question. This is a webhook, so I will have to check if we can store a 'foreign key' of sorts from our db in their records for an account. I assume we can
| } | ||
|
|
||
| // Check if group already has a Stripe account | ||
| // TODO STRIPE: You may want to store the accountId in your Group model |
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.
ok so it seems like each group will have its own "account"
| model: MemberGroupRole, | ||
| attributes: [ | ||
| 'id', | ||
| 'expires_at', |
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.
intriguing, so this would be for a product that gives you a certain role?
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.
For example: for the astrology group, they define three tiers of membership with different subscription rates.
So each of those tiers could provide a role/badge that is unique to them. I don't think we have it specced at the moment but in the future this could be used to gate chat rooms or perhaps even specific content or be another way to unlock access to a track. And those role grants don't need to be paid for either; in general, groups might want to restrict content to specific roles
| type Track { | ||
| id: ID | ||
| # Whether this track requires purchased access (paid content) | ||
| accessControlled: Boolean |
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.
seems like we should call this the same thing we call it on group, so either both are paywall (or paywalled?) or both are accessControlled. i lean towards paywalled
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.
So groups already have a property and concept related to restricted access but tracks don't. So the properties, despite both being booleans and having some conceptual overlap, don't have the same scope. So I don't want to give them the same name and thus infer that they have the same scope.
The doc note here is outdated; admins can grant access without a payment or stripe being involved.
| } | ||
| } | ||
|
|
||
| // Add role-specific access records |
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.
still a little unclear on this. this means that everyone with this role gets access to this thing? or that people with this content get this role?
| **When access is revoked or expires:** | ||
| 1. Status changes to 'revoked' or 'expired' in `content_access` | ||
| 2. Trigger `content_access_expires_at_clear` fires | ||
| 3. Function `clear_content_access_expires_at()` executes: |
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.
so it always has an expires_at value if content is accessible? and for lifetime we just put one far in the future?
| // Creates: content_access { user_id, group_id, expires_at: '2026-01-01', track_id: NULL } | ||
| // Trigger updates: group_memberships.expires_at = '2026-01-01' ✓ | ||
|
|
||
| // Step 2: Same user buys 1-month access to Track A (3 months later) |
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.
i think racks will always be lifetime?
| - Email includes: | ||
| - Purchase confirmation details | ||
| - Links to access the content they purchased | ||
| - Instructions for accessing their new content |
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.
a special group link that sets up the access? or already they will have been marked as being able to access the group in the database so then its just the normal group link
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.
maybe "specific" is a better word than "special" here; a specific link to the content.
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.
So, this is worth checking in about and there is still a bit of gray area based on exactly how the stripe hand-off goes:
Say, we are a user, in the stripe checkout and I hit purchase.
- I think then stripe redirects the user to the successUrl that we provide.
- Meanwhile, there is an async update between Stripe (via webhook) and us, letting us know that the payment worked out
- So, I am thinking that we made the successUrl pretty generic and simple and more or less just say: check your email
this saves us if there is a significant lag between transaction and webhook update.... but if it turns out that turn-around time is really fast (consistently?) then we are just adding another step (checking the email) for the user...
Any initial thoughts? Might just be something we figure out once we start seeing it implemented
…separately, instead of implicitly assuming they are the same
…tus; add link to stripe dashboard for connected accounts
…dation for group paywall toggle
…iptions via the Stripe portal
️✅ 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. |
…nto 604-paid-content
… stripe offering records
… for agreements if they are already done
I want to get initial feedback on the migration.
The stripe connect guide allows you to select a few different options to model your overall use-case and then it generates a prompt for a coding assistant. I tweaked the prompt a little more and its generated a lot of code-changes and a lot of TODOs. But so far it looks like a decent start.
I want to be fairly measured about pulling in the changes piece by piece tho, and starting with the migration stuff