diff --git a/.env.tpl b/.env.tpl index 93512050..1f07fd05 100644 --- a/.env.tpl +++ b/.env.tpl @@ -43,9 +43,16 @@ MAILSLURP_TIMEOUT = '120000' # Stripe # these values are from the Stripe test environment STRIPE_PRICING_TABLE_ID = 'prctbl_1NzhdvF6A5ufQX5vKNZuRhie' +STRIPE_FREE_TRIAL_PRICING_TABLE_ID = 'prctbl_1QHa8sF6A5ufQX5vJ8SUZUjq' STRIPE_PUBLISHABLE_KEY = 'pk_test_51LO87hF6A5ufQX5viNsPTbuErzfavdrEFoBuaJJPfoIhzQXdOUdefwL70YewaXA32ZrSRbK4U4fqebC7SVtyeNcz00qmgNgueC' # this is used in tests and should always be set to the test env secret key STRIPE_TEST_SECRET_KEY = '' +STRIPE_BILLING_METER_ID = '' +STRIPE_BILLING_METER_EVENT_NAME = '' # Feature flags REQUIRE_PAYMENT_PLAN = 'true' + +# Referrals + +REFERRALS_ENDPOINT = 'https://staging.referrals.storacha.network' \ No newline at end of file diff --git a/stacks/upload-api-stack.js b/stacks/upload-api-stack.js index b963d177..aa12c70a 100644 --- a/stacks/upload-api-stack.js +++ b/stacks/upload-api-stack.js @@ -141,9 +141,11 @@ export function UploadApiStack({ stack, app }) { REQUIRE_PAYMENT_PLAN: process.env.REQUIRE_PAYMENT_PLAN ?? '', UPLOAD_API_DID: process.env.UPLOAD_API_DID ?? '', STRIPE_PRICING_TABLE_ID: process.env.STRIPE_PRICING_TABLE_ID ?? '', + STRIPE_FREE_TRIAL_PRICING_TABLE_ID: process.env.STRIPE_FREE_TRIAL_PRICING_TABLE_ID ?? '', STRIPE_PUBLISHABLE_KEY: process.env.STRIPE_PUBLISHABLE_KEY ?? '', DEAL_TRACKER_DID: process.env.DEAL_TRACKER_DID ?? '', DEAL_TRACKER_URL: process.env.DEAL_TRACKER_URL ?? '', + REFERRALS_ENDPOINT: process.env.REFERRALS_ENDPOINT ?? '', CONTENT_CLAIMS_DID, CONTENT_CLAIMS_URL, CONTENT_CLAIMS_PROOF, diff --git a/upload-api/functions/validate-email.jsx b/upload-api/functions/validate-email.jsx index 5b6f5e17..dfb65b88 100644 --- a/upload-api/functions/validate-email.jsx +++ b/upload-api/functions/validate-email.jsx @@ -11,6 +11,7 @@ import { createWorkflowStore } from '../buckets/workflow-store.js' import { createSubscriptionTable } from '../tables/subscription.js' import { createConsumerTable } from '../tables/consumer.js' import { createRevocationsTable } from '../stores/revocations.js' +import { createReferralStore } from '../stores/referrals.js' import * as AgentStore from '../stores/agent.js' import { useProvisionStore } from '../stores/provisions.js' // eslint-disable-next-line import/extensions @@ -90,7 +91,9 @@ function createAuthorizeContext() { UPLOAD_API_DID = '', PROVIDERS = '', STRIPE_PRICING_TABLE_ID = '', + STRIPE_FREE_TRIAL_PRICING_TABLE_ID = '', STRIPE_PUBLISHABLE_KEY = '', + REFERRALS_ENDPOINT = '', UCAN_LOG_STREAM_NAME = '', SST_STAGE = '', // set for testing @@ -122,6 +125,7 @@ function createAuthorizeContext() { { region: AWS_REGION }, { tableName: CUSTOMER_TABLE_NAME } ) + const referralStore = createReferralStore({ endpoint: REFERRALS_ENDPOINT }) const spaceMetricsTable = createSpaceMetricsTable( AWS_REGION, SPACE_METRICS_TABLE_NAME @@ -171,8 +175,10 @@ function createAuthorizeContext() { ), rateLimitsStorage: createRateLimitTable(AWS_REGION, RATE_LIMIT_TABLE_NAME), customerStore, + referralStore, agentStore, stripePricingTableId: STRIPE_PRICING_TABLE_ID, + stripeFreeTrialPricingTableId: STRIPE_FREE_TRIAL_PRICING_TABLE_ID, stripePublishableKey: STRIPE_PUBLISHABLE_KEY, } } @@ -183,7 +189,6 @@ function createAuthorizeContext() { * @param {import('aws-lambda').APIGatewayProxyEventV2} request */ export async function validateEmailPost(request) { - console.log("VALIDATING EMAIL") const encodedUcan = request.queryStringParameters?.ucan if (!encodedUcan) { return toLambdaResponse( @@ -192,13 +197,9 @@ export async function validateEmailPost(request) { ) ) } - console.log("CREATING CONTEXT") - const context = createAuthorizeContext() - console.log("AUTHORIZING") const authorizeResult = await authorize(encodedUcan, context) - console.log("AUTHORIZED", JSON.stringify(authorizeResult)) if (authorizeResult.error) { console.error(authorizeResult.error) @@ -215,21 +216,28 @@ export async function validateEmailPost(request) { } const { email, audience, ucan } = authorizeResult.ok - console.log("CHECKING PLAN") const planCheckResult = await context.customerStore.get({ customer: DidMailto.fromEmail(email), }) + let isReferred = false + try { + // if we can find a referral code for this user, offer them a free trial + if ((await context.referralStore.getReferredBy(email)).refcode) { + isReferred = true + } + } catch (e){ + // if we fail here, log the error and move on + console.warn('encountered an error checking the referrals service, please see the error logs for more information') + console.error(e) + } let stripePricingTableId let stripePublishableKey - console.log("CHECKED PLAN", JSON.stringify(planCheckResult)) if (!planCheckResult.ok?.product) { - stripePricingTableId = context.stripePricingTableId stripePublishableKey = context.stripePublishableKey + stripePricingTableId = isReferred ? context.stripeFreeTrialPricingTableId : context.stripePricingTableId } - - return toLambdaResponse( new html.HtmlResponse( ( @@ -239,6 +247,7 @@ export async function validateEmailPost(request) { ucan={ucan} stripePricingTableId={stripePricingTableId} stripePublishableKey={stripePublishableKey} + isReferred={isReferred} /> ) ) diff --git a/upload-api/html-storacha/index.jsx b/upload-api/html-storacha/index.jsx index d25ed956..07ec0573 100644 --- a/upload-api/html-storacha/index.jsx +++ b/upload-api/html-storacha/index.jsx @@ -194,6 +194,7 @@ export const PendingValidateEmail = ({ autoApprove }) => ( * @param {string} props.audience * @param {string} [props.stripePricingTableId] * @param {string} [props.stripePublishableKey] + * @param {boolean} [props.isReferred] */ export const ValidateEmail = ({ ucan, @@ -201,6 +202,7 @@ export const ValidateEmail = ({ audience, stripePricingTableId, stripePublishableKey, + isReferred }) => { const showPricingTable = stripePricingTableId && stripePublishableKey return ( @@ -220,9 +222,24 @@ export const ValidateEmail = ({ {showPricingTable && (
- In order to upload data you need to sign up for a billing plan: -
+ {isReferred ? ( + <> ++ Congratulations! You are eligible for a free trial of our Lite or Business subscriptions. That means + we won't charge you anything today. + If you choose a Lite plan, you will get two months for free! If you choose Business, you will get one month for free! + We do need you to provide a valid credit card before we can start your + trial - pick a plan below and complete the checkout flow to get started! +
++ Please note that after your free trial ends, you will be charged 10 USD per month for Lite or 100 USD per month for Business tier. +
+ > + ) : ( ++ In order to upload data you need to sign up for a billing plan: +
+ )}By registering with Storacha you agree to the Storacha{' '} - Terms of Service. + Terms of Service.