Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin' into 1626-cards-wallet-backend-…
Browse files Browse the repository at this point in the history
…view-card-details
  • Loading branch information
sanducb committed Sep 25, 2024
2 parents 15b2335 + 2bd0b31 commit a81ea9f
Show file tree
Hide file tree
Showing 57 changed files with 998 additions and 1,274 deletions.
13 changes: 3 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Please read the [contribution guidelines](.github/contributing.md) before submit

- [Docker](https://docs.docker.com/get-docker/)
- [NVM](https://github.com/nvm-sh/nvm)
- [Rapyd](https://www.rapyd.net) account in Sandbox mode
- [GateHub](https://sandbox.gatehub.net) account in Sandbox mode

### Environment Setup

Expand Down Expand Up @@ -86,21 +86,14 @@ cp ./docker/dev/.env.example ./docker/dev/.env
```

Using your preferred text editor, open the `./docker/dev/.env` file and configure the necessary environment variables.
The `RAPYD_ACCESS_KEY` and `RAPYD_SECRET_KEY` variables values can be found in your Rapyd Sandbox account (you need to create an account at [rapyd.net](https://www.rapyd.net)), under the Developers menu item. The `RAPYD_SETTLEMENT_EWALLET` variable value can be found in your Rapyd Sandbox account details.
The `GATEHUB` related environment variables are necessary in order to complete Sandbox KYC, and add play money to your account. In order to have the correct variables, create a `GateHub` Sandbox account. Optionally you could send an email to `[email protected]` and request these variables.

To create a new Interledger Test Wallet account, a verification email will be sent to the provided email address. If you want to send emails within the development environment, you will need to have a personal Sendgrid account and update the following environment variables: `SEND_EMAIL` to `true`, `SENDGRID_API_KEY` and `FROM_EMAIL`. If you prefer not to send emails in the development environment, simply set `SEND_EMAIL` to `false` and use the verification link found in the Docker `wallet-backend` container logs to finalize the registration process for a new user.

Cross-currency transactions are supported. To enable this functionality, you will need to register at [freecurrencyapi.com/](https://freecurrencyapi.com/) and update the `RATE_API_KEY` environment variable with your own API key.
Currencies can be added in the `admin` environment. For example `assetCode` is `EUR`, `assetScale` is `2`, and you will need to add an amount to `liquidity`.

To have everything ready for `DEV` environment, we already set up some default values for Interledger Test Wallet, this way developers are ready to login without validation, and test e-commerce application without any additional setup:

- a `USD` asset set by default in the `admin` environment
- a user with email address `[email protected]` and password `123456`, with a `USD` account, payment pointer and test money
- a user with email address `[email protected]` and password `123456`, with a `USD` account and a payment pointer `boutique`, which is used as a receiver payment pointer at the e-commerce application
- developer keys for the `boutique` payment pointer, these values will be copied to `.env` file from `.env.example`, as mentioned above

If you would like to set up e-commerce application manually for another payment pointer, you will need to create a USD payment pointer, then generate public and private key for the payment pointer in the `Developer Keys` found in the `Settings` menu of Interledger Test Wallet. You also need to update the following environment variables: `PRIVATE_KEY` to the generated base64 encoded private key, `KEY_ID` to the payment pointer key id and `PAYMENT_POINTER` to the created payment pointer address.
If you would like to set up e-commerce application, you will need to create a USD payment pointer, then generate public and private key for the payment pointer in the `Developer Keys` found in the `Settings` menu of Interledger Test Wallet. You also need to update the following environment variables: `PRIVATE_KEY` to the generated base64 encoded private key, `KEY_ID` to the payment pointer key id and `PAYMENT_POINTER` to the created payment pointer address.

### Local Playground

Expand Down
6 changes: 3 additions & 3 deletions docker/dev/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ GATEHUB_SETTLEMENT_WALLET_ADDRESS=

# commerce env variables
# encoded base 64 private key
PRIVATE_KEY=LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1DNENBUUF3QlFZREsyVndCQ0lFSUI0YzgyOVlSMDZCTUhtQmpIVTNrOHZmMEZhVEFvOHNCTzIrRFZwY1lPSk0KLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQ==
KEY_ID=32499ede-78d9-424d-a43e-3796ee07b60c
PAYMENT_POINTER=https://rafiki-backend/boutique
PRIVATE_KEY=
KEY_ID=
PAYMENT_POINTER=
2 changes: 1 addition & 1 deletion docker/dev/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ services:
- testnet

tigerbeetle:
image: ghcr.io/tigerbeetle/tigerbeetle:0.16.2
image: ghcr.io/tigerbeetle/tigerbeetle:0.16.3
privileged: true
volumes:
- tigerbeetle-data:/var/lib/tigerbeetle
Expand Down
3 changes: 1 addition & 2 deletions docker/prod/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ services:
args:
PORT: ${WALLET_FRONTEND_PORT}
COOKIE_NAME: ${WALLET_BACKEND_COOKIE_NAME}
NEXT_PUBLIC_USE_TEST_KYC_DATA: ${WALLET_FRONTEND_USE_TEST_KYC_DATA}
NEXT_PUBLIC_BACKEND_URL: ${WALLET_FRONTEND_BACKEND_URL}
NEXT_PUBLIC_OPEN_PAYMENTS_HOST: ${WALLET_FRONTEND_OPEN_PAYMENTS_HOST}
NEXT_PUBLIC_AUTH_HOST: ${WALLET_FRONTEND_AUTH_HOST}
Expand Down Expand Up @@ -232,7 +231,7 @@ services:
<<: *logging

tigerbeetle:
image: ghcr.io/tigerbeetle/tigerbeetle:0.16.2
image: ghcr.io/tigerbeetle/tigerbeetle:0.16.3
privileged: true
volumes:
- tigerbeetle-data:/var/lib/tigerbeetle
Expand Down
37 changes: 17 additions & 20 deletions packages/boutique/backend/src/open-payments/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,13 @@ interface CreateOutgoingPaymentParams {
authServer: string
identifier: string
walletAddress: string
debitAmount: Amount
receiveAmount: Amount
nonce: string
finishUrl?: string
limits: {
debitAmount: Amount
receiveAmount: Amount
receiver?: string
}
}

interface CreateIncomingPaymentParams {
Expand Down Expand Up @@ -127,9 +130,11 @@ export class OpenPayments implements IOpenPayments {
identifier: order.id,
walletAddress: customerWalletAddress.id,
authServer: customerWalletAddress.authServer,

debitAmount: quote.debitAmount,
receiveAmount: quote.receiveAmount,
limits: {
debitAmount: quote.debitAmount,
receiveAmount: quote.receiveAmount,
receiver: incomingPayment.id
},
nonce: clientNonce
})

Expand Down Expand Up @@ -260,8 +265,10 @@ export class OpenPayments implements IOpenPayments {
walletAddress: walletAddress.id,
authServer: walletAddress.authServer,
identifier: clientIdentifer,
debitAmount: amountData,
receiveAmount: amountData,
limits: {
debitAmount: amountData,
receiveAmount: amountData
},
finishUrl: `${this.env.FRONTEND_URL}/cart/finish?identifier=${clientIdentifer}`
})

Expand Down Expand Up @@ -402,15 +409,8 @@ export class OpenPayments implements IOpenPayments {
private async createOutgoingPaymentGrant(
params: CreateOutgoingPaymentParams
): Promise<PendingGrant> {
const {
nonce,
authServer,
identifier,
walletAddress,
debitAmount,
receiveAmount,
finishUrl
} = params
const { nonce, authServer, identifier, walletAddress, limits, finishUrl } =
params

const finish =
finishUrl ??
Expand All @@ -426,10 +426,7 @@ export class OpenPayments implements IOpenPayments {
type: 'outgoing-payment',
actions: ['create', 'read', 'list'],
identifier: walletAddress,
limits: {
debitAmount,
receiveAmount
}
limits
}
]
},
Expand Down
2 changes: 1 addition & 1 deletion packages/boutique/backend/src/payment/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export interface IPaymentService {

export class PaymentService implements IPaymentService {
private readonly TRESHOLD_MS: number = 5000
private readonly MAX_ATTEMPTS: number = 5
private readonly MAX_ATTEMPTS: number = 24

constructor(
private logger: Logger,
Expand Down
1 change: 1 addition & 0 deletions packages/boutique/frontend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_CURRENCY=
10 changes: 5 additions & 5 deletions packages/boutique/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"preview": "vite preview"
},
"dependencies": {
"@boutique/shared": "workspace:*",
"@hookform/resolvers": "^3.9.0",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1",
Expand All @@ -17,9 +18,8 @@
"@radix-ui/react-tabs": "^1.1.0",
"@radix-ui/react-toast": "^1.2.1",
"@tanstack/react-query": "^5.56.2",
"@boutique/shared": "workspace:*",
"class-variance-authority": "^0.7.0",
"framer-motion": "^11.5.4",
"framer-motion": "^11.5.6",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.53.0",
Expand All @@ -33,13 +33,13 @@
"devDependencies": {
"@tailwindcss/forms": "^0.5.9",
"@tailwindcss/typography": "^0.5.15",
"@types/react": "18.3.7",
"@types/react": "18.3.8",
"@types/react-dom": "18.3.0",
"@vitejs/plugin-react-swc": "^3.7.0",
"autoprefixer": "^10.4.20",
"postcss": "^8.4.47",
"tailwindcss": "^3.4.12",
"tailwindcss": "^3.4.13",
"typescript": "^5.6.2",
"vite": "^5.4.6"
"vite": "^5.4.7"
}
}
2 changes: 1 addition & 1 deletion packages/boutique/frontend/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export function cn(...inputs: CxOptions) {
export function formatPrice(price: number): string {
const formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
currency: import.meta.env.VITE_CURRENCY || 'USD',
maximumFractionDigits: 2,
minimumFractionDigits: 2
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ exports.up = function (knex) {
table.string('firstName')
table.string('address')
table.string('country')
table.string('kycId') // TODO: replace with GateHub kyc check

table.boolean('kycVerified').defaultTo(false)
table.string('gateHubUserId')

table.timestamp('createdAt').notNullable()
Expand Down
4 changes: 2 additions & 2 deletions packages/wallet/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@
"objection": "^3.1.4",
"pg": "^8.13.0",
"randexp": "^0.5.3",
"socket.io": "^4.7.5",
"socket.io": "^4.8.0",
"uuid": "^10.0.0",
"winston": "^3.14.2",
"zod": "^3.23.8"
},
"devDependencies": {
"@faker-js/faker": "^9.0.1",
"@faker-js/faker": "^9.0.2",
"@graphql-codegen/cli": "^5.0.2",
"@graphql-codegen/typescript": "^4.0.9",
"@graphql-codegen/typescript-operations": "^4.2.3",
Expand Down
19 changes: 6 additions & 13 deletions packages/wallet/backend/src/account/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ interface IAccountService {
includeWalletKeys?: boolean
) => Promise<Account[]>
getAccountById: (userId: string, accountId: string) => Promise<Account>
getAccountBalance: (userId: string, account: Account) => Promise<number>
getAccountBalance: (account: Account) => Promise<number>
}

export class AccountService implements IAccountService {
Expand Down Expand Up @@ -110,7 +110,7 @@ export class AccountService implements IAccountService {

if (!includeWalletAddress) {
accounts.forEach(async (acc) => {
const balance = await this.getAccountBalance(userId, acc)
const balance = await this.getAccountBalance(acc)
acc.balance = transformBalance(balance, acc.assetScale)
})
}
Expand All @@ -132,7 +132,7 @@ export class AccountService implements IAccountService {
}

account.balance = transformBalance(
await this.getAccountBalance(userId, account),
await this.getAccountBalance(account),
account.assetScale
)

Expand All @@ -152,23 +152,16 @@ export class AccountService implements IAccountService {
}

account.balance = transformBalance(
await this.getAccountBalance(userId, account),
await this.getAccountBalance(account),
account.assetScale
)

return account
}

async getAccountBalance(userId: string, account: Account): Promise<number> {
const user = await User.query().findById(userId)

if (!user || !user.gateHubUserId) {
throw new NotFound()
}

async getAccountBalance(account: Account): Promise<number> {
const balances = await this.gateHubClient.getWalletBalance(
account.gateHubWalletId,
userId
account.gateHubWalletId
)
return Number(
balances.find((balance) => balance.vault.assetCode === account.assetCode)
Expand Down
6 changes: 6 additions & 0 deletions packages/wallet/backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,12 @@ export class App {

// GateHub
router.get('/iframe-urls/:type', isAuth, gateHubController.getIframeUrl)
router.post('/gatehub-webhooks', gateHubController.webhook)
router.post(
'/gatehub/add-user-to-gateway',
isAuth,
gateHubController.addUserToGateway
)

// Cards
router.get(
Expand Down
2 changes: 1 addition & 1 deletion packages/wallet/backend/src/auth/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export class AuthController implements IAuthController {
id: user.id,
email: user.email,
needsWallet: !user.gateHubUserId,
needsIDProof: !user.kycId
needsIDProof: !user.kycVerified
}

await req.session.save()
Expand Down
Loading

0 comments on commit a81ea9f

Please sign in to comment.