Skip to content

Commit

Permalink
chore: almost work e2e
Browse files Browse the repository at this point in the history
  • Loading branch information
Nicolas Burtey committed Sep 16, 2023
1 parent b97b9a4 commit 4ad04ee
Show file tree
Hide file tree
Showing 10 changed files with 177 additions and 28 deletions.
29 changes: 23 additions & 6 deletions apps/boltcard/bats/e2e-test.bats
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
load "../../../test/bats/helpers/setup-and-teardown"
load "../../../test/bats/helpers/ln"

@test "auth: create user" {
login_user "alice" "+16505554321" "000000"
}

@test "auth: create card" {
accountId="b12871e9-01e7-4aec-8597-873ebab7df1f"
echo "TOKEN_ALICE=$(read_value "alice")"
export TOKEN_ALICE=$(read_value "alice")

RESPONSE=$(curl -s "http://localhost:3000/createboltcard?accountId=${accountId}")
RESPONSE=$(curl -s "http://localhost:3000/createboltcard?token=${TOKEN_ALICE}")
CALLBACK_URL=$(echo $RESPONSE | jq -r '.url')

# Making the follow-up curl request
Expand All @@ -11,9 +18,7 @@
[[ $(echo $RESPONSE | jq -r '.PROTOCOL_NAME') == "create_bolt_card_response" ]] || exit 1
}


@test "auth: create payment and follow up" {

P_VALUE="4E2E289D945A66BB13377A728884E867"
C_VALUE="E19CCB1FED8892CE"

Expand All @@ -26,6 +31,18 @@
echo "CALLBACK_URL: $CALLBACK_URL"
echo "K1_VALUE: $K1_VALUE"

# Making the follow-up curl request
curl -s "${CALLBACK_URL}?k1=${K1_VALUE}"
cache_value "k1" "$K1_VALUE"
cache_value "CALLBACK_URL" "$CALLBACK_URL"
}

@test "callback" {
K1_VALUE=$(read_value "k1")
CALLBACK_URL=$(read_value "CALLBACK_URL")

invoice_response="$(lnd_outside_2_cli addinvoice --amt 1000)"
payment_request=$(echo $invoice_response | jq -r '.payment_request')
echo $payment_request

result=$(curl -s "${CALLBACK_URL}?k1=${K1_VALUE}&pr=${payment_request}")
[[ result.status == "OK" ]] || exit 1
}
9 changes: 9 additions & 0 deletions apps/boltcard/bats/gql/user-login.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
mutation userLogin($input: UserLoginInput!) {
userLogin(input: $input) {
errors {
code
message
}
authToken
}
}
12 changes: 12 additions & 0 deletions apps/boltcard/bats/gql/wallets-for-account.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
query me {
me {
defaultAccount {
id
wallets {
id
walletCurrency
balance
}
}
}
}
Binary file modified apps/boltcard/bun.lockb
Binary file not shown.
114 changes: 109 additions & 5 deletions apps/boltcard/callback.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,75 @@
import express from "express"

import { gql, GraphQLClient } from "graphql-request"

import { boltcardRouter } from "./router"
import { fetchByK1 } from "./knex"
import { fetchByCardId, fetchByK1 } from "./knex"
import { apiUrl } from "./config"

type GetUsdWalletIdQuery = {
readonly __typename: "Query"
readonly me?: {
readonly __typename: "User"
readonly defaultAccount: {
readonly __typename: "ConsumerAccount"
readonly id: string
readonly defaultWalletId: string
readonly wallets: ReadonlyArray<
| {
readonly __typename: "BTCWallet"
readonly id: string
readonly walletCurrency: WalletCurrency
readonly balance: number
}
| {
readonly __typename: "UsdWallet"
readonly id: string
readonly walletCurrency: WalletCurrency
readonly balance: number
}
>
}
} | null
}

type LnInvoicePaymentSendMutation = {
readonly __typename: "Mutation"
readonly lnInvoicePaymentSend: {
readonly __typename: "PaymentSendPayload"
readonly status?: string | null
readonly errors: ReadonlyArray<{
readonly __typename: "GraphQLApplicationError"
readonly message: string
}>
}
}

const getUsdWalletIdQuery = gql`
query getUsdWalletId {
me {
defaultAccount {
id
defaultWalletId
wallets {
id
walletCurrency
balance
}
}
}
}
`

const lnInvoicePaymentSendMutation = gql`
mutation lnInvoicePaymentSend($input: LnInvoicePaymentInput!) {
lnInvoicePaymentSend(input: $input) {
errors {
message
}
status
}
}
`

boltcardRouter.get("/callback", async (req: express.Request, res: express.Response) => {
const k1 = req?.query?.k1
Expand All @@ -20,11 +88,47 @@ boltcardRouter.get("/callback", async (req: express.Request, res: express.Respon
}

const payment = await fetchByK1(k1)
console.log(payment)
// fetch user from k1
// payInvoice(pr)
const { cardId } = payment

const card = await fetchByCardId(cardId)

const graphQLClient = new GraphQLClient(apiUrl, {
headers: {
authorization: `Bearer ${card.token}`,
},
})

const data = await graphQLClient.request<GetUsdWalletIdQuery>(getUsdWalletIdQuery)
const wallets = data.me?.defaultAccount.wallets

res.json({ status: "OK" })
if (!wallets) {
res.status(400).send({ status: "ERROR", reason: "no wallets found" })
return
}

const usdWallet = wallets.find((wallet) => wallet.walletCurrency === "USD")
const usdWalletId = usdWallet?.id

console.log({ usdWallet, wallets })

if (!usdWalletId) {
res.status(400).send({ status: "ERROR", reason: "no usd wallet found" })
return
}

const result = await graphQLClient.request<LnInvoicePaymentSendMutation>({
document: lnInvoicePaymentSendMutation,
variables: { input: { walletId: usdWalletId, paymentRequest: pr } },
})

if (result.lnInvoicePaymentSend.status === "SUCCESS") {
res.json({ status: "OK" })
} else {
res.status(400).send({
status: "ERROR",
reason: `payment failed: ${result.lnInvoicePaymentSend.errors[0].message}`,
})
}
})

const callback = "dummy"
Expand Down
2 changes: 2 additions & 0 deletions apps/boltcard/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export const serverUrl = process.env.SERVER_URL ?? "http://localhost:3000"

export const apiUrl = process.env.API_URL ?? "http://localhost:4002/graphql"

const AES_DECRYPT_KEY = process.env.AES_DECRYPT_KEY ?? "0c3b25d92b38ae443229dd59ad34b85d"

export const aesDecryptKey = Buffer.from(AES_DECRYPT_KEY, "hex")
17 changes: 9 additions & 8 deletions apps/boltcard/knex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ export async function createTable() {

// if a card is resetted, the uid would stay the same
table.string("uid").notNullable().index()
table.uuid("accountId").notNullable().index()
table.string("token").notNullable()

table.integer("ctr").notNullable()
table.boolean("enabled").notNullable().defaultTo(true)

Expand All @@ -59,7 +60,7 @@ export async function createTable() {
table.string("oneTimeCode").notNullable().index().unique()
table.timestamp("created_at").defaultTo(knex.fn.now())
table.string("status").defaultTo("init") // init, fetched, used
table.uuid("accountId").notNullable()
table.string("token").notNullable()

table.string("k0AuthKey").notNullable()
table.string("k2CmacKey").notNullable().index().unique()
Expand Down Expand Up @@ -94,7 +95,7 @@ export async function fetchByCardId(cardId: string) {

export interface CardInitInput {
oneTimeCode: string
accountId: string
token: string
k0AuthKey: string
k2CmacKey: string
k3: string
Expand All @@ -103,15 +104,15 @@ export interface CardInitInput {

export async function createCardInit(cardData: CardInitInput) {
try {
const { oneTimeCode, k0AuthKey, k2CmacKey, k3, k4, accountId } = cardData
const { oneTimeCode, k0AuthKey, k2CmacKey, k3, k4, token } = cardData

const result = await knex("CardInit").insert({
oneTimeCode,
k0AuthKey,
k2CmacKey,
k3,
k4,
accountId,
token,
})

return result
Expand All @@ -128,12 +129,12 @@ interface CardInput {
k3: string
k4: string
ctr: number
accountId: string
token: string
}

export async function createCard(cardData: CardInput) {
try {
const { uid, k0AuthKey, k2CmacKey, k3, k4, ctr, accountId } = cardData
const { uid, k0AuthKey, k2CmacKey, k3, k4, ctr, token } = cardData

const [result] = await knex("Card")
.insert({
Expand All @@ -143,7 +144,7 @@ export async function createCard(cardData: CardInput) {
k3,
k4,
ctr,
accountId,
token,
})
.returning("*")

Expand Down
4 changes: 2 additions & 2 deletions apps/boltcard/lnurlw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ boltcardRouter.get("/ln", async (req: express.Request, res: express.Response) =>
const result = await maybeSetupCard({ uidRaw, ctrRawInverseBytes, ba_c })

if (result) {
const { k0AuthKey, k2CmacKey, k3, k4, accountId } = result
const { k0AuthKey, k2CmacKey, k3, k4, token } = result
await markCardInitAsUsed(k2CmacKey)

card = await createCard({ uid, k0AuthKey, k2CmacKey, k3, k4, ctr, accountId })
card = await createCard({ uid, k0AuthKey, k2CmacKey, k3, k4, ctr, token })
} else {
res.status(400).send({ status: "ERROR", reason: "card not found" })
return
Expand Down
16 changes: 9 additions & 7 deletions apps/boltcard/new.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,21 @@ function randomHex(): string {
boltcardRouter.get(
"/createboltcard",
async (req: express.Request, res: express.Response) => {
const accountId = req.query.accountId
// should be pass with POST? not sure if this would be compatible
// with the wallet that can create cards
const token = req.query.token

if (!accountId) {
res.status(400).send({ status: "ERROR", reason: "accountId missing" })
if (!token) {
res.status(400).send({ status: "ERROR", reason: "token missing" })
return
}

if (typeof accountId !== "string") {
res.status(400).send({ status: "ERROR", reason: "accountId is not a string" })
if (typeof token !== "string") {
res.status(400).send({ status: "ERROR", reason: "token is not a string" })
return
}

// TODO: accountId uuid validation
// TODO: token validation?

const oneTimeCode = randomHex()
const k0AuthKey = "0c3b25d92b38ae443229dd59ad34b85d"
Expand All @@ -49,7 +51,7 @@ boltcardRouter.get(
k2CmacKey,
k3,
k4,
accountId,
token,
})

if (result instanceof Error) {
Expand Down
2 changes: 2 additions & 0 deletions apps/boltcard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"aes-js": "^3.1.2",
"body-parser": "^1.20.2",
"express": "^4.18.2",
"graphql": "^16.8.0",
"graphql-request": "^6.1.0",
"knex": "^2.5.1",
"node-aes-cmac": "^0.1.1",
"pg": "^8.11.3"
Expand Down

0 comments on commit 4ad04ee

Please sign in to comment.