@@ -3,9 +3,11 @@ package to.bitkit.services
33import io.ktor.client.HttpClient
44import io.ktor.client.request.get
55import io.ktor.http.HttpStatusCode
6+ import org.lightningdevkit.ldknode.ConfirmationStatus
67import org.lightningdevkit.ldknode.Network
78import org.lightningdevkit.ldknode.PaymentDetails
89import org.lightningdevkit.ldknode.PaymentDirection
10+ import org.lightningdevkit.ldknode.PaymentKind
911import org.lightningdevkit.ldknode.PaymentStatus
1012import to.bitkit.async.ServiceQueue
1113import to.bitkit.env.Env
@@ -46,6 +48,7 @@ import uniffi.bitkitcore.openChannel
4648import uniffi.bitkitcore.removeTags
4749import uniffi.bitkitcore.updateActivity
4850import uniffi.bitkitcore.updateBlocktankUrl
51+ import uniffi.bitkitcore.upsertActivity
4952import javax.inject.Inject
5053import javax.inject.Singleton
5154import kotlin.random.Random
@@ -164,47 +167,110 @@ class ActivityService(
164167 ServiceQueue .CORE .background {
165168 var addedCount = 0
166169 var updatedCount = 0
167-
168- for (payment in payments) { // Skip pending inbound payments, just means they created an invoice
169- if (payment.status == PaymentStatus .PENDING && payment.direction == PaymentDirection .INBOUND ) {
170- continue
171- }
172-
173- val state = when (payment.status) {
174- PaymentStatus .FAILED -> PaymentState .FAILED
175- PaymentStatus .PENDING -> PaymentState .PENDING
176- PaymentStatus .SUCCEEDED -> PaymentState .SUCCEEDED
170+ var latestCaughtError: Throwable ? = null
171+
172+ for (payment in payments) {
173+ try {
174+ val state = when (payment.status) {
175+ PaymentStatus .FAILED -> PaymentState .FAILED
176+ PaymentStatus .PENDING -> PaymentState .PENDING
177+ PaymentStatus .SUCCEEDED -> PaymentState .SUCCEEDED
178+ }
179+
180+ when (val kind = payment.kind) {
181+ is PaymentKind .Onchain -> {
182+ var isConfirmed = false
183+ var confirmedTimestamp: ULong? = null
184+
185+ val status = kind.status
186+ if (status is ConfirmationStatus .Confirmed ) {
187+ isConfirmed = true
188+ confirmedTimestamp = status.timestamp
189+ }
190+
191+ // Ensure confirmTimestamp is at least equal to timestamp when confirmed
192+ val timestamp = payment.latestUpdateTimestamp
193+
194+ if (isConfirmed && confirmedTimestamp != null && confirmedTimestamp < timestamp) {
195+ confirmedTimestamp = timestamp
196+ }
197+
198+ val onchain = OnchainActivity (
199+ id = payment.id,
200+ txType = payment.direction.toPaymentType(),
201+ txId = kind.txid,
202+ value = payment.amountSats ? : 0u ,
203+ fee = (payment.feePaidMsat ? : 0u ) / 1000u ,
204+ feeRate = 1u , // TODO: get from somewhere
205+ address = " todo_find_address" , // TODO: find address
206+ confirmed = isConfirmed,
207+ timestamp = timestamp,
208+ isBoosted = false , // TODO: handle
209+ isTransfer = false , // TODO: handle when paying for order
210+ doesExist = true ,
211+ confirmTimestamp = confirmedTimestamp,
212+ channelId = null , // TODO: get from linked order
213+ transferTxId = null , // TODO: get from linked order
214+ createdAt = timestamp,
215+ updatedAt = timestamp,
216+ )
217+
218+ if (getActivityById(payment.id) != null ) {
219+ updateActivity(payment.id, Activity .Onchain (onchain))
220+ updatedCount++
221+ } else {
222+ upsertActivity(Activity .Onchain (onchain))
223+ addedCount++
224+ }
225+ }
226+
227+ is PaymentKind .Bolt11 -> {
228+ // Skip pending inbound payments, just means they created an invoice
229+ if (payment.status == PaymentStatus .PENDING && payment.direction == PaymentDirection .INBOUND ) {
230+ continue
231+ }
232+
233+ val ln = LightningActivity (
234+ id = payment.id,
235+ txType = payment.direction.toPaymentType(),
236+ status = state,
237+ value = payment.amountSats ? : 0u ,
238+ fee = null , // TODO
239+ invoice = " lnbc123" , // TODO
240+ message = " " ,
241+ timestamp = payment.latestUpdateTimestamp,
242+ preimage = null ,
243+ createdAt = payment.latestUpdateTimestamp,
244+ updatedAt = payment.latestUpdateTimestamp,
245+ )
246+
247+ if (getActivityById(payment.id) != null ) {
248+ updateActivity(payment.id, Activity .Lightning (ln))
249+ updatedCount++
250+ } else {
251+ upsertActivity(Activity .Lightning (ln))
252+ addedCount++
253+ }
254+ }
255+
256+ else -> Unit // Handle spontaneous payments if needed
257+ }
258+ } catch (e: Throwable ) {
259+ Logger .error(" Error syncing LDK payment:" , e, context = " CoreService" )
260+ latestCaughtError = e
177261 }
178-
179- val ln = LightningActivity (
180- id = payment.id,
181- txType = if (payment.direction == PaymentDirection .OUTBOUND ) PaymentType .SENT else PaymentType .RECEIVED ,
182- status = state,
183- value = payment.amountSats ? : 0u ,
184- fee = null , // TODO
185- invoice = " lnbc123" , // TODO
186- message = " " ,
187- timestamp = payment.latestUpdateTimestamp,
188- preimage = null ,
189- createdAt = payment.latestUpdateTimestamp,
190- updatedAt = payment.latestUpdateTimestamp,
191- )
192-
193- if (getActivityById(payment.id) != null ) {
194- updateActivity(payment.id, Activity .Lightning (ln))
195- updatedCount++
196- } else {
197- insertActivity(Activity .Lightning (ln))
198- addedCount++
199- }
200-
201- // TODO: handle onchain activity when it comes in ldk-node
202262 }
203263
204- Logger .info(" Synced LDK payments - Added: $addedCount , Updated: $updatedCount " )
264+ // If any of the inserts failed, we want to throw the error up
265+ latestCaughtError?.let { throw it }
266+
267+ Logger .info(" Synced LDK payments - Added: $addedCount - Updated: $updatedCount " , context = " CoreService" )
205268 }
206269 }
207270
271+ private fun PaymentDirection.toPaymentType (): PaymentType =
272+ if (this == PaymentDirection .OUTBOUND ) PaymentType .SENT else PaymentType .RECEIVED
273+
208274 suspend fun getActivity (id : String ): Activity ? {
209275 return ServiceQueue .CORE .background {
210276 getActivityById(id)
@@ -240,7 +306,7 @@ class ActivityService(
240306
241307 // MARK: - Tag Methods
242308
243- suspend fun appendTags (toActivityId : String , tags : List <String >) : Result <Unit >{
309+ suspend fun appendTags (toActivityId : String , tags : List <String >): Result <Unit > {
244310 return try {
245311 ServiceQueue .CORE .background {
246312 addTags(toActivityId, tags)
0 commit comments