diff --git a/packages/core/src/handlers/webhooks.ts b/packages/core/src/handlers/webhooks.ts index e0f503d7..e16bc729 100644 --- a/packages/core/src/handlers/webhooks.ts +++ b/packages/core/src/handlers/webhooks.ts @@ -12,31 +12,32 @@ const webhookSchema = z.object({ async function handleIncomingPayment() { const node = createMoneyDevKitNode(); const client = createMoneyDevKitClient(); - const payments = node.receivePayments(); - if (payments.length === 0) { - return; - } - - payments.forEach((payment) => { + // Use callback-based receive to process payments IMMEDIATELY as they arrive + // This allows instant customer confirmation while node continues running + node.receivePaymentsWithCallback((payment) => { + // Mark payment received in local state IMMEDIATELY markPaymentReceived(payment.paymentHash); - }); - try { - await client.checkouts.paymentReceived({ - payments: payments.map((payment) => ({ - paymentHash: payment.paymentHash, - // amount comes in msat from the node, convert to sats - amountSats: payment.amount / 1000, - sandbox: false, - })), - }); - } catch (error) { - warn( - "Failed to notify MoneyDevKit checkout about received payments. Will rely on local state and retry on next webhook.", - error, - ); - } + // Notify backend asynchronously (fire and forget for speed) + // Note: payment.amount is in msat, convert to sats + client.checkouts + .paymentReceived({ + payments: [ + { + paymentHash: payment.paymentHash, + amountSats: payment.amount / 1000, + sandbox: false, + }, + ], + }) + .catch((error) => { + warn( + "Failed to notify MoneyDevKit checkout about received payment. Will rely on local state and retry on next webhook.", + error, + ); + }); + }); } export async function handleMdkWebhook(request: Request): Promise { diff --git a/packages/core/src/lightning-node.ts b/packages/core/src/lightning-node.ts index 83fe735e..7c2a2628 100644 --- a/packages/core/src/lightning-node.ts +++ b/packages/core/src/lightning-node.ts @@ -128,6 +128,29 @@ export class MoneyDevKitNode { ) } + /** + * Receive payments with an immediate callback for each payment received. + * + * This method behaves like `receivePayments` but fires a callback immediately + * when each payment is received, allowing for instant customer confirmation + * while the node continues to run for the full timeout period. + */ + receivePaymentsWithCallback( + onPaymentReceived: (payment: { paymentHash: string; amount: number }) => void + ) { + return this.node.receivePaymentWithCallback( + RECEIVE_PAYMENTS_MIN_THRESHOLD_MS, + RECEIVE_PAYMENTS_QUIET_THRESHOLD_MS, + (err: unknown, payment: { paymentHash: string; amount: number }) => { + if (err) { + console.error('[MoneyDevKitNode] Payment callback error:', err) + return + } + onPaymentReceived(payment) + } + ) + } + payBolt12Offer(bolt12: string, amountMsat: number): string { return this.node.payBolt12Offer(bolt12, amountMsat, 30) }