Fix lightning paymentRequests handling

This commit is contained in:
minibits-cash
2025-02-08 00:33:43 +01:00
parent 8a044a18fc
commit c3b8dcf99d
9 changed files with 106 additions and 60 deletions
@@ -14,7 +14,7 @@ import java.util.concurrent.TimeUnit
class CorePlaneOkHttpClientFactory : OkHttpClientFactory {
override fun createNewNetworkModuleClient(): OkHttpClient {
val okHttpVersion = OkHttp.VERSION
Log.d("OkHttpFactory", "Using OkHttp version: $okHttpVersion")
// Log.d("OkHttpFactory", "Using OkHttp version: $okHttpVersion")
return OkHttpClientProvider.createClientBuilder()
.dns(CorePlaneOkHttpDNSSelector(CorePlaneOkHttpDNSSelector.IPvMode.IPV4_FIRST))
+6 -2
View File
@@ -105,8 +105,12 @@ export const LightningPayScreen = function LightningPayScreen({ route }: Props)
const gotoContacts = function () {
navigation.navigate('Contacts', {
paymentOption: SendOption.LNURL_ADDRESS
//@ts-ignore
navigation.navigate('ContactsNavigator', {
screen: 'Contacts',
params: {
paymentOption: SendOption.LNURL_ADDRESS
}
})
}
+15 -2
View File
@@ -5,7 +5,7 @@ import { validateMnemonic } from '@scure/bip39'
import QuickCrypto from 'react-native-quick-crypto'
import { wordlist } from '@scure/bip39/wordlists/english'
import { mnemonicToSeedSync } from '@scure/bip39'
import { spacing, useThemeColor } from '../theme'
import { colors, spacing, useThemeColor } from '../theme'
import { Icon, ListItem, Screen, Text, Card, Loading, ErrorModal, Button } from '../components'
import { useHeader } from '../utils/useHeader'
import AppError, { Err } from '../utils/AppError'
@@ -166,17 +166,30 @@ export const RecoverWalletAddressScreen = observer(function RecoverWalletAddress
onError={handleError}
/>
{isValidMnemonic && profileToRecover && (
<>
<Card
style={$card}
ContentComponent={
<ListItem
text={profileToRecover.nip05}
subText="This is the wallet address linked to the provided seed. If you continue, your current address will reset to this one, but wallet seed phrase will NOT be updated."
subText="This is the wallet address linked to the provided seed."
LeftComponent={<View style={[$numIcon, { backgroundColor: numIconColor }]}><Text text='2' /></View>}
style={$item}
/>
}
/>
<Card
style={[$card]}
ContentComponent={
<ListItem
text={'Do not forget!'}
subText="Your current address will reset, but current wallet seed phrase will NOT be changed. Make a backup!"
LeftComponent={<View style={[$numIcon, { backgroundColor: numIconColor }]}><Text text='3' /></View>}
style={$item}
/>
}
/>
</>
)}
</View>
{isValidMnemonic && (
+2
View File
@@ -810,6 +810,8 @@ export const SendScreen = observer(function SendScreen({ route }: Props) {
setIsNostrDMModalVisible(false)
setIsProofSelectorModalVisible(false)
setIsLoading(false)
setResultModalInfo(undefined)
setIsResultModalVisible(false)
navigation.dispatch(
StackActions.popToTop()
+10 -2
View File
@@ -20,6 +20,7 @@ import { verticalScale } from '@gocodingnow/rn-size-matters'
import { translate } from '../i18n'
import { MintUnit } from '../services/wallet/currency'
import { StaticScreenProps, useNavigation } from '@react-navigation/native'
import { SendOption } from './SendScreen'
type Props = StaticScreenProps<{
unit: MintUnit
@@ -90,14 +91,21 @@ export const TokenReceiveScreen = function TokenReceiveScreen({ route }: Props)
const gotoScan = async function () {
tokenInputRef.current?.blur()
navigation.navigate('Scan', {mintUrl: mint?.mintUrl, unit})
navigation.navigate('Scan', {
mintUrl: mint?.mintUrl,
unit
})
}
/* const gotoContacts = function () {
//@ts-ignore
navigation.navigate('ContactsNavigator', {
screen: 'Contacts',
params: {paymentOption: SendOption.LNURL_ADDRESS}})
params: {
paymentOption: SendOption.LNURL_ADDRESS
}
})
} */
+8 -3
View File
@@ -532,7 +532,6 @@ export const TopupScreen = observer(function TopupScreen({ route }: Props) {
if (updated.length > 1) {
updated[1].sentToRelays = relaysToShareTo
updated[1].sentEvent = sentEvent
// status does not change, just add event and relay info to tx.data
transaction.setStatus(
@@ -549,8 +548,12 @@ export const TopupScreen = observer(function TopupScreen({ route }: Props) {
}
const gotoContacts = function () {
navigation.navigate('Contacts', {
paymentOption: ReceiveOption.SEND_PAYMENT_REQUEST
//@ts-ignore
navigation.navigate('ContactsNavigator', {
screen: 'Contacts',
params: {
paymentOption: ReceiveOption.SEND_PAYMENT_REQUEST
}
})
}
@@ -616,6 +619,8 @@ export const TopupScreen = observer(function TopupScreen({ route }: Props) {
setIsWithdrawModalVisible(false)
setIsWithdrawRequestSending(false)
setPaymentOption(ReceiveOption.SHOW_INVOICE)
setResultModalInfo(undefined)
setIsResultModalVisible(false)
navigation.dispatch(
StackActions.popToTop()
+35 -18
View File
@@ -1322,17 +1322,25 @@ const TopupInfoBlock = function (props: {
}) {
const {transaction, navigation, mint} = props
const {mintsStore} = useStores()
// retrieve pr from NOT COMPLETED transaction as it might have been expired and removed from storage
const paymentRequest = getPaymentRequestToRetry(transaction)
const isInternetReachable = useIsInternetReachable()
const [paymentRequest, setPaymentRequest] = useState<PaymentRequest | undefined>(undefined)
const [isPendingTopupTaskSentToQueue, setIsPendingTopupTaskSentToQueue] = useState<boolean>(false)
const [isResultModalVisible, setIsResultModalVisible] = useState<boolean>(false)
const [resultModalInfo, setResultModalInfo] = useState<
{status: TransactionStatus; message: string} | undefined
>()
const [isLoading, setIsLoading] = useState(false)
const [isLoading, setIsLoading] = useState(false)
useFocusEffect(useCallback(() => {
// retrieve pr from NOT COMPLETED transaction as it might have been expired and removed from storage
log.trace('[TopupInfoBlock] useFocusEffect start')
const pr = getPaymentRequestToRetry(transaction)
if(pr) {
log.trace('[TopupInfoBlock] found payment request')
setPaymentRequest(pr)
}
}, []))
useFocusEffect(useCallback(() => {
const handlePendingTopupTaskResult = async (result: TransactionTaskResult) => {
@@ -1470,6 +1478,16 @@ const TopupInfoBlock = function (props: {
</>
}
/>
{transaction.status === TransactionStatus.PENDING && paymentRequest && (
<View style={{marginBottom: spacing.small}}>
<QRCodeBlock
qrCodeData={paymentRequest.encodedInvoice}
title={translate("tranDetailScreen.invoice")}
type='Bolt11Invoice'
size={spacing.screenWidth * 0.8}
/>
</View>
)}
<Card
labelTx='tranDetailScreen.topupTo'
style={$dataCard}
@@ -1484,17 +1502,7 @@ const TopupInfoBlock = function (props: {
<Text text={transaction.mint} />
)
}
/>
{transaction.status === TransactionStatus.PENDING && paymentRequest && (
<View style={{marginBottom: spacing.small}}>
<QRCodeBlock
qrCodeData={paymentRequest.encodedInvoice}
title={translate("tranDetailScreen.invoice")}
type='Bolt11Invoice'
size={spacing.screenWidth * 0.8}
/>
</View>
)}
/>
<BottomModal
isVisible={isResultModalVisible ? true : false}
ContentComponent={
@@ -1900,6 +1908,7 @@ const getPaymentRequestToRetry = (
transaction: Transaction,
): PaymentRequest | undefined => {
try {
log.trace('[getPaymentRequestToRetry] start')
if(transaction.type !== (TransactionType.TOPUP)) {
return undefined
}
@@ -1910,14 +1919,20 @@ const getPaymentRequestToRetry = (
return undefined
}
const {mintsStore} = useStores()
/* const {mintsStore} = useStores()
// skip if mint is still offline
const {mint} = transaction
const mintInstance = mintsStore.findByUrl(mint)
log.trace('[getPaymentRequestToRetry] mint', {mint: mintInstance})
if(!mintInstance || mintInstance.status === MintStatus.OFFLINE) {
return undefined
}
}*/
const data = JSON.parse(transaction.data)
const pendingRecord = data.find(
@@ -1926,6 +1941,8 @@ const getPaymentRequestToRetry = (
const paymentRequest: PaymentRequest = pendingRecord.paymentRequest
log.trace('[getPaymentRequestToRetry]', {paymentRequest})
if(!paymentRequest) {return undefined}
if(pollerExists(`handlePendingTopupPoller-${paymentRequest.paymentHash}`)) {return undefined}
+2 -1
View File
@@ -649,7 +649,8 @@ export const WalletScreen = observer(function WalletScreen({ route }: Props) {
}
style={{
width: spacing.screenWidth * 0.28,
marginRight: spacing.small,
marginRight: spacing.small,
marginBottom: spacing.extraSmall
}}
/>
)}
+27 -31
View File
@@ -1510,34 +1510,29 @@ const handlePendingTopupTask = async function (params: {paymentRequest: PaymentR
throw new AppError(Err.VALIDATION_ERROR, 'Returned quote is different then the one requested', {mintUrl, quote, mintQuote})
}
switch (state) {
/*
* UNPAID or ISSUED
*/
case MintQuoteState.UNPAID:
case MintQuoteState.ISSUED:
if (isBefore(pr.expiresAt as Date, new Date())) {
log.debug('[handlePendingTopupTask]', `Invoice expired, removing: ${pr.paymentHash}`)
// expire related tx - but only if it has not been completed before this check
if(transaction.status !== TransactionStatus.COMPLETED) {
const transactionDataUpdate = {
status: TransactionStatus.EXPIRED,
message: 'Invoice expired',
createdAt: new Date(),
}
await transactionsStore.updateStatuses(
[transactionId],
TransactionStatus.EXPIRED,
JSON.stringify(transactionDataUpdate),
)
}
stopPolling(`handlePendingTopupPoller-${paymentHash}`)
paymentRequestsStore.removePaymentRequest(pr)
}
// continue
if (isBefore(pr.expiresAt as Date, new Date())) {
log.debug('[handlePendingTopupTask]', `Invoice expired, removing: ${pr.paymentHash}`)
// expire related tx - but only if it has not been completed before this check
if(transaction.status !== TransactionStatus.COMPLETED) {
const transactionDataUpdate = {
status: TransactionStatus.EXPIRED,
message: 'Invoice expired',
createdAt: new Date(),
}
await transactionsStore.updateStatuses(
[transactionId],
TransactionStatus.EXPIRED,
JSON.stringify(transactionDataUpdate),
)
}
stopPolling(`handlePendingTopupPoller-${paymentHash}`)
paymentRequestsStore.removePaymentRequest(pr)
}
switch (state) {
case MintQuoteState.UNPAID:
log.trace('[handlePendingTopupTask] Quote not paid', {mintUrl, mintQuote})
@@ -1622,7 +1617,7 @@ const handlePendingTopupTask = async function (params: {paymentRequest: PaymentR
)
transaction.setProfile(
JSON.stringify(getSnapshot(pr.contactTo))
JSON.stringify(pr.contactTo)
)
}
@@ -1648,6 +1643,8 @@ const handlePendingTopupTask = async function (params: {paymentRequest: PaymentR
case MintQuoteState.ISSUED:
log.trace('[handlePendingTopupTask] Quote already issued', {mintUrl, mintQuote})
paymentRequestsStore.removePaymentRequest(pr)
return {
taskFunction: HANDLE_PENDING_TOPUP_TASK,
transaction,
@@ -1673,8 +1670,7 @@ const handlePendingTopupTask = async function (params: {paymentRequest: PaymentR
} as WalletTaskResult
}
} catch (e: any) {
} catch (e: any) {
return {
taskFunction: HANDLE_PENDING_TOPUP_TASK,
mintUrl,