Skip to content
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

feat: implement "create admin session" capability #358

Closed
wants to merge 10 commits into from
11 changes: 10 additions & 1 deletion billing/utils/stripe.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ import * as DidMailto from '@web3-storage/did-mailto'
* @typedef {import('stripe').Stripe.CustomerSubscriptionCreatedEvent} CustomerSubscriptionCreatedEvent
*/

/**
*
* @param {string} stripeID
* @returns {AccountID}
*/
export function stripeIDToAccountID(stripeID) {
return /** @type {AccountID} */(`stripe:${stripeID}`)
}

/**
*
* @param {Stripe} stripe
Expand All @@ -21,7 +30,7 @@ export async function handleCustomerSubscriptionCreated(stripe, event, customerS
return { error: new Error(`Invalid product: ${product}`) }
}

const account = /** @type {AccountID} */ (`stripe:${customerId}`)
const account = stripeIDToAccountID(customerId)
const stripeCustomer = await stripe.customers.retrieve(customerId)
if (stripeCustomer.deleted) {
return {
Expand Down
127 changes: 55 additions & 72 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 26 additions & 2 deletions upload-api/billing.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,11 @@ export class BillingProviderUpdateError extends Failure {

/**
*
* @param {import('stripe').Stripe} stripe
* @param {import('stripe').Stripe} stripe
* @param {import("@web3-storage/w3infra-billing/lib/api").CustomerStore} customerStore
* @returns {import("./types").BillingProvider}
*/
export function createStripeBillingProvider(stripe) {
export function createStripeBillingProvider(stripe, customerStore) {
return {
async hasCustomer(customer) {
const customersResponse = await stripe.customers.list({ email: toEmail(/** @type {import('@web3-storage/did-mailto').DidMailto} */(customer)) })
Expand Down Expand Up @@ -84,6 +85,29 @@ export function createStripeBillingProvider(stripe) {
} catch (/** @type {any} */ err) {
return { error: new BillingProviderUpdateError(err.message, { cause: err }) }
}
},

async createAdminSession(account, returnURL) {
const response = await customerStore.get({ customer: account })
if (response.error) {
return {
error: {
name: 'CustomerNotFound',
message: 'Error getting customer',
cause: response.error
}
}
}
const customer = response.ok.account.slice('stripe:'.length)
const session = await stripe.billingPortal.sessions.create({
customer,
return_url: returnURL
})
return {
ok: {
url: session.url
}
}
}
}
}
2 changes: 1 addition & 1 deletion upload-api/functions/ucan-invocation-router.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ export async function ucanInvocationRouter(request) {
const customerStore = createCustomerStore({ region: AWS_REGION }, { tableName: customerTableName })
if (!STRIPE_SECRET_KEY) throw new Error('missing secret: STRIPE_SECRET_KEY')
const stripe = new Stripe(STRIPE_SECRET_KEY, { apiVersion: '2023-10-16' })
const plansStorage = usePlansStore(customerStore, createStripeBillingProvider(stripe))
const plansStorage = usePlansStore(customerStore, createStripeBillingProvider(stripe, customerStore))
const rateLimitsStorage = createRateLimitTable(AWS_REGION, rateLimitTableName)
const spaceMetricsTable = createSpaceMetricsTable(AWS_REGION, spaceMetricsTableName)

Expand Down
4 changes: 1 addition & 3 deletions upload-api/stores/plans.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,6 @@ export function usePlansStore(customerStore, billingProvider) {
return { ok: {} }
},

createAdminSession: (account, returnURL) => {
throw new Error('not implemented')
}
createAdminSession: async (account, returnURL) => billingProvider.createAdminSession(account, returnURL)
}
}
Loading
Loading