diff --git a/services.py b/services.py index 4c65e38..1427065 100644 --- a/services.py +++ b/services.py @@ -64,7 +64,8 @@ async def create_new_order( merchant = await get_merchant_by_pubkey(merchant_public_key) assert merchant, "Cannot find merchant for order!" - if await get_order(merchant.id, data.id): + existing_order = await get_order(merchant.id, data.id) + if existing_order: return None if data.event_id and await get_order_by_event_id(merchant.id, data.event_id): return None @@ -74,11 +75,12 @@ async def create_new_order( ) await create_order(merchant.id, order) - return PaymentRequest( + payment_request = PaymentRequest( id=data.id, payment_options=[PaymentOption(type="ln", link=invoice)], message=receipt, ) + return payment_request async def build_order_with_payment( @@ -107,9 +109,10 @@ async def build_order_with_payment( if not success: raise ValueError(message) + total_amount_sat = round(product_cost_sat + shipping_cost_sat) payment = await create_invoice( wallet_id=wallet_id, - amount=round(product_cost_sat + shipping_cost_sat), + amount=total_amount_sat, memo=f"Order '{data.id}' for pubkey '{data.public_key}'", extra={ "tag": "nostrmarket", @@ -314,32 +317,26 @@ async def compute_products_new_quantity( async def process_nostr_message(msg: str): - logger.info(f"[NOSTRMARKET DEBUG] Received nostr message: {msg[:200]}...") try: type_, *rest = json.loads(msg) if type_.upper() == "EVENT": _, event = rest event = NostrEvent(**event) - logger.info(f"[NOSTRMARKET DEBUG] Processing event - kind: {event.kind}, id: {event.id}, pubkey: {event.pubkey}") if event.kind == 0: - logger.info(f"[NOSTRMARKET DEBUG] Handling customer profile update - event: {event.id}") await _handle_customer_profile_update(event) elif event.kind == 4: - logger.info(f"[NOSTRMARKET DEBUG] Handling NIP04 message - event: {event.id}, from: {event.pubkey}, content_length: {len(event.content)}") await _handle_nip04_message(event) elif event.kind == 30017: - logger.info(f"[NOSTRMARKET DEBUG] Handling stall event - event: {event.id}") await _handle_stall(event) elif event.kind == 30018: - logger.info(f"[NOSTRMARKET DEBUG] Handling product event - event: {event.id}") await _handle_product(event) else: - logger.info(f"[NOSTRMARKET DEBUG] Unhandled event kind: {event.kind} - event: {event.id}") + logger.info(f"[NOSTRMARKET] Unhandled event kind: {event.kind} - event: {event.id}") return except Exception as ex: - logger.error(f"[NOSTRMARKET DEBUG] Error processing nostr message: {ex}") + logger.error(f"[NOSTRMARKET] Error processing nostr message: {ex}") async def create_or_update_order_from_dm( @@ -421,53 +418,40 @@ async def extract_customer_order_from_dm( async def _handle_nip04_message(event: NostrEvent): - logger.info(f"[NOSTRMARKET DEBUG] _handle_nip04_message - event: {event.id}, from: {event.pubkey}, tags: {event.tags}") merchant_public_key = event.pubkey merchant = await get_merchant_by_pubkey(merchant_public_key) - logger.info(f"[NOSTRMARKET DEBUG] Looking for merchant by pubkey {merchant_public_key}, found: {merchant is not None}") if not merchant: p_tags = event.tag_values("p") - logger.info(f"[NOSTRMARKET DEBUG] No merchant found for sender, checking p_tags: {p_tags}") if len(p_tags) and p_tags[0]: merchant_public_key = p_tags[0] merchant = await get_merchant_by_pubkey(merchant_public_key) - logger.info(f"[NOSTRMARKET DEBUG] Looking for merchant by p_tag {merchant_public_key}, found: {merchant is not None}") assert merchant, f"Merchant not found for public key '{merchant_public_key}'" if event.pubkey == merchant_public_key: - logger.info(f"[NOSTRMARKET DEBUG] Processing outgoing message from merchant {merchant_public_key}") assert len(event.tag_values("p")) != 0, "Outgong message has no 'p' tag" clear_text_msg = merchant.decrypt_message( event.content, event.tag_values("p")[0] ) - logger.info(f"[NOSTRMARKET DEBUG] Decrypted outgoing message: {clear_text_msg[:100]}...") await _handle_outgoing_dms(event, merchant, clear_text_msg) elif event.has_tag_value("p", merchant_public_key): - logger.info(f"[NOSTRMARKET DEBUG] Processing incoming message to merchant {merchant_public_key} from {event.pubkey}") clear_text_msg = merchant.decrypt_message(event.content, event.pubkey) - logger.info(f"[NOSTRMARKET DEBUG] Decrypted incoming message: {clear_text_msg[:100]}...") await _handle_incoming_dms(event, merchant, clear_text_msg) else: - logger.warning(f"[NOSTRMARKET DEBUG] Bad NIP04 event: '{event.id}' - pubkey: {event.pubkey}, merchant: {merchant_public_key}") + logger.warning(f"[NOSTRMARKET] Bad NIP04 event: '{event.id}' - pubkey: {event.pubkey}, merchant: {merchant_public_key}") async def _handle_incoming_dms( event: NostrEvent, merchant: Merchant, clear_text_msg: str ): - logger.info(f"[NOSTRMARKET DEBUG] _handle_incoming_dms - merchant: {merchant.id}, customer: {event.pubkey}") customer = await get_customer(merchant.id, event.pubkey) if not customer: - logger.info(f"[NOSTRMARKET DEBUG] Creating new customer for pubkey: {event.pubkey}") await _handle_new_customer(event, merchant) else: - logger.info(f"[NOSTRMARKET DEBUG] Existing customer found, incrementing unread messages") await increment_customer_unread_messages(merchant.id, event.pubkey) - logger.info(f"[NOSTRMARKET DEBUG] Parsing message: {clear_text_msg}") dm_type, json_data = PartialDirectMessage.parse_message(clear_text_msg) - logger.info(f"[NOSTRMARKET DEBUG] Parsed message - type: {dm_type}, has_json_data: {json_data is not None}") new_dm = await _persist_dm( merchant.id, dm_type.value, @@ -506,21 +490,17 @@ async def _handle_outgoing_dms( async def _handle_incoming_structured_dm( merchant: Merchant, dm: DirectMessage, json_data: dict ) -> Tuple[DirectMessageType, Optional[str]]: - logger.info(f"[NOSTRMARKET DEBUG] _handle_incoming_structured_dm - merchant: {merchant.id}, dm_type: {dm.type}, merchant_active: {merchant.config.active}") - logger.info(f"[NOSTRMARKET DEBUG] JSON data: {json_data}") try: if dm.type == DirectMessageType.CUSTOMER_ORDER.value and merchant.config.active: - logger.info(f"[NOSTRMARKET DEBUG] Processing CUSTOMER_ORDER for merchant {merchant.id}") json_resp = await _handle_new_order( merchant.id, merchant.public_key, dm, json_data ) - logger.info(f"[NOSTRMARKET DEBUG] New order processed, response: {json_resp[:100] if json_resp else None}...") return DirectMessageType.PAYMENT_REQUEST, json_resp else: - logger.info(f"[NOSTRMARKET DEBUG] Skipping order processing - type: {dm.type}, expected: {DirectMessageType.CUSTOMER_ORDER.value}, merchant_active: {merchant.config.active}") + logger.info(f"[NOSTRMARKET] Skipping order processing - type: {dm.type}, expected: {DirectMessageType.CUSTOMER_ORDER.value}, merchant_active: {merchant.config.active}") except Exception as ex: - logger.error(f"[NOSTRMARKET DEBUG] Error in _handle_incoming_structured_dm: {ex}") + logger.error(f"[NOSTRMARKET] Error in _handle_incoming_structured_dm: {ex}") return DirectMessageType.PLAIN_TEXT, None @@ -602,8 +582,29 @@ async def _handle_new_order( assert wallet, f"Cannot find wallet for product id: {first_product_id}" payment_req = await create_new_order(merchant_public_key, partial_order) + + if payment_req is None: + # Return existing order data instead of creating a failed order + existing_order = await get_order(merchant_id, partial_order.id) + if existing_order and existing_order.invoice_id != "None": + # Order exists with invoice, return existing payment request + duplicate_response = json.dumps({ + "type": DirectMessageType.PAYMENT_REQUEST.value, + "id": existing_order.id, + "message": "Order already received and processed", + "payment_options": [] + }, separators=(",", ":"), ensure_ascii=False) + return duplicate_response + else: + # Order exists but no invoice, skip processing + logger.info(f"[NOSTRMARKET] Order exists but no invoice, returning empty string") + return "" + except Exception as e: - logger.debug(e) + logger.error(f"[NOSTRMARKET] Error creating order: {e}") + logger.error(f"[NOSTRMARKET] Order data: {json_data}") + logger.error(f"[NOSTRMARKET] Exception type: {type(e).__name__}") + logger.error(f"[NOSTRMARKET] Exception details: {str(e)}") payment_req = await create_new_failed_order( merchant_id, merchant_public_key, @@ -611,12 +612,17 @@ async def _handle_new_order( json_data, "Order received, but cannot be processed. Please contact merchant.", ) - assert payment_req + + if not payment_req: + logger.error(f"[NOSTRMARKET] No payment request returned for order: {partial_order.id}") + return "" + response = { "type": DirectMessageType.PAYMENT_REQUEST.value, **payment_req.dict(), } - return json.dumps(response, separators=(",", ":"), ensure_ascii=False) + response_json = json.dumps(response, separators=(",", ":"), ensure_ascii=False) + return response_json async def create_new_failed_order(