import { Product , Payment } from '../../lib/models.js' ;
import { API , developer _admin } from "../../lib/api.js" ;
import logging from '../../lib/logging.js' ;
import dayjs from 'dayjs' ;
import { product _id , register _enabled } from "../../client/config.js" ;
import assert from 'assert' ;
import * as queues from "../../lib/queues.js" ;
import stripe _create from "stripe" ;
import { stripe _private } from "../../lib/config.js" ;
const log = logging . create ( "api/payments/stripe.js" ) ;
const stripe = stripe _create ( stripe _private . secret ) ;
const product = await Product . first ( { id : product _id } ) ;
const rules = {
"payment_intent" : "required" ,
"payment_intent_client_secret" : "required" ,
"redirect_status" : "required"
} ;
const create _stripe _payment = async ( user ) => {
assert ( product . price > 1 , "Stripe requires the price to be greater than $1." ) ;
const intent = await stripe . paymentIntents . create ( {
amount : product . price * 100 ,
currency : product . currency . toLowerCase ( ) ,
automatic _payment _methods : {
enabled : true ,
} ,
} ) ;
assert ( intent , "Failed to create stripe intent." ) ;
let internal _id = Payment . gen _internal _id ( ) ;
// TODO: see if we can use date-fns instead of dayjs here
let payment = await Payment . insert ( {
user _id : user . id ,
system : 'stripe' ,
status : 'pending' ,
status _reason : "started" ,
internal _id ,
sys _primary _id : intent . id ,
sys _secondary _id : intent . client _secret ,
sys _created _on : new Date ( )
} ) ;
assert ( payment && payment . internal _id , "Failed to save payment." ) ;
return [ intent , payment ] ;
}
export const post = async ( req , res ) => {
const api = new API ( req , res ) ;
if ( ! register _enabled ) {
return api . error ( 500 , { errors : { main : "Registration is disabled." } } ) ;
}
try {
let clientSecret ;
const pending = await Payment . first ( { user _id : api . user . id , status : "pending" , system : "stripe" } ) ;
if ( pending ) {
// this already exists so send back the original secret
clientSecret = pending . sys _secondary _id ;
} else {
// this is a new request so create it
const [ intent , payment ] = await create _stripe _payment ( api . user ) ;
clientSecret = intent . client _secret ;
}
api . reply ( 200 , { clientSecret } ) ;
} catch ( error ) {
log . error ( error ) ;
api . error ( 500 , "Fatal error in the server. Tell Zed." ) ;
}
}
post . authenticated = true ;
export const get = async ( req , res ) => {
const api = new API ( req , res ) ;
if ( ! register _enabled ) {
return api . error ( 500 , { errors : { main : "Registration is disabled." } } ) ;
}
try {
const msg = api . validate ( rules ) ;
if ( ! msg . _valid ) {
log . error ( msg . _errors , "Stripe validation failure" ) ;
return api . validation _error ( res , msg ) ;
} if ( msg . redirect _status === "succeeded" ) {
// find the user's payment attempt using the stripe id
let payment = await Payment . first ( {
user _id : api . user . id ,
system : "stripe" ,
sys _primary _id : msg . payment _intent
} ) ;
// redirect status is succeeded so this is complete
await Payment . update ( { id : payment . id } , { status : "complete" } ) ;
// send out all of the emails and everything
queues . send _welcome ( api . user ) ;
queues . send _receipt ( api . user , payment . id , product . id ) ;
// downside to stripe is they do a redirect to finish
return api . redirect ( "/client/#/payment/finished/" ) ;
} else {
// need to handle the other stripe redirect states
log . debug ( "STRIPE ERROR" , msg ) ;
return api . error ( 402 , ` Stripe error: ${ JSON . stringify ( msg ) } ` ) ;
}
} catch ( error ) {
log . error ( error ) ;
return api . error ( 403 , 'Stripe configuration failed' ) ;
}
}
get . authenticated = true ;