basic working payments
This commit is contained in:
parent
0a4e05ad32
commit
ad7be5449d
|
@ -16,7 +16,8 @@
|
||||||
"fetch": "1.1.0",
|
"fetch": "1.1.0",
|
||||||
"firebase-admin": "10.0.0",
|
"firebase-admin": "10.0.0",
|
||||||
"firebase-functions": "3.16.0",
|
"firebase-functions": "3.16.0",
|
||||||
"lodash": "4.17.21"
|
"lodash": "4.17.21",
|
||||||
|
"stripe": "8.194.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"firebase-functions-test": "0.3.3",
|
"firebase-functions-test": "0.3.3",
|
||||||
|
|
|
@ -5,3 +5,4 @@ admin.initializeApp()
|
||||||
export * from './keep-awake'
|
export * from './keep-awake'
|
||||||
export * from './place-bet'
|
export * from './place-bet'
|
||||||
export * from './resolve-market'
|
export * from './resolve-market'
|
||||||
|
export * from './stripe'
|
||||||
|
|
|
@ -101,7 +101,7 @@ const getPayouts = (outcome: string, contract: Contract, bets: Bet[]) => {
|
||||||
]) // add creator fee
|
]) // add creator fee
|
||||||
}
|
}
|
||||||
|
|
||||||
const payUser = ([userId, payout]: [string, number]) => {
|
export const payUser = ([userId, payout]: [string, number]) => {
|
||||||
return firestore.runTransaction(async (transaction) => {
|
return firestore.runTransaction(async (transaction) => {
|
||||||
const userDoc = firestore.doc(`users/${userId}`)
|
const userDoc = firestore.doc(`users/${userId}`)
|
||||||
const userSnap = await transaction.get(userDoc)
|
const userSnap = await transaction.get(userDoc)
|
||||||
|
|
78
functions/src/stripe.ts
Normal file
78
functions/src/stripe.ts
Normal 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])
|
||||||
|
}
|
40
web/components/add-funds-button.tsx
Normal file
40
web/components/add-funds-button.tsx
Normal 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}`
|
||||||
|
}
|
|
@ -16,6 +16,7 @@ import {
|
||||||
getProbabilityAfterBet,
|
getProbabilityAfterBet,
|
||||||
} from '../lib/calculation/contract'
|
} from '../lib/calculation/contract'
|
||||||
import { firebaseLogin } from '../lib/firebase/users'
|
import { firebaseLogin } from '../lib/firebase/users'
|
||||||
|
import { AddFundsButton } from './add-funds-button'
|
||||||
|
|
||||||
export function BetPanel(props: { contract: Contract; className?: string }) {
|
export function BetPanel(props: { contract: Contract; className?: string }) {
|
||||||
const { contract, className } = props
|
const { contract, className } = props
|
||||||
|
@ -136,6 +137,9 @@ export function BetPanel(props: { contract: Contract; className?: string }) {
|
||||||
Remaining balance
|
Remaining balance
|
||||||
</div>
|
</div>
|
||||||
<div>{formatMoney(remainingBalance > 0 ? remainingBalance : 0)}</div>
|
<div>{formatMoney(remainingBalance > 0 ? remainingBalance : 0)}</div>
|
||||||
|
<div>
|
||||||
|
<AddFundsButton />
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user