From 8976cc5fb83f3839fe6cda1f781b1277d8939c51 Mon Sep 17 00:00:00 2001 From: Juan P Lopez Date: Mon, 27 Nov 2023 10:00:52 -0500 Subject: [PATCH] feat(core): add missing callbacks (#3585) * refactor: isolate notifications type for each channel * refactor: update notification type enum * feat: add missing callbacks * fix: lint issues * fix: notification type resolver --- .../dev/apollo-federation/supergraph.graphql | 4 +- core/api/src/app/payments/send-intraledger.ts | 48 +++++++++++++++---- core/api/src/app/payments/send-lightning.ts | 29 +++++++++++ .../app/payments/update-pending-payments.ts | 40 ++++++++++++++++ core/api/src/domain/callback/index.ts | 4 ++ core/api/src/domain/notifications/index.ts | 3 +- core/api/src/graphql/public/schema.graphql | 2 +- .../types/scalar/tx-notification-type.ts | 2 +- core/api/src/services/notifications/index.ts | 29 ++++++++--- .../services/notifications/transactions.ts | 18 +++---- .../apollo-federation/supergraph.graphql | 2 +- 11 files changed, 153 insertions(+), 28 deletions(-) diff --git a/core/api/dev/apollo-federation/supergraph.graphql b/core/api/dev/apollo-federation/supergraph.graphql index 861c96d64b..5d0371e22a 100644 --- a/core/api/dev/apollo-federation/supergraph.graphql +++ b/core/api/dev/apollo-federation/supergraph.graphql @@ -180,6 +180,7 @@ type ApiKey expired: Boolean! lastUsedAt: Timestamp expiresAt: Timestamp! + readOnly: Boolean! } input ApiKeyCreateInput @@ -187,6 +188,7 @@ input ApiKeyCreateInput { name: String! expireInDays: Int + readOnly: Boolean! = false } type ApiKeyCreatePayload @@ -1698,7 +1700,7 @@ enum TxNotificationType { IntraLedgerPayment @join__enumValue(graph: PUBLIC) IntraLedgerReceipt @join__enumValue(graph: PUBLIC) - LnInvoicePaid @join__enumValue(graph: PUBLIC) + LigtningReceipt @join__enumValue(graph: PUBLIC) OnchainPayment @join__enumValue(graph: PUBLIC) OnchainReceipt @join__enumValue(graph: PUBLIC) OnchainReceiptPending @join__enumValue(graph: PUBLIC) diff --git a/core/api/src/app/payments/send-intraledger.ts b/core/api/src/app/payments/send-intraledger.ts index 6c65f0c694..6428d39fd5 100644 --- a/core/api/src/app/payments/send-intraledger.ts +++ b/core/api/src/app/payments/send-intraledger.ts @@ -322,8 +322,9 @@ const executePaymentViaIntraledger = async < })) } + const senderWalletDescriptor = paymentFlow.senderWalletDescriptor() const recipientWalletDescriptor = paymentFlow.recipientWalletDescriptor() - if (recipientWalletDescriptor === undefined) + if (!senderWalletDescriptor || !recipientWalletDescriptor) return new InvalidLightningPaymentFlowBuilderStateError() const journal = await LedgerFacade.recordIntraledger({ @@ -332,7 +333,7 @@ const executePaymentViaIntraledger = async < btc: paymentFlow.btcPaymentAmount, usd: paymentFlow.usdPaymentAmount, }, - senderWalletDescriptor: paymentFlow.senderWalletDescriptor(), + senderWalletDescriptor, recipientWalletDescriptor, metadata, additionalDebitMetadata, @@ -344,13 +345,13 @@ const executePaymentViaIntraledger = async < const recipientUser = await UsersRepository().findById(recipientAccount.kratosUserId) if (recipientUser instanceof Error) return recipientUser - const walletTransaction = await getTransactionForWalletByJournalId({ + const recipientWalletTransaction = await getTransactionForWalletByJournalId({ walletId: recipientWalletDescriptor.id, journalId: journal.journalId, }) - if (walletTransaction instanceof Error) return walletTransaction + if (recipientWalletTransaction instanceof Error) return recipientWalletTransaction - const result = await NotificationsService().sendTransaction({ + const recipientResult = await NotificationsService().sendTransaction({ recipient: { accountId: recipientAccount.id, walletId: recipientWalletDescriptor.id, @@ -359,11 +360,42 @@ const executePaymentViaIntraledger = async < notificationSettings: recipientAccount.notificationSettings, level: recipientAccount.level, }, - transaction: walletTransaction, + transaction: recipientWalletTransaction, }) - if (result instanceof DeviceTokensNotRegisteredNotificationsServiceError) { - await removeDeviceTokens({ userId: recipientUser.id, deviceTokens: result.tokens }) + if (recipientResult instanceof DeviceTokensNotRegisteredNotificationsServiceError) { + await removeDeviceTokens({ + userId: recipientUser.id, + deviceTokens: recipientResult.tokens, + }) + } + + const senderUser = await UsersRepository().findById(senderAccount.kratosUserId) + if (senderUser instanceof Error) return senderUser + + const senderWalletTransaction = await getTransactionForWalletByJournalId({ + walletId: senderWalletDescriptor.id, + journalId: journal.journalId, + }) + if (senderWalletTransaction instanceof Error) return senderWalletTransaction + + const senderResult = await NotificationsService().sendTransaction({ + recipient: { + accountId: senderAccount.id, + walletId: senderWalletDescriptor.id, + deviceTokens: senderUser.deviceTokens, + language: senderUser.language, + notificationSettings: senderAccount.notificationSettings, + level: senderAccount.level, + }, + transaction: senderWalletTransaction, + }) + + if (senderResult instanceof DeviceTokensNotRegisteredNotificationsServiceError) { + await removeDeviceTokens({ + userId: senderUser.id, + deviceTokens: senderResult.tokens, + }) } return PaymentSendStatus.Success diff --git a/core/api/src/app/payments/send-lightning.ts b/core/api/src/app/payments/send-lightning.ts index 0e870ff6b9..cbc42cfb29 100644 --- a/core/api/src/app/payments/send-lightning.ts +++ b/core/api/src/app/payments/send-lightning.ts @@ -114,6 +114,7 @@ export const payInvoiceByWalletId = async ({ decodedInvoice, paymentFlow, senderWallet, + senderAccount, senderDisplayCurrency: senderAccount.displayCurrency, memo, }) @@ -159,6 +160,7 @@ const payNoAmountInvoiceByWalletId = async ({ decodedInvoice, paymentFlow, senderWallet, + senderAccount, senderDisplayCurrency: senderAccount.displayCurrency, memo, }) @@ -560,12 +562,14 @@ const executePaymentViaLn = async ({ decodedInvoice, paymentFlow, senderWallet, + senderAccount, senderDisplayCurrency, memo, }: { decodedInvoice: LnInvoice paymentFlow: PaymentFlow senderWallet: Wallet + senderAccount: Account senderDisplayCurrency: DisplayCurrency memo: string | null }): Promise => { @@ -742,6 +746,31 @@ const executePaymentViaLn = async ({ if (reimbursed instanceof Error) return reimbursed } + const senderUser = await UsersRepository().findById(senderAccount.kratosUserId) + if (senderUser instanceof Error) return senderUser + + const walletTransaction = await getTransactionForWalletByJournalId({ + walletId: senderWallet.id, + journalId, + }) + if (walletTransaction instanceof Error) return walletTransaction + + const result = await NotificationsService().sendTransaction({ + recipient: { + accountId: senderWallet.accountId, + walletId: senderWallet.id, + deviceTokens: senderUser.deviceTokens, + language: senderUser.language, + notificationSettings: senderAccount.notificationSettings, + level: senderAccount.level, + }, + transaction: walletTransaction, + }) + + if (result instanceof DeviceTokensNotRegisteredNotificationsServiceError) { + await removeDeviceTokens({ userId: senderUser.id, deviceTokens: result.tokens }) + } + return PaymentSendStatus.Success }) } diff --git a/core/api/src/app/payments/update-pending-payments.ts b/core/api/src/app/payments/update-pending-payments.ts index 4b63915619..3c8ab580e8 100644 --- a/core/api/src/app/payments/update-pending-payments.ts +++ b/core/api/src/app/payments/update-pending-payments.ts @@ -4,6 +4,9 @@ import { reimburseFailedUsdPayment } from "./reimburse-failed-usd" import { PaymentFlowFromLedgerTransaction } from "./translations" +import { getTransactionForWalletByJournalId } from "@/app/wallets" +import { removeDeviceTokens } from "@/app/users/remove-device-tokens" + import { toSats } from "@/domain/bitcoin" import { defaultTimeToExpiryInSeconds, PaymentStatus } from "@/domain/bitcoin/lightning" import { InconsistentDataError } from "@/domain/errors" @@ -16,6 +19,7 @@ import { } from "@/domain/ledger" import { MissingPropsInTransactionForPaymentFlowError } from "@/domain/payments" import { setErrorCritical, WalletCurrency } from "@/domain/shared" +import { DeviceTokensNotRegisteredNotificationsServiceError } from "@/domain/notifications" import { LedgerService, getNonEndUserWalletIds } from "@/services/ledger" import * as LedgerFacade from "@/services/ledger/facade" @@ -24,8 +28,10 @@ import { LockService } from "@/services/lock" import { AccountsRepository, PaymentFlowStateRepository, + UsersRepository, WalletsRepository, } from "@/services/mongoose" +import { NotificationsService } from "@/services/notifications" import { addAttributesToCurrentSpan, wrapAsyncToRunInSpan } from "@/services/tracing" import { runInParallel } from "@/utils" @@ -263,6 +269,40 @@ const updatePendingPayment = wrapAsyncToRunInSpan({ return new MissingExpectedDisplayAmountsForTransactionError() } + const senderWallet = await WalletsRepository().findById(walletId) + if (senderWallet instanceof Error) return senderWallet + + const senderAccount = await AccountsRepository().findById(senderWallet.accountId) + if (senderAccount instanceof Error) return senderAccount + + const senderUser = await UsersRepository().findById(senderAccount.kratosUserId) + if (senderUser instanceof Error) return senderUser + + const walletTransaction = await getTransactionForWalletByJournalId({ + walletId, + journalId: pendingPayment.journalId, + }) + if (walletTransaction instanceof Error) return walletTransaction + + const result = await NotificationsService().sendTransaction({ + recipient: { + accountId: senderWallet.accountId, + walletId, + deviceTokens: senderUser.deviceTokens, + language: senderUser.language, + notificationSettings: senderAccount.notificationSettings, + level: senderAccount.level, + }, + transaction: walletTransaction, + }) + + if (result instanceof DeviceTokensNotRegisteredNotificationsServiceError) { + await removeDeviceTokens({ + userId: senderUser.id, + deviceTokens: result.tokens, + }) + } + return reimburseFee({ paymentFlow, senderDisplayAmount: displayAmount, diff --git a/core/api/src/domain/callback/index.ts b/core/api/src/domain/callback/index.ts index 94b85bbd50..b557631599 100644 --- a/core/api/src/domain/callback/index.ts +++ b/core/api/src/domain/callback/index.ts @@ -1,4 +1,8 @@ export const CallbackEventType = { + SendIntraledger: "send.intraledger", ReceiveIntraledger: "receive.intraledger", + SendLightning: "send.lightning", ReceiveLightning: "receive.lightning", + SendOnchain: "send.onchain", + ReceiveOnchain: "receive.onchain", } as const diff --git a/core/api/src/domain/notifications/index.ts b/core/api/src/domain/notifications/index.ts index 5b199cb572..2a84a9e302 100644 --- a/core/api/src/domain/notifications/index.ts +++ b/core/api/src/domain/notifications/index.ts @@ -8,7 +8,8 @@ export const NotificationType = { OnchainReceipt: "onchain_receipt", OnchainReceiptPending: "onchain_receipt_pending", OnchainPayment: "onchain_payment", - LnInvoicePaid: "paid-invoice", + LigtningReceipt: "paid-invoice", + LigtningPayment: "lightning_payment", } as const export const NotificationChannel = { diff --git a/core/api/src/graphql/public/schema.graphql b/core/api/src/graphql/public/schema.graphql index a10647a3d0..1679a58f1f 100644 --- a/core/api/src/graphql/public/schema.graphql +++ b/core/api/src/graphql/public/schema.graphql @@ -1314,7 +1314,7 @@ enum TxDirection { enum TxNotificationType { IntraLedgerPayment IntraLedgerReceipt - LnInvoicePaid + LigtningReceipt OnchainPayment OnchainReceipt OnchainReceiptPending diff --git a/core/api/src/graphql/public/types/scalar/tx-notification-type.ts b/core/api/src/graphql/public/types/scalar/tx-notification-type.ts index a9bb602eec..378faeff5c 100644 --- a/core/api/src/graphql/public/types/scalar/tx-notification-type.ts +++ b/core/api/src/graphql/public/types/scalar/tx-notification-type.ts @@ -6,7 +6,7 @@ const TxNotificationType = GT.Enum({ OnchainReceipt: { value: "onchain_receipt" }, OnchainReceiptPending: { value: "onchain_receipt_pending" }, OnchainPayment: { value: "onchain_payment" }, - LnInvoicePaid: { value: "paid-invoice" }, + LigtningReceipt: { value: "paid-invoice" }, IntraLedgerReceipt: { value: "intra_ledger_receipt" }, IntraLedgerPayment: { value: "intra_ledger_payment" }, }, diff --git a/core/api/src/services/notifications/index.ts b/core/api/src/services/notifications/index.ts index e64de77265..da7a5ddfe7 100644 --- a/core/api/src/services/notifications/index.ts +++ b/core/api/src/services/notifications/index.ts @@ -40,7 +40,7 @@ export const NotificationsService = (): INotificationsService => { const type = getPubSubNotificationEventType(transaction) if (!type) return - if (type === NotificationType.LnInvoicePaid) { + if (type === NotificationType.LigtningReceipt) { const lnTx = transaction as WalletLnTransaction const paymentHash = lnTx.initiationVia.paymentHash // Notify public subscribers @@ -379,7 +379,7 @@ const getPubSubNotificationEventType = ( ): NotificationType | undefined => { const type = translateToNotificationType(transaction) switch (type) { - case NotificationType.LnInvoicePaid: + case NotificationType.LigtningReceipt: case NotificationType.IntraLedgerReceipt: case NotificationType.OnchainReceiptPending: case NotificationType.OnchainPayment: @@ -402,7 +402,7 @@ const getPushNotificationEventType = ( ): NotificationType | undefined => { const type = translateToNotificationType(transaction) switch (type) { - case NotificationType.LnInvoicePaid: + case NotificationType.LigtningReceipt: case NotificationType.IntraLedgerReceipt: case NotificationType.OnchainReceiptPending: case NotificationType.OnchainPayment: @@ -425,10 +425,25 @@ const getCallbackEventType = ( ): CallbackEventType | undefined => { const type = translateToNotificationType(transaction) switch (type) { - case NotificationType.LnInvoicePaid: + case NotificationType.LigtningReceipt: return CallbackEventType.ReceiveLightning + + case NotificationType.LigtningPayment: + return CallbackEventType.SendLightning + case NotificationType.IntraLedgerReceipt: return CallbackEventType.ReceiveIntraledger + + case NotificationType.IntraLedgerPayment: + return CallbackEventType.SendIntraledger + + case NotificationType.OnchainReceiptPending: + case NotificationType.OnchainReceipt: + return CallbackEventType.ReceiveOnchain + + case NotificationType.OnchainPayment: + return CallbackEventType.SendOnchain + default: return undefined } @@ -440,11 +455,13 @@ const translateToNotificationType = ( const type = transaction.initiationVia.type const isReceive = transaction.settlementAmount > 0 if (type === "lightning") { - return isReceive ? NotificationType.LnInvoicePaid : undefined + return isReceive ? NotificationType.LigtningReceipt : NotificationType.LigtningPayment } if (type === "intraledger") { - return isReceive ? NotificationType.IntraLedgerReceipt : undefined + return isReceive + ? NotificationType.IntraLedgerReceipt + : NotificationType.IntraLedgerPayment } if (type === "onchain") { diff --git a/core/api/test/unit/services/notifications/transactions.ts b/core/api/test/unit/services/notifications/transactions.ts index 58b9e8eed9..6639eed416 100644 --- a/core/api/test/unit/services/notifications/transactions.ts +++ b/core/api/test/unit/services/notifications/transactions.ts @@ -58,7 +58,7 @@ export const btcTransactions = [ title: "BTC Transaction", }, { - type: NotificationType.LnInvoicePaid, + type: NotificationType.LigtningReceipt, paymentAmount: { amount: 1000n, currency: WalletCurrency.Btc }, body: "+1,000 sats", title: "BTC Transaction", @@ -102,7 +102,7 @@ export const btcTransactionsWithDisplayCurrency = [ title: "BTC Transaction", }, { - type: NotificationType.LnInvoicePaid, + type: NotificationType.LigtningReceipt, paymentAmount: { amount: 1000n, currency: WalletCurrency.Btc }, displayPaymentAmount: usdDisplayPaymentAmount, body: "+$5 | 1,000 sats", @@ -144,7 +144,7 @@ export const btcTransactionsWithDisplayCurrency = [ title: "BTC Transaction", }, { - type: NotificationType.LnInvoicePaid, + type: NotificationType.LigtningReceipt, paymentAmount: { amount: 1000n, currency: WalletCurrency.Btc }, displayPaymentAmount: crcDisplayPaymentAmount, body: "+₡3,500.50 | 1,000 sats", @@ -184,7 +184,7 @@ export const usdTransactions = [ title: "USD Transaction", }, { - type: NotificationType.LnInvoicePaid, + type: NotificationType.LigtningReceipt, paymentAmount: { amount: 1000n, currency: WalletCurrency.Usd }, body: "+$10", title: "USD Transaction", @@ -220,7 +220,7 @@ export const usdTransactions = [ title: "USD Transaction", }, { - type: NotificationType.LnInvoicePaid, + type: NotificationType.LigtningReceipt, paymentAmount: { amount: 1030n, currency: WalletCurrency.Usd }, body: "+$10.30", title: "USD Transaction", @@ -264,7 +264,7 @@ export const usdTransactionsWithDisplayCurrency = [ title: "USD Transaction", }, { - type: NotificationType.LnInvoicePaid, + type: NotificationType.LigtningReceipt, paymentAmount: { amount: 500n, currency: WalletCurrency.Usd }, displayPaymentAmount: usdDisplayPaymentAmount, body: "+$5", @@ -306,7 +306,7 @@ export const usdTransactionsWithDisplayCurrency = [ title: "USD Transaction", }, { - type: NotificationType.LnInvoicePaid, + type: NotificationType.LigtningReceipt, paymentAmount: { amount: 500n, currency: WalletCurrency.Usd }, displayPaymentAmount: crcDisplayPaymentAmount, body: "+₡3,500.50 | $5", @@ -348,7 +348,7 @@ export const usdTransactionsWithDisplayCurrency = [ title: "USD Transaction", }, { - type: NotificationType.LnInvoicePaid, + type: NotificationType.LigtningReceipt, paymentAmount: { amount: 507n, currency: WalletCurrency.Usd }, displayPaymentAmount: usdDisplayPaymentAmount, body: "+$5.07", @@ -390,7 +390,7 @@ export const usdTransactionsWithDisplayCurrency = [ title: "USD Transaction", }, { - type: NotificationType.LnInvoicePaid, + type: NotificationType.LigtningReceipt, paymentAmount: { amount: 507n, currency: WalletCurrency.Usd }, displayPaymentAmount: crcDisplayPaymentAmount, body: "+₡3,500.50 | $5.07", diff --git a/dev/config/apollo-federation/supergraph.graphql b/dev/config/apollo-federation/supergraph.graphql index b0c019e4eb..5d0371e22a 100644 --- a/dev/config/apollo-federation/supergraph.graphql +++ b/dev/config/apollo-federation/supergraph.graphql @@ -1700,7 +1700,7 @@ enum TxNotificationType { IntraLedgerPayment @join__enumValue(graph: PUBLIC) IntraLedgerReceipt @join__enumValue(graph: PUBLIC) - LnInvoicePaid @join__enumValue(graph: PUBLIC) + LigtningReceipt @join__enumValue(graph: PUBLIC) OnchainPayment @join__enumValue(graph: PUBLIC) OnchainReceipt @join__enumValue(graph: PUBLIC) OnchainReceiptPending @join__enumValue(graph: PUBLIC)