@@ -3,6 +3,7 @@ const { add_successful_transactions_to_account } = require('./user_management')
33const { nip19 } = require ( 'nostr-tools' )
44const { v4 : uuidv4 } = require ( 'uuid' )
55const { current_time } = require ( './utils' )
6+ const error = require ( "debug" ) ( "api:error" )
67
78const PURPLE_ONE_MONTH = "purple_one_month"
89const PURPLE_ONE_YEAR = "purple_one_year"
@@ -68,6 +69,9 @@ class PurpleInvoiceManager {
6869 if ( PURGE_OLD_INVOICES ) {
6970 this . purging_interval_timer = setInterval ( ( ) => this . purge_old_invoices ( ) , 10 * 60 * 1000 )
7071 }
72+ // Poll for unpaid invoices periodically to handle cases where the client failed to complete the checkout flow
73+ const polling_interval_ms = parseInt ( process . env . LN_INVOICE_POLLING_INTERVAL_MS ) || 60 * 1000
74+ this . polling_interval_timer = setInterval ( ( ) => this . poll_unpaid_invoices ( ) , polling_interval_ms )
7175 }
7276
7377 // Purge old invoices from the database
@@ -139,17 +143,40 @@ class PurpleInvoiceManager {
139143 // Checks the status of the invoice associated with the given checkout object directly with the LN node, and handles successful payments.
140144 async check_checkout_object_invoice ( checkout_id ) {
141145 const checkout_object = await this . get_checkout_object ( checkout_id )
146+ if ( ! checkout_object ) {
147+ return null
148+ }
149+ if ( checkout_object . completed ) {
150+ return checkout_object // Already completed, nothing to do
151+ }
142152 if ( checkout_object ?. invoice ) {
143153 checkout_object . invoice . paid = await this . check_invoice_is_paid ( checkout_object . invoice . label )
144154 if ( checkout_object . invoice . paid ) {
145- this . handle_successful_payment ( checkout_object )
155+ await this . handle_successful_payment ( checkout_object )
146156 checkout_object . completed = true
147157 await this . checkout_sessions_db . put ( checkout_id , checkout_object ) // Update the checkout object since the state has changed
148158 }
149159 }
150160 return checkout_object
151161 }
152162
163+ // Polls all incomplete checkout sessions to check for successful payments.
164+ // Returns a promise that resolves when all checks are done.
165+ async poll_unpaid_invoices ( ) {
166+ const checks = [ ]
167+ for ( const checkout_id of this . checkout_sessions_db . getKeys ( ) ) {
168+ const checkout_object = this . checkout_sessions_db . get ( checkout_id )
169+ if ( ! checkout_object . completed && checkout_object . invoice ) {
170+ checks . push (
171+ this . check_checkout_object_invoice ( checkout_id ) . catch ( e => {
172+ error ( "Error polling invoice for checkout %s: %s" , checkout_id , e . toString ( ) )
173+ } )
174+ )
175+ }
176+ }
177+ return Promise . all ( checks )
178+ }
179+
153180 // Call this when the user wants to checkout a purple subscription and needs an invoice to pay
154181 async request_invoice ( npub , template_name ) {
155182 if ( ! this . invoice_templates [ template_name ] ) {
@@ -240,6 +267,9 @@ class PurpleInvoiceManager {
240267 if ( this . purging_interval_timer ) {
241268 clearInterval ( this . purging_interval_timer )
242269 }
270+ if ( this . polling_interval_timer ) {
271+ clearInterval ( this . polling_interval_timer )
272+ }
243273 }
244274}
245275
0 commit comments