mirror of
https://github.com/stronk-dev/RandomChad.git
synced 2025-07-05 02:35:08 +02:00
✨ secret merch tool in production, use at own risk if you manage to find it
This commit is contained in:
parent
72dd09d684
commit
64a2a59311
Binary file not shown.
Before Width: | Height: | Size: 559 KiB |
Binary file not shown.
Before Width: | Height: | Size: 597 KiB |
@ -29,12 +29,12 @@ app.put( '/api/rocketeer/:id/outfits', setPrimaryOutfit )
|
|||||||
/* ///////////////////////////////
|
/* ///////////////////////////////
|
||||||
// Notification API
|
// Notification API
|
||||||
// /////////////////////////////*/
|
// /////////////////////////////*/
|
||||||
app.post( '/api/notifications/:address', ( req, res ) => order_merch( req.body ) )
|
app.post( '/api/notifications/:address', subscribe_address_to_notifications )
|
||||||
|
|
||||||
/* ///////////////////////////////
|
/* ///////////////////////////////
|
||||||
// Merch API
|
// Merch API
|
||||||
// /////////////////////////////*/
|
// /////////////////////////////*/
|
||||||
app.post( '/api/merch/order', subscribe_address_to_notifications )
|
app.post( '/api/merch/order', order_merch )
|
||||||
|
|
||||||
// ///////////////////////////////
|
// ///////////////////////////////
|
||||||
// Static collection data
|
// Static collection data
|
||||||
|
@ -9,15 +9,17 @@ const fetch = require( 'isomorphic-fetch' )
|
|||||||
// /////////////////////////////*/
|
// /////////////////////////////*/
|
||||||
async function call_printapi( endpoint, data, method='POST', format='json', authenticated=true ) {
|
async function call_printapi( endpoint, data, method='POST', format='json', authenticated=true ) {
|
||||||
|
|
||||||
|
const logs = []
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
log( `Call requested: ${method}/${format} ${endpoint} with `, data )
|
logs.push( `Call requested: ${method}/${format} ${endpoint} with `, JSON.stringify( data ) )
|
||||||
|
|
||||||
// Format url, if it has https use the link as provided
|
// Format url, if it has https use the link as provided
|
||||||
const url = endpoint.includes( 'https://' ) ? endpoint : `${ printapi.base_url }${ endpoint }`
|
const url = endpoint.includes( 'https://' ) ? endpoint : `${ printapi.base_url }${ endpoint }`
|
||||||
|
|
||||||
const access_token = authenticated && await get_auth_token()
|
const access_token = authenticated && await get_auth_token()
|
||||||
if( authenticated ) log( `Found access token: `, access_token.slice( 0, 10 ) )
|
if( authenticated ) log( `Found access token: `, access_token && access_token.slice( 0, 10 ) )
|
||||||
if( authenticated && !access_token ) throw new Error( `No access_token found` )
|
if( authenticated && !access_token ) throw new Error( `No access_token found` )
|
||||||
|
|
||||||
// Generate headers based on input
|
// Generate headers based on input
|
||||||
@ -26,7 +28,7 @@ async function call_printapi( endpoint, data, method='POST', format='json', auth
|
|||||||
...( format == 'form' && data && { 'Content-Type': 'application/x-www-form-urlencoded' } ),
|
...( format == 'form' && data && { 'Content-Type': 'application/x-www-form-urlencoded' } ),
|
||||||
...( authenticated && { Authorization: `Bearer ${ access_token }` } )
|
...( authenticated && { Authorization: `Bearer ${ access_token }` } )
|
||||||
}
|
}
|
||||||
log( `Headers `, headers )
|
logs.push( `Headers `, JSON.stringify( headers ) )
|
||||||
// Generate data body
|
// Generate data body
|
||||||
let body = {}
|
let body = {}
|
||||||
|
|
||||||
@ -36,17 +38,39 @@ async function call_printapi( endpoint, data, method='POST', format='json', auth
|
|||||||
// Formdata being formdata
|
// Formdata being formdata
|
||||||
if( format == 'form' ) body = new URLSearchParams( data )
|
if( format == 'form' ) body = new URLSearchParams( data )
|
||||||
|
|
||||||
|
logs.push( `API request data `, body )
|
||||||
|
|
||||||
// Focmat fetch options
|
// Focmat fetch options
|
||||||
const options = {
|
const options = {
|
||||||
method,
|
method,
|
||||||
headers,
|
headers,
|
||||||
body
|
body
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call api
|
// Call api
|
||||||
log( `Calling ${ url }`, )
|
logs.push( `Calling ${ url }`, )
|
||||||
const response = await fetch( url, options ).then( res => res.json( ) )
|
const response = await fetch( url, options ).then( async res => {
|
||||||
log( `Received `, response )
|
|
||||||
|
const json_res = res.clone()
|
||||||
|
const text_res = res.clone()
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const json_response = await json_res.json()
|
||||||
|
logs.push( `API json response: `, json_response )
|
||||||
|
return json_response
|
||||||
|
|
||||||
|
} catch( e ) {
|
||||||
|
|
||||||
|
const text_response = await text_res.text()
|
||||||
|
logs.push( `API text response: `, text_response )
|
||||||
|
throw new Error( `Non JSON output from API` )
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} )
|
||||||
|
logs.push( `Production call: `, JSON.stringify( { ...headers, ...body } ) )
|
||||||
|
logs.push( `Received `, JSON.stringify( response ) )
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@ -54,7 +78,8 @@ async function call_printapi( endpoint, data, method='POST', format='json', auth
|
|||||||
|
|
||||||
console.error( `Error calling printapi: `, e )
|
console.error( `Error calling printapi: `, e )
|
||||||
return {
|
return {
|
||||||
error: e.message
|
error: e.message,
|
||||||
|
tracelog: logs
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -64,41 +89,49 @@ async function call_printapi( endpoint, data, method='POST', format='json', auth
|
|||||||
async function get_auth_token( ) {
|
async function get_auth_token( ) {
|
||||||
|
|
||||||
const token_grace_period = 1000 * 60
|
const token_grace_period = 1000 * 60
|
||||||
|
const logs = []
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// Get cached token
|
// Get cached token
|
||||||
let { expires=0, access_token } = await db.collection( 'secrets' ).doc( 'printapi' ).get( ).then( dataFromSnap )
|
let { expires=0, access_token } = await db.collection( 'secrets' ).doc( 'printapi' ).get( ).then( dataFromSnap )
|
||||||
log( `Old access token: `, access_token && access_token.slice( 0, 10 ) )
|
logs.push( `Old access token: `, access_token && access_token.slice( 0, 10 ) )
|
||||||
|
|
||||||
// If token is still valid
|
// If token is still valid
|
||||||
if( ( expires - token_grace_period ) > Date.now() ) {
|
if( ( expires - token_grace_period ) > Date.now() ) {
|
||||||
log( `Old access token still valid` )
|
logs.push( `Old access token still valid` )
|
||||||
return access_token
|
return access_token
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grab new token and save it
|
// Grab new token and save it
|
||||||
log( `Requesting new token` )
|
logs.push( `Requesting new token` )
|
||||||
const credentials = {
|
const credentials = {
|
||||||
grant_type: 'client_credentials',
|
grant_type: 'client_credentials',
|
||||||
client_id: printapi.client_credentials,
|
client_id: printapi.client_credentials,
|
||||||
client_secret: printapi.client_secret
|
client_secret: printapi.client_secret
|
||||||
}
|
}
|
||||||
const { access_token: new_access_token, expires_in } = await call_printapi( `/v2/oauth`, credentials, 'POST', 'form', false )
|
const { access_token: new_access_token, expires_in, ...errors } = await call_printapi( `/v2/oauth`, credentials, 'POST', 'form', false )
|
||||||
|
|
||||||
log( `New access token: `, new_access_token && new_access_token.slice( 0, 10 ) )
|
logs.push( `New access token: `, new_access_token && new_access_token.slice( 0, 10 ) )
|
||||||
|
if( errors ) logs.push( `Access token error: `, errors )
|
||||||
|
if( !new_access_token ) throw new Error( `No access token available` )
|
||||||
|
|
||||||
|
// Write new access token to cache
|
||||||
await db.collection( 'secrets' ).doc( 'printapi' ).set( {
|
await db.collection( 'secrets' ).doc( 'printapi' ).set( {
|
||||||
access_token: new_access_token,
|
access_token: new_access_token,
|
||||||
// expires_in is in seconds
|
// expires_in is in seconds
|
||||||
expires: Date.now() + ( expires_in * 1000 )
|
expires: Date.now() + ( expires_in * 1000 )
|
||||||
}, { merge: true } )
|
}, { merge: true } )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return new_access_token
|
return new_access_token
|
||||||
|
|
||||||
|
|
||||||
} catch( e ) {
|
} catch( e ) {
|
||||||
|
|
||||||
console.error( `Error getting auth token `, e )
|
console.error( `Error getting auth token `, e )
|
||||||
|
console.log( 'Access token error: ', JSON.stringify( logs ) )
|
||||||
return false
|
return false
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -124,6 +157,8 @@ async function make_printapi_order ( { image_url, product_id, quantity=1, addre
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
const logs = []
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// Validations
|
// Validations
|
||||||
@ -144,18 +179,30 @@ async function make_printapi_order ( { image_url, product_id, quantity=1, addre
|
|||||||
address
|
address
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log( `Creating order: `, order )
|
logs.push( `Creating order: `, order )
|
||||||
const { checkout, ...details } = await call_printapi( `/v2/orders`, order )
|
const { checkout, error: order_error, ...order_details } = await call_printapi( `/v2/orders`, order )
|
||||||
log( `Order made with `, checkout, details )
|
logs.push( `Order made with `, checkout, order_details )
|
||||||
|
|
||||||
|
if( order_error ) {
|
||||||
|
logs.push( `Order errored with `, order_error )
|
||||||
|
throw new Error( order_error )
|
||||||
|
}
|
||||||
|
|
||||||
// Generate pament link
|
// Generate pament link
|
||||||
const { paymentUrl, amount } = await call_printapi( checkout.setupUrl, {
|
const { error: checkout_error, paymentUrl, amount, ...checkout_details } = await call_printapi( checkout.setupUrl, {
|
||||||
billing: {
|
billing: {
|
||||||
address
|
address
|
||||||
},
|
},
|
||||||
returnUrl: `https://tools.rocketeer.fans/#/merch/success/${ details.id }`
|
returnUrl: `https://tools.rocketeer.fans/#/merch/success/${ order_details.id }`
|
||||||
} )
|
} )
|
||||||
|
|
||||||
|
logs.push( `Checkout responded with`, paymentUrl, amount, checkout_details )
|
||||||
|
|
||||||
|
if( checkout_error ) {
|
||||||
|
logs.push( `Checkout errored with `, checkout_error )
|
||||||
|
throw new Error( checkout_error )
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
paymentUrl,
|
paymentUrl,
|
||||||
amount
|
amount
|
||||||
@ -164,7 +211,8 @@ async function make_printapi_order ( { image_url, product_id, quantity=1, addre
|
|||||||
} catch( e ) {
|
} catch( e ) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
error: e.message
|
error: e.message,
|
||||||
|
tracelog: logs
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -173,16 +221,21 @@ async function make_printapi_order ( { image_url, product_id, quantity=1, addre
|
|||||||
|
|
||||||
exports.order_merch = async ( req, res ) => {
|
exports.order_merch = async ( req, res ) => {
|
||||||
|
|
||||||
|
const logs = []
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
logs.push( `Making API request based on body: `, req.body )
|
||||||
const { error, ...order } = await make_printapi_order( req.body )
|
const { error, ...order } = await make_printapi_order( req.body )
|
||||||
|
logs.push( `Received: `, error, order )
|
||||||
if( error ) throw new Error( error )
|
if( error ) throw new Error( error )
|
||||||
|
|
||||||
return res.json( order )
|
return res.json( { ...order, tracelog: logs } )
|
||||||
|
|
||||||
} catch( e ) {
|
} catch( e ) {
|
||||||
return res.json( {
|
return res.json( {
|
||||||
error: e.message
|
error: e.message,
|
||||||
|
tracelog: logs
|
||||||
} )
|
} )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 590 KiB |
@ -23,7 +23,7 @@ export default function Verifier() {
|
|||||||
const [ rocketeer, setRocketeer ] = useState( )
|
const [ rocketeer, setRocketeer ] = useState( )
|
||||||
const [ order, setOrder ] = useState( { } )
|
const [ order, setOrder ] = useState( { } )
|
||||||
const [ payment, setPayment ] = useState( )
|
const [ payment, setPayment ] = useState( )
|
||||||
const { rocketeer_id } = useParams()
|
const { rocketeer_id, order_id } = useParams()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const rocketeers = useRocketeers( rocketeer_id )
|
const rocketeers = useRocketeers( rocketeer_id )
|
||||||
const [ loading, setLoading ] = useState( )
|
const [ loading, setLoading ] = useState( )
|
||||||
@ -83,6 +83,7 @@ export default function Verifier() {
|
|||||||
product_id: 'kurk_20x20'
|
product_id: 'kurk_20x20'
|
||||||
} )
|
} )
|
||||||
|
|
||||||
|
log( `API responded with `, error, pending_order )
|
||||||
if( error ) throw new Error( error )
|
if( error ) throw new Error( error )
|
||||||
|
|
||||||
log( `Order created: `, pending_order )
|
log( `Order created: `, pending_order )
|
||||||
@ -107,6 +108,11 @@ export default function Verifier() {
|
|||||||
|
|
||||||
if( loading ) return <Loading message={ loading } />
|
if( loading ) return <Loading message={ loading } />
|
||||||
|
|
||||||
|
if( order_id ) return <Container align='flex-start'>
|
||||||
|
<H1>Order { order_id } confirmed</H1>
|
||||||
|
<Text>Write that number down in case of apocalypse. You'll be kept up to date via email. Emails might be in Dutch. Sorry, not sorry.</Text>
|
||||||
|
</Container>
|
||||||
|
|
||||||
if( payment ) return <Container align='flex-start'>
|
if( payment ) return <Container align='flex-start'>
|
||||||
|
|
||||||
<H1>Payment link generated</H1>
|
<H1>Payment link generated</H1>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user