basic working payments

This commit is contained in:
mantikoros 2021-12-19 16:23:15 -06:00
parent 0a4e05ad32
commit ad7be5449d
6 changed files with 126 additions and 2 deletions

View File

@ -16,7 +16,8 @@
"fetch": "1.1.0",
"firebase-admin": "10.0.0",
"firebase-functions": "3.16.0",
"lodash": "4.17.21"
"lodash": "4.17.21",
"stripe": "8.194.0"
},
"devDependencies": {
"firebase-functions-test": "0.3.3",

View File

@ -5,3 +5,4 @@ admin.initializeApp()
export * from './keep-awake'
export * from './place-bet'
export * from './resolve-market'
export * from './stripe'

View File

@ -101,7 +101,7 @@ const getPayouts = (outcome: string, contract: Contract, bets: Bet[]) => {
]) // add creator fee
}
const payUser = ([userId, payout]: [string, number]) => {
export const payUser = ([userId, payout]: [string, number]) => {
return firestore.runTransaction(async (transaction) => {
const userDoc = firestore.doc(`users/${userId}`)
const userSnap = await transaction.get(userDoc)

78
functions/src/stripe.ts Normal file
View File

@ -0,0 +1,78 @@
import * as functions from 'firebase-functions'
import Stripe from 'stripe'
import { payUser } from './resolve-market'
const stripe = new Stripe(functions.config().stripe.apikey, {
apiVersion: '2020-08-27',
typescript: true,
})
const manticDollarStripePrice = {
500: 'price_1K8W10GdoFKoCJW7KWORLec1',
}
export const createCheckoutSession = functions
.runWith({ minInstances: 1 })
.https.onRequest(async (req, res) => {
const userId = req.query.userId?.toString()
const manticDollarQuantity =
req.query.manticDollarQuantity?.toString() || '500'
if (!userId) {
res.send('Invalid user ID')
return
}
const referrer = req.headers.referer || 'https://mantic.markets'
const session = await stripe.checkout.sessions.create({
metadata: {
userId,
manticDollarQuantity,
},
line_items: [
{
price: manticDollarStripePrice[500],
quantity: 1,
},
],
mode: 'payment',
success_url: `${referrer}/?success=true`,
cancel_url: `${referrer}/?success=false`,
})
res.redirect(303, session.url || '')
})
export const stripeWebhook = functions
.runWith({ minInstances: 1 })
.https.onRequest(async (req, res) => {
let event
try {
event = stripe.webhooks.constructEvent(
req.rawBody,
req.headers['stripe-signature'] as string,
functions.config().stripe.webhooksecret
)
} catch (e: any) {
console.log(`Webhook Error: ${e.message}`)
res.status(400).send(`Webhook Error: ${e.message}`)
return
}
if (event.type === 'checkout.session.completed') {
const session = event.data.object as any
issueMoneys(session)
}
res.status(200)
})
const issueMoneys = async (session: any) => {
const { userId, manticDollarQuantity } = session.metadata
const payout = Number.parseInt(manticDollarQuantity)
return await payUser([userId, payout])
}

View File

@ -0,0 +1,40 @@
import clsx from 'clsx'
import { useUser } from '../hooks/use-user'
export function AddFundsButton(props: {}) {
const {} = props
const user = useUser()
return (
<>
<label htmlFor="add-funds" className={clsx('btn modal-button')}>
Add funds
</label>
<input type="checkbox" id="add-funds" className="modal-toggle" />
<div className="modal">
<div className="modal-box">
Buy M$500
<div className="modal-action">
<label htmlFor="add-funds" className={clsx('btn')}>
Back
</label>
<form action={checkoutURL(user?.id || '', 500)} method="POST">
<button type="submit" className="btn">
Checkout
</button>
</form>
</div>
</div>
</div>
</>
)
}
const checkoutURL = (userId: string, manticDollarQuantity: number) => {
const endpoint =
'https://us-central1-mantic-markets.cloudfunctions.net/createCheckoutSession'
return `${endpoint}?userId=${userId}&manticDollarQuantity=${manticDollarQuantity}`
}

View File

@ -16,6 +16,7 @@ import {
getProbabilityAfterBet,
} from '../lib/calculation/contract'
import { firebaseLogin } from '../lib/firebase/users'
import { AddFundsButton } from './add-funds-button'
export function BetPanel(props: { contract: Contract; className?: string }) {
const { contract, className } = props
@ -136,6 +137,9 @@ export function BetPanel(props: { contract: Contract; className?: string }) {
Remaining balance
</div>
<div>{formatMoney(remainingBalance > 0 ? remainingBalance : 0)}</div>
<div>
<AddFundsButton />
</div>
</>
)}