11/* eslint-disable camelcase */
22import * as express from 'express' ;
3- import { InternalServerError } from 'http-errors' ;
4- import { Body , Post , Request , Route , Security } from 'tsoa' ;
3+ import { HttpError , InternalServerError } from 'http-errors' ;
4+ import { Body , Get , Post , Request , Route , Security } from 'tsoa' ;
5+ import * as TipsService from '../business/tips/TipsService' ;
56import { getUserFromRequestOrCreateAndSetCookie } from './auth/userAuthUtils' ;
6- import stripe from './stripe' ;
77
8+ import * as GiftRegistry from '../business/tips/GiftRegistry' ;
9+ import { Gift } from '../business/tips/GiftRegistry' ;
810import * as UserService from '../business/users/UserService' ;
11+ import TipFrequency from '../enum/TipFrequency' ;
912
1013type TipSessionRequest = {
1114 amount : number ;
1215 successUrl : string ;
1316 cancelUrl : string ;
17+ frequency ?: TipFrequency ;
18+ gift ?: Gift ;
1419} ;
1520
21+ type CustomerPortalRequest = {
22+ returnUrl : string ;
23+ } ;
24+
25+ interface GiftApiResponse {
26+ gift : GiftRegistry . Gift ;
27+ minimumAmount : number ;
28+ frequency : TipFrequency ;
29+ }
1630@Route ( 'tips' )
1731export class TipsController {
1832 @Security ( 'user-token' )
@@ -21,48 +35,59 @@ export class TipsController {
2135 @Body ( ) body : TipSessionRequest ,
2236 @Request ( ) req : express . Request
2337 ) : Promise < { sessionId : string } > {
24- const { amount, successUrl, cancelUrl } = body ;
38+ const {
39+ amount,
40+ successUrl,
41+ cancelUrl,
42+ gift,
43+ frequency = TipFrequency . ONCE ,
44+ } = body ;
2545 const userId = await getUserFromRequestOrCreateAndSetCookie ( req ) ;
2646
2747 const user = await UserService . getUser ( userId ) ;
2848
29- const stripeCustomerId : string | undefined =
30- user ?. stripeCustomerId ?? undefined ;
31- const hasExistingCustomer = ! ! stripeCustomerId ;
32-
3349 try {
34- const session = await stripe . checkout . sessions . create ( {
35- cancel_url : cancelUrl ,
36- mode : 'payment' ,
37- success_url : successUrl ,
38- line_items : [
39- {
40- price_data : {
41- currency : 'USD' ,
42- product_data : {
43- name : 'Tip for 1940s.nyc' ,
44- } ,
45- unit_amount : amount ,
46- } ,
47- quantity : 1 ,
48- } ,
49- ] ,
50- metadata : {
51- userId,
52- } ,
53- customer : stripeCustomerId ,
54- // History: Stripe used to always create a customer, then it started creating "guest customers" instead (annoying!). This is to force it to always create a customer which we can attach to the user in the webhook.
55- customer_creation : hasExistingCustomer ? undefined : 'always' ,
56- customer_email : hasExistingCustomer
57- ? undefined
58- : user ?. email ?? undefined ,
59- payment_intent_data : { } ,
50+ const sessionId = await TipsService . createTipCheckoutSession ( {
51+ amountMinorUnits : amount ,
52+ successUrl,
53+ cancelUrl,
54+ user,
55+ frequency,
56+ gift,
6057 } ) ;
6158
62- return { sessionId : session . id } ;
59+ return { sessionId : sessionId } ;
6360 } catch ( err ) {
61+ if ( err instanceof HttpError ) {
62+ throw err ;
63+ }
6464 console . error ( 'Failed to create session' , err ) ;
6565 throw new InternalServerError ( 'Failed to create session' ) ;
6666 }
6767 }
68+
69+ @Get ( '/gifts' )
70+ public getGifts ( ) : GiftApiResponse [ ] {
71+ return GiftRegistry . getAllAvailableGifts ( ) . map ( ( gift ) => {
72+ return {
73+ gift : gift . gift ,
74+ minimumAmount : gift . minimumAmount ,
75+ frequency : gift . frequency ,
76+ } ;
77+ } ) ;
78+ }
79+
80+ @Security ( 'user-token' )
81+ @Post ( '/customer-portal-session' )
82+ public async createCustomerPortalSession (
83+ @Body ( ) { returnUrl } : CustomerPortalRequest ,
84+ @Request ( ) req : express . Request
85+ ) : Promise < { url : string } > {
86+ const userId = await getUserFromRequestOrCreateAndSetCookie ( req ) ;
87+
88+ const user = await UserService . getUser ( userId ) ;
89+ const url = await TipsService . createCustomerPortalSession ( user , returnUrl ) ;
90+
91+ return { url : url } ;
92+ }
6893}
0 commit comments